TDME2  1.9.200
GUIScreenNode.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <map>
5 #include <memory>
6 #include <span>
7 #include <string>
8 #include <unordered_map>
9 #include <unordered_set>
10 
11 #include <tdme/tdme.h>
12 #include <tdme/engine/Texture.h>
27 #include <tdme/gui/nodes/GUINode.h>
38 #include <tdme/gui/GUI.h>
39 #include <tdme/gui/GUIParser.h>
43 #include <tdme/utilities/Integer.h>
47 
48 using std::make_unique;
49 using std::map;
50 using std::remove;
51 using std::reverse;
52 using std::span;
53 using std::string;
54 using std::to_string;
55 using std::unique_ptr;
56 using std::unordered_map;
57 using std::unordered_set;
58 
86 using tdme::gui::GUI;
95 
96 GUIScreenNode::GUIScreenNode(
97  const string& fileName,
98  const string& applicationRootPathName,
99  const string& applicationSubPathName,
100  const string& id,
101  GUINode_Flow* flow,
102  GUIParentNode_Overflow* overflowX,
103  GUIParentNode_Overflow* overflowY,
104  const GUINode_Alignments& alignments,
105  const GUINode_RequestedConstraints& requestedConstraints,
106  const GUIColor& backgroundColor,
107  const string& backgroundImage,
108  const GUINode_Scale9Grid& backgroundImageScale9Grid,
109  const GUIColor& backgroundImageEffectColorMul,
110  const GUIColor& backgroundImageEffectColorAdd,
111  const GUINode_Border& border,
112  const GUINode_Padding& padding,
113  const GUIScreenNode_SizeConstraints& sizeConstraints,
114  const GUINodeConditions& showOn,
115  const GUINodeConditions& hideOn,
116  const string& tooltip,
117  bool scrollable,
118  bool popUp,
119  const string& scriptFileName,
120  const EngineMiniScript::ScriptVariable& miniScriptArguments,
121  Context* context
122 ):
123  GUIParentNode(this, nullptr, id, flow, overflowX, overflowY, alignments, requestedConstraints, backgroundColor, backgroundImage, backgroundImageScale9Grid, backgroundImageEffectColorMul, backgroundImageEffectColorAdd, border, padding, showOn, hideOn, tooltip)
124 {
125  this->fileName = fileName;
126  this->applicationRootPathName = applicationRootPathName;
127  this->applicationSubPathName = applicationSubPathName;
128  this->sizeConstraints = sizeConstraints;
129  this->gui = nullptr;
130  this->nodeCounter = 0;
131  this->screenWidth = 0;
132  this->screenHeight = 0;
133  this->inputEventHandler = nullptr;
134  this->screenNode = this;
135  this->parentNode = nullptr;
136  this->enabled = true;
137  this->popUp = popUp;
138  this->foccussedBorderColor = GUIColor(applicationSubPathName == "project"?GUIParser::getProjectThemeProperties()->get("color.focus", "#ff0000"):GUIParser::getEngineThemeProperties()->get("color.focus", "#ff0000"));
139  if (scriptFileName.empty() == false) {
140  this->script = make_unique<GUIMiniScript>(this);
141  // compute project script path and file name
142  string projectScriptPathName;
143  string projectScriptFileName;
144  getProjectFilePathNameAndFileName(scriptFileName, projectScriptPathName, projectScriptFileName);
145  //
146  this->script->parseScript(
147  projectScriptPathName,
148  projectScriptFileName
149  );
150  // check if valid
151  if (this->script->isValid() == false) {
152  // nope
153  Console::println("GUIScreenNode::GUIScreenNode(): " + projectScriptFileName + ": script not valid. Not using it.");
154  this->script = nullptr;
155  } else {
156  // yup
157  Console::println(this->script->getInformation());
158  //
159  this->miniScriptArguments = miniScriptArguments;
160  //
161  this->scriptOnActionAvailable = this->script->hasFunction("onAction");
162  this->scriptOnChangeAvailable = this->script->hasFunction("onChange");
163  this->scriptOnMouseOverAvailable = this->script->hasFunction("onMouseOver");
164  this->scriptOnContextMenuRequestAvailable = this->script->hasFunction("onContextMenuRequest");
165  this->scriptOnFocusAvailable = this->script->hasFunction("onFocus");
166  this->scriptOnUnfocusAvailable = this->script->hasFunction("onUnfocus");
167  this->scriptOnMoveAvailable = this->script->hasFunction("onMove");
168  this->scriptOnMoveReleaseAvailable = this->script->hasFunction("onMoveRelease");
169  this->scriptOnTooltipShowRequestAvailable = this->script->hasFunction("onTooltipShowRequest");
170  this->scriptOnTooltipCloseRequestAvailable = this->script->hasFunction("onTooltipCloseRequest");
171  this->scriptOnDragRequestAvailable = this->script->hasFunction("onDragRequest");
172  this->scriptOnTickAvailable = this->script->hasFunction("onTick");
173  //
174  Console::println("Available event script handler functions:");
175  Console::println("onAction: " + string(this->scriptOnActionAvailable == true?"YES":"NO"));
176  Console::println("onChange: " + string(this->scriptOnChangeAvailable == true?"YES":"NO"));
177  Console::println("onMouseOver: " + string(this->scriptOnMouseOverAvailable == true?"YES":"NO"));
178  Console::println("onContextMenuRequest: " + string(this->scriptOnContextMenuRequestAvailable == true?"YES":"NO"));
179  Console::println("onFocus: " + string(this->scriptOnFocusAvailable == true?"YES":"NO"));
180  Console::println("onUnfocus: " + string(this->scriptOnUnfocusAvailable == true?"YES":"NO"));
181  Console::println("onMove: " + string(this->scriptOnMoveAvailable == true?"YES":"NO"));
182  Console::println("onMoveRelease: " + string(this->scriptOnMoveReleaseAvailable == true?"YES":"NO"));
183  Console::println("onTooltipShowRequest: " + string(this->scriptOnTooltipShowRequestAvailable == true?"YES":"NO"));
184  Console::println("onTooltipCloseRequest: " + string(this->scriptOnTooltipCloseRequestAvailable == true?"YES":"NO"));
185  Console::println("onDragRequest: " + string(this->scriptOnDragRequestAvailable == true?"YES":"NO"));
186  Console::println("onTick: " + string(this->scriptOnTickAvailable == true?"YES":"NO"));
187  Console::println();
188  //
189  this->context = context;
190  }
191  }
192 }
193 
195  // remove sub nodes
196  for (auto i = 0; i < subNodes.size(); i++) {
197  removeNode(subNodes[i]);
198  }
199  subNodes.clear();
200 
201  // dispose
203 
204  // delete chaches
205  for (const auto& [fontId, font]: fontCache) {
206  font->dispose();
207  delete font;
208  }
209  fontCache.clear();
210  for (const auto& [imageId, image]: imageCache) {
211  image->releaseReference();
212  }
213  imageCache.clear();
214 }
215 
217  //
218  if (script != nullptr && script->hasFunction("initialize") == true) {
219  vector<EngineMiniScript::ScriptVariable> argumentValues { miniScriptArguments };
220  span argumentValuesSpan(argumentValues);
221  EngineMiniScript::ScriptVariable returnValue;
222  script->call("initialize", argumentValuesSpan, returnValue);
223  }
224 }
225 
227 {
228  return gui;
229 }
230 
232 {
233  this->gui = gui;
234 }
235 
236 void GUIScreenNode::setEnabled(bool enabled)
237 {
238  if (this->enabled == enabled) return;
239  this->enabled = enabled;
240  if (gui != nullptr &&
241  ((enabled == false && gui->getFocussedNode() != nullptr && gui->getFocussedNode()->getScreenNode() == this) ||
242  enabled == true)) {
243  //
245  }
246  //
247  if (gui != nullptr) gui->unsetMouseStates();
248 }
249 
250 void GUIScreenNode::setPopUp(bool popUp)
251 {
252  this->popUp = popUp;
253 }
254 
255 const vector<GUINode*>& GUIScreenNode::getFloatingNodes()
256 {
257  return floatingNodes;
258 }
259 
261 {
262  return false;
263 }
264 
266 {
267  return -1;
268 }
269 
271 {
272  return -1;
273 }
274 
276 {
277  for (auto i = 0; i < subNodes.size(); i++) {
278  subNodes[i]->layout();
279  }
281  for (auto i = 0; i < childControllerNodes.size(); i++) {
282  auto node = childControllerNodes[i];
283  auto controller = node->getController();
284  if (controller != nullptr && node->layouted == true) {
285  controller->postLayout();
286  }
287  }
288  layouted = true;
289 }
290 
292  {
293  auto _node = node;
294  while (_node != nullptr) {
295  if (_node->conditionsMet == false) {
296  return nullptr;
297  }
298  _node = _node->parentNode;
299  }
300  }
301 
302  // first step, make sure all parents up to screen node are layouted
303  auto _node = node;
304  auto __node = node;
305  if (node->parentNode != nullptr) {
306  __node = __node->parentNode;
307  while (_node != nullptr) {
308  if (_node->layouted == false) __node = _node;
309  _node = _node->parentNode;
310  }
311  _node = __node;
312  }
313 
314  // invalidate all nodes from node to _node
315  for (__node = node; __node != _node; __node = __node->parentNode) {
316  __node->layouted = false;
317  }
318 
319  // find a node that is a valid base for layouting from
320  while (
321  _node->parentNode != nullptr &&
322  // auto depends on its children dimensions, so do relayout the parent
323  ((_node->requestedConstraints.leftType == GUINode_RequestedConstraints_RequestedConstraintsType::AUTO ||
324  _node->requestedConstraints.topType == GUINode_RequestedConstraints_RequestedConstraintsType::AUTO ||
325  _node->requestedConstraints.widthType == GUINode_RequestedConstraints_RequestedConstraintsType::AUTO ||
326  _node->requestedConstraints.heightType == GUINode_RequestedConstraints_RequestedConstraintsType::AUTO) ||
327  // percent depend on its parent dimensions so make sure its already layouted
328  (_node->layouted == false &&
329  (_node->requestedConstraints.leftType == GUINode_RequestedConstraints_RequestedConstraintsType::PERCENT ||
330  _node->requestedConstraints.topType == GUINode_RequestedConstraints_RequestedConstraintsType::PERCENT ||
331  _node->requestedConstraints.widthType == GUINode_RequestedConstraints_RequestedConstraintsType::PERCENT ||
332  _node->requestedConstraints.heightType == GUINode_RequestedConstraints_RequestedConstraintsType::PERCENT)) ||
333  // star depend on its parent dimensions, so make sure its already layouted
334  (_node->layouted == false &&
335  (_node->requestedConstraints.leftType == GUINode_RequestedConstraints_RequestedConstraintsType::STAR ||
336  _node->requestedConstraints.topType == GUINode_RequestedConstraints_RequestedConstraintsType::STAR ||
337  _node->requestedConstraints.widthType == GUINode_RequestedConstraints_RequestedConstraintsType::STAR ||
338  _node->requestedConstraints.heightType == GUINode_RequestedConstraints_RequestedConstraintsType::STAR)))) {
339  _node->layouted = false;
340  _node = _node->parentNode;
341  }
342 
343  //
344  _node->layouted = false;
345 
346  //
347  auto parentNode = dynamic_cast<GUIParentNode*>(_node);
348  if (parentNode != nullptr) parentNode->invalidateRenderCaches();
349 
350  //
351  return _node;
352 }
353 
355  // invalidate layouts and mark nodes that are required to start layouting with
356  // in a map with hierarchical id which gets sorted from root -> child node
357  map<string, GUINode*> nodesToForceLayout;
358  for (const auto& nodeId: invalidateLayoutNodeIds) {
359  auto node = getNodeById(nodeId);
360  if (node == nullptr) continue;
361  auto layoutNode = forceInvalidateLayout(node);
362  if (layoutNode == nullptr) continue;
363  nodesToForceLayout[layoutNode->getHierarchicalId()] = layoutNode;
364  }
365  //
366  invalidateLayoutNodeIds.clear();
367  // force layouts
368  for (const auto& [nodeHierarchicalId, node]: nodesToForceLayout) {
369  // check if parent node was layouted in this layout sequence already
370  auto parentNodeLayouted = false;
371  auto _node = node->parentNode;
372  // check if node's parent nodes were layouted
373  while (_node != nullptr) {
374  if (nodesToForceLayout.find(_node->getHierarchicalId()) != nodesToForceLayout.end()) {
375  parentNodeLayouted = true;
376  break;
377  }
378  _node = _node->parentNode;
379  }
380  // jup, skip
381  if (parentNodeLayouted == true) continue;
382  // otherwise layout
383  forceLayout(node);
384  }
385 }
386 
388 {
389  // do the magic
390  if (dynamic_cast<GUIParentNode*>(node) != nullptr) {
391  auto parentNode = required_dynamic_cast<GUIParentNode*>(node);
392  parentNode->layouted = true;
396  for (auto i = 0; i < childControllerNodes.size(); i++) {
397  auto childNode = childControllerNodes[i];
398  auto childController = childNode->getController();
399  if (childController != nullptr) childController->postLayout();
400  }
401  } else {
402  node->layout();
403  node->getScreenNode()->layoutSubNodes();
404  node->computeContentAlignment();
405  auto nodeController = node->getController();
406  if (nodeController != nullptr) nodeController->postLayout();
407  }
408 }
409 
411  for (const auto& scrollToNodeX: scrollToNodesX) {
412  auto node = getNodeById(scrollToNodeX.node);
413  auto toNode = scrollToNodeX.toNode.empty() == true?nullptr:dynamic_cast<GUIParentNode*>(getNodeById(scrollToNodeX.toNode));
414  if (node != nullptr) node->_scrollToNodeX(toNode);
415  }
416  scrollToNodesX.clear();
417  for (const auto& scrollToNodeY: scrollToNodesY) {
418  auto node = getNodeById(scrollToNodeY.node);
419  auto toNode = scrollToNodeY.toNode.empty() == true?nullptr:dynamic_cast<GUIParentNode*>(getNodeById(scrollToNodeY.toNode));
420  if (node != nullptr) node->_scrollToNodeY(toNode);
421  }
422  scrollToNodesY.clear();
423 }
424 
425 void GUIScreenNode::setScreenSize(int width, int height)
426 {
427  this->screenWidth = width;
428  this->screenHeight = height;
430  this->requestedConstraints.width = width;
432  this->requestedConstraints.height = height;
433  this->computedConstraints.left = 0;
434  this->computedConstraints.top = 0;
435  this->computedConstraints.width = width;
436  this->computedConstraints.height = height;
437  this->layouted = false;
438 }
439 
440 
442 {
443  return "screen";
444 }
445 
447 {
448  // if node does exist do not insert it and return
449  if (nodesById.find(node->id) != nodesById.end()) {
450  return false;
451  }
452  // otherwise go
453  nodesById[node->id] = node;
454 
455  // add to floating nodes
456  if (node->flow == GUINode_Flow::FLOATING) floatingNodes.push_back(node);
457 
458  return true;
459 }
460 
461 void GUIScreenNode::removeNodeById(const string& nodeId, bool resetScrollOffsets) {
462  auto node = getNodeById(nodeId);
463  if (node == nullptr) {
464  Console::println("GUIScreenNode::removeNodeById(): node not found: " + nodeId);
465  return;
466  }
467  if (node->parentNode != nullptr) node->parentNode->removeSubNode(node, resetScrollOffsets);
468  removeNode(node);
469 }
470 
472 {
473  //
474  const auto& nodeId = node->getId();
475  //
476  for (auto& [elementNodeId, nodeIds]: elementNodeToNodeMapping) {
477  nodeIds.erase(nodeId);
478  }
479  //
480  elementNodeToNodeMapping.erase(nodeId);
481  //
482  if (dynamic_cast<GUIParentNode*>(node) != nullptr) {
483  auto parentNode = required_dynamic_cast<GUIParentNode*>(node);
484  for (auto i = 0; i < parentNode->subNodes.size(); i++) {
486  }
487  parentNode->subNodes.clear();
488  }
489  nodesById.erase(nodeId);
490  tickNodesById.erase(nodeId);
491  floatingNodes.erase(remove(floatingNodes.begin(), floatingNodes.end(), node), floatingNodes.end());
492  node->dispose();
493  delete node;
494  //
495  return true;
496 }
497 
499 {
500  guiRenderer->initScreen(this);
502  if (hasEffects() == true) applyEffects(guiRenderer);
503  GUIParentNode::render(guiRenderer);
504  if (hasEffects() == true) undoEffects(guiRenderer);
505  guiRenderer->doneScreen();
506 }
507 
509 {
510  guiRenderer->initScreen(this);
511  for (auto i = 0; i < floatingNodes.size(); i++) {
512  auto floatingNode = floatingNodes[i];
513  auto skipFloatingNode = false;
514  auto _floatingNode = floatingNode;
515  do {
516  if (_floatingNode->conditionsMet == false) {
517  skipFloatingNode = true;
518  break;
519  }
520  _floatingNode = _floatingNode->parentNode;
521  } while (_floatingNode != nullptr);
522  if (skipFloatingNode == true) continue;
523  guiRenderer->setRenderAreaLeft(GUIRenderer::SCREEN_LEFT);
524  guiRenderer->setRenderAreaTop(GUIRenderer::SCREEN_TOP);
525  guiRenderer->setRenderAreaRight(GUIRenderer::SCREEN_RIGHT);
526  guiRenderer->setRenderAreaBottom(GUIRenderer::SCREEN_BOTTOM);
527  floatingNodes[i]->render(guiRenderer);
528  }
529  guiRenderer->doneScreen();
530 }
531 
532 void GUIScreenNode::determineFocussedNodes(GUIParentNode* parentNode, vector<GUIElementNode*>& focusableNodes)
533 {
534  if (parentNode->conditionsMet == false) {
535  return;
536  }
537  if (dynamic_cast<GUIElementNode*>(parentNode) != nullptr) {
538  auto parentElementNode = required_dynamic_cast<GUIElementNode*>(parentNode);
539  if (parentElementNode->focusable == true && (parentElementNode->getController() == nullptr || parentElementNode->getController()->isDisabled() == false)) {
540  focusableNodes.push_back(required_dynamic_cast<GUIElementNode*>(parentNode));
541  }
542  }
543  for (auto i = 0; i < parentNode->subNodes.size(); i++) {
544  auto subNode = parentNode->subNodes[i];
545  if (dynamic_cast<GUIParentNode*>(subNode) != nullptr) {
546  determineFocussedNodes(required_dynamic_cast<GUIParentNode*>(subNode), focusableNodes);
547  }
548  }
549 }
550 
551 void GUIScreenNode::determineMouseEventNodes(GUIMouseEvent* event, bool floatingNode, unordered_set<string>& eventNodeIds, unordered_set<string>& eventFloatingNodeIds, int flags)
552 {
553  for (auto i = 0; i < floatingNodes.size(); i++) {
554  floatingNodes[i]->determineMouseEventNodes(event, floatingNode == true || flow == GUINode_Flow::FLOATING, eventNodeIds, eventFloatingNodeIds, flags);
555  }
556  GUIParentNode::determineMouseEventNodes(event, floatingNode, eventNodeIds, eventFloatingNodeIds, flags);
557 }
558 
560 {
561  removeActionListener(listener);
562  actionListener.push_back(listener);
563 }
564 
566 {
567  actionListener.erase(std::remove(actionListener.begin(), actionListener.end(), listener), actionListener.end());
568 }
569 
571 {
572  return inputEventHandler;
573 }
574 
576 {
577  this->inputEventHandler = inputEventHandler;
578 }
579 
581 {
582  forwardEventList.emplace_back(
584  node->getId(),
585  -1,
586  -1,
587  type
588  );
589 }
590 
592 {
593  removeChangeListener(listener);
594  changeListener.push_back(listener);
595 }
596 
598 {
599  changeListener.erase(std::remove(changeListener.begin(), changeListener.end(), listener), changeListener.end());
600 }
601 
603 {
605  //
606  forwardEventList.emplace_back(
608  node->getId(),
609  -1,
610  -1,
611  -1
612  );
613 }
614 
616 {
617  removeMouseOverListener(listener);
618  mouseOverListener.push_back(listener);
619 }
620 
622 {
623  mouseOverListener.erase(std::remove(mouseOverListener.begin(), mouseOverListener.end(), listener), mouseOverListener.end());
624 }
625 
627 {
628  forwardEventList.emplace_back(
630  node->getId(),
631  -1,
632  -1,
633  -1
634  );
635 }
636 
639  contextMenuRequestListener.push_back(listener);
640 }
641 
644 }
645 
646 void GUIScreenNode::forwardContextMenuRequest(GUIElementNode* node, int mouseX, int mouseY) {
647  forwardEventList.emplace_back(
649  node->getId(),
650  mouseX,
651  mouseY,
652  -1
653  );
654 }
655 
657 {
658  removeFocusListener(listener);
659  focusListener.push_back(listener);
660 }
661 
663 {
664  focusListener.erase(std::remove(focusListener.begin(), focusListener.end(), listener), focusListener.end());
665 }
666 
668  forwardEventList.emplace_back(
670  node->getId(),
671  -1,
672  -1,
673  -1
674  );
675 }
676 
678  forwardEventList.emplace_back(
680  node->getId(),
681  -1,
682  -1,
683  -1
684  );
685 }
686 
688  removeMoveListener(listener);
689  moveListener.push_back(listener);
690 }
691 
693  moveListener.erase(std::remove(moveListener.begin(), moveListener.end(), listener), moveListener.end());
694 }
695 
697  for (auto listener: moveListener) {
698  if (listener->accept(node) == true) return true;
699  }
700  return moveListener.empty();
701 }
702 
704  forwardEventList.emplace_back(
706  node->getId(),
707  -1,
708  -1,
709  -1
710  );
711 }
712 
713 void GUIScreenNode::forwardMoveRelease(GUINode* node, int mouseX, int mouseY) {
714  forwardEventList.emplace_back(
716  node->getId(),
717  mouseX,
718  mouseY,
719  -1
720  );
721 }
722 
725  tooltipRequestListener.push_back(listener);
726 }
727 
729  tooltipRequestListener.erase(std::remove(tooltipRequestListener.begin(), tooltipRequestListener.end(), listener), tooltipRequestListener.end());
730 }
731 
732 void GUIScreenNode::forwardTooltipShowRequest(GUINode* node, int mouseX, int mouseY) {
733  forwardEventList.emplace_back(
735  node->getId(),
736  mouseX,
737  mouseY,
738  -1
739  );
740 }
741 
743  forwardEventList.emplace_back(
745  string(),
746  -1,
747  -1,
748  -1
749  );
750 }
751 
753  removeDragRequestListener(listener);
754  dragRequestListener.push_back(listener);
755 }
756 
758  dragRequestListener.erase(std::remove(dragRequestListener.begin(), dragRequestListener.end(), listener), dragRequestListener.end());
759 }
760 
761 void GUIScreenNode::forwardDragRequest(GUIElementNode* node, int mouseX, int mouseY) {
762  forwardEventList.emplace_back(
764  node->getId(),
765  mouseX,
766  mouseY,
767  -1
768  );
769 }
770 
772  auto now = Time::getCurrentMillis();
773  vector<int64_t> timedExpressionsToRemove;
774  for (const auto& [timedExpressionsTime, timedExpressionsExpression]: timedExpressions) {
775  if (now >= timedExpressionsTime) {
776  timedExpressionsToRemove.push_back(timedExpressionsTime);
777  GUIElementNode::executeExpression(this, timedExpressionsExpression);
778  }
779  }
780  for (const auto& timedExpressionToRemove: timedExpressionsToRemove) {
781  timedExpressions.erase(timedExpressionToRemove);
782  }
783  auto _tickNodesById = tickNodesById;
784  for (const auto& [nodeId, node]: _tickNodesById) {
785  if (node->controller != nullptr) node->controller->tick();
786  }
787  //
788  if (scriptOnTickAvailable == true) {
789  vector<EngineMiniScript::ScriptVariable> argumentValues(0);
790  span argumentValuesSpan(argumentValues);
791  EngineMiniScript::ScriptVariable returnValue;
792  script->call("onTick", argumentValuesSpan, returnValue);
793  }
794 }
795 
796 void GUIScreenNode::getValues(unordered_map<string, MutableString>& values)
797 {
798  values.clear();
800  for (auto i = 0; i < childControllerNodes.size(); i++) {
801  auto childControllerNode = childControllerNodes[i];
802  if (dynamic_cast<GUIElementNode*>(childControllerNode) != nullptr == false)
803  continue;
804 
805  auto guiElementNode = required_dynamic_cast<GUIElementNode*>(childControllerNode);
806  auto guiElementNodeController = guiElementNode->getController();
807  if (guiElementNodeController->hasValue()) {
808  const auto& name = guiElementNode->getName();
809  const auto& value = guiElementNodeController->getValue();
810  auto currentValueIt = values.find(name);
811  if (currentValueIt == values.end() || currentValueIt->second.size() == 0) {
812  values[name] = value;
813  }
814  }
815  }
816 }
817 
818 void GUIScreenNode::setValues(const unordered_map<string, MutableString>& values)
819 {
821  for (auto i = 0; i < childControllerNodes.size(); i++) {
822  auto childControllerNode = childControllerNodes[i];
823  if (dynamic_cast<GUIElementNode*>(childControllerNode) != nullptr == false)
824  continue;
825 
826  auto guiElementNode = required_dynamic_cast<GUIElementNode*>(childControllerNode);
827  auto guiElementNodeController = guiElementNode->getController();
828  if (guiElementNodeController->hasValue()) {
829  auto name = guiElementNode->getName();
830  auto newValueIt = values.find(name);
831  if (newValueIt == values.end())
832  continue;
833  guiElementNodeController->setValue(newValueIt->second);
834  }
835  }
836 }
837 
838 GUIScreenNode_SizeConstraints GUIScreenNode::createSizeConstraints(const string& minWidth, const string& minHeight, const string& maxWidth, const string& maxHeight)
839 {
840  GUIScreenNode_SizeConstraints constraints;
841  constraints.minWidth = minWidth.empty() == true?-1:Integer::parse(minWidth);
842  constraints.minHeight = minHeight.empty() == true?-1:Integer::parse(minHeight);
843  constraints.maxWidth = maxWidth.empty() == true?-1:Integer::parse(maxWidth);
844  constraints.maxHeight = maxHeight.empty() == true?-1:Integer::parse(maxHeight);
845  return constraints;
846 }
847 
848 GUIFont* GUIScreenNode::getFont(const string& fileName, int size)
849 {
850  // get canonical file name
851  string fontPathName;
852  string fontFileName;
853  getProjectFilePathNameAndFileName(fileName, fontPathName, fontFileName);
854 
855  // use cache or load font
856  auto cacheId = fontPathName + "/" + fontFileName + ":" + to_string(size);
857  auto fontCacheIt = fontCache.find(cacheId);
858  auto font = fontCacheIt != fontCache.end()?fontCacheIt->second:nullptr;
859  if (font == nullptr) {
860  try {
861  font = GUIFont::parse(fontPathName, fontFileName, size);
862  } catch (Exception& exception) {
863  Console::print("GUIScreenNode::getFont(): An error occurred: " + id + ": " + cacheId + ": " + string(exception.what()));
864  return nullptr;
865  }
866  fontCache[cacheId] = font;
867  }
868  //
869  return font;
870 }
871 
872 Texture* GUIScreenNode::getImage(const string& fileName)
873 {
874  // get canonical file name
875  string imagePathName;
876  string imageFileName;
877  getProjectFilePathNameAndFileName(fileName, imagePathName, imageFileName);
878 
879  //
880  auto cacheId = imagePathName + "/" + imageFileName;
881  auto imageCacheIt = imageCache.find(cacheId);
882  auto image = imageCacheIt != imageCache.end()?imageCacheIt->second:nullptr;
883  if (image == nullptr) {
884  try {
885  image = TextureReader::read(imagePathName, imageFileName, false, false, "tdme.gui." + screenNode->getId() + ".");
886  if (image != nullptr) {
887  image->setUseCompression(false);
888  image->setUseMipMap(false);
889  image->setRepeat(false);
890  image->setClampMode(Texture::CLAMPMODE_TRANSPARENTPIXEL);
891  }
892  } catch (Exception& exception) {
893  Console::print("GUIScreenNode::getImage(): An error occurred: " + id + ": " + cacheId + ": " + string(exception.what()));
894  return nullptr;
895  }
896  if (image != nullptr) imageCache[cacheId] = image;
897  }
898  return image;
899 }
900 
902  auto forwardEventCount = 0;
903  while (forwardEventList.empty() == false && forwardEventCount++ < 10) {
904  auto forwardEventListCopy = forwardEventList;
905  forwardEventList.clear();
906  for (const auto& event: forwardEventListCopy) {
907  switch(event.eventType) {
909  {
910  for (auto i = 0; i < actionListener.size(); i++) {
911  auto elementNode = dynamic_cast<GUIElementNode*>(getNodeById(event.nodeId));
912  if (elementNode == nullptr) break;
913  actionListener[i]->onAction(static_cast<GUIActionListenerType>(event.type), elementNode);
914  }
915  }
916  //
917  if (scriptOnActionAvailable == true) {
918  vector<EngineMiniScript::ScriptVariable> argumentValues {
919  static_cast<int64_t>(event.type),
920  event.nodeId
921  };
922  span argumentValuesSpan(argumentValues);
923  EngineMiniScript::ScriptVariable returnValue;
924  script->call("onAction", argumentValuesSpan, returnValue);
925  }
926  //
927  break;
929  {
930  for (auto i = 0; i < changeListener.size(); i++) {
931  auto elementNode = dynamic_cast<GUIElementNode*>(getNodeById(event.nodeId));
932  if (elementNode == nullptr) break;
933  changeListener[i]->onChange(elementNode);
934  }
935  }
936  //
937  if (scriptOnChangeAvailable == true) {
938  vector<EngineMiniScript::ScriptVariable> argumentValues {
939  event.nodeId
940  };
941  span argumentValuesSpan(argumentValues);
942  EngineMiniScript::ScriptVariable returnValue;
943  script->call("onChange", argumentValuesSpan, returnValue);
944  }
945  //
946  break;
948  {
949  for (auto i = 0; i < mouseOverListener.size(); i++) {
950  auto elementNode = dynamic_cast<GUIElementNode*>(getNodeById(event.nodeId));
951  if (elementNode == nullptr) break;
952  mouseOverListener[i]->onMouseOver(elementNode);
953  }
954  }
955  //
956  if (scriptOnMouseOverAvailable == true) {
957  vector<EngineMiniScript::ScriptVariable> argumentValues {
958  event.nodeId
959  };
960  span argumentValuesSpan(argumentValues);
961  EngineMiniScript::ScriptVariable returnValue;
962  script->call("onMouseOver", argumentValuesSpan, returnValue);
963  }
964  //
965  break;
967  {
968  for (auto i = 0; i < contextMenuRequestListener.size(); i++) {
969  auto elementNode = dynamic_cast<GUIElementNode*>(getNodeById(event.nodeId));
970  if (elementNode == nullptr) break;
971  contextMenuRequestListener[i]->onContextMenuRequest(elementNode, event.mouseX, event.mouseY);
972  }
973  }
974  //
976  vector<EngineMiniScript::ScriptVariable> argumentValues {
977  event.nodeId,
978  static_cast<int64_t>(event.mouseX),
979  static_cast<int64_t>(event.mouseY)
980  };
981  span argumentValuesSpan(argumentValues);
982  EngineMiniScript::ScriptVariable returnValue;
983  script->call("onContextMenuRequest", argumentValuesSpan, returnValue);
984  }
985  //
986  break;
988  {
989  for (auto i = 0; i < focusListener.size(); i++) {
990  auto elementNode = dynamic_cast<GUIElementNode*>(getNodeById(event.nodeId));
991  if (elementNode == nullptr) break;
992  focusListener[i]->onFocus(elementNode);
993  }
994  }
995  //
996  if (scriptOnFocusAvailable == true) {
997  vector<EngineMiniScript::ScriptVariable> argumentValues {
998  event.nodeId
999  };
1000  span argumentValuesSpan(argumentValues);
1001  EngineMiniScript::ScriptVariable returnValue;
1002  script->call("onFocus", argumentValuesSpan, returnValue);
1003  }
1004  //
1005  break;
1007  {
1008  for (auto i = 0; i < focusListener.size(); i++) {
1009  auto elementNode = dynamic_cast<GUIElementNode*>(getNodeById(event.nodeId));
1010  if (elementNode == nullptr) break;
1011  focusListener[i]->onUnfocus(elementNode);
1012  }
1013  }
1014  //
1015  if (scriptOnUnfocusAvailable == true) {
1016  vector<EngineMiniScript::ScriptVariable> argumentValues {
1017  event.nodeId
1018  };
1019  span argumentValuesSpan(argumentValues);
1020  EngineMiniScript::ScriptVariable returnValue;
1021  script->call("onUnfocus", argumentValuesSpan, returnValue);
1022  }
1023  //
1024  break;
1026  {
1027  for (auto i = 0; i < moveListener.size(); i++) {
1028  auto node = getNodeById(event.nodeId);
1029  if (node == nullptr) break;
1030  moveListener[i]->onMove(node);
1031  }
1032  }
1033  //
1034  if (scriptOnMoveAvailable == true) {
1035  vector<EngineMiniScript::ScriptVariable> argumentValues {
1036  event.nodeId
1037  };
1038  span argumentValuesSpan(argumentValues);
1039  EngineMiniScript::ScriptVariable returnValue;
1040  script->call("onMove", argumentValuesSpan, returnValue);
1041  }
1042  //
1043  break;
1045  {
1046  for (auto i = 0; i < moveListener.size(); i++) {
1047  auto node = getNodeById(event.nodeId);
1048  if (node == nullptr) break;
1049  moveListener[i]->onRelease(node, event.mouseX, event.mouseY);
1050  }
1051  }
1052  //
1053  if (scriptOnMoveReleaseAvailable == true) {
1054  vector<EngineMiniScript::ScriptVariable> argumentValues {
1055  event.nodeId,
1056  static_cast<int64_t>(event.mouseX),
1057  static_cast<int64_t>(event.mouseY)
1058  };
1059  span argumentValuesSpan(argumentValues);
1060  EngineMiniScript::ScriptVariable returnValue;
1061  script->call("onMoveRelease", argumentValuesSpan, returnValue);
1062  }
1063  //
1064  break;
1066  {
1067  for (auto i = 0; i < tooltipRequestListener.size(); i++) {
1068  auto node = getNodeById(event.nodeId);
1069  if (node == nullptr) break;
1070  tooltipRequestListener[i]->onTooltipShowRequest(node, event.mouseX, event.mouseY);
1071  }
1072  }
1073  //
1075  vector<EngineMiniScript::ScriptVariable> argumentValues {
1076  event.nodeId,
1077  static_cast<int64_t>(event.mouseX),
1078  static_cast<int64_t>(event.mouseY)
1079  };
1080  span argumentValuesSpan(argumentValues);
1081  EngineMiniScript::ScriptVariable returnValue;
1082  script->call("onTooltipShowRequest", argumentValuesSpan, returnValue);
1083  }
1084  //
1085  break;
1087  {
1088  for (auto i = 0; i < tooltipRequestListener.size(); i++) {
1089  tooltipRequestListener[i]->onTooltipCloseRequest();
1090  }
1091  }
1092  //
1094  vector<EngineMiniScript::ScriptVariable> argumentValues(0);
1095  span argumentValuesSpan(argumentValues);
1096  EngineMiniScript::ScriptVariable returnValue;
1097  script->call("onTooltipCloseRequest", argumentValuesSpan, returnValue);
1098  }
1099  //
1100  break;
1101  }
1103  {
1104  for (auto i = 0; i < dragRequestListener.size(); i++) {
1105  auto elementNode = dynamic_cast<GUIElementNode*>(getNodeById(event.nodeId));
1106  if (elementNode == nullptr) break;
1107  dragRequestListener[i]->onDragRequest(elementNode, event.mouseX, event.mouseY);
1108  }
1109  }
1110  //
1111  if (scriptOnDragRequestAvailable == true) {
1112  vector<EngineMiniScript::ScriptVariable> argumentValues {
1113  event.nodeId,
1114  static_cast<int64_t>(event.mouseX),
1115  static_cast<int64_t>(event.mouseY)
1116  };
1117  span argumentValuesSpan(argumentValues);
1118  EngineMiniScript::ScriptVariable returnValue;
1119  script->call("onDragRequest", argumentValuesSpan, returnValue);
1120  }
1121  //
1122  break;
1123  }
1124  }
1125  }
1126 }
1127 
1128 void GUIScreenNode::getProjectFilePathNameAndFileName(const string &fileName, string& projectFilePathName, string& projectFileFileName) {
1129  try {
1130  string projectFileCanonicalFileName;
1131  if (FileSystem::getInstance()->exists(fileName) == true) {
1132  projectFileCanonicalFileName = fileName;
1133  } else {
1134  projectFileCanonicalFileName = FileSystem::getInstance()->getCanonicalURI(applicationRootPathName, fileName);
1135  }
1136  projectFilePathName = FileSystem::getInstance()->getPathName(projectFileCanonicalFileName);
1137  projectFileFileName = FileSystem::getInstance()->getFileName(projectFileCanonicalFileName);
1138  } catch (Exception& exception) {
1139  Console::print("GUIScreenNode::getProjectFilePathNameAndFileName(): An error occurred: " + string(exception.what()));
1140  }
1141 }
Texture entity.
Definition: Texture.h:24
GUI parser.
Definition: GUIParser.h:40
static const Properties * getEngineThemeProperties()
Definition: GUIParser.h:54
static const Properties * getProjectThemeProperties()
Definition: GUIParser.h:61
GUI module class.
Definition: GUI.h:64
GUIElementNode * getFocussedNode()
Definition: GUI.cpp:224
void unsetMouseStates()
Render screens change.
Definition: GUI.cpp:977
void invalidateFocussedNode()
Invalidate focussed node.
Definition: GUI.cpp:197
void executeOnChangeExpression()
Execute on change expression.
static void executeExpression(GUIScreenNode *screenNode, const string &expression)
Execute expression.
GUI element node conditions.
GUI node controller base class.
virtual void postLayout()=0
Post layout event.
static STATIC_DLL_IMPEXT GUINode_Flow * FLOATING
Definition: GUINode_Flow.h:31
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * PERCENT
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * STAR
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * PIXEL
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * AUTO
GUI node base class.
Definition: GUINode.h:64
virtual void layoutOnDemand()
Layout on demand.
Definition: GUINode.cpp:473
void _scrollToNodeX(GUIParentNode *toNode=nullptr)
Scroll to node X.
Definition: GUINode.cpp:1055
virtual void computeContentAlignment()
Do content alignment.
Definition: GUINode.cpp:207
unique_ptr< GUINodeController > controller
Definition: GUINode.h:163
GUIParentNode * parentNode
Definition: GUINode.h:148
void _scrollToNodeY(GUIParentNode *toNode=nullptr)
Scroll to node Y.
Definition: GUINode.cpp:1077
virtual void undoEffects(GUIRenderer *guiRenderer)
Undo effects.
Definition: GUINode.cpp:504
GUINode_ComputedConstraints computedConstraints
Definition: GUINode.h:152
GUINodeController * getController()
Definition: GUINode.h:661
GUIScreenNode * screenNode
Definition: GUINode.h:147
GUINode_RequestedConstraints requestedConstraints
Definition: GUINode.h:151
virtual void dispose()
Dispose node.
Definition: GUINode.cpp:462
GUIScreenNode * getScreenNode()
Definition: GUINode.h:325
virtual void applyEffects(GUIRenderer *guiRenderer)
Apply effects.
Definition: GUINode.cpp:480
const string & getId()
Definition: GUINode.h:339
virtual void layout()
Layout.
Definition: GUINode.cpp:191
GUINode_Flow * flow
Definition: GUINode.h:86
GUI parent node base class thats supporting child nodes.
Definition: GUIParentNode.h:42
void getChildControllerNodes(vector< GUINode * > &childControllerNodes, bool requireConditionsMet=false)
Get child controller nodes.
void determineMouseEventNodes(GUIMouseEvent *event, bool floatingNode, unordered_set< string > &eventNodeIds, unordered_set< string > &eventFloatingNodeIds, int flags=DETERMINEMOUSEEVENTNODES_FLAG_NONE) override
Determine mouse event nodes.
void render(GUIRenderer *guiRenderer) override
Render.
virtual void layoutSubNodes()
Layout sub nodes.
vector< GUINode * > subNodes
Definition: GUIParentNode.h:62
void invalidateRenderCaches()
Invalidate render caches.
GUI screen node that represents a screen that can be rendered via GUI system.
Definition: GUIScreenNode.h:72
GUIInputEventHandler * inputEventHandler
void setInputEventHandler(GUIInputEventHandler *inputEventHandler)
Set input event handler.
void getProjectFilePathNameAndFileName(const string &fileName, string &projectFilePathName, string &projectFileFileName)
Get project path and filename of given file name.
const string getNodeType() override
void determineFocussedNodes(GUIParentNode *parentNode, vector< GUIElementNode * > &focusableNodes)
Determine focussed nodes.
bool removeNode(GUINode *node)
Add node.
vector< ForwardEvent > forwardEventList
void scrollToNodeX(const string &node, const string &toNode)
Register deferred scroll to node X.
void forwardEvents()
Forward events.
void removeDragRequestListener(GUIDragRequestListener *listener)
Remove drag request listener.
void scrollToNodeY(const string &node, const string &toNode)
Register deferred scroll to node Y.
void setEnabled(bool enabled)
Set enabled.
GUIInputEventHandler * getInputEventHandler()
unordered_map< string, GUINode * > tickNodesById
Definition: GUIScreenNode.h:90
void forwardChange(GUIElementNode *node)
Forward change event.
void addContextMenuRequestListener(GUIContextMenuRequestListener *listener)
Add context menu request listener.
void addMoveListener(GUIMoveListener *listener)
Add move listener.
vector< GUIMouseOverListener * > mouseOverListener
Definition: GUIScreenNode.h:94
void forwardContextMenuRequest(GUIElementNode *node, int mouseX, int mouseY)
Forward context menu request event.
vector< GUINode * > childControllerNodes
void forwardTooltipShowRequest(GUINode *node, int mouseX, int mouseY)
Forward tooltip show request event.
void addDragRequestListener(GUIDragRequestListener *listener)
Add drag request listener.
void scrollToNodes()
Scroll to nodes.
void addChangeListener(GUIChangeListener *listener)
Add change listener.
void removeContextMenuRequestListener(GUIContextMenuRequestListener *listener)
Remove context menu request listener.
void setPopUp(bool popUp)
Set pop up.
void addTooltipRequestListener(GUITooltipRequestListener *listener)
Add tooltip request listener.
vector< GUIChangeListener * > changeListener
Definition: GUIScreenNode.h:93
void setValues(const unordered_map< string, MutableString > &values)
Set values.
GUINode * forceInvalidateLayout(GUINode *node)
Actually do the invalidate layout.
void removeChangeListener(GUIChangeListener *listener)
Remove change listener.
void removeTooltipRequestListener(GUITooltipRequestListener *listener)
Remove tooltip request listener.
void removeNodeById(const string &nodeId, bool resetScrollOffsets)
Remove GUI node by id.
void layout() override
Layout.
void removeActionListener(GUIActionListener *listener)
Remove action listener.
void removeFocusListener(GUIFocusListener *listener)
Remove focus listener.
void removeMouseOverListener(GUIMouseOverListener *listener)
Remove mouse over listener.
void removeMoveListener(GUIMoveListener *listener)
Remove move listener.
void forwardAction(GUIActionListenerType type, GUIElementNode *node)
Forward action event.
vector< GUIFocusListener * > focusListener
Definition: GUIScreenNode.h:96
bool isMoveAccepted(GUINode *node)
Returns if move is accepted by move listener.
static GUIScreenNode_SizeConstraints createSizeConstraints(const string &minWidth, const string &minHeight, const string &maxWidth, const string &maxHeight)
Create size constraints.
void tick()
Calls registered tick nodes controller tick method.
void addMouseOverListener(GUIMouseOverListener *listener)
Add mouse over listener.
void renderFloatingNodes(GUIRenderer *guiRenderer)
Render floating nodes.
void determineMouseEventNodes(GUIMouseEvent *event, bool floatingNode, unordered_set< string > &eventNodeIds, unordered_set< string > &eventFloatingNodeIds, int flags=DETERMINEMOUSEEVENTNODES_FLAG_NONE) override
Determine mouse event nodes.
unique_ptr< GUIMiniScript > script
unordered_map< string, Texture * > imageCache
GUIScreenNode_SizeConstraints sizeConstraints
vector< ScrollToNodeStruct > scrollToNodesY
void setScreenSize(int width, int height)
Set screen size.
GUIFont * getFont(const string &fileName, int size)
Get font.
void getValues(unordered_map< string, MutableString > &values)
Get values.
vector< GUIActionListener * > actionListener
Definition: GUIScreenNode.h:92
void render(GUIRenderer *guiRenderer) override
Render screen.
void addActionListener(GUIActionListener *listener)
Add action listener.
void initializeMiniScript()
Initialize mini script.
void forwardMove(GUINode *node)
Forward move event.
unordered_map< string, GUINode * > nodesById
Definition: GUIScreenNode.h:89
vector< GUIDragRequestListener * > dragRequestListener
Definition: GUIScreenNode.h:99
void forwardMouseOver(GUIElementNode *node)
Forward mouse over event.
void forwardUnfocus(GUIElementNode *node)
Forward unfocus event.
vector< GUINode * > floatingNodes
Definition: GUIScreenNode.h:91
bool addNode(GUINode *node)
Add node.
void invalidateLayouts()
Actually do the nodes marked for layout invalidation.
unordered_map< int64_t, string > timedExpressions
unordered_map< string, unordered_set< string > > elementNodeToNodeMapping
void setGUI(GUI *gui)
Set GUI.
void forwardTooltipCloseRequest()
Forward tooltip close request event.
vector< GUIMoveListener * > moveListener
Definition: GUIScreenNode.h:97
void forwardMoveRelease(GUINode *node, int mouseX, int mouseY)
Forward move release event.
unordered_map< string, GUIFont * > fontCache
void forwardFocus(GUIElementNode *node)
Forward focus event.
EngineMiniScript::ScriptVariable miniScriptArguments
vector< ScrollToNodeStruct > scrollToNodesX
vector< GUITooltipRequestListener * > tooltipRequestListener
Definition: GUIScreenNode.h:98
void addFocusListener(GUIFocusListener *listener)
Add focus listener.
unordered_set< string > invalidateLayoutNodeIds
Texture * getImage(const string &fileName)
Get image.
void forceLayout(GUINode *node)
Force layout node content (e.g.
void forwardDragRequest(GUIElementNode *node, int mouseX, int mouseY)
Forward drag request event.
vector< GUIContextMenuRequestListener * > contextMenuRequestListener
Definition: GUIScreenNode.h:95
GUINode * getNodeById(const string &nodeId)
Get GUI node by id.
const vector< GUINode * > & getFloatingNodes()
GUI font class.
Definition: GUIFont.h:41
void setRenderAreaLeft(float renderAreaLeft)
Set up render area left.
Definition: GUIRenderer.h:263
void initScreen(GUIScreenNode *screenNode)
Init screen.
void setRenderAreaRight(float renderAreaRight)
Set up render area right.
Definition: GUIRenderer.h:309
void setRenderAreaTop(float renderAreaTop)
Set up render area top.
Definition: GUIRenderer.h:286
void setRenderAreaBottom(float renderAreaBottom)
Set up render area bottom.
Definition: GUIRenderer.h:332
File system singleton class.
Definition: FileSystem.h:17
Integer class.
Definition: Integer.h:25
Mutable utf8 aware string class.
Definition: MutableString.h:23
Properties class, which helps out with storeing or loading key value pairs from/to property files.
Definition: Properties.h:23
std::exception Exception
Exception base class.
Definition: Exception.h:18
GUI action listener interface.
GUI change listener interface.
GUI focus listener interface.
GUI input event handler interface.
GUI move listener interface.
GUI node border entity.
GUI node padding entity.
GUINode_RequestedConstraints_RequestedConstraintsType * widthType
GUINode_RequestedConstraints_RequestedConstraintsType * heightType
GUI node scale 9 grid entity.