TDME2  1.9.200
UIEditorTabView.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <memory>
5 #include <string>
6 #include <unordered_set>
7 #include <vector>
8 
9 #include <tdme/tdme.h>
11 #include <tdme/engine/Color4.h>
12 #include <tdme/engine/model/Face.h>
16 #include <tdme/engine/model/Node.h>
20 #include <tdme/engine/Engine.h>
22 #include <tdme/engine/Object.h>
31 #include <tdme/gui/GUI.h>
32 #include <tdme/gui/GUIParser.h>
33 #include <tdme/math/Vector3.h>
46 #include <tdme/utilities/Console.h>
49 
50 using std::make_unique;
51 using std::sort;
52 using std::string;
53 using std::unique_ptr;
54 using std::unordered_set;
55 using std::vector;
56 
58 
80 using tdme::gui::GUI;
96 
97 UIEditorTabView::UIEditorTabView(EditorView* editorView, const string& tabId, GUIScreenNode* screenNode, const string& fileName)
98 {
99  this->editorView = editorView;
100  this->tabId = tabId;
101  this->screenNode = screenNode;
102  this->popUps = editorView->getPopUps();
103  this->screenFileName = fileName;
104  guiEngine = unique_ptr<Engine>(Engine::createOffScreenInstance(1920, 1080, false, false, false));
105  guiEngine->setSceneColor(Color4(39.0f / 255.0f, 39.0f / 255.0f, 39.0f / 255.0f, 1.0f));
107 }
108 
110 }
111 
113  // no op
114 }
115 
117  // no op
118 }
119 
121 {
122  if (visualEditor == false && projectedUi == true) {
123  // mouse wheel events that happened to route to GUI engine
124  vector<int> checkedGUIEngineMouseEventIndices;
125  vector<int> checkedEngineMouseEventIndices;
126  vector<int> unusedEngineMouseEventIndices;
127  //
128  auto modelEntity = dynamic_cast<Object*>(engine->getEntity("model"));
129  if (modelEntity != nullptr && modelMeshNode.empty() == false && modelEntity->getModel()->getNodeById(modelMeshNode) != nullptr) {
130  auto modelEntityWorldMatrix = modelEntity->getNodeTransformMatrix(modelMeshNode);
131  auto modelEntityModelImportMatrixInverted = modelEntity->getModel()->getImportTransformMatrix().clone().invert();
132  auto modelEntityWorldMatrixInverted = modelEntityWorldMatrix.clone().multiply(modelEntity->getTransformMatrix()).multiply(modelEntityModelImportMatrixInverted).invert();
133  // handle mouse events
134  const auto& engineMouseEvents = engine->getGUI()->getMouseEvents();
135  auto& guiEngineMouseEvents = guiEngine->getGUI()->getMouseEvents();
136  auto mouseEventIdx = 0;
137  for (const auto& event: engine->getGUI()->getMouseEvents()) {
138  if (event.isProcessed() == true) {
139  mouseEventIdx++;
140  continue;
141  }
142  // try to push event to gui engine if in book space
143  Vector3 mouseWorldCoordinate = engine->computeWorldCoordinateByMousePosition(event.getXUnscaled(), event.getYUnscaled());
144  auto bookLocalCoordinate = modelEntityWorldMatrixInverted.multiply(mouseWorldCoordinate);
145  auto clonedEvent = event;
146  clonedEvent.setX((bookLocalCoordinate.getX() - projectedUiMinX) * (guiEngine->getWidth() / (projectedUiMaxX - projectedUiMinX)));
147  clonedEvent.setY((bookLocalCoordinate.getZ() - projectedUiMinZ) * (guiEngine->getHeight() / (projectedUiMaxZ - projectedUiMinZ)));
148  clonedEvent.setXUnscaled(clonedEvent.getX());
149  clonedEvent.setYUnscaled(clonedEvent.getY());
150  if (clonedEvent.getX() >= 0 && clonedEvent.getX() < guiEngine->getWidth() &&
151  clonedEvent.getY() >= 0 && clonedEvent.getY() < guiEngine->getHeight()) {
152  checkedGUIEngineMouseEventIndices.push_back(guiEngineMouseEvents.size());
153  guiEngineMouseEvents.push_back(clonedEvent);
154  // ok we add this mouse event to our checked mouse event indices list
155  checkedEngineMouseEventIndices.push_back(mouseEventIdx);
156  } else {
157  // ok add this to unused mouse event indices list
158  unusedEngineMouseEventIndices.push_back(mouseEventIdx);
159  }
160  mouseEventIdx++;
161  }
162  } else {
163  // just add all events into unused mouse event indices
164  const auto& engineMouseEvents = engine->getGUI()->getMouseEvents();
165  for (auto i = 0; i < engineMouseEvents.size(); i++) unusedEngineMouseEventIndices.push_back(i);
166  }
167  // handle GUI engine events
168  guiEngine->getGUI()->handleEvents(false);
169  // clear mouse events of main engine
170  auto engineMouseEvents = engine->getGUI()->getMouseEvents();
171  const auto& guiEngineMouseEvents = guiEngine->getGUI()->getMouseEvents();
172  engine->getGUI()->getMouseEvents().clear();
173  // TODO: we might want to sort the events by creation time or id
174  // restore mouse events of main engine from GUI engine events
175  for (auto i = 0; i < checkedEngineMouseEventIndices.size(); i++) {
176  if (guiEngineMouseEvents[checkedGUIEngineMouseEventIndices[i]].isProcessed() == false) engine->getGUI()->getMouseEvents().push_back(engineMouseEvents[checkedEngineMouseEventIndices[i]]);
177  }
178  for (auto i = 0; i < unusedEngineMouseEventIndices.size(); i++) {
179  engine->getGUI()->getMouseEvents().push_back(engineMouseEvents[unusedEngineMouseEventIndices[i]]);
180  }
181  // clear GUI engine events, as we did not do this before after handing events
182  guiEngine->getGUI()->getKeyboardEvents().clear();
183  guiEngine->getGUI()->getMouseEvents().clear();
184  // camera rotation input handler, which uses main engine events
185  cameraRotationInputHandler->handleInputEvents();
186  // clear main engine events
187  engine->getGUI()->getMouseEvents().clear();
188  engine->getGUI()->getKeyboardEvents().clear();
189  } else {
190  // just handle events from GUI engine
191  guiEngine->getGUI()->handleEvents();
192  }
193 }
194 
196 {
197 
198  guiEngine->display();
199  guiEngine->getGUI()->render();
200  if (projectedUi == true) engine->display();
201 }
202 
204 {
205  try {
206  uiTabController = make_unique<UIEditorTabController>(this);
208  } catch (Exception& exception) {
209  Console::println("UIEditorTabView::initialize(): An error occurred: " + string(exception.what()));
210  }
211  // TODO: load settings
212 
213  //
214  textNode = required_dynamic_cast<GUIStyledTextNode*>(screenNode->getInnerNodeById(tabId + "_tab_text"));
215 
216  // initial text format
217  TextFormatter::getInstance()->format("xml", textNode);
218  // load code completion
219  codeCompletion = TextFormatter::getInstance()->loadCodeCompletion("xml");
220 
221  //
222  {
223  // add text node change listener
224  class TextChangeListener: public GUIStyledTextNodeController::ChangeListener {
225  public:
226  TextChangeListener(UIEditorTabView* uiEditorTabView): uiEditorTabView(uiEditorTabView) {
227  }
228 
229  virtual ~TextChangeListener() {
230  }
231 
232  virtual void onRemoveText(int idx, int count) override {
233  if (uiEditorTabView->countEnabled == true) {
234  TextFormatter::getInstance()->format("xml", uiEditorTabView->textNode, 0, uiEditorTabView->textNode->getText().size());
235  uiEditorTabView->countEnabled = false;
236  } else {
237  TextFormatter::getInstance()->format("xml", uiEditorTabView->textNode, idx, idx + count);
238  }
239  }
240  virtual void onInsertText(int idx, int count) override {
241  if (uiEditorTabView->countEnabled == true) {
242  TextFormatter::getInstance()->format("xml", uiEditorTabView->textNode, 0, uiEditorTabView->textNode->getText().size());
243  uiEditorTabView->countEnabled = false;
244  } else {
245  TextFormatter::getInstance()->format("xml", uiEditorTabView->textNode, idx, idx + count);
246  }
247  }
248  private:
249  UIEditorTabView* uiEditorTabView;
250  };
251  //
252  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->addChangeListener((textNodeChangeListener = make_unique<TextChangeListener>(this)).get());
253  }
254 
255  //
256  {
257  // add code completion listener
258  class TextCodeCompletionListener: public GUIStyledTextNodeController::CodeCompletionListener {
259  public:
260  TextCodeCompletionListener(UIEditorTabView* uiEditorTabView): uiEditorTabView(uiEditorTabView) {
261  }
262 
263  virtual ~TextCodeCompletionListener() {
264  }
265 
266  virtual void onCodeCompletion(int idx) override {
267  auto codeCompletion = uiEditorTabView->codeCompletion;
268  if (codeCompletion == nullptr) return;
269  if (codeCompletion->delimiters.find(uiEditorTabView->textNode->getText().getCharAt(idx)) != string::npos) {
270  if (idx > 0) idx--;
271  }
272  auto previousDelimiterPos = uiEditorTabView->textNode->getPreviousDelimiter(idx, codeCompletion->delimiters);
273  string search = StringTools::substring(uiEditorTabView->textNode->getText().getString(), previousDelimiterPos == 0?0:previousDelimiterPos + 1, idx);
274  vector<CodeCompletionSymbol> codeCompletionSymbolCandidates;
275  #define MAX_ENTRIES 40
276  for (const auto& symbol: codeCompletion->symbols) {
277  if (StringTools::startsWith(symbol.name, search) == true) {
278  if (symbol.overloadList.empty() == true) {
279  if (codeCompletionSymbolCandidates.size() == MAX_ENTRIES) {
280  codeCompletionSymbolCandidates.push_back(
281  {
283  .display = "...",
284  .name = {},
285  .parameters = {},
286  .returnValue = {}
287  }
288  );
289  break;
290  } else {
291  codeCompletionSymbolCandidates.push_back(
292  {
294  .display = symbol.name,
295  .name = symbol.name,
296  .parameters = {},
297  .returnValue = {}
298  }
299  );
300  }
301  } else {
302  for (const auto& overload: symbol.overloadList) {
303  if (codeCompletionSymbolCandidates.size() == MAX_ENTRIES) {
304  codeCompletionSymbolCandidates.push_back(
305  {
307  .display = "...",
308  .name = {},
309  .parameters = {},
310  .returnValue = {}
311  }
312  );
313  break;
314  } else {
315  string parameters;
316  for (const auto& parameter: overload.parameters) {
317  if (parameters.empty() == false) parameters+= ", ";
318  parameters+= parameter;
319  }
320  codeCompletionSymbolCandidates.push_back(
321  {
323  .display = symbol.name + "(" + parameters + ") = " + overload.returnValue,
324  .name = symbol.name,
325  .parameters = overload.parameters,
326  .returnValue = overload.returnValue
327  }
328  );
329  }
330  }
331  if (codeCompletionSymbolCandidates.size() == MAX_ENTRIES + 1) break;
332  }
333  }
334  }
335  auto popUps = uiEditorTabView->getPopUps();
336  // clear
338  //
339  sort(
340  codeCompletionSymbolCandidates.begin(),
341  codeCompletionSymbolCandidates.begin() + (Math::min(codeCompletionSymbolCandidates.size(), MAX_ENTRIES)),
342  [](const CodeCompletionSymbol& lhs, const CodeCompletionSymbol& rhs) {
343  return lhs.display < rhs.display;
344  }
345  );
346  //
347  {
348  auto i = 0;
349  for (const auto& codeCompletionSymbolCandidate: codeCompletionSymbolCandidates) {
350  // add light
351  class OnCodeCompletionAction: public virtual Action
352  {
353  public:
354  OnCodeCompletionAction(UIEditorTabView* uiEditorTabView, int idx, const CodeCompletionSymbol& symbol): uiEditorTabView(uiEditorTabView), idx(idx), symbol(symbol) {}
355  void performAction() override {
356  if (symbol.name.empty() == true) return;
357  auto codeCompletion = uiEditorTabView->codeCompletion;
358  if (codeCompletion == nullptr) return;
359  auto previousDelimiterPos = uiEditorTabView->textNode->getPreviousDelimiter(idx, codeCompletion->delimiters);
360  auto nextDelimiterPos = uiEditorTabView->textNode->getNextDelimiter(idx, codeCompletion->delimiters);
361  auto withoutWhiteSpaceDelimiters = codeCompletion->delimiters;
362  if (withoutWhiteSpaceDelimiters.find(' ') != string::npos) withoutWhiteSpaceDelimiters.erase(withoutWhiteSpaceDelimiters.find(' '), 1);
363  if (withoutWhiteSpaceDelimiters.find('\t') != string::npos) withoutWhiteSpaceDelimiters.erase(withoutWhiteSpaceDelimiters.find('\t'), 1);
364  if (withoutWhiteSpaceDelimiters.find('\n') != string::npos) withoutWhiteSpaceDelimiters.erase(withoutWhiteSpaceDelimiters.find('\n'), 1);
365  auto nextDelimiterPos2 = uiEditorTabView->textNode->getNextDelimiter(idx, withoutWhiteSpaceDelimiters);
366  auto idxToDelimiterString = StringTools::trim(StringTools::substring(uiEditorTabView->textNode->getText().getString(), idx + 1 < uiEditorTabView->textNode->getTextLength()?idx + 1:idx, nextDelimiterPos2));
367  string parameterString;
368  if (symbol.type == CodeCompletionSymbol::TYPE_FUNCTION && uiEditorTabView->textNode->getText().getCharAt(nextDelimiterPos2) != '(') {
369  for (const auto& parameter: symbol.parameters) {
370  auto parameterTokenized = StringTools::tokenize(parameter, " \t\n");
371  if (parameterString.empty() == false) parameterString+= ", ";
372  parameterString+= parameterTokenized[parameterTokenized.size() - 1];
373  }
374  parameterString = "(" + parameterString + ")"/* + codeCompletion->statementDelimiter*/;
375  }
376  uiEditorTabView->textNode->removeText(previousDelimiterPos == 0?0:previousDelimiterPos + 1, nextDelimiterPos - (previousDelimiterPos == 0?0:previousDelimiterPos + 1));
377  uiEditorTabView->textNode->insertText(previousDelimiterPos == 0?0:previousDelimiterPos + 1, symbol.name + parameterString);
378  TextFormatter::getInstance()->format("xml", uiEditorTabView->textNode, previousDelimiterPos == 0?0:previousDelimiterPos + 1, (previousDelimiterPos == 0?0:previousDelimiterPos + 1) + symbol.name.size() + parameterString.size());
379  }
380  private:
381  UIEditorTabView* uiEditorTabView;
382  int idx;
383  CodeCompletionSymbol symbol;
384  };
385  popUps->getContextMenuScreenController()->addMenuItem(codeCompletionSymbolCandidate.display, "contextmenu_codecompletion_" + to_string(i), new OnCodeCompletionAction(uiEditorTabView, idx, codeCompletionSymbolCandidate));
386  //
387  i++;
388  }
389  }
390  if (codeCompletionSymbolCandidates.empty() == false) {
391  //
392  int left, top, width, height, offsetX, offsetY;
393  auto selectedTab = uiEditorTabView->getEditorView()->getScreenController()->getSelectedTab();
394  if (selectedTab != nullptr) {
395  uiEditorTabView->getEditorView()->getViewPort(selectedTab->getFrameBufferNode(), left, top, width, height, offsetX, offsetY);
397  left + uiEditorTabView->textNode->getIndexPositionX() - uiEditorTabView->textNode->computeParentChildrenRenderOffsetXTotal(),
398  top + uiEditorTabView->textNode->getIndexPositionY() - uiEditorTabView->textNode->computeParentChildrenRenderOffsetYTotal()
399  );
400  }
401  }
402  }
403  private:
404  UIEditorTabView* uiEditorTabView;
405  };
406  //
407  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->addCodeCompletionListener((textNodeCodeCompletionListener = make_unique<TextCodeCompletionListener>(this)).get());
408  }
409  //
410  addScreen();
412  //
414  //
415  setVisualEditor();
416 }
417 
419 {
420  uiTabController->closeFindReplaceWindow();
421  guiEngine->dispose();
422  if (engine != nullptr) engine->dispose();
423 }
424 
426 }
427 
429  return projectedUi == false && visualEditor == true;
430 }
431 
433  if (visualEditor == false) return nullptr;
434  return projectedUi == true?engine.get():guiEngine.get();
435 }
436 
438  uiTabController->setOutlinerAddDropDownContent();
439  uiTabController->setOutlinerContent();
442 }
443 
446  uiTabController->closeFindReplaceWindow();
447 }
448 
450  uiTabController->setOutlinerContent();
452 }
453 
455  uiScreenNodes.push_back(
456  {
457  .fileName = string(),
458  .xml = string(),
459  .screenNode = nullptr,
460  .width = -1,
461  .height = -1
462  }
463  );
464 }
465 
466 void UIEditorTabView::setScreen(int screenIdx, const string& fileName) {
467  //
468  if (screenIdx < 0 || screenIdx >= uiScreenNodes.size()) return;
469  //
470  this->screenIdx = screenIdx;
471  //
472  string xml;
473  try {
474  // parse XML
475  xml = FileSystem::getInstance()->getContentAsString(
476  Tools::getPathName(fileName),
477  Tools::getFileName(fileName)
478  );
479  } catch (Exception& exception) {
480  Console::println("UIEditorTabView::setScreen(): an error occurred: " + screenNode->getFileName() + ": " + string(exception.what()));
481  }
482  //
483  uiScreenNodes[screenIdx].fileName = fileName;
484  uiScreenNodes[screenIdx].xml = xml;
485  uiScreenNodes[screenIdx].screenNode = nullptr;
486  uiScreenNodes[screenIdx].width = -1;
487  uiScreenNodes[screenIdx].height = -1;
488  //
489  if (visualEditor == true) reAddScreens();
490  //
492 }
493 
494 void UIEditorTabView::unsetScreen(int screenIdx) {
495  if (screenIdx < 0 || screenIdx >= uiScreenNodes.size()) return;
496  if (uiScreenNodes[screenIdx].screenNode != nullptr) {
497  uiScreenNodes[screenIdx].screenNode->removeTooltipRequestListener(uiTabController.get());
498  guiEngine->getGUI()->removeScreen(uiScreenNodes[screenIdx].screenNode->getId());
499  }
500  uiScreenNodes[screenIdx].fileName.clear();
501  uiScreenNodes[screenIdx].xml.clear();
502  uiScreenNodes[screenIdx].screenNode = nullptr;
503  uiScreenNodes[screenIdx].width = -1;
504  uiScreenNodes[screenIdx].height = -1;
505 }
506 
507 void UIEditorTabView::removeScreen(int screenIdx) {
508  if (screenIdx < 0 || screenIdx >= uiScreenNodes.size()) return;
509  if (uiScreenNodes[screenIdx].screenNode != nullptr) {
510  uiScreenNodes[screenIdx].screenNode->removeTooltipRequestListener(uiTabController.get());
511  }
512  guiEngine->getGUI()->removeScreen(uiScreenNodes[screenIdx].screenNode->getId());
513  uiScreenNodes.erase(uiScreenNodes.begin() + screenIdx);
514 }
515 
517  guiEngine->getGUI()->resetRenderScreens();
518 }
519 
521  guiEngine->getGUI()->reset();
522  auto screensMaxWidth = -1;
523  auto screensMaxHeight = -1;
524  for (auto i = 0; i < uiScreenNodes.size(); i++) {
525  //
526  uiScreenNodes[i].screenNode = nullptr;
527 
528  //
529  if (uiScreenNodes[i].xml.empty() == true) continue;
530 
531  // fetch root node
532  GUIScreenNode* screenNode = nullptr;
533  string xmlRootNode;
534  try {
535  xmlRootNode = GUIParser::getRootNode(uiScreenNodes[i].xml);
536  } catch (Exception& exception) {
537  Console::println("UIEditorTabView::reAddScreens(): an error occurred: " + string(exception.what()));
538  // error handling
539  try {
540  screenNode = GUIParser::parse(
541  "resources/engine/gui/",
542  "screen_text.xml",
543  {{ "text", StringTools::replace(StringTools::replace("An error occurred: " + string(exception.what()), "[", "\\["), "]", "\\]") }}
544  );
545  } catch (Exception& exception2) {
546  Console::println("UIEditorTabView::reAddScreens(): an error occurred: " + string(exception2.what()));
547  }
548  }
549  if (xmlRootNode == "screen") {
550  //
551  try {
552  screenNode = GUIParser::parse(
553  uiScreenNodes[i].xml,
554  {},
555  Tools::getPathName(uiScreenNodes[i].fileName),
556  Tools::getFileName(uiScreenNodes[i].fileName)
557  );
558  } catch (Exception& exception) {
559  Console::println("UIEditorTabView::reAddScreens(): an error occurred: " + string(exception.what()));
560  // error handling
561  try {
562  screenNode = GUIParser::parse(
563  "resources/engine/gui/",
564  "screen_text.xml",
565  {{ "text", StringTools::replace(StringTools::replace("An error occurred: " + string(exception.what()), "[", "\\["), "]", "\\]") }}
566  );
567  } catch (Exception& exception2) {
568  Console::println("UIEditorTabView::reAddScreens(): an error occurred: " + string(exception2.what()));
569  }
570  }
571  } else
572  if (xmlRootNode == "template") {
573  /*
574  <!-- You can now specify default preview attributes within templates -->
575  <defaults>
576  <attribute name="preview-id" value="sound-preview" />
577  <attribute name="preview-container-width" value="300" />
578  <attribute name="preview-container-height" value="400" />
579  </defaults>
580  */
581  unordered_map<string, string> templateAttributes;
582  //
583  try {
584  templateAttributes = GUIParser::parseTemplateAttributes(uiScreenNodes[i].xml);
585  } catch (Exception& exception) {
586  Console::println("UIEditorTabView::reAddScreens(): an error occurred: " + string(exception.what()));
587  }
588  //
589  if (templateAttributes.find("preview-id") == templateAttributes.end()) templateAttributes["preview-id"] = "preview-id";
590  if (templateAttributes.find("preview-container-width") == templateAttributes.end()) templateAttributes["preview-container-width"] = "100%";
591  if (templateAttributes.find("preview-container-height") == templateAttributes.end()) templateAttributes["preview-container-height"] = "100%";
592  //
593  try {
594  screenNode = GUIParser::parse(
595  string() +
596  "<screen id='screen_template'>\n" +
597  " <layout width='{$preview-container-width}' height='{$preview-container-height}' alignment='none' horizontal-align='center' vertical-align='center'>\n" +
598  GUIParser::getInnerXml(StringTools::replace(uiScreenNodes[i].xml, "{$id}", "{$preview-id}")) +
599  " </layout>'>\n" +
600  "</screen>>\n",
601  templateAttributes,
602  Tools::getPathName(uiScreenNodes[i].fileName),
603  Tools::getFileName(uiScreenNodes[i].fileName)
604  );
605  } catch (Exception& exception) {
606  Console::println("UIEditorTabView::reAddScreens(): an error occurred: " + string(exception.what()));
607  // error handling
608  try {
609  screenNode = GUIParser::parse(
610  "resources/engine/gui/",
611  "screen_text.xml",
612  {{ "text", StringTools::replace(StringTools::replace("An error occurred: " + string(exception.what()), "[", "\\["), "]", "\\]") }}
613  );
614  } catch (Exception& exception2) {
615  Console::println("UIEditorTabView::reAddScreens(): an error occurred: " + string(exception2.what()));
616  }
617  }
618  }
619 
620  //
621  uiScreenNodes[i].screenNode = screenNode;
622  if (screenNode == nullptr) continue;
623 
624  //
625  uiScreenNodes[i].width = screenNode == nullptr?-1:screenNode->getSizeConstraints().maxWidth;
626  uiScreenNodes[i].height = screenNode == nullptr?-1:screenNode->getSizeConstraints().maxHeight;
627  if (uiScreenNodes[i].width > screensMaxWidth) screensMaxWidth = uiScreenNodes[i].width;
628  if (uiScreenNodes[i].height > screensMaxHeight) screensMaxHeight = uiScreenNodes[i].height;
629 
630  //
635 
636  //
638  guiEngine->getGUI()->addScreen(screenNode->getId(), screenNode);
639  guiEngine->getGUI()->addRenderScreen(screenNode->getId());
640  }
641  if (screensMaxWidth == -1) screensMaxWidth = 1920;
642  if (screensMaxHeight == -1) screensMaxHeight = 1080;
643  if (guiEngine->getWidth() != screensMaxWidth || guiEngine->getHeight() != screensMaxHeight) {
644  guiEngine->reshape(screensMaxWidth, screensMaxHeight);
645  }
646 }
647 
649  return prototype.get();
650 }
651 
652 Prototype* UIEditorTabView::loadPrototype(const string& pathName, const string& fileName, const string& modelMeshNode, const string& modelMeshAnimation) {
653  //
654  if (projectedUi == true) engine->reset();
655  prototype = nullptr;
656 
657  //
658  try {
659  prototype = unique_ptr<Prototype>(PrototypeReader::read(pathName, fileName));
660  } catch (Exception& exception) {
661  Console::println("UIEditorTabView::loadPrototype(): An error occurred: " + string(exception.what()));
662  //
663  return nullptr;
664  }
665 
666  //
667  auto projectedUiLast = projectedUi;
668  if (projectedUi == false) {
669  engine = unique_ptr<Engine>(Engine::createOffScreenInstance(512, 512, true, true, false));
670  engine->setPartition(new SimplePartition());
671  engine->setShadowMapLightEyeDistanceScale(0.1f);
672  engine->setSceneColor(Color4(39.0f / 255.0f, 39.0f / 255.0f, 39.0f / 255.0f, 1.0f));
673  guiEngine->setSceneColor(Color4(0.0f, 0.0f, 0.0f, 0.0f));
674  cameraRotationInputHandler = make_unique<CameraRotationInputHandler>(engine.get(), this);
675  projectedUi = true;
676  }
677  Vector3 objectScale;
678  Tools::setupPrototype(prototype.get(), engine.get(), cameraRotationInputHandler->getLookFromRotations(), 1, objectScale, cameraRotationInputHandler.get(), 1.5f, projectedUiLast == true);
679 
680  // scale model, ground * 2
681  auto modelEntity = engine->getEntity("model");
682  if (modelEntity != nullptr) {
683  modelEntity->setScale(modelEntity->getScale() * 2.0f);
684  modelEntity->update();
685  static_cast<Object*>(modelEntity)->bindDiffuseTexture(guiEngine->getFrameBuffer(), modelMeshNode);
686  static_cast<Object*>(modelEntity)->setAnimation(modelMeshAnimation);
687  }
688  auto groundEntity = engine->getEntity("ground");
689  if (groundEntity != nullptr) {
690  groundEntity->setScale(groundEntity->getScale() * 2.0f);
691  groundEntity->update();
692  }
693 
694  //
696 
697  //
698  return prototype.get();
699 }
700 
701 void UIEditorTabView::setModelMeshNode(const string& modelMeshNode) {
702  if (projectedUi == false) return;
703  //
704  auto modelEntity = dynamic_cast<Object*>(engine->getEntity("model"));
705  if (modelEntity != nullptr) {
706  modelEntity->unbindDiffuseTexture();
707  if (modelMeshNode.empty() == false) modelEntity->bindDiffuseTexture(guiEngine->getFrameBuffer(), modelMeshNode);
708  }
709 
710  this->modelMeshNode = modelMeshNode;
711  projectedUiMinX = Float::MAX_VALUE;
712  projectedUiMinZ = Float::MAX_VALUE;
713  projectedUiMaxX = Float::MIN_VALUE;
714  projectedUiMaxZ = Float::MIN_VALUE;
715 
716  auto model = prototype->getModel();
717  if (model == nullptr || model->getNodeById(modelMeshNode) == nullptr) return;
718 
719  //
720  const auto& modelMeshNodeFacesEntities = model->getNodeById(modelMeshNode)->getFacesEntities();
721  unordered_set<string> materialIds;
722  for (const auto& facesEntity: modelMeshNodeFacesEntities) {
723  if (facesEntity.getMaterial() != nullptr) materialIds.insert(facesEntity.getMaterial()->getId());
724  for (const auto& face: facesEntity.getFaces()) {
725  for (auto i = 0; i < 3; i++) {
726  projectedUiMinZ = Math::min(projectedUiMinZ, face.getNode()->getVertices()[face.getVertexIndices()[i]].getZ());
727  projectedUiMinX = Math::min(projectedUiMinX, face.getNode()->getVertices()[face.getVertexIndices()[i]].getX());
728  projectedUiMaxZ = Math::max(projectedUiMaxZ, face.getNode()->getVertices()[face.getVertexIndices()[i]].getZ());
729  projectedUiMaxX = Math::max(projectedUiMaxX, face.getNode()->getVertices()[face.getVertexIndices()[i]].getX());
730  }
731  }
732  }
733 
734  //
735  for (const auto& materialId: materialIds) {
736  auto materialIt = model->getMaterials().find(materialId);
737  auto material = materialIt != model->getMaterials().end()?materialIt->second:nullptr;
738  if (material == nullptr) continue;
739  material->getSpecularMaterialProperties()->setDiffuseColor(Color4(0.8f, 0.8f, 0.8f, 0.9999f));
740  if (material->getPBRMaterialProperties() != nullptr) material->getPBRMaterialProperties()->setBaseColorFactor(Color4(1.0f, 1.0f, 1.0f, 0.9999f));
741  }
742 }
743 
744 void UIEditorTabView::setModelMeshAnimation(const string& modelMeshAnimation) {
745  if (projectedUi == false) return;
746  //
747  auto modelEntity = dynamic_cast<Object*>(engine->getEntity("model"));
748  if (modelEntity != nullptr) {
749  modelEntity->setAnimation(modelMeshAnimation);
750  }
751 }
752 
754  //
755  if (projectedUi == true) {
756  engine->dispose();
757  engine = nullptr;
758  cameraRotationInputHandler = nullptr;
759  projectedUi = false;
760  }
761  prototype = nullptr;
762 
763  //
764  guiEngine->setSceneColor(Color4(39.0f / 255.0f, 39.0f / 255.0f, 39.0f / 255.0f, 1.0f));
765 }
766 
767 void UIEditorTabView::setScreenIdx(int screenIdx) {
768  //
769  storeUIXML();
770  //
771  this->screenIdx = screenIdx;
772  //
774 }
775 
777  if (screenIdx < 0 || screenIdx >= uiScreenNodes.size()) return;
779 }
780 
782  if (visualEditor == true) return;
783  //
784  uiTabController->closeFindReplaceWindow();
785  //
786  visualEditor = true;
787  //
788  auto editorNode = dynamic_cast<GUIElementNode*>(screenNode->getNodeById(tabId + "_tab_editor"));
789  if (editorNode != nullptr) editorNode->getActiveConditions().set("visualization");
790  //
791  reAddScreens();
792 }
793 
795  if (visualEditor == false) return;
796  visualEditor = false;
797  //
798  removeScreens();
799  //
800  auto editorNode = dynamic_cast<GUIElementNode*>(screenNode->getNodeById(tabId + "_tab_editor"));
801  if (editorNode != nullptr) editorNode->getActiveConditions().set("text");
802  //
804 }
805 
807  //
808  if (screenIdx < 0 || screenIdx >= uiScreenNodes.size()) return;
809  //
810  textNode->setText(MutableString(StringTools::replace(StringTools::replace(uiScreenNodes[screenIdx].xml, "[", "\\["), "]", "\\]")));
811  // initial text format
812  TextFormatter::getInstance()->format("xml", textNode);
813 }
814 
816  auto textNodeController = required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController());
817  return textNodeController->getIndex();
818 }
819 
820 bool UIEditorTabView::find(const string& findString, bool matchCase, bool wholeWord, bool selection, bool firstSearch, int& index) {
821  cancelFind();
822  return TextTools::find(textNode, findString, matchCase, wholeWord, selection, firstSearch, index);
823 }
824 
825 int UIEditorTabView::count(const string& findString, bool matchCase, bool wholeWord, bool selection) {
826  cancelFind();
827  countEnabled = true;
828  return TextTools::count(textNode, findString, matchCase, wholeWord, selection);
829 }
830 
831 bool UIEditorTabView::replace(const string& findString, const string& replaceString, bool matchCase, bool wholeWord, bool selection, int& index) {
832  cancelFind();
833  auto success = TextTools::replace(textNode, findString, replaceString, matchCase, wholeWord, selection, index);
834  TextFormatter::getInstance()->format("xml", textNode, 0, textNode->getText().size());
835  return success;
836 }
837 
838 bool UIEditorTabView::replaceAll(const string& findString, const string& replaceString, bool matchCase, bool wholeWord, bool selection) {
839  auto success = TextTools::replaceAll(textNode, findString, replaceString, matchCase, wholeWord, selection);
840  cancelFind();
841  return success;
842 }
843 
844 
846  TextFormatter::getInstance()->format("xml", textNode, 0, textNode->getText().size());
847  countEnabled = false;
848 }
849 
851  if (visualEditor == false) {
852  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->redo();
853  }
854 }
855 
857  if (visualEditor == false) {
858  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->undo();
859  }
860 }
861 
863  if (visualEditor == false) {
864  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->selectAll();
865  }
866 }
867 
869  if (visualEditor == false) {
870  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->cut();
871  }
872 }
873 
875  if (visualEditor == false) {
876  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->copy();
877  }
878 }
879 
881  if (visualEditor == false) {
882  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->paste();
883  }
884 }
885 
887  if (visualEditor == false) {
888  required_dynamic_cast<GUIStyledTextNodeController*>(textNode->getController())->delete_();
889  }
890 }
#define MAX_ENTRIES
Color 4 definition class.
Definition: Color4.h:18
Engine main class.
Definition: Engine.h:131
Frame buffer class.
Definition: FrameBuffer.h:22
Object to be used with engine class.
Definition: Object.h:60
Bogus/Simple partition implementation.
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Definition: Face.h:18
Node faces entity A node can have multiple entities containing faces and a applied material.
Definition: FacesEntity.h:23
Represents a material.
Definition: Material.h:23
Representation of a 3D model.
Definition: Model.h:35
Model node.
Definition: Node.h:32
Represents specular material properties.
Represents specular material properties.
Prototype definition.
Definition: Prototype.h:55
void setAnimation(const string &id, float speed=1.0f)
Sets up a base animation to play.
Definition: ObjectBase.h:189
const Matrix4x4 getNodeTransformMatrix(const string &id)
Returns transform matrix for given node.
Definition: ObjectBase.h:269
void unbindDiffuseTexture(const string &nodeId=string(), const string &facesEntityId=string())
Unbind dynamic texture to a node and faces entity of this object.
GUI parser.
Definition: GUIParser.h:40
GUI module class.
Definition: GUI.h:64
GUINodeConditions & getActiveConditions()
void set(const string &condition)
Set condition.
float computeParentChildrenRenderOffsetXTotal()
Definition: GUINode.cpp:952
GUINodeController * getController()
Definition: GUINode.h:661
float computeParentChildrenRenderOffsetYTotal()
Definition: GUINode.cpp:962
const string & getId()
Definition: GUINode.h:339
GUI screen node that represents a screen that can be rendered via GUI system.
Definition: GUIScreenNode.h:72
void addTooltipRequestListener(GUITooltipRequestListener *listener)
Add tooltip request listener.
GUIScreenNode_SizeConstraints & getSizeConstraints()
GUINode * getInnerNodeById(const string &nodeId)
Get inner GUI node by id.
GUINode * getNodeById(const string &nodeId)
Get GUI node by id.
int getNextDelimiter(int index, const string &delimiters)
Get next delimiter.
void insertText(int32_t idx, int c)
Insert character c at idx.
void setText(const MutableString &text)
Set text.
int getPreviousDelimiter(int index, const string &delimiters)
Get previous delimiter.
void removeText(int32_t idx, int32_t count)
Remove characters at idx with given length.
const MutableString & getText() const
Matrix4x4 clone() const
Clones this matrix.
Definition: Matrix4x4.h:619
Vector3 multiply(const Vector3 &vector3) const
Multiplies this matrix with vector3.
Definition: Matrix4x4.h:225
Matrix4x4 & invert()
Inverts this matrix.
Definition: Matrix4x4.h:479
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
File system singleton class.
Definition: FileSystem.h:17
void show(int mouseX, int mouseY)
Shows the pop up.
void addMenuItem(const string &text, const string &id, Action *action=nullptr)
Add menu item.
void restoreOutlinerState(const TabView::OutlinerState &outlinerState)
Restore outliner state.
void storeOutlinerState(TabView::OutlinerState &outlinerState)
Store outliner state.
void setDetailsContent(const string &xml)
Set details content.
ContextMenuScreenController * getContextMenuScreenController()
Definition: PopUps.h:96
const TextFormatter::CodeCompletion * codeCompletion
void dispose() override
Disposes the view.
bool hasFixedSize() override
If this viewport framebuffer has a fixed size.
void display() override
Renders the view.
void reloadOutliner() override
Reload outliner.
unique_ptr< GUIStyledTextNodeController::CodeCompletionListener > textNodeCodeCompletionListener
void onCameraRotation() override
On rotation event to be overloaded.
bool replace(const string &findString, const string &replaceString, bool matchCase, bool wholeWord, bool selection, int &index)
Replace string.
Prototype * loadPrototype(const string &pathName, const string &fileName, const string &modelMeshNode, const string &modelMeshAnimation)
Load prototype.
void initialize() override
Initiates the view.
void setScreen(int screenIdx, const string &fileName)
Set screen.
void handleInputEvents() override
Handle input events that have not yet been processed.
void setModelMeshAnimation(const string &modelMeshAnimation)
Set model mesh animation.
void removeScreen(int screenIdx)
Remove screen.
unique_ptr< UIEditorTabController > uiTabController
unique_ptr< CameraRotationInputHandler > cameraRotationInputHandler
int count(const string &findString, bool matchCase, bool wholeWord, bool selection)
Count string.
unique_ptr< GUIStyledTextNodeController::ChangeListener > textNodeChangeListener
bool find(const string &findString, bool matchCase, bool wholeWord, bool selection, bool firstSearch, int &index)
Find string.
void unsetScreen(int screenIdx)
Unset screen.
void updateRendering() override
Update rendering.
void setModelMeshNode(const string &modelMeshNode)
Set model mesh node.
void setScreenIdx(int screenIdx)
Set screen index.
void onCameraScale() override
On scale event to be overloaded.
bool replaceAll(const string &findString, const string &replaceString, bool matchCase, bool wholeWord, bool selection)
Replace all string.
void getViewPort(GUINode *viewPortNode, int &left, int &top, int &width, int &height, int &offsetX, int &offsetY)
Determine viewport screen constraints.
Definition: EditorView.cpp:427
EditorScreenController * getScreenController()
Definition: EditorView.h:69
Character class.
Definition: Character.h:17
Console class.
Definition: Console.h:29
Mutable utf8 aware string class.
Definition: MutableString.h:23
char getCharAt(int32_t idx) const
Get char at given binary index.
Definition: MutableString.h:78
const string & getString() const
String tools class.
Definition: StringTools.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18
Action Interface.
Definition: Action.h:11