TDME2  1.9.200
GUIGridController.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <string>
5 #include <unordered_set>
6 #include <vector>
7 
8 #include <tdme/tdme.h>
14 #include <tdme/gui/nodes/GUINode.h>
18 #include <tdme/gui/GUI.h>
19 #include <tdme/gui/GUIParser.h>
21 #include <tdme/utilities/Integer.h>
25 
26 using std::count;
27 using std::string;
28 using std::to_string;
29 using std::unordered_set;
30 using std::vector;
31 
42 using tdme::gui::GUI;
49 
50 string GUIGridController::CONDITION_DISABLED = "disabled";
51 string GUIGridController::CONDITION_ENABLED = "enabled";
52 
53 GUIGridController::GUIGridController(GUINode* node)
54  : GUIElementController(node)
55 {
56  this->disabled = required_dynamic_cast<GUIElementNode*>(node)->isDisabled();
57  this->multipleSelection = required_dynamic_cast<GUIElementNode*>(node)->hasOption("multiple");
58  this->keyControl = false;
59  this->focussedItemIdx = -1;
60 }
61 
63  return multipleSelection;
64 }
65 
67  return keyControl;
68 }
69 
71 {
72  GUIElementController::initialize();
73  value.reset();
75  for (auto gridItemController: gridItemControllers) {
76  if (gridItemController->isSelected() == false) continue;
77  if (multipleSelection == true) {
78  if (value.empty() == true) {
80  }
81  value.append(required_dynamic_cast<GUIElementNode*>(gridItemController->getNode())->getValue());
83  } else {
84  value.set(required_dynamic_cast<GUIElementNode*>(gridItemController->getNode())->getValue());
85  }
86  }
88  selectCurrent();
89  try {
90  auto horizontalItemsOptionValue = required_dynamic_cast<GUIElementNode*>(node)->getOptionValue("horizontal-items");
91  if (horizontalItemsOptionValue.empty() == false) horizontalItems = Integer::parse(horizontalItemsOptionValue);
92  } catch (Exception &exception) {
93  Console::println("GUIGridController::initialize(): options: horizontal-items: invalid value: " + required_dynamic_cast<GUIElementNode*>(node)->getOptionValue("horizontal-items"));
94  }
95  if (horizontalItems <= 0) horizontalItems = 1;
96 }
97 
99 {
100  GUIElementController::dispose();
101 }
102 
104 {
105 }
106 
108 {
109  if (gridItemControllers.empty() == true) {
110  focussedItemIdx = -1;
111  } else
112  if (focussedItemIdx == -1) {
113  focussedItemIdx = 0;
114  } else
115  if (focussedItemIdx < 0) {
116  focussedItemIdx = 0;
117  } else
118  if (focussedItemIdx >= gridItemControllers.size()) {
120  }
121  return focussedItemIdx;
122 }
123 
125  auto itemElementNodeController = gridItemElementNode->getController();
126  for (auto i = 0; i < gridItemControllers.size(); i++) {
127  if (gridItemControllers[i] == itemElementNodeController) return i;
128  }
129  return -1;
130 }
131 
133 {
135  auto itemIdx = getFocussedItemIdx();
136  if (itemIdx == -1) return;
137  gridItemControllers[itemIdx]->unselect();
138 }
139 
140 void GUIGridController::select(int itemIdx) {
141  if (itemIdx < 0 || itemIdx >= gridItemControllers.size()) return;
142  if (multipleSelection == true) {
143  for (auto gridItemController: gridItemControllers) gridItemController->unselect();
144  }
145  gridItemControllers[itemIdx]->select();
146  value.reset();
148  value.append(required_dynamic_cast<GUIElementNode*>(gridItemControllers[itemIdx]->getNode())->getValue());
150 }
151 
152 void GUIGridController::select(GUIElementNode* gridItemElementNode) {
153  auto itemIdx = getItemIdx(gridItemElementNode);
154  if (itemIdx == -1) return;
155  select(itemIdx);
156 }
157 
159 {
160  auto itemIdx = getFocussedItemIdx();
161  if (itemIdx == -1) return;
162  gridItemControllers[itemIdx]->unfocus();
163 }
164 
165 void GUIGridController::focus(int itemIdx)
166 {
167  if (itemIdx < 0 || itemIdx >= gridItemControllers.size()) return;
168  gridItemControllers[itemIdx]->focus();
169  gridItemControllers[itemIdx]->getNode()->scrollToNodeX(required_dynamic_cast<GUIParentNode*>(node));
170  gridItemControllers[itemIdx]->getNode()->scrollToNodeY(required_dynamic_cast<GUIParentNode*>(node));
171  focussedItemIdx = itemIdx;
172 }
173 
174 void GUIGridController::focus(GUIElementNode* gridItemElementNode)
175 {
176  auto itemIdx = getItemIdx(gridItemElementNode);
177  if (itemIdx == -1) return;
178  focus(itemIdx);
179 }
180 
182 {
183  setValue(getValue());
184 }
185 
187 {
188  auto itemIdx = getFocussedItemIdx();
189  if (itemIdx == -1) return;
190 
191  unfocus();
192 
193  auto disabledCount = 0;
194  while (disabledCount < gridItemControllers.size()) {
195  if (++itemIdx >= gridItemControllers.size()) itemIdx = gridItemControllers.size() - 1;
196  if (gridItemControllers[itemIdx]->isDisabled() == false) break;
197  disabledCount++;
198  }
199  if (disabledCount == gridItemControllers.size()) {
200  itemIdx = -1;
201  return;
202  }
203 
204  focus(itemIdx);
205 }
206 
208 {
209  auto itemIdx = getFocussedItemIdx();
210  if (itemIdx == -1) return;
211 
212  unfocus();
213 
214  auto disabledCount = 0;
215  while (disabledCount < gridItemControllers.size()) {
216  if (--itemIdx < 0) itemIdx = 0;
217  if (gridItemControllers[itemIdx]->isDisabled() == false) break;
218  disabledCount++;
219  }
220  if (disabledCount == gridItemControllers.size()) {
221  itemIdx = -1;
222  return;
223  }
224 
225  focus(itemIdx);
226 }
227 
228 void GUIGridController::toggle(int itemIdx)
229 {
230  if (itemIdx < 0 || itemIdx >= gridItemControllers.size()) return;
231  gridItemControllers[itemIdx]->toggle();
232  gridItemControllers[itemIdx]->getNode()->scrollToNodeX(required_dynamic_cast<GUIParentNode*>(node));
233  gridItemControllers[itemIdx]->getNode()->scrollToNodeY(required_dynamic_cast<GUIParentNode*>(node));
234  string selectionValue;
235  selectionValue+= VALUE_DELIMITER;
236  selectionValue+= required_dynamic_cast<GUIElementNode*>(gridItemControllers[itemIdx]->getNode())->getValue();
237  selectionValue+= VALUE_DELIMITER;
238  if (gridItemControllers[itemIdx]->isSelected() == true) {
239  value.append(value.getString().empty() == false?selectionValue.substr(1):selectionValue);
240  } else {
241  value.replace(selectionValue, "|");
242  if (value.equals("|") == true) value.reset();
243  }
244 }
245 
246 void GUIGridController::toggle(GUIElementNode* gridItemElementNode)
247 {
248  auto itemIdx = getItemIdx(gridItemElementNode);
249  if (itemIdx == -1) return;
250  toggle(itemIdx);
251 }
252 
254 {
255  auto itemIdx = getFocussedItemIdx();
256  if (itemIdx == -1) return;
257  gridItemControllers[itemIdx]->select();
258  gridItemControllers[itemIdx]->getNode()->scrollToNodeX(required_dynamic_cast<GUIParentNode*>(node));
259  gridItemControllers[itemIdx]->getNode()->scrollToNodeY(required_dynamic_cast<GUIParentNode*>(node));
260 }
261 
263  vector<GUINode*> childControllerNodes;
264  //
265  required_dynamic_cast<GUIParentNode*>(node)->getChildControllerNodes(childControllerNodes);
266  //
267  gridItemControllers.clear();
268  for (auto i = 0; i < childControllerNodes.size(); i++) {
269  auto childControllerNode = childControllerNodes[i];
270  auto childController = childControllerNode->getController();
271  auto gridItemController = dynamic_cast<GUIGridItemController*>(childController);
272  if (gridItemController != nullptr) {
273  gridItemControllers.push_back(gridItemController);
274  }
275  }
276 }
277 
279 {
280  GUIElementController::handleMouseEvent(node, event);
281  auto disabled = required_dynamic_cast<GUIGridController*>(this->node->getController())->isDisabled();
282  if (disabled == false && node == this->node && node->isEventBelongingToNode(event) && event->getButton() == MOUSE_BUTTON_LEFT) {
283  if (event->getType() == GUIMouseEvent::MOUSEEVENT_PRESSED) {
284  node->getScreenNode()->getGUI()->setFoccussedNode(required_dynamic_cast<GUIElementNode*>(node));
285  }
286  }
287 }
288 
290 {
291  GUIElementController::handleKeyboardEvent(event);
292  if (disabled == false) {
293  if (event->getType() != GUIKeyboardEvent::KEYBOARDEVENT_KEY_TYPED) {
294  auto isKeyDown = event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED;
295  if (event->getKeyCode() == KEYBOARD_KEYCODE_LEFT_CTRL) keyControl = isKeyDown;
296  }
297 
298  switch (event->getKeyCode()) {
299  case GUIKeyboardEvent::KEYCODE_UP: {
300  event->setProcessed(true);
301  if (event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED) {
302  if (multipleSelection == false) {
303  unselect();
304  }
305  focusPrevious();
306  if (multipleSelection == false) {
308  }
309  } else
310  if (event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_RELEASED) {
311  node->getScreenNode()->forwardChange(required_dynamic_cast<GUIElementNode*>(node));
312  }
313  }
314  break;
315  case GUIKeyboardEvent::KEYCODE_DOWN: {
316  event->setProcessed(true);
317  if (event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED) {
318  if (multipleSelection == false) {
319  unselect();
320  }
321  focusNext();
322  if (multipleSelection == false) {
324  }
325  } else
326  if (event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_RELEASED) {
327  node->getScreenNode()->forwardChange(required_dynamic_cast<GUIElementNode*>(node));
328  }
329  }
330  break;
331  case GUIKeyboardEvent::KEYCODE_SPACE: {
332  event->setProcessed(true);
333  if (event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED) {
334  if (multipleSelection == true) {
336  }
337  } else
338  if (event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_RELEASED) {
339  node->getScreenNode()->forwardChange(required_dynamic_cast<GUIElementNode*>(node));
340  }
341  }
342  break;
343  }
344  }
345 }
346 
348 {
349  GUIElementController::tick();
350 }
351 
353 {
354 }
355 
357 {
358  keyControl = false;
359 }
360 
362 {
363  return true;
364 }
365 
367 {
368  // check if single value
369  if (multipleSelection == true &&
370  count(value.getString().begin(), value.getString().end(), '|') == 2) {
371  singleValue.set(StringTools::substring(value.getString(), 1, value.getString().size() - 1));
372  return singleValue;
373  }
374  // nope, take multiple value
375  return value;
376 }
377 
379 {
380  unfocus();
381  determineItems();
382  unordered_set<string> valueSet;
383  StringTokenizer valueTokenizer;
384  valueTokenizer.tokenize(value.getString(), "|");
385  while (valueTokenizer.hasMoreTokens()) {
386  valueSet.insert(valueTokenizer.nextToken());
387  }
388  MutableString searchValue;
389  GUIGridItemController* gridItemControllerLast = nullptr;
390  // TODO: actually we should rebuild value to remove items that have not been found
391  for (auto i = 0; i < gridItemControllers.size(); i++) {
392  auto gridItemController = gridItemControllers[i];
393  auto gridItemNode = required_dynamic_cast<GUIElementNode*>(gridItemController->getNode());
394  auto itemValue = gridItemNode->getValue();
395  if (gridItemController->isSelected() == true) gridItemController->unselect();
396  if (gridItemController->isFocussed() == true) gridItemController->unfocus();
397  if (valueSet.find(itemValue) != valueSet.end()) {
398  if (multipleSelection == true) toggle(i);
399  gridItemControllerLast = gridItemController;
400  }
401  }
402  if (gridItemControllerLast != nullptr) {
403  if (multipleSelection == false) select(required_dynamic_cast<GUIElementNode*>(gridItemControllerLast->getNode()));
404  focus(required_dynamic_cast<GUIElementNode*>(gridItemControllerLast->getNode()));
405  gridItemControllerLast->getNode()->scrollToNodeX(required_dynamic_cast<GUIParentNode*>(node));
406  gridItemControllerLast->getNode()->scrollToNodeY(required_dynamic_cast<GUIParentNode*>(node));
407  } else
408  if (gridItemControllers.empty() == false) {
409  auto gridItemController = gridItemControllers[0];
410  auto gridItemNode = required_dynamic_cast<GUIElementNode*>(gridItemController->getNode());
411  select(0);
412  focus(0);
413  gridItemNode->scrollToNodeX(required_dynamic_cast<GUIParentNode*>(node));
414  gridItemNode->scrollToNodeY(required_dynamic_cast<GUIParentNode*>(node));
415  }
416  // TODO: this is a workaround, actually due to condition updates, the relayout should happen automatically
418  //
419  this->value.reset();
420  for (auto& valueSetValue: valueSet) {
421  this->value.append(VALUE_DELIMITER);
422  this->value.append(valueSetValue);
423  }
424  if (valueSet.empty() == false) this->value.append(VALUE_DELIMITER);
425 }
426 
428  // TODO: find a better way later maybe, this is working, but has some issues with adding nodes
429  // we need a <grid-layout> later for the following code
430  if (onSubTreeChangeRun == true) return;
431  //
432  auto unlayoutedParentNode = required_dynamic_cast<GUIParentNode*>(node->getScreenNode()->getNodeById(node->getId() + "_unlayouted"));
433  if (unlayoutedParentNode->getSubNodesCount() == 0) return;
434  //
435  onSubTreeChangeRun = true;
436  //
437  determineItems();
438  //
439  auto layoutedParentNode = required_dynamic_cast<GUIParentNode*>(node->getScreenNode()->getInnerNodeById(node->getId()));
440  layoutedParentNode->clearSubNodes();
441  auto gridItemIdx = 0;
442  auto gridHorizontalLayoutIdx = 0;
443  auto gridHorizontalLayoutId = node->getId() +"_hl_" + to_string(gridHorizontalLayoutIdx++);
444  GUIParser::parse(layoutedParentNode, "<layout id=\"" + gridHorizontalLayoutId + "\" alignment=\"horizontal\" width=\"auto\"></layout>\n");
445  //
446  while (unlayoutedParentNode->getSubNodesCount() > 0) {
447  required_dynamic_cast<GUIParentNode*>(node->getScreenNode()->getNodeById(gridHorizontalLayoutId))->moveSubNode(unlayoutedParentNode, 0);
448  gridItemIdx++;
449  if ((gridItemIdx % horizontalItems) == 0 && unlayoutedParentNode->getSubNodesCount() > 0) {
450  gridHorizontalLayoutId = node->getId() +"_hl_" + to_string(gridHorizontalLayoutIdx++);
451  GUIParser::parse(layoutedParentNode, "<layout id=\"" + gridHorizontalLayoutId + "\" alignment=\"horizontal\" width=\"auto\"></layout>\n");
452  }
453  }
454  //
455  onSubTreeChangeRun = false;
456 }
#define KEYBOARD_KEYCODE_LEFT_CTRL
#define MOUSE_BUTTON_LEFT
GUI parser.
Definition: GUIParser.h:40
static GUIScreenNode * parse(const string &pathName, const string &fileName, const unordered_map< string, string > &variables={}, const EngineMiniScript::ScriptVariable &miniScriptArguments=EngineMiniScript::ScriptVariable(), Context *context=nullptr)
Parses a GUI XML file.
GUI module class.
Definition: GUI.h:64
void setFoccussedNode(GUIElementNode *newFoccussedNode)
Set focussed node.
Definition: GUI.cpp:267
void selectCurrent()
Select current item.
vector< GUIGridItemController * > gridItemControllers
void onFocusGained() override
On focus gained.
void dispose() override
Dispose controller.
void postLayout() override
Post layout event.
void initialize() override
Initialize controller after element has been created.
void handleKeyboardEvent(GUIKeyboardEvent *event) override
Handle keyboard event.
void onFocusLost() override
On focus lost.
void setValue(const MutableString &value) override
Set value.
void handleMouseEvent(GUINode *node, GUIMouseEvent *event) override
Handle mouse event.
void tick() override
Tick method will be executed once per frame.
static constexpr STATIC_DLL_IMPEXT char VALUE_DELIMITER
void focusPrevious()
Focus previous item.
int getItemIdx(GUIElementNode *gridItemElementNode)
Get item idx.
const MutableString & getValue() override
void onSubTreeChange() override
On sub tree change.
int getFocussedItemIdx()
Get focussed item idx.
GUIKeyboardEventType getType() const
GUIMouseEventType getType() const
Definition: GUIMouseEvent.h:78
GUI element node conditions.
GUI node base class.
Definition: GUINode.h:64
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
bool isEventBelongingToNode(GUIMouseEvent *event, Vector2 &nodeCoordinate)
Is event belonging to node.
Definition: GUINode.h:604
GUINodeController * getController()
Definition: GUINode.h:661
GUIScreenNode * getScreenNode()
Definition: GUINode.h:325
const string & getId()
Definition: GUINode.h:339
GUI parent node base class thats supporting child nodes.
Definition: GUIParentNode.h:42
GUI screen node that represents a screen that can be rendered via GUI system.
Definition: GUIScreenNode.h:72
void invalidateLayout(GUINode *node)
Mark a node to be invalidated regarding layout.
void forwardChange(GUIElementNode *node)
Forward change event.
GUINode * getInnerNodeById(const string &nodeId)
Get inner GUI node by id.
GUINode * getNodeById(const string &nodeId)
Get GUI node by id.
Integer class.
Definition: Integer.h:25
Mutable utf8 aware string class.
Definition: MutableString.h:23
MutableString & append(char c)
Append character.
bool equals(const string &s2) const
Equals.
const string & getString() const
MutableString & set(char c)
Set character.
MutableString & reset()
Reset.
Definition: MutableString.h:95
void replace(const string &what, const string &by, int beginIndex=0)
Replace string with another string.
String tokenizer class.
void tokenize(const string &str, const string &delimiters, bool emptyTokens=false)
Tokenize.
String tools class.
Definition: StringTools.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18