TDME2  1.9.200
GUINode.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <array>
5 #include <memory>
6 #include <string>
7 #include <unordered_set>
8 #include <vector>
9 
10 #include <tdme/tdme.h>
11 #include <tdme/engine/Texture.h>
13 #include <tdme/engine/Engine.h>
36 #include <tdme/gui/GUI.h>
37 #include <tdme/math/Vector2.h>
38 #include <tdme/utilities/Action.h>
39 #include <tdme/utilities/Console.h>
40 #include <tdme/utilities/Integer.h>
43 
44 using std::array;
45 using std::find;
46 using std::make_unique;
47 using std::string;
48 using std::to_string;
49 using std::unique_ptr;
50 using std::unordered_set;
51 using std::vector;
52 
54 
80 using tdme::gui::GUI;
87 
88 GUINode::GUINode(
89  GUIScreenNode* screenNode,
90  GUIParentNode* parentNode,
91  const string& id,
92  GUINode_Flow* flow,
93  const GUINode_Alignments& alignments,
94  const GUINode_RequestedConstraints& requestedConstraints,
95  const GUIColor& backgroundColor,
96  const string& backgroundImage,
97  const GUINode_Scale9Grid& backgroundImageScale9Grid,
98  const GUIColor& backgroundImageEffectColorMul,
99  const GUIColor& backgroundImageEffectColorAdd,
100  const GUINode_Border& border,
101  const GUINode_Padding& padding,
102  const GUINodeConditions& showOn,
103  const GUINodeConditions& hideOn,
104  const string& tooltip)
105 {
106  this->screenNode = screenNode;
107  this->parentNode = parentNode;
108  this->id = id;
109  this->flow = flow;
110  this->alignments = alignments;
111  this->requestedConstraints = requestedConstraints;
116  this->backgroundColor = backgroundColor;
117  this->backgroundTexture = nullptr;
118  this->backgroundTextureId = 0;
119  this->setBackgroundImage(backgroundImage);
120  this->backgroundImageScale9Grid = backgroundImageScale9Grid;
121  this->backgroundImageEffectColorMul = backgroundImageEffectColorMul;
122  this->backgroundImageEffectColorAdd = backgroundImageEffectColorAdd;
123  this->border = border;
124  this->padding = padding;
125  this->showOn = showOn;
126  this->hideOn = hideOn;
127  this->tooltip = tooltip;
128  this->guiEffectOffsetX = 0;
129  this->guiEffectOffsetY = 0;
130  this->conditionsMet = false;
131  this->layouted = false;
132  this->haveOutEffect = false;
133  // register this id with related element nodes
134  vector<string> elementNodeDependencies;
135  cfDetermineElementNodeDependencies(elementNodeDependencies);
136  for (const auto& elementNodeId: elementNodeDependencies) screenNode->addNodeElementNodeDependency(elementNodeId, id);
137 }
138 
140  // remove effects
141  vector<string> effectsToRemove;
142  for (const auto& [effectId, effect]: effects) {
143  effectsToRemove.push_back(effectId);
144  }
145  for (const auto& effectToRemoveId: effectsToRemove) {
146  removeEffect(effectToRemoveId);
147  }
148 }
149 
151  string hierarchicalId;
152  auto _parentNode = parentNode;
153  while (_parentNode != nullptr) {
154  hierarchicalId = _parentNode->id + "." + hierarchicalId;
155  _parentNode = _parentNode->parentNode;
156  }
157  hierarchicalId = hierarchicalId + id;
158  return hierarchicalId;
159 }
160 
162 {
164  return getContentWidth();
165  } else {
166  return computedConstraints.width;
167  }
168 }
169 
171 {
173  return getContentHeight();
174  } else {
176  }
177 }
178 
179 void GUINode::setLeft(int left)
180 {
182  computedConstraints.left = left;
183 }
184 
185 void GUINode::setTop(int top)
186 {
188  computedConstraints.top = top;
189 }
190 
192 {
193  if (conditionsMet == false) {
195  return;
196  }
204  layouted = true;
205 }
206 
208 {
209  if (isContentNode() == true) {
210  {
211  auto v = alignments.horizontal;
214  } else
217  } else
220  }
221  }
222 
223  {
224  auto v = alignments.vertical;
227  } else
230  } else
233  }
234  }
235 
236  }
237 }
238 
240 {
242  return 0;
243  } else
245  return value;
246  } else
248  return static_cast<int>((parentValue / 100.0 * value));
249  } else
251  return autoValue;
252  } else
254  return value;
255  } else
257  return value;
258  }
259  return -1;
260 }
261 
262 GUINode_Alignments GUINode::createAlignments(const string& horizontal, const string& vertical)
263 {
265  alignments.horizontal = GUINode_AlignmentHorizontal::valueOf(horizontal.empty() == false && horizontal.length() > 0?StringTools::toUpperCase(horizontal):"LEFT");
266  alignments.vertical = GUINode_AlignmentVertical::valueOf(vertical.empty() == false && vertical.length() > 0?StringTools::toUpperCase(vertical):"TOP");
267  return alignments;
268 }
269 
270 GUINode_RequestedConstraints GUINode::createRequestedConstraints(const string& left, const string& top, const string& width, const string& height, int factor)
271 {
272  GUINode_RequestedConstraints constraints;
274  constraints.left = getRequestedConstraintsValue(StringTools::trim(left), 0);
276  constraints.top = getRequestedConstraintsValue(StringTools::trim(top), 0);
278  constraints.width = getRequestedConstraintsValue(StringTools::trim(width), -1);
280  constraints.height = getRequestedConstraintsValue(StringTools::trim(height), -1);
281  if (constraints.leftType == GUINode_RequestedConstraints_RequestedConstraintsType::PIXEL) constraints.left*= factor;
282  if (constraints.topType == GUINode_RequestedConstraints_RequestedConstraintsType::PIXEL) constraints.top*= factor;
283  if (constraints.widthType == GUINode_RequestedConstraints_RequestedConstraintsType::PIXEL) constraints.width*= factor;
284  if (constraints.heightType == GUINode_RequestedConstraints_RequestedConstraintsType::PIXEL) constraints.height*= factor;
285  return constraints;
286 }
287 
289 {
290  if (constraint.empty() == true || constraint.length() == 0) {
291  return defaultConstraintsType;
292  } else
293  if (constraint.compare("auto") == 0) {
295  } else
296  if (constraint.compare("*") == 0) {
298  } else
299  if (StringTools::endsWith(constraint, "%")) {
301  } else {
303  }
304 }
305 
306 int GUINode::getRequestedConstraintsValue(const string& constraint, int defaultConstraintsValue)
307 {
308  if (constraint.empty() == true || constraint.length() == 0) {
309  return defaultConstraintsValue;
310  } else
311  if (constraint.compare("auto") == 0) {
312  return -1;
313  } else
314  if (constraint.compare("*") == 0) {
315  return -1;
316  } else
317  if (StringTools::endsWith(constraint, "%")) {
318  return (Integer::parse(constraint.substr(0, constraint.length() - 1)));
319  } else {
320  return (Integer::parse(constraint));
321  }
322 }
323 
324 int GUINode::getRequestedPixelValue(const string& value, int defaultValue)
325 {
326  if (value.empty() == true || value.length() == 0) {
327  return defaultValue;
328  } else {
329  return (Integer::parse(value));
330  }
331 }
332 
333 GUIColor GUINode::getRequestedColor(const string& color, const GUIColor& defaultColor)
334 {
335  if (color.empty() == true || color.length() == 0) {
336  return defaultColor;
337  } else {
338  return GUIColor(color);
339  }
340 }
341 
342 GUINode_Flow* GUINode::createFlow(const string& flow)
343 {
344  return GUINode_Flow::valueOf(flow.empty() == false && flow.length() > 0 ? StringTools::toUpperCase(flow) : "INTEGRATED");
345 }
346 
347 GUINode_Border GUINode::createBorder(const string& allBorder, const string& left, const string& top, const string& right, const string& bottom, const string& allBorderColor, const string& leftColor, const string& topColor, const string& rightColor, const string& bottomColor)
348 {
350  border.left = getRequestedPixelValue(allBorder, 0);
351  border.top = getRequestedPixelValue(allBorder, 0);
352  border.right = getRequestedPixelValue(allBorder, 0);
353  border.bottom = getRequestedPixelValue(allBorder, 0);
366  return border;
367 }
368 
369 GUINode_Padding GUINode::createPadding(const string& allPadding, const string& left, const string& top, const string& right, const string& bottom)
370 {
372  padding.left = getRequestedPixelValue(allPadding, 0);
373  padding.top = getRequestedPixelValue(allPadding, 0);
374  padding.right = getRequestedPixelValue(allPadding, 0);
375  padding.bottom = getRequestedPixelValue(allPadding, 0);
380  return padding;
381 }
382 
383 GUINode_Scale9Grid GUINode::createScale9Grid(const string& all, const string& left, const string& top, const string& right, const string& bottom)
384 {
385  GUINode_Scale9Grid scale9Grid;
386  scale9Grid.left = getRequestedPixelValue(all, 0);
387  scale9Grid.top = getRequestedPixelValue(all, 0);
388  scale9Grid.right = getRequestedPixelValue(all, 0);
389  scale9Grid.bottom = getRequestedPixelValue(all, 0);
390  scale9Grid.left = getRequestedPixelValue(left, scale9Grid.left);
391  scale9Grid.top = getRequestedPixelValue(top, scale9Grid.top);
392  scale9Grid.right = getRequestedPixelValue(right, scale9Grid.right);
393  scale9Grid.bottom = getRequestedPixelValue(bottom, scale9Grid.bottom);
394  return scale9Grid;
395 }
396 
398 {
399  auto arguments = 0;
400  GUINodeConditions guiNodeConditions;
401  string condition;
402  for (auto i = 0; i < conditions.size(); i++) {
403  auto c = conditions[i];
404  if (c == '(') {
405  condition+= c;
406  arguments++;
407  } else
408  if (c == ')') {
409  condition+= c;
410  arguments--;
411  } else
412  if (arguments == 0 && c == ',') {
413  guiNodeConditions.add(StringTools::trim(condition));
414  condition.clear();
415  } else {
416  condition+= c;
417  }
418  }
419  if (condition.empty() == false) {
420  guiNodeConditions.add(StringTools::trim(condition));
421  condition.clear();
422  }
423  return guiNodeConditions;
424 }
425 
427 {
428  const auto& showOn = this->showOn.conditions;
429  const auto& hideOn = this->hideOn.conditions;
430 
431  if (showOn.empty() == true && hideOn.empty() == true) return true;
432 
433  for (auto i = 0; i < showOn.size(); i++) {
435  return true;
436 
437  }
438  for (auto i = 0; i < hideOn.size(); i++) {
440  return false;
441 
442  }
443 
444  GUIElementNode* elementNode = nullptr;
445  for (GUINode* node = parentNode; node != nullptr && (elementNode = dynamic_cast<GUIElementNode*>(node)) == nullptr; node = node->parentNode);
446 
447  string function;
448  vector<string> arguments;
449  for (auto i = 0; i < hideOn.size(); i++) {
450  auto conditionTerm = hideOn[i];
451  cfParse(hideOn[i], function, arguments);
452  if (cfCall(elementNode, function, arguments) == true) return false;
453  }
454  for (auto i = 0; i < showOn.size(); i++) {
455  cfParse(showOn[i], function, arguments);
456  if (cfCall(elementNode, function, arguments) == true) return true;
457  }
458 
459  return showOn.empty() == true;
460 }
461 
463 {
464  if (backgroundTexture != nullptr) Engine::getInstance()->getTextureManager()->removeTexture(backgroundTexture->getId());
465  if (controller != nullptr) controller->dispose();
466 }
467 
469 {
471 }
472 
474  if (conditionsMet == false || layouted == true) return;
475  if (conditionsMet == true && layouted == false) {
476  screenNode->forceLayout(this);
477  }
478 }
479 
480 void GUINode::applyEffects(GUIRenderer* guiRenderer) {
481  if (hasEffects() == false) return;
482  vector<Action*> actions;
483  vector<GUIEffect*> activeEffects;
484  for (const auto& [effectId, effect]: effects) {
485  if (effect->isActive() == true) {
486  if (effect->update(guiRenderer) == true && effect->getAction() != nullptr) {
487  actions.push_back(effect->getAction());
488  } else {
489  activeEffects.push_back(effect);
490  }
491  }
492  }
493  for (auto action: actions) action->performAction();
494  if (activeEffects.empty() == true) {
495  auto defaultEffect = getEffect("tdme.xmleffect.default");
496  if (defaultEffect != nullptr) {
497  defaultEffect->start();
498  activeEffects.push_back(defaultEffect);
499  }
500  }
501  guiRenderer->pushEffects(activeEffects);
502 }
503 
504 void GUINode::undoEffects(GUIRenderer* guiRenderer) {
505  if (hasEffects() == false) return;
506  guiRenderer->popEffects();
507 }
508 
509 void GUINode::render(GUIRenderer* guiRenderer)
510 {
511  layoutOnDemand();
512 
513  if (shouldRender() == false) return;
514 
515  // floating nodes should always be in screen constraints
516  // if this does not seem to be feasible we can add a property for this
517  if (flow == GUINode_Flow::FLOATING) {
518  if (computedConstraints.left < 0) {
519  setLeft(0);
520  } else
523  }
524  if (computedConstraints.top < 0) {
525  setTop(0);
526  } else
529  }
530  }
531 
532  //
533  auto screenWidth = screenNode->getScreenWidth();
534  auto screenHeight = screenNode->getScreenHeight();
537  float width = computedConstraints.width - border.left - border.right;
538  float height = computedConstraints.height - border.top - border.bottom;
540  auto bgColorData = &backgroundColor.getArray();
541  guiRenderer->addQuad(
542  ((left) / (screenWidth / 2.0f)) - 1.0f,
543  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
544  (*bgColorData)[0],
545  (*bgColorData)[1],
546  (*bgColorData)[2],
547  (*bgColorData)[3],
548  0.0f,
549  1.0f,
550  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
551  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
552  (*bgColorData)[0],
553  (*bgColorData)[1],
554  (*bgColorData)[2],
555  (*bgColorData)[3],
556  1.0f,
557  1.0f,
558  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
559  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
560  (*bgColorData)[0],
561  (*bgColorData)[1],
562  (*bgColorData)[2],
563  (*bgColorData)[3],
564  1.0f,
565  0.0f,
566  ((left) / (screenWidth / 2.0f)) - 1.0f,
567  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
568  (*bgColorData)[0],
569  (*bgColorData)[1],
570  (*bgColorData)[2],
571  (*bgColorData)[3],
572  0.0f,
573  0.0f
574  );
575  guiRenderer->render();
576  }
577  if (backgroundTexture != nullptr) {
578  guiRenderer->bindTexture(backgroundTextureId);
582  if (scale9Grid.left == 0 &&
583  scale9Grid.right == 0 &&
584  scale9Grid.top == 0 &&
585  scale9Grid.bottom == 0) {
588  float width = computedConstraints.width - border.left - border.right;
589  float height = computedConstraints.height - border.top - border.bottom;
590  guiRenderer->addQuad(
591  ((left) / (screenWidth / 2.0f)) - 1.0f,
592  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
593  1.0f, 1.0f, 1.0f, 1.0f,
594  0.0f,
595  0.0f,
596  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
597  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
598  1.0f, 1.0f, 1.0f, 1.0f,
599  1.0f,
600  0.0f,
601  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
602  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
603  1.0f, 1.0f, 1.0f, 1.0f,
604  1.0f,
605  1.0f,
606  ((left) / (screenWidth / 2.0f)) - 1.0f,
607  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
608  1.0f, 1.0f, 1.0f, 1.0f,
609  0.0f,
610  1.0f
611  );
612  } else {
613  auto scaleX = 1.0f;
614  auto scaleY = 1.0f;
615  // we have a scale here, because we have a axis without "scale9grid"
616  if (scale9Grid.top == 0 && scale9Grid.bottom == 0) {
617  scaleY = (float)getContentHeight() / (float)backgroundTexture->getHeight();
618  scaleX = scaleY;
619  } else
620  // we have a scale here, because we have a axis without "scale9grid"
621  if (scale9Grid.left == 0 && scale9Grid.right == 0) {
622  scaleX = (float)getContentWidth() / (float)backgroundTexture->getWidth();
623  scaleY = scaleX;
624  } else {
625  // scale Y if content height is too small to fit scale 9 top and bottom
626  if (getContentHeight() < scale9Grid.top + scale9Grid.bottom) {
627  scaleY = getContentHeight() < Math::EPSILON?0.0f:(float)getContentHeight() / (float)(scale9Grid.top + scale9Grid.bottom);
628  }
629  // scale X if content width is too small to fit scale 9 left and top
630  if (getContentWidth() < scale9Grid.left + scale9Grid.right) {
631  scaleX = getContentWidth() < Math::EPSILON?0.0f:(float)getContentWidth() / (float)(scale9Grid.left + scale9Grid.right);
632  }
633  }
634  // we have no certain scale, take original image size
635  GUINode_Scale9Grid scaledScale9Grid;
636  scaledScale9Grid.left = static_cast<int>(scale9Grid.left * scaleX);
637  scaledScale9Grid.right = static_cast<int>(scale9Grid.right * scaleX);
638  scaledScale9Grid.top = static_cast<int>(scale9Grid.top * scaleY);
639  scaledScale9Grid.bottom = static_cast<int>(scale9Grid.bottom * scaleY);
642  float imageScreenScale9Left = imageScreenLeft + scaledScale9Grid.left;
643  float imageScreenScale9Top = imageScreenTop + scaledScale9Grid.top;
644  float imageScreenWidth = computedConstraints.width - border.left - border.right;
645  float imageScreenHeight = computedConstraints.height - border.top - border.bottom;
646  float imageScreenScale9Right = imageScreenLeft + imageScreenWidth - scaledScale9Grid.right;
647  float imageScreenScale9Bottom = imageScreenTop + imageScreenHeight - scaledScale9Grid.bottom;
648  float imageTextureWidth = backgroundTexture->getWidth();
649  float imageTextureHeight = backgroundTexture->getHeight();
650  // left top
651  if (scaledScale9Grid.left > 0 && scaledScale9Grid.top > 0) {
652  float left = imageScreenLeft;
653  float top = imageScreenTop;
654  float width = scaledScale9Grid.left;
655  float height = scaledScale9Grid.top;
656  guiRenderer->addQuad(
657  ((left) / (screenWidth / 2.0f)) - 1.0f,
658  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
659  1.0f, 1.0f, 1.0f, 1.0f,
660  0.0f,
661  0.0f,
662  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
663  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
664  1.0f, 1.0f, 1.0f, 1.0f,
665  1.0f / imageTextureWidth * scale9Grid.left,
666  0.0f,
667  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
668  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
669  1.0f, 1.0f, 1.0f, 1.0f,
670  1.0f / imageTextureWidth * scale9Grid.left,
671  1.0f / imageTextureHeight * scale9Grid.top,
672  ((left) / (screenWidth / 2.0f)) - 1.0f,
673  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
674  1.0f, 1.0f, 1.0f, 1.0f,
675  0.0f,
676  1.0f / imageTextureHeight * scale9Grid.top
677  );
678  }
679  // middle top
680  if (scaledScale9Grid.top > 0) {
681  float left = imageScreenScale9Left;
682  float top = imageScreenTop;
683  float width = imageScreenWidth - scaledScale9Grid.left - scaledScale9Grid.right;
684  float height = scaledScale9Grid.top;
685  guiRenderer->addQuad(
686  ((left) / (screenWidth / 2.0f)) - 1.0f,
687  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
688  1.0f, 1.0f, 1.0f, 1.0f,
689  1.0f / imageTextureWidth * scale9Grid.left,
690  0.0f,
691  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
692  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
693  1.0f, 1.0f, 1.0f, 1.0f,
694  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
695  0.0f,
696  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
697  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
698  1.0f, 1.0f, 1.0f, 1.0f,
699  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
700  1.0f / imageTextureHeight * scale9Grid.top,
701  ((left) / (screenWidth / 2.0f)) - 1.0f,
702  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
703  1.0f, 1.0f, 1.0f, 1.0f,
704  1.0f / imageTextureWidth * scale9Grid.left,
705  1.0f / imageTextureHeight * scale9Grid.top
706  );
707  }
708  // right top
709  if (scaledScale9Grid.right > 0 && scaledScale9Grid.top > 0) {
710  float left = imageScreenScale9Right;
711  float top = imageScreenTop;
712  float width = scaledScale9Grid.right;
713  float height = scaledScale9Grid.top;
714  guiRenderer->addQuad(
715  ((left) / (screenWidth / 2.0f)) - 1.0f,
716  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
717  1.0f, 1.0f, 1.0f, 1.0f,
718  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
719  0.0f,
720  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
721  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
722  1.0f, 1.0f, 1.0f, 1.0f,
723  1.0f,
724  0.0f,
725  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
726  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
727  1.0f, 1.0f, 1.0f, 1.0f,
728  1.0f,
729  1.0f / imageTextureHeight * scale9Grid.top,
730  ((left) / (screenWidth / 2.0f)) - 1.0f,
731  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
732  1.0f, 1.0f, 1.0f, 1.0f,
733  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
734  1.0f / imageTextureHeight * scale9Grid.top
735  );
736  }
737  // right bottom
738  if (scaledScale9Grid.right > 0 && scaledScale9Grid.bottom > 0) {
739  float left = imageScreenScale9Right;
740  float top = imageScreenScale9Bottom;
741  float width = scaledScale9Grid.right;
742  float height = scaledScale9Grid.bottom;
743  guiRenderer->addQuad(
744  ((left) / (screenWidth / 2.0f)) - 1.0f,
745  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
746  1.0f, 1.0f, 1.0f, 1.0f,
747  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
748  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
749  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
750  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
751  1.0f, 1.0f, 1.0f, 1.0f,
752  1.0f,
753  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
754  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
755  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
756  1.0f, 1.0f, 1.0f, 1.0f,
757  1.0f,
758  1.0f,
759  ((left) / (screenWidth / 2.0f)) - 1.0f,
760  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
761  1.0f, 1.0f, 1.0f, 1.0f,
762  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
763  1.0f
764  );
765  }
766  // middle bottom
767  if (scaledScale9Grid.bottom > 0) {
768  float left = imageScreenScale9Left;
769  float top = imageScreenScale9Bottom;
770  float width = imageScreenWidth - scaledScale9Grid.left - scaledScale9Grid.right;
771  float height = scaledScale9Grid.bottom;
772  guiRenderer->addQuad(
773  ((left) / (screenWidth / 2.0f)) - 1.0f,
774  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
775  1.0f, 1.0f, 1.0f, 1.0f,
776  1.0f / imageTextureWidth * scale9Grid.left,
777  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
778  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
779  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
780  1.0f, 1.0f, 1.0f, 1.0f,
781  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
782  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
783  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
784  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
785  1.0f, 1.0f, 1.0f, 1.0f,
786  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
787  1.0f,
788  ((left) / (screenWidth / 2.0f)) - 1.0f,
789  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
790  1.0f, 1.0f, 1.0f, 1.0f,
791  1.0f / imageTextureWidth * scale9Grid.left,
792  1.0f
793  );
794  }
795  // left bottom
796  if (scaledScale9Grid.left > 0 && scaledScale9Grid.bottom > 0) {
797  float left = imageScreenLeft;
798  float top = imageScreenScale9Bottom;
799  float width = scaledScale9Grid.left;
800  float height = scaledScale9Grid.bottom;
801  guiRenderer->addQuad(
802  ((left) / (screenWidth / 2.0f)) - 1.0f,
803  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
804  1.0f, 1.0f, 1.0f, 1.0f,
805  0.0f,
806  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
807  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
808  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
809  1.0f, 1.0f, 1.0f, 1.0f,
810  1.0f / imageTextureWidth * scale9Grid.left,
811  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
812  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
813  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
814  1.0f, 1.0f, 1.0f, 1.0f,
815  1.0f / imageTextureWidth * scale9Grid.left,
816  1.0f,
817  ((left) / (screenWidth / 2.0f)) - 1.0f,
818  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
819  1.0f, 1.0f, 1.0f, 1.0f,
820  0.0f,
821  1.0f
822  );
823  }
824  // left middle
825  if (scaledScale9Grid.left > 0) {
826  float left = imageScreenLeft;
827  float top = imageScreenScale9Top;
828  float width = scaledScale9Grid.left;
829  float height = imageScreenHeight - scaledScale9Grid.top - scaledScale9Grid.bottom;
830  guiRenderer->addQuad(
831  ((left) / (screenWidth / 2.0f)) - 1.0f,
832  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
833  1.0f, 1.0f, 1.0f, 1.0f,
834  0.0f,
835  1.0f / imageTextureHeight * scale9Grid.top,
836  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
837  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
838  1.0f, 1.0f, 1.0f, 1.0f,
839  1.0f / imageTextureWidth * scale9Grid.left,
840  1.0f / imageTextureHeight * scale9Grid.top,
841  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
842  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
843  1.0f, 1.0f, 1.0f, 1.0f,
844  1.0f / imageTextureWidth * scale9Grid.left,
845  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
846  ((left) / (screenWidth / 2.0f)) - 1.0f,
847  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
848  1.0f, 1.0f, 1.0f, 1.0f,
849  0.0f,
850  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom)
851  );
852  }
853  // right middle
854  if (scaledScale9Grid.right > 0) {
855  float left = imageScreenScale9Right;
856  float top = imageScreenScale9Top;
857  float width = scaledScale9Grid.right;
858  float height = imageScreenHeight - scaledScale9Grid.top - scaledScale9Grid.bottom;
859  guiRenderer->addQuad(
860  ((left) / (screenWidth / 2.0f)) - 1.0f,
861  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
862  1.0f, 1.0f, 1.0f, 1.0f,
863  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
864  1.0f / imageTextureHeight * scale9Grid.top,
865  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
866  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
867  1.0f, 1.0f, 1.0f, 1.0f,
868  1.0f,
869  1.0f / imageTextureHeight * scale9Grid.top,
870  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
871  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
872  1.0f, 1.0f, 1.0f, 1.0f,
873  1.0f,
874  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
875  ((left) / (screenWidth / 2.0f)) - 1.0f,
876  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
877  1.0f, 1.0f, 1.0f, 1.0f,
878  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
879  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom)
880  );
881  }
882  // center
883  {
884  float left = imageScreenScale9Left;
885  float top = imageScreenScale9Top;
886  float width = imageScreenWidth - scaledScale9Grid.left - scaledScale9Grid.right;
887  float height = imageScreenHeight - scaledScale9Grid.top - scaledScale9Grid.bottom;
888  guiRenderer->addQuad(
889  ((left) / (screenWidth / 2.0f)) - 1.0f,
890  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
891  1.0f, 1.0f, 1.0f, 1.0f,
892  1.0f / imageTextureWidth * scale9Grid.left,
893  1.0f / imageTextureHeight * scale9Grid.top,
894  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
895  ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f,
896  1.0f, 1.0f, 1.0f, 1.0f,
897  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
898  1.0f / imageTextureHeight * scale9Grid.top,
899  ((left + width) / (screenWidth / 2.0f)) - 1.0f,
900  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
901  1.0f, 1.0f, 1.0f, 1.0f,
902  1.0f / imageTextureWidth * (imageTextureWidth - scale9Grid.right),
903  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom),
904  ((left) / (screenWidth / 2.0f)) - 1.0f,
905  ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f,
906  1.0f, 1.0f, 1.0f, 1.0f,
907  1.0f / imageTextureWidth * scale9Grid.left,
908  1.0f / imageTextureHeight * (imageTextureHeight - scale9Grid.bottom)
909  );
910  }
911  }
912  guiRenderer->render();
913  guiRenderer->bindTexture(0);
914  }
915  if (border.top > 0 || border.left > 0 || border.right > 0 || border.bottom > 0) {
916  if (border.top > 0) {
919  float width = computedConstraints.width;
920  float height = border.top;
921  const auto& borderColor = border.topColor;
922  guiRenderer->addQuad(((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 0.0f, ((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 0.0f);
923  }
924  if (border.bottom > 0) {
927  float width = computedConstraints.width;
928  float height = border.bottom;
929  const auto& borderColor = border.bottomColor;
930  guiRenderer->addQuad(((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 0.0f, ((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 0.0f);
931  }
932  if (border.left > 0) {
935  float width = border.left;
936  float height = computedConstraints.height;
937  const auto& borderColor = border.leftColor;
938  guiRenderer->addQuad(((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 0.0f, ((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 0.0f);
939  }
940  if (border.right > 0) {
943  float width = border.right;
944  float height = computedConstraints.height;
945  const auto& borderColor = border.rightColor;
946  guiRenderer->addQuad(((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 1.0f, ((left + width) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 1.0f, 0.0f, ((left) / (screenWidth / 2.0f)) - 1.0f, ((screenHeight - top - height) / (screenHeight / 2.0f)) - 1.0f, borderColor[0], borderColor[1], borderColor[2], borderColor[3], 0.0f, 0.0f);
947  }
948  guiRenderer->render();
949  }
950 }
951 
953  auto childrenRenderOffSetX = 0.0f;
954  auto parentNode = this->parentNode;
955  while (parentNode != nullptr) {
956  childrenRenderOffSetX += parentNode->getChildrenRenderOffsetX();
958  }
959  return childrenRenderOffSetX;
960 }
961 
963  auto childrenRenderOffSetY = 0.0f;
964  auto parentNode = this->parentNode;
965  while (parentNode != nullptr) {
966  childrenRenderOffSetY += parentNode->getChildrenRenderOffsetY();
968  }
969  return childrenRenderOffSetY;
970 }
971 
973 {
974  auto eventXScreen = event->getX();
975  auto eventYScreen = event->getY();
976  auto eventX = eventXScreen + computeParentChildrenRenderOffsetXTotal();
977  auto eventY = eventYScreen + computeParentChildrenRenderOffsetYTotal();
982  if (eventX < left) {
983  position[0] = static_cast<int>((eventX - left));
984  } else if (eventX > right) {
985  position[0] = static_cast<int>((eventX - right));
986  } else {
987  position[0] = 0;
988  }
989  if (eventY < top) {
990  position[1] = static_cast<int>((eventY - top));
991  } else if (eventY > bottom) {
992  position[1] = static_cast<int>((eventY - bottom));
993  } else {
994  position[1] = 0;
995  }
996 }
997 
999  auto eventXScreen = event->getX();
1000  auto eventYScreen = event->getY();
1001  auto eventX = eventXScreen + computeParentChildrenRenderOffsetXTotal();
1002  auto eventY = eventYScreen + computeParentChildrenRenderOffsetYTotal();
1007  position[0] = Math::clamp(static_cast<int>(eventX), left, right) - left;
1008  position[1] = Math::clamp(static_cast<int>(eventY), top, bottom) - top;
1009 }
1010 
1012 {
1013  auto node = this->parentNode;
1014  while (node != nullptr && node->controller == nullptr) {
1015  node = node->parentNode;
1016  }
1017  return node;
1018 }
1019 
1020 void GUINode::determineNodesByCoordinate(const Vector2& coordinate, unordered_set<string>& nodeIds) {
1021  if (conditionsMet == false)
1022  return;
1023 
1024  //
1025  if (isCoordinateBelongingToNode(coordinate) == true) {
1026  nodeIds.insert(id);
1027  }
1028 }
1029 
1030 void GUINode::determineMouseEventNodes(GUIMouseEvent* event, bool floatingNode, unordered_set<string>& eventNodeIds, unordered_set<string>& eventFloatingNodeIds, int flags)
1031 {
1032  if (conditionsMet == false)
1033  return;
1034 
1035  // node belongs to event?
1036  if (isEventBelongingToNode(event) == true) {
1038  // no op
1039  } else {
1040  // yep insert
1041  if (floatingNode == true || flow == GUINode_Flow::FLOATING) eventFloatingNodeIds.insert(id); else eventNodeIds.insert(id);
1042  }
1043  }
1044 }
1045 
1047 {
1048  if (this->getController() != nullptr) {
1049  screenNode->removeTickNode(this);
1050  this->getController()->dispose();
1051  }
1052  this->controller = unique_ptr<GUINodeController>(controller);
1053 }
1054 
1056 {
1057  if (layouted == false) return;
1058  auto scrollXParentNode = this->parentNode;
1059  while (true == true) {
1060  if (scrollXParentNode == toNode || scrollXParentNode == nullptr)
1061  return;
1062 
1063  if (scrollXParentNode->overflowX == GUIParentNode_Overflow::SCROLL) {
1064  break;
1065  }
1066  scrollXParentNode = scrollXParentNode->parentNode;
1067  }
1068  if (computedConstraints.left < scrollXParentNode->getChildrenRenderOffsetX() + scrollXParentNode->computedConstraints.left) {
1069  scrollXParentNode->setChildrenRenderOffsetX(computedConstraints.left - scrollXParentNode->computedConstraints.left);
1070  }
1071  if (computedConstraints.left + computedConstraints.width > scrollXParentNode->getChildrenRenderOffsetX() + scrollXParentNode->computedConstraints.left + scrollXParentNode->computedConstraints.width) {
1072  scrollXParentNode->setChildrenRenderOffsetX(computedConstraints.left + computedConstraints.width - scrollXParentNode->computedConstraints.left - scrollXParentNode->computedConstraints.width);
1073  }
1074  scrollXParentNode->_scrollToNodeX(toNode);
1075 }
1076 
1078 {
1079  if (layouted == false) return;
1080  auto scrollYParentNode = this->parentNode;
1081  while (true == true) {
1082  if (scrollYParentNode == toNode || scrollYParentNode == nullptr) return;
1083  if (scrollYParentNode->overflowY == GUIParentNode_Overflow::SCROLL) break;
1084  scrollYParentNode = scrollYParentNode->parentNode;
1085  if (scrollYParentNode == nullptr) return;
1086  }
1087  if (computedConstraints.top < scrollYParentNode->getChildrenRenderOffsetY() + scrollYParentNode->computedConstraints.top) {
1088  scrollYParentNode->setChildrenRenderOffsetY(computedConstraints.top - scrollYParentNode->computedConstraints.top);
1089  }
1090  if (computedConstraints.top + computedConstraints.height > scrollYParentNode->getChildrenRenderOffsetY() + scrollYParentNode->computedConstraints.top + scrollYParentNode->computedConstraints.height) {
1091  scrollYParentNode->setChildrenRenderOffsetY(computedConstraints.top + computedConstraints.height - scrollYParentNode->computedConstraints.top - scrollYParentNode->computedConstraints.height);
1092  }
1093  scrollYParentNode->_scrollToNodeY(toNode);
1094 }
1095 
1097  screenNode->scrollToNodeX(getId(), toNode != nullptr?toNode->getId():string());
1098 }
1099 
1101  screenNode->scrollToNodeY(getId(), toNode != nullptr?toNode->getId():string());
1102 }
1103 
1104 void GUINode::dumpNode(GUINode* node, int depth, int indent, int depthIdx) {
1105  string indentString;
1106  for (auto i = 0; i < indent; i++) indentString+= " ";
1107  Console::println(
1108  indentString +
1109  node->id + ": " +
1110  node->getNodeType() + ": constaints: " +
1111  to_string(node->computedConstraints.left) + ", " +
1112  to_string(node->computedConstraints.top) + "; " +
1113  to_string(node->computedConstraints.width) + ", " +
1114  to_string(node->computedConstraints.height) + "; alignment: " +
1115  to_string(node->computedConstraints.alignmentLeft) + ", " +
1116  to_string(node->computedConstraints.alignmentTop) + "; content alignment: " +
1117  to_string(node->computedConstraints.contentAlignmentLeft) + ", " +
1118  to_string(node->computedConstraints.contentAlignmentTop) + "; " +
1119  StringTools::substring(node->requestedConstraints.leftType->getName(), 0, 2) + "/" +
1120  StringTools::substring(node->requestedConstraints.topType->getName(), 0, 2) + "/" +
1121  StringTools::substring(node->requestedConstraints.widthType->getName(), 0, 2) + "/" +
1122  StringTools::substring(node->requestedConstraints.heightType->getName(), 0, 2) + ";" +
1123  ": conditions met: " +
1124  to_string(node->conditionsMet) + "; layouted: " +
1125  to_string(node->layouted) +
1126  (dynamic_cast<GUIParentNode*>(node) != nullptr?"; child count: " + to_string(dynamic_cast<GUIParentNode*>(node)->subNodes.size()):"") +
1127  (node->getController() != nullptr?"; controller attached":"; no controller")
1128  );
1129  if (dynamic_cast<GUIParentNode*>(node) != nullptr && (depth == 0 || depthIdx + 1 < depth)) {
1130  auto parentNode = required_dynamic_cast<GUIParentNode*>(node);
1131  for (auto subNode: parentNode->subNodes) {
1132  dumpNode(subNode, depth, indent + 1, depthIdx + 1);
1133  }
1134  }
1135 }
1136 
1137 void GUINode::dumpParentNodes(GUINode* node, int indent) {
1138  string indentString;
1139  for (auto i = 0; i < indent; i++) indentString+= " ";
1140  Console::println(
1141  indentString +
1142  node->id + ": " +
1143  node->getNodeType() + ": constaints: " +
1144  to_string(node->computedConstraints.left) + ", " +
1145  to_string(node->computedConstraints.top) + ", " +
1146  to_string(node->computedConstraints.width) + "; " +
1147  to_string(node->computedConstraints.height) + ", alignment: " +
1148  to_string(node->computedConstraints.alignmentLeft) + ", " +
1149  to_string(node->computedConstraints.alignmentTop) + "; content alignment: " +
1150  to_string(node->computedConstraints.contentAlignmentLeft) + ", " +
1151  to_string(node->computedConstraints.contentAlignmentTop) + "; " +
1152  StringTools::substring(node->requestedConstraints.leftType->getName(), 0, 2) + "/" +
1153  StringTools::substring(node->requestedConstraints.topType->getName(), 0, 2) + "/" +
1154  StringTools::substring(node->requestedConstraints.widthType->getName(), 0, 2) + "/" +
1155  StringTools::substring(node->requestedConstraints.heightType->getName(), 0, 2) + ";" +
1156  ": conditions met: " +
1157  to_string(node->conditionsMet) + "; layouted: " +
1158  to_string(node->layouted) +
1159  (dynamic_cast<GUIParentNode*>(node) != nullptr?"; child count: " + to_string(dynamic_cast<GUIParentNode*>(node)->subNodes.size()):"")
1160  );
1161  if (node->parentNode != nullptr) dumpParentNodes(node->parentNode, indent + 1);
1162 }
1163 
1164 void GUINode::cfDetermineElementNodeDependencies(vector<string>& elementNodeDependencies) {
1165  const auto& showOn = this->showOn.conditions;
1166  const auto& hideOn = this->hideOn.conditions;
1167 
1168  StringTokenizer t;
1169  string function;
1170  vector<string> arguments;
1171  for (auto i = 0; i < hideOn.size(); i++) {
1172  auto conditionTerm = hideOn[i];
1173  cfParse(hideOn[i], function, arguments);
1174  cfCallDetermineElementNodeDependencies(function, arguments, elementNodeDependencies);
1175  }
1176  for (auto i = 0; i < showOn.size(); i++) {
1177  cfParse(showOn[i], function, arguments);
1178  cfCallDetermineElementNodeDependencies(function, arguments, elementNodeDependencies);
1179  }
1180 
1181 }
1182 
1183 void GUINode::cfParse(const string& term, string& function, vector<string>& arguments) {
1184  auto leftParenthesis = term.find('(');
1185  auto rightParenthesis = term.find_last_of(')');
1186  function = "hasCondition";
1187  if (leftParenthesis != string::npos && rightParenthesis != string::npos && leftParenthesis < rightParenthesis) {
1188  function = StringTools::trim(StringTools::substring(term, 0, leftParenthesis));
1189  }
1190  arguments.clear();
1191  auto argumentStartIdx = leftParenthesis != string::npos?leftParenthesis + 1:0;
1192  auto argumentEndIdx = rightParenthesis != string::npos?rightParenthesis:term.size();
1193  auto quote = false;
1194  auto doubleQuote = false;
1195  string argument;
1196  for (auto i = argumentStartIdx; i < argumentEndIdx; i++) {
1197  auto c = term[i];
1198  if (c == '\'') {
1199  argument+= c;
1200  if (quote == true) {
1201  quote = false;
1202  arguments.push_back(StringTools::trim(argument));
1203  argument.clear();
1204  }
1205  } else
1206  if (c == '\"') {
1207  argument+= c;
1208  if (doubleQuote == true) {
1209  doubleQuote = false;
1210  arguments.push_back(argument);
1211  argument.clear();
1212  }
1213  } else
1214  if (quote == false && doubleQuote == false && c == ',') {
1215  arguments.push_back(StringTools::trim(argument));
1216  argument.clear();
1217  } else {
1218  argument+= c;
1219  }
1220  }
1221  if (argument.empty() == false) {
1222  arguments.push_back(StringTools::trim(argument));
1223  argument.clear();
1224  }
1225 }
1226 
1227 bool GUINode::cfCall(GUIElementNode* elementNode, const string& function, const vector<string>& arguments) {
1228  if (function == "empty") {
1229  return cfEmpty(arguments);
1230  } else
1231  if (function == "notEmpty") {
1232  return cfEmpty(arguments) == false;
1233  } else
1234  if (function == "hasCondition") {
1235  return cfHasCondition(elementNode, arguments);
1236  } else {
1237  Console::println("GUINode::cfCall(): Unknown function: " + function + ": returning false");
1238  return false;
1239  }
1240 }
1241 
1242 void GUINode::cfCallDetermineElementNodeDependencies(const string& function, const vector<string>& arguments, vector<string>& elementNodeDependencies) {
1243  if (function == "empty") {
1244  // no op
1245  } else
1246  if (function == "notEmpty") {
1247  // no op
1248  } else
1249  if (function == "hasCondition") {
1250  cfHasConditionDetermineElementNodeDependencies(arguments, elementNodeDependencies);
1251  } else {
1252  Console::println("GUINode::cfCallDetermineElementNodeDependencies(): Unknown function: " + function + ": returning false");
1253  }
1254 
1255 }
1256 
1257 bool GUINode::cfHasCondition(GUIElementNode* elementNode, const vector<string>& arguments) {
1258  StringTokenizer t;
1259  for (const auto& argument: arguments) {
1260  string elementNodeId;
1261  auto condition = argument;
1262  if (condition.find('.') != -1) {
1263  t.tokenize(condition, ".");
1264  elementNodeId = t.nextToken();
1265  condition = t.nextToken();
1266  }
1267  auto elementNodeToCheck = elementNodeId.size() == 0?elementNode:dynamic_cast<GUIElementNode*>(screenNode->getNodeById(elementNodeId));
1268  if (elementNodeToCheck == nullptr) {
1269  Console::println("GUINode::checkConditions(): element node '" + elementNodeId + "': not found");
1270  continue;
1271  }
1272  if (elementNodeToCheck->activeConditions.has(condition) == true) return true;
1273  }
1274  return false;
1275 }
1276 
1277 void GUINode::cfHasConditionDetermineElementNodeDependencies(const vector<string>& arguments, vector<string>& elementNodeDependencies) {
1278  StringTokenizer t;
1279  for (const auto& argument: arguments) {
1280  string elementNodeId;
1281  auto condition = argument;
1282  if (condition.find('.') != string::npos) {
1283  t.tokenize(condition, ".");
1284  elementNodeId = t.nextToken();
1285  condition = t.nextToken();
1286  }
1287  if (elementNodeId.empty() == false) {
1288  elementNodeDependencies.push_back(elementNodeId);
1289  }
1290  }
1291 }
1292 
1293 bool GUINode::cfEmpty(const vector<string>& arguments) {
1294  for (const auto& argument: arguments) {
1295  if (argument == "false" ||
1296  argument == "0" ||
1297  argument == "0.0" ||
1298  argument == "\"\"" ||
1299  argument == "''") return true;
1300  }
1301  return false;
1302 }
1303 
1304 void GUINode::setBackgroundImage(const string& backgroundImage) {
1305  if (backgroundTexture != nullptr) {
1306  Engine::getInstance()->getTextureManager()->removeTexture(backgroundTexture->getId());
1307  backgroundTexture = nullptr;
1308  backgroundTextureId = 0;
1309  }
1310  if (backgroundImage.length() > 0) {
1311  backgroundTexture = screenNode->getImage(backgroundImage);
1312  backgroundTextureId = Engine::getInstance()->getTextureManager()->addTexture(backgroundTexture, 0);
1313  }
1314 }
1315 
1316 void GUINode::addEffect(const string& id, GUIEffect* effect)
1317 {
1318  removeEffect(id);
1319  if (effectState == nullptr && effects.empty() == true) effectState = make_unique<GUIEffectState>();
1320  effects[id] = effect;
1321 }
1322 
1323 GUIEffect* GUINode::getEffect(const string& id)
1324 {
1325  auto effectIt = effects.find(id);
1326  if (effectIt == effects.end()) return nullptr;
1327  return effectIt->second;
1328 }
1329 
1330 void GUINode::removeEffect(const string& id)
1331 {
1332  auto effectIt = effects.find(id);
1333  if (effectIt == effects.end()) return;
1334  delete effectIt->second;
1335  effects.erase(effectIt);
1336  if (effectState != nullptr && effects.empty() == true) {
1337  effectState = nullptr;
1338  }
1339 }
1340 
1341 void GUINode::onSetConditions(const vector<string>& conditions) {
1342  // no op if no effects
1343  if (hasEffects() == false) return;
1344 
1345  //
1346  auto defaultEffect = getEffect("tdme.xmleffect.default");
1347 
1348  //
1349  auto haveInEffect = false;
1350  auto issuedOutEffect = false;
1351 
1352  //
1353  for (const auto& condition: conditions) {
1354  {
1355  auto effect = getEffect("tdme.xmleffect.in.color.on." + condition);
1356  if (effect != nullptr && effect->isActive() == false) {
1357  haveInEffect = true;
1358  effect->start();
1359  }
1360  }
1361  {
1362  auto effect = getEffect("tdme.xmleffect.in.position.on." + condition);
1363  if (effect != nullptr && effect->isActive() == false) {
1364  haveInEffect = true;
1365  effect->start();
1366  }
1367  }
1368  }
1369  if (haveInEffect == true) {
1370  if (defaultEffect != nullptr) defaultEffect->stop();
1371  for (const auto& [effectId, effect]: effects) {
1372  if (StringTools::startsWith(effectId, "tdme.xmleffect.out.") == true && effect->isActive() == true) {
1373  effect->stop();
1374  }
1375  }
1376  } else {
1377  for (const auto& condition: lastConditions) {
1378  if (find(conditions.begin(), conditions.end(), condition) != conditions.end()) continue;
1379  {
1380  auto effect = getEffect("tdme.xmleffect.out.color.on." + condition);
1381  if (effect != nullptr && effect->isActive() == false) {
1382  issuedOutEffect = true;
1383  haveOutEffect = true;
1384  effect->start();
1385  }
1386  }
1387  {
1388  auto effect = getEffect("tdme.xmleffect.out.position.on." + condition);
1389  if (effect != nullptr && effect->isActive() == false) {
1390  issuedOutEffect = true;
1391  haveOutEffect = true;
1392  effect->start();
1393  }
1394  }
1395  }
1396  if (issuedOutEffect == true) {
1397  if (defaultEffect != nullptr && defaultEffect->isActive() == true) defaultEffect->stop();
1398  for (const auto& [effectId, effect]: effects) {
1399  if (StringTools::startsWith(effectId, "tdme.xmleffect.in.") == true && effect->isActive() == true) {
1400  effect->stop();
1401  }
1402  }
1403  }
1404  }
1405  lastConditions = conditions;
1406 
1407  // check if we need to start default effect
1408  auto haveColorEffect = false;
1409  auto havePositionEffect = false;
1410  for (const auto& [effectId, effect]: effects) {
1411  if (effect->isActive() == true) {
1412  switch (effect->getType()) {
1413  case GUIEffect::EFFECTTYPE_COLOR:
1414  haveColorEffect = true;
1415  break;
1416  case GUIEffect::EFFECTTYPE_POSITION:
1417  havePositionEffect = true;
1418  break;
1419  case GUIEffect::EFFECTTYPE_NONE:
1420  break;
1421  }
1422  }
1423  }
1424  if (haveColorEffect == false || havePositionEffect == false) {
1425  if (defaultEffect != nullptr) {
1426  switch (defaultEffect->getType()) {
1427  case GUIEffect::EFFECTTYPE_COLOR:
1428  haveColorEffect = true;
1429  break;
1430  case GUIEffect::EFFECTTYPE_POSITION:
1431  havePositionEffect = true;
1432  break;
1433  case GUIEffect::EFFECTTYPE_NONE:
1434  break;
1435  }
1436  }
1437  // also reset color effect if not applied
1438  if (haveColorEffect == false) {
1439  GUIColorEffect::resetEffectState(effectState.get());
1440  } else
1441  // also position effect if not applied
1442  if (havePositionEffect == false) {
1443  GUIPositionEffect::resetEffectState(effectState.get());
1444  }
1445  // finally start default effect if we have none
1446  if (defaultEffect != nullptr && haveColorEffect == false && havePositionEffect == false) {
1447  defaultEffect->start();
1448  }
1449  }
1450 }
1451 
1453  if (haveOutEffect == false) return false;
1454  // do not change condition met if we have a active effect
1455  haveOutEffect = false;
1456  for (const auto& [effectId, effect]: effects) {
1457  if (effect->isActive() == true) {
1458  haveOutEffect = true;
1459  break;
1460  }
1461  }
1462  return haveOutEffect;
1463 }
1464 
bool equals(const Color4 &color, float tolerance=Math::EPSILON) const
Compares this color with given color.
Definition: Color4.h:233
const array< float, 4 > & getArray() const
Definition: Color4.h:262
Engine main class.
Definition: Engine.h:131
Texture entity.
Definition: Texture.h:24
uint16_t getWidth() const
Definition: Texture.h:197
const string & getId() const
Definition: Texture.h:172
uint16_t getHeight() const
Definition: Texture.h:204
GUI module class.
Definition: GUI.h:64
GUI effect base class.
Definition: GUIEffect.h:26
static STATIC_DLL_IMPEXT GUIColor GUICOLOR_BLACK
Definition: GUIColor.h:30
static STATIC_DLL_IMPEXT GUIColor GUICOLOR_TRANSPARENT
Definition: GUIColor.h:34
static STATIC_DLL_IMPEXT string CONDITION_ALWAYS
GUI element node conditions.
bool add(const string &condition)
Add a condition.
GUI node controller base class.
virtual void dispose()=0
Dispose controller.
static STATIC_DLL_IMPEXT GUINode_AlignmentHorizontal * RIGHT
static GUINode_AlignmentHorizontal * valueOf(const string &name)
Returns enum object given by name.
static STATIC_DLL_IMPEXT GUINode_AlignmentHorizontal * CENTER
static STATIC_DLL_IMPEXT GUINode_AlignmentHorizontal * LEFT
static STATIC_DLL_IMPEXT GUINode_AlignmentVertical * BOTTOM
static GUINode_AlignmentVertical * valueOf(const string &name)
Returns enum object given by name.
static STATIC_DLL_IMPEXT GUINode_AlignmentVertical * CENTER
static STATIC_DLL_IMPEXT GUINode_AlignmentVertical * TOP
static STATIC_DLL_IMPEXT GUINode_Flow * FLOATING
Definition: GUINode_Flow.h:31
static GUINode_Flow * valueOf(const string &name)
Returns enum object given by name.
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * PERCENT
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * TABLECELL
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * STAR
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * NONE
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * PIXEL
static STATIC_DLL_IMPEXT GUINode_RequestedConstraints_RequestedConstraintsType * AUTO
GUI node base class.
Definition: GUINode.h:64
GUINodeConditions hideOn
Definition: GUINode.h:162
void cfCallDetermineElementNodeDependencies(const string &function, const vector< string > &arguments, vector< string > &elementNodeDependencies)
Determine element node dependencies - Call condition function with arguments.
Definition: GUINode.cpp:1242
bool cfCall(GUIElementNode *elementNode, const string &function, const vector< string > &arguments)
Call condition function with arguments.
Definition: GUINode.cpp:1227
virtual int getContentHeight()=0
static GUINode_RequestedConstraints_RequestedConstraintsType * getRequestedConstraintsType(const string &constraint, GUINode_RequestedConstraints_RequestedConstraintsType *defaultConstraintsType)
Get requested constraints type.
Definition: GUINode.cpp:288
GUIColor backgroundImageEffectColorMul
Definition: GUINode.h:157
float computeParentChildrenRenderOffsetXTotal()
Definition: GUINode.cpp:952
GUINode_Border border
Definition: GUINode.h:160
virtual void determineMouseEventNodes(GUIMouseEvent *event, bool floatingNode, unordered_set< string > &eventNodeIds, unordered_set< string > &eventFloatingNodeIds, int flags=DETERMINEMOUSEEVENTNODES_FLAG_NONE)
Determine mouse event nodes.
Definition: GUINode.cpp:1030
static GUINode_Alignments createAlignments(const string &horizontal, const string &vertical)
Create alignments.
Definition: GUINode.cpp:262
void scrollToNodeY(GUIParentNode *toNode=nullptr)
Scroll to node Y.
Definition: GUINode.cpp:1100
void scrollToNodeX(GUIParentNode *toNode=nullptr)
Scroll to node X.
Definition: GUINode.cpp:1096
void onSetConditions(const vector< string > &conditions)
On set condition.
Definition: GUINode.cpp:1341
virtual int getContentWidth()=0
void addEffect(const string &id, GUIEffect *effect)
Add effect, effect already registered with the is will be removed.
Definition: GUINode.cpp:1316
virtual void setLeft(int left)
Set computed left.
Definition: GUINode.cpp:179
virtual void layoutOnDemand()
Layout on demand.
Definition: GUINode.cpp:473
GUINode_Scale9Grid backgroundImageScale9Grid
Definition: GUINode.h:156
virtual void determineNodesByCoordinate(const Vector2 &coordinate, unordered_set< string > &nodeIds)
Determine nodes by coordinate.
Definition: GUINode.cpp:1020
bool cfHasCondition(GUIElementNode *elementNode, const vector< string > &arguments)
Condition function: has condition.
Definition: GUINode.cpp:1257
virtual ~GUINode()
Destructor.
Definition: GUINode.cpp:139
vector< string > lastConditions
Definition: GUINode.h:170
void _scrollToNodeX(GUIParentNode *toNode=nullptr)
Scroll to node X.
Definition: GUINode.cpp:1055
void cfDetermineElementNodeDependencies(vector< string > &elementNodeDependencies)
Determine element node dependencies.
Definition: GUINode.cpp:1164
static GUIColor getRequestedColor(const string &color, const GUIColor &defaultColor)
Get color.
Definition: GUINode.cpp:333
virtual void render(GUIRenderer *guiRenderer)
Render.
Definition: GUINode.cpp:509
static int getRequestedPixelValue(const string &value, int defaultValue)
Get requested pixel value.
Definition: GUINode.cpp:324
void cfParse(const string &term, string &function, vector< string > &arguments)
Parse condition function term.
Definition: GUINode.cpp:1183
static constexpr int DETERMINEMOUSEEVENTNODES_FLAG_TOOLTIP
Definition: GUINode.h:320
GUIColor backgroundImageEffectColorAdd
Definition: GUINode.h:158
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 const string getNodeType()=0
static void dumpNode(GUINode *node, int depth=0, int indent=0, int depthIdx=0)
Dump node.
Definition: GUINode.cpp:1104
virtual void undoEffects(GUIRenderer *guiRenderer)
Undo effects.
Definition: GUINode.cpp:504
virtual bool isContentNode()=0
bool isEventBelongingToNode(GUIMouseEvent *event, Vector2 &nodeCoordinate)
Is event belonging to node.
Definition: GUINode.h:604
Texture * backgroundTexture
Definition: GUINode.h:154
bool haveActiveOutEffect()
Determine if we have a out effect active.
Definition: GUINode.cpp:1452
GUINode_ComputedConstraints computedConstraints
Definition: GUINode.h:152
GUINode_Padding padding
Definition: GUINode.h:159
void cfHasConditionDetermineElementNodeDependencies(const vector< string > &arguments, vector< string > &elementNodeDependencies)
Determine element node dependencies - Condition function: has condition.
Definition: GUINode.cpp:1277
unordered_map< string, GUIEffect * > effects
Definition: GUINode.h:164
virtual void setTop(int top)
Set computed top.
Definition: GUINode.cpp:185
virtual void setConditionsMet()
Set conditions met for this node and its subnodes.
Definition: GUINode.cpp:468
GUINodeController * getController()
Definition: GUINode.h:661
GUIScreenNode * screenNode
Definition: GUINode.h:147
const string getHierarchicalId()
Definition: GUINode.cpp:150
virtual int getAutoWidth()
Definition: GUINode.cpp:161
int layoutConstraintPixel(GUINode_RequestedConstraints_RequestedConstraintsType *type, int autoValue, int parentValue, int value)
Layout constraint.
Definition: GUINode.cpp:239
GUINode_RequestedConstraints requestedConstraints
Definition: GUINode.h:151
float computeParentChildrenRenderOffsetYTotal()
Definition: GUINode.cpp:962
bool shouldRender()
Returns if to render.
Definition: GUINode.h:302
GUIParentNode * getParentControllerNode()
Definition: GUINode.cpp:1011
virtual void dispose()
Dispose node.
Definition: GUINode.cpp:462
bool cfEmpty(const vector< string > &arguments)
Condition function: empty.
Definition: GUINode.cpp:1293
void removeEffect(const string &id)
Remove effect.
Definition: GUINode.cpp:1330
void setBackgroundImage(const string &backgroundImage)
Set background image.
Definition: GUINode.cpp:1304
static GUINode_RequestedConstraints createRequestedConstraints(const string &left, const string &top, const string &width, const string &height, int factor)
Create requested constraints.
Definition: GUINode.cpp:270
virtual void applyEffects(GUIRenderer *guiRenderer)
Apply effects.
Definition: GUINode.cpp:480
GUINode_Alignments alignments
Definition: GUINode.h:150
const string & getId()
Definition: GUINode.h:339
static void dumpParentNodes(GUINode *node, int indent=0)
Dump parent nodes.
Definition: GUINode.cpp:1137
bool isCoordinateBelongingToNode(const Vector2 &coordinate, Vector2 &nodeCoordinate)
Is coordinate belonging to node.
Definition: GUINode.h:575
void setController(GUINodeController *controller)
Set up node controller.
Definition: GUINode.cpp:1046
bool checkConditions()
Check if conditions are met.
Definition: GUINode.cpp:426
GUIEffect * getEffect(const string &id)
Get effect.
Definition: GUINode.cpp:1323
unique_ptr< GUIEffectState > effectState
Definition: GUINode.h:171
static GUINode_Border createBorder(const string &allBorder, const string &left, const string &top, const string &right, const string &bottom, const string &allBorderColor, const string &leftColor, const string &topColor, const string &rightColor, const string &bottomColor)
Create border.
Definition: GUINode.cpp:347
void getEventNodePosition(GUIMouseEvent *event, Vector2 &position)
Get event position clamped to node constraints TODO: use Vector2 instead of array<float,...
Definition: GUINode.cpp:998
virtual void layout()
Layout.
Definition: GUINode.cpp:191
GUINodeConditions showOn
Definition: GUINode.h:161
static GUINodeConditions createConditions(const string &conditions)
Create conditions.
Definition: GUINode.cpp:397
static GUINode_Padding createPadding(const string &allPadding, const string &left, const string &top, const string &right, const string &bottom)
Create padding.
Definition: GUINode.cpp:369
GUINode_Flow * flow
Definition: GUINode.h:86
static GUINode_Scale9Grid createScale9Grid(const string &all, const string &left, const string &top, const string &right, const string &bottom)
Create scale 9 grid.
Definition: GUINode.cpp:383
void getEventOffNodeRelativePosition(GUIMouseEvent *event, Vector2 &position)
Get event off node relative position TODO: use Vector2 instead of array<float, 2>
Definition: GUINode.cpp:972
virtual int getAutoHeight()
Definition: GUINode.cpp:170
static int getRequestedConstraintsValue(const string &constraint, int defaultConstraintsValue)
Get requested constraints value.
Definition: GUINode.cpp:306
static GUINode_Flow * createFlow(const string &flow)
Create flow.
Definition: GUINode.cpp:342
static STATIC_DLL_IMPEXT GUIParentNode_Overflow * SCROLL
GUI parent node base class thats supporting child nodes.
Definition: GUIParentNode.h:42
void setChildrenRenderOffsetX(float childrenRenderOffSetX)
Set children render offset x.
vector< GUINode * > subNodes
Definition: GUIParentNode.h:62
void setChildrenRenderOffsetY(float childrenRenderOffSetY)
Set children render offset y.
GUI screen node that represents a screen that can be rendered via GUI system.
Definition: GUIScreenNode.h:72
void scrollToNodeX(const string &node, const string &toNode)
Register deferred scroll to node X.
void scrollToNodeY(const string &node, const string &toNode)
Register deferred scroll to node Y.
void removeTickNode(GUINode *node)
Remove tick node.
Texture * getImage(const string &fileName)
Get image.
void forceLayout(GUINode *node)
Force layout node content (e.g.
GUINode * getNodeById(const string &nodeId)
Get GUI node by id.
void addNodeElementNodeDependency(const string &elementNodeId, const string &nodeId)
Add node to element node dependency.
void bindTexture(int32_t textureId)
Bind texture.
void setEffectColorAdd(const GUIColor &color)
Set effect color add.
Definition: GUIRenderer.h:192
void popEffects()
Pop effects.
Definition: GUIRenderer.h:391
void setEffectColorMul(const GUIColor &color)
Set effect color mul.
Definition: GUIRenderer.h:184
void pushEffects(const vector< GUIEffect * > &effects)
Push effects @oaran effects effects.
Definition: GUIRenderer.h:378
void addQuad(float x1, float y1, float colorR1, float colorG1, float colorB1, float colorA1, float tu1, float tv1, float x2, float y2, float colorR2, float colorG2, float colorB2, float colorA2, float tu2, float tv2, float x3, float y3, float colorR3, float colorG3, float colorB3, float colorA3, float tu3, float tv3, float x4, float y4, float colorR4, float colorG4, float colorB4, float colorA4, float tu4, float tv4, bool solidColor=false, bool rotated=false)
Add quad Note: quad vertices order 1 2 +-—+ | | | | +-—+ 4 3.
Definition: GUIRenderer.h:507
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Definition: Vector2.h:20
Console class.
Definition: Console.h:29
const string & getName() const
Definition: Enum.h:37
bool equals(Enum *enumObject) const
Compare enum with another enum.
Definition: Enum.h:52
Integer class.
Definition: Integer.h:25
String tokenizer class.
void tokenize(const string &str, const string &delimiters, bool emptyTokens=false)
Tokenize.
String tools class.
Definition: StringTools.h:22
GUINode_AlignmentVertical * vertical
GUINode_AlignmentHorizontal * horizontal
GUI node border entity.
GUI node padding entity.
GUINode_RequestedConstraints_RequestedConstraintsType * topType
GUINode_RequestedConstraints_RequestedConstraintsType * widthType
GUINode_RequestedConstraints_RequestedConstraintsType * leftType
GUINode_RequestedConstraints_RequestedConstraintsType * heightType
GUI node scale 9 grid entity.
Action Interface.
Definition: Action.h:11