1 /**
2 * @file
3 * @brief C interface to allow to access to cpp node code.
4 */
5
6 /*
7 Copyright (C) 2002-2013 UFO: Alien Invasion.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 */
25
26 #include <typeinfo>
27 #include "ui_main.h"
28 #include "ui_behaviour.h"
29 #include "ui_nodes.h"
30 #include "ui_node.h"
31 #include "node/ui_node_abstractnode.h"
32 #include "node/ui_node_abstractoption.h"
33 #include "node/ui_node_battlescape.h"
34 #include "node/ui_node_panel.h"
35 #include "ui_parse.h"
36 #include "ui_components.h"
37 #include "ui_internal.h"
38
UI_Node_IsVirtual(uiNode_t const * node)39 bool UI_Node_IsVirtual (uiNode_t const* node)
40 {
41 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
42 return b == nullptr;
43 }
44
UI_Node_IsDrawable(uiNode_t const * node)45 bool UI_Node_IsDrawable (uiNode_t const* node)
46 {
47 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
48 return b != nullptr;
49 }
50
UI_Node_IsOptionContainer(uiNode_t const * node)51 bool UI_Node_IsOptionContainer (uiNode_t const* node)
52 {
53 uiAbstractOptionNode* b = dynamic_cast<uiAbstractOptionNode*>(node->behaviour->manager.get());
54 return b != nullptr;
55 }
56
UI_Node_IsWindow(uiNode_t const * node)57 bool UI_Node_IsWindow (uiNode_t const* node)
58 {
59 uiWindowNode* b = dynamic_cast<uiWindowNode*>(node->behaviour->manager.get());
60 return b != nullptr;
61 }
62
UI_Node_IsBattleScape(uiNode_t const * node)63 bool UI_Node_IsBattleScape (uiNode_t const* node)
64 {
65 uiBattleScapeNode *b = dynamic_cast<uiBattleScapeNode*>(node->behaviour->manager.get());
66 return b != nullptr;
67 }
68
UI_Node_IsAbstract(uiNode_t const * node)69 bool UI_Node_IsAbstract (uiNode_t const* node)
70 {
71 return node->behaviour->isAbstract;
72 }
73
UI_Node_IsDrawItselfChild(uiNode_t const * node)74 bool UI_Node_IsDrawItselfChild (uiNode_t const* node)
75 {
76 return node->behaviour->drawItselfChild;
77 }
78
79 /**
80 * @todo Use typeid when it is possible
81 */
UI_Node_IsFunction(uiNode_t const * node)82 bool UI_Node_IsFunction (uiNode_t const* node)
83 {
84 return node->behaviour->isFunction;
85 }
86
87 /**
88 * @todo Use typeid when it is possible
89 */
UI_Node_IsScrollableContainer(uiNode_t const * node)90 bool UI_Node_IsScrollableContainer (uiNode_t const* node)
91 {
92 uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
93 return b != nullptr;
94 }
95
UI_Node_GetWidgetName(uiNode_t const * node)96 const char* UI_Node_GetWidgetName (uiNode_t const* node)
97 {
98 return node->behaviour->name;
99 }
100
UI_Node_GetMemorySize(uiNode_t const * node)101 intptr_t UI_Node_GetMemorySize (uiNode_t const* node)
102 {
103 return sizeof(*node) + node->behaviour->extraDataSize;
104 }
105
UI_Node_Draw(uiNode_t * node)106 void UI_Node_Draw (uiNode_t* node)
107 {
108 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
109 b->draw(node);
110 }
111
UI_Node_DrawTooltip(const uiNode_t * node,int x,int y)112 void UI_Node_DrawTooltip (const uiNode_t* node, int x, int y)
113 {
114 const uiLocatedNode* b = dynamic_cast<const uiLocatedNode*>(node->behaviour->manager.get());
115 b->drawTooltip(node, x, y);
116 }
117
UI_Node_DrawOverWindow(uiNode_t * node)118 void UI_Node_DrawOverWindow (uiNode_t* node)
119 {
120 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
121 b->drawOverWindow(node);
122 }
123
124 /* mouse events */
UI_Node_LeftClick(uiNode_t * node,int x,int y)125 void UI_Node_LeftClick (uiNode_t* node, int x, int y)
126 {
127 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
128 b->onLeftClick(node, x, y);
129 }
130
UI_Node_RightClick(uiNode_t * node,int x,int y)131 void UI_Node_RightClick (uiNode_t* node, int x, int y)
132 {
133 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
134 b->onRightClick(node, x, y);
135 }
136
UI_Node_MiddleClick(uiNode_t * node,int x,int y)137 void UI_Node_MiddleClick (uiNode_t* node, int x, int y)
138 {
139 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
140 b->onMiddleClick(node, x, y);
141 }
142
UI_Node_Scroll(uiNode_t * node,int deltaX,int deltaY)143 bool UI_Node_Scroll (uiNode_t* node, int deltaX, int deltaY)
144 {
145 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
146 return b->onScroll(node, deltaX, deltaY);
147 }
148
UI_Node_MouseMove(uiNode_t * node,int x,int y)149 void UI_Node_MouseMove (uiNode_t* node, int x, int y)
150 {
151 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
152 b->onMouseMove(node, x, y);
153 }
154
UI_Node_MouseDown(uiNode_t * node,int x,int y,int button)155 void UI_Node_MouseDown (uiNode_t* node, int x, int y, int button)
156 {
157 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
158 b->onMouseDown(node, x, y, button);
159 }
160
UI_Node_MouseUp(uiNode_t * node,int x,int y,int button)161 void UI_Node_MouseUp (uiNode_t* node, int x, int y, int button)
162 {
163 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
164 b->onMouseUp(node, x, y, button);
165 }
166
UI_Node_MouseEnter(uiNode_t * node)167 void UI_Node_MouseEnter (uiNode_t* node)
168 {
169 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
170 b->onMouseEnter(node);
171 }
172
UI_Node_MouseLeave(uiNode_t * node)173 void UI_Node_MouseLeave (uiNode_t* node)
174 {
175 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
176 b->onMouseLeave(node);
177 }
178
UI_Node_MouseLongPress(uiNode_t * node,int x,int y,int button)179 bool UI_Node_MouseLongPress (uiNode_t* node, int x, int y, int button)
180 {
181 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
182 return b->onMouseLongPress(node, x, y, button);
183 }
184
UI_Node_StartDragging(uiNode_t * node,int startX,int startY,int currentX,int currentY,int button)185 bool UI_Node_StartDragging (uiNode_t* node, int startX, int startY, int currentX, int currentY, int button)
186 {
187 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
188 return b->onStartDragging(node, startX, startY, currentX, currentY, button);
189 }
190
UI_Node_CapturedMouseMove(uiNode_t * node,int x,int y)191 void UI_Node_CapturedMouseMove (uiNode_t* node, int x, int y)
192 {
193 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
194 b->onCapturedMouseMove(node, x, y);
195 }
196
UI_Node_CapturedMouseLost(uiNode_t * node)197 void UI_Node_CapturedMouseLost (uiNode_t* node)
198 {
199 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
200 b->onCapturedMouseLost(node);
201 }
202
203 /* system allocation */
204
UI_Node_Loading(uiNode_t * node)205 void UI_Node_Loading (uiNode_t* node)
206 {
207 uiNode* b = node->behaviour->manager.get();
208 b->onLoading(node);
209 }
210
UI_Node_Loaded(uiNode_t * node)211 void UI_Node_Loaded (uiNode_t* node)
212 {
213 uiNode* b = node->behaviour->manager.get();
214 b->onLoaded(node);
215 }
216
UI_Node_Clone(uiNode_t const * source,uiNode_t * clone)217 void UI_Node_Clone (uiNode_t const* source, uiNode_t* clone)
218 {
219 uiNode* b = source->behaviour->manager.get();
220 b->clone(source, clone);
221 }
222
UI_Node_NewNode(uiNode_t * node)223 void UI_Node_NewNode (uiNode_t* node)
224 {
225 uiNode* b = node->behaviour->manager.get();
226 b->newNode(node);
227 }
228
UI_Node_DeleteNode(uiNode_t * node)229 void UI_Node_DeleteNode (uiNode_t* node)
230 {
231 uiNode* b = node->behaviour->manager.get();
232 b->deleteNode(node);
233 }
234
235 /* system callback */
236
UI_Node_WindowOpened(uiNode_t * node,linkedList_t * params)237 void UI_Node_WindowOpened (uiNode_t* node, linkedList_t* params)
238 {
239 uiNode* b = node->behaviour->manager.get();
240 b->onWindowOpened(node, params);
241 }
242
UI_Node_WindowClosed(uiNode_t * node)243 void UI_Node_WindowClosed (uiNode_t* node)
244 {
245 uiNode* b = node->behaviour->manager.get();
246 b->onWindowClosed(node);
247 }
248
UI_Node_WindowActivate(uiNode_t * node)249 void UI_Node_WindowActivate (uiNode_t* node)
250 {
251 uiNode* b = node->behaviour->manager.get();
252 b->onWindowActivate(node);
253 }
254
UI_Node_DoLayout(uiNode_t * node)255 void UI_Node_DoLayout (uiNode_t* node)
256 {
257 if (UI_Node_IsDrawable(node)) {
258 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
259 b->doLayout(node);
260 }
261 }
262
UI_Node_Activate(uiNode_t * node)263 void UI_Node_Activate (uiNode_t* node)
264 {
265 uiNode* b = node->behaviour->manager.get();
266 b->onActivate(node);
267 }
268
UI_Node_PropertyChanged(uiNode_t * node,const value_t * property)269 void UI_Node_PropertyChanged (uiNode_t* node, const value_t* property)
270 {
271 uiNode* b = node->behaviour->manager.get();
272 b->onPropertyChanged(node, property);
273 }
274
UI_Node_SizeChanged(uiNode_t * node)275 void UI_Node_SizeChanged (uiNode_t* node)
276 {
277 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
278 b->onSizeChanged(node);
279 }
280
UI_Node_GetClientPosition(uiNode_t const * node,vec2_t position)281 void UI_Node_GetClientPosition (uiNode_t const* node, vec2_t position)
282 {
283 uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
284 b->getClientPosition(node, position);
285 }
286
287 /* drag and drop callback */
288
UI_Node_DndEnter(uiNode_t * node)289 bool UI_Node_DndEnter (uiNode_t* node)
290 {
291 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
292 return b->onDndEnter(node);
293 }
294
UI_Node_DndMove(uiNode_t * node,int x,int y)295 bool UI_Node_DndMove (uiNode_t* node, int x, int y)
296 {
297 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
298 return b->onDndMove(node, x, y);
299 }
300
UI_Node_DndLeave(uiNode_t * node)301 void UI_Node_DndLeave (uiNode_t* node)
302 {
303 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
304 b->onDndLeave(node);
305 }
306
UI_Node_DndDrop(uiNode_t * node,int x,int y)307 bool UI_Node_DndDrop (uiNode_t* node, int x, int y)
308 {
309 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
310 return b->onDndDrop(node, x, y);
311 }
312
UI_Node_DndFinished(uiNode_t * node,bool isDroped)313 bool UI_Node_DndFinished (uiNode_t* node, bool isDroped)
314 {
315 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
316 return b->onDndFinished(node, isDroped);
317 }
318
319 /* focus and keyboard events */
320
UI_Node_FocusGained(uiNode_t * node)321 void UI_Node_FocusGained (uiNode_t* node)
322 {
323 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
324 b->onFocusGained(node);
325 }
326
UI_Node_FocusLost(uiNode_t * node)327 void UI_Node_FocusLost (uiNode_t* node)
328 {
329 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
330 b->onFocusLost(node);
331 }
332
UI_Node_KeyPressed(uiNode_t * node,unsigned int key,unsigned short unicode)333 bool UI_Node_KeyPressed (uiNode_t* node, unsigned int key, unsigned short unicode)
334 {
335 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
336 return b->onKeyPressed(node, key, unicode);
337 }
338
UI_Node_KeyReleased(uiNode_t * node,unsigned int key,unsigned short unicode)339 bool UI_Node_KeyReleased (uiNode_t* node, unsigned int key, unsigned short unicode)
340 {
341 uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
342 return b->onKeyReleased(node, key, unicode);
343 }
344
345 /* cell size */
346
UI_Node_GetCellWidth(uiNode_t * node)347 int UI_Node_GetCellWidth (uiNode_t* node)
348 {
349 uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
350 return b->getCellWidth(node);
351 }
352
UI_Node_GetCellHeight(uiNode_t * node)353 int UI_Node_GetCellHeight (uiNode_t* node)
354 {
355 uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
356 return b->getCellHeight(node);
357 }
358
359 #ifdef DEBUG
360
UI_Node_DebugCountWidget(uiNode_t * node,int count)361 void UI_Node_DebugCountWidget (uiNode_t* node, int count)
362 {
363 node->behaviour->count += count;
364 }
365
366 #endif
367
368 /**
369 * @brief Check the node inheritance
370 * @param[in] node Requested node
371 * @param[in] behaviourName Behaviour name we check
372 * @return True if the node inherits from the behaviour
373 */
UI_NodeInstanceOf(const uiNode_t * node,const char * behaviourName)374 bool UI_NodeInstanceOf (const uiNode_t* node, const char* behaviourName)
375 {
376 const uiBehaviour_t* behaviour;
377 for (behaviour = node->behaviour; behaviour; behaviour = behaviour->super) {
378 if (Q_streq(behaviour->name, behaviourName))
379 return true;
380 }
381 return false;
382 }
383
384 /**
385 * @brief Check the node inheritance
386 * @param[in] node Requested node
387 * @param[in] behaviour Behaviour we check
388 * @return True if the node inherits from the behaviour
389 */
UI_NodeInstanceOfPointer(const uiNode_t * node,const uiBehaviour_t * behaviour)390 bool UI_NodeInstanceOfPointer (const uiNode_t* node, const uiBehaviour_t* behaviour)
391 {
392 const uiBehaviour_t* b;
393 for (b = node->behaviour; b; b = b->super) {
394 if (b == behaviour)
395 return true;
396 }
397 return false;
398 }
399
400 /**
401 * @brief return a relative position of a point into a node.
402 * @param [in] node Requested node
403 * @param [out] pos Result position
404 * @param [in] direction
405 * @note For example we can request the right-bottom corner with LAYOUTALIGN_BOTTOMRIGHT
406 */
UI_NodeGetPoint(const uiNode_t * node,vec2_t pos,int direction)407 void UI_NodeGetPoint (const uiNode_t* node, vec2_t pos, int direction)
408 {
409 switch (UI_GET_HORIZONTAL_ALIGN(direction)) {
410 case LAYOUTALIGN_H_LEFT:
411 pos[0] = 0;
412 break;
413 case LAYOUTALIGN_H_MIDDLE:
414 pos[0] = (int)(node->box.size[0] / 2);
415 break;
416 case LAYOUTALIGN_H_RIGHT:
417 pos[0] = node->box.size[0];
418 break;
419 default:
420 Com_Printf("UI_NodeGetPoint: Align '%d' (0x%X) is not a common alignment", direction, direction);
421 pos[0] = 0;
422 pos[1] = 0;
423 return;
424 }
425
426 switch (UI_GET_VERTICAL_ALIGN(direction)) {
427 case LAYOUTALIGN_V_TOP:
428 pos[1] = 0;
429 break;
430 case LAYOUTALIGN_V_MIDDLE:
431 pos[1] = (int)(node->box.size[1] / 2);
432 break;
433 case LAYOUTALIGN_V_BOTTOM:
434 pos[1] = node->box.size[1];
435 break;
436 default:
437 Com_Printf("UI_NodeGetPoint: Align '%d' (0x%X) is not a common alignment", direction, direction);
438 pos[0] = 0;
439 pos[1] = 0;
440 return;
441 }
442 }
443
444 /**
445 * @brief Returns the absolute position of a node.
446 * @param[in] node Context node
447 * @param[out] pos Absolute position
448 */
UI_GetNodeAbsPos(const uiNode_t * node,vec2_t pos)449 void UI_GetNodeAbsPos (const uiNode_t* node, vec2_t pos)
450 {
451 assert(node);
452 assert(pos);
453
454 /* if we request the position of a non drawable node, there is a problem */
455 if (node->behaviour->isVirtual)
456 Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' doesn't have a position", node->name);
457
458 Vector2Set(pos, 0, 0);
459 while (node) {
460 #ifdef DEBUG
461 if (node->box.pos[0] != (int)node->box.pos[0] || node->box.pos[1] != (int)node->box.pos[1])
462 Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' position %f,%f is not integer", UI_GetPath(node), node->box.pos[0], node->box.pos[1]);
463 #endif
464 pos[0] += node->box.pos[0];
465 pos[1] += node->box.pos[1];
466 node = node->parent;
467 }
468 }
469
470 /**
471 * @brief Returns the absolute position of a node in the screen.
472 * Screen position is not used for the node rendering cause we use OpenGL
473 * translations. But this function is need for some R_functions methodes.
474 * @param[in] node Context node
475 * @param[out] pos Absolute position into the screen
476 */
UI_GetNodeScreenPos(const uiNode_t * node,vec2_t pos)477 void UI_GetNodeScreenPos (const uiNode_t* node, vec2_t pos)
478 {
479 assert(node);
480 assert(pos);
481
482 /* if we request the position of a non drawable node, there is a problem */
483 if (node->behaviour->isVirtual)
484 Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' doesn't have a position", node->name);
485
486 Vector2Set(pos, 0, 0);
487 while (node) {
488 #ifdef DEBUG
489 if (node->box.pos[0] != (int)node->box.pos[0] || node->box.pos[1] != (int)node->box.pos[1])
490 Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' position %f,%f is not integer", UI_GetPath(node), node->box.pos[0], node->box.pos[1]);
491 #endif
492 pos[0] += node->box.pos[0];
493 pos[1] += node->box.pos[1];
494 node = node->parent;
495
496 if (node && UI_Node_IsScrollableContainer(node)) {
497 vec2_t clientPosition = {0, 0};
498 UI_Node_GetClientPosition(node, clientPosition);
499 pos[0] += clientPosition[0];
500 pos[1] += clientPosition[1];
501 }
502 }
503 }
504
505 /**
506 * @brief Update a relative point to an absolute one
507 * @param[in] node The requested node
508 * @param[in,out] pos A point to transform
509 */
UI_NodeRelativeToAbsolutePoint(const uiNode_t * node,vec2_t pos)510 void UI_NodeRelativeToAbsolutePoint (const uiNode_t* node, vec2_t pos)
511 {
512 assert(node);
513 assert(pos);
514 while (node) {
515 pos[0] += node->box.pos[0];
516 pos[1] += node->box.pos[1];
517 node = node->parent;
518 }
519 }
520
521 /**
522 * @brief Update an absolute position to a relative one
523 * @param[in] node Context node
524 * @param[in,out] x an absolute x position
525 * @param[in,out] y an absolute y position
526 */
UI_NodeAbsoluteToRelativePos(const uiNode_t * node,int * x,int * y)527 void UI_NodeAbsoluteToRelativePos (const uiNode_t* node, int* x, int* y)
528 {
529 assert(node != nullptr);
530 /* if we request the position of an undrawable node, there is a problem */
531 assert(node->behaviour->isVirtual == false);
532 assert(x != nullptr);
533 assert(y != nullptr);
534
535 /* if we request the position of an undrawable node, there is a problem */
536 if (node->behaviour->isVirtual)
537 Com_Error(ERR_DROP, "UI_NodeAbsoluteToRelativePos: Node '%s' doesn't have a position", node->name);
538
539 while (node) {
540 *x -= node->box.pos[0];
541 *y -= node->box.pos[1];
542
543 if (UI_Node_IsScrollableContainer(node)) {
544 vec2_t clientPosition = {0, 0};
545 UI_Node_GetClientPosition(node, clientPosition);
546 *x -= clientPosition[0];
547 *y -= clientPosition[1];
548 }
549
550 node = node->parent;
551 }
552 }
553
554 /**
555 * @brief Hides a given node
556 * @note Sanity check whether node is null included
557 */
UI_HideNode(uiNode_t * node)558 void UI_HideNode (uiNode_t* node)
559 {
560 if (node)
561 node->invis = true;
562 else
563 Com_Printf("UI_HideNode: No node given\n");
564 }
565
566 /**
567 * @brief Unhides a given node
568 * @note Sanity check whether node is null included
569 */
UI_UnHideNode(uiNode_t * node)570 void UI_UnHideNode (uiNode_t* node)
571 {
572 if (node)
573 node->invis = false;
574 else
575 Com_Printf("UI_UnHideNode: No node given\n");
576 }
577
578 /**
579 * @brief Update the node size and fire the size callback
580 */
UI_NodeSetSize(uiNode_t * node,vec2_t size)581 void UI_NodeSetSize (uiNode_t* node, vec2_t size)
582 {
583 if (Vector2Equal(node->box.size, size))
584 return;
585 node->box.size[0] = size[0];
586 node->box.size[1] = size[1];
587 UI_Node_SizeChanged(node);
588 }
589
590 /**
591 * @brief Search a child node by given name
592 * @note Only search with one depth
593 */
UI_GetNode(const uiNode_t * const node,const char * name)594 uiNode_t* UI_GetNode (const uiNode_t* const node, const char* name)
595 {
596 uiNode_t* current = nullptr;
597
598 if (!node)
599 return nullptr;
600
601 for (current = node->firstChild; current; current = current->next)
602 if (Q_streq(name, current->name))
603 break;
604
605 return current;
606 }
607
608 /**
609 * @brief Insert a node next another one into a node. If prevNode is nullptr add the node on the head of the window
610 * @param[in] node Node where inserting a node
611 * @param[in] prevNode previous node, will became before the newNode; else nullptr if newNode will become the first child of the node
612 * @param[in] newNode node we insert
613 */
UI_InsertNode(uiNode_t * const node,uiNode_t * prevNode,uiNode_t * newNode)614 void UI_InsertNode (uiNode_t* const node, uiNode_t* prevNode, uiNode_t* newNode)
615 {
616 if (newNode->root == nullptr)
617 newNode->root = node->root;
618
619 assert(node);
620 assert(newNode);
621 /* insert only a single element */
622 assert(!newNode->next);
623 /* everything come from the same window (force the dev to update himself this links) */
624 assert(!prevNode || (prevNode->root == newNode->root));
625
626 uiNode_t** const anchor = prevNode ? &prevNode->next : &node->firstChild;
627 newNode->next = *anchor;
628 *anchor = newNode;
629 newNode->parent = node;
630
631 if (!newNode->next)
632 node->lastChild = newNode;
633
634 if (newNode->root && newNode->indexed)
635 UI_WindowNodeAddIndexedNode(newNode->root, newNode);
636
637 UI_Invalidate(node);
638 }
639
640 /**
641 * @brief Remove a node from a parent node
642 * @return The removed node, else nullptr
643 * @param[in] node Node where is the child
644 * @param[in] child Node we want to remove
645 */
UI_RemoveNode(uiNode_t * const node,uiNode_t * child)646 uiNode_t* UI_RemoveNode (uiNode_t* const node, uiNode_t* child)
647 {
648 assert(node);
649 assert(child);
650 assert(child->parent == node);
651 assert(node->firstChild);
652
653 /** remove the 'child' node */
654 for (uiNode_t** anchor = &node->firstChild;; anchor = &(*anchor)->next) {
655 if (!*anchor)
656 return 0;
657
658 if (*anchor == child) {
659 *anchor = child->next;
660 break;
661 }
662 }
663 UI_Invalidate(node);
664
665 /** update cache */
666 if (node->lastChild == child) {
667 node->lastChild = node->firstChild;
668 while (node->lastChild && node->lastChild->next)
669 node->lastChild = node->lastChild->next;
670 }
671 if (child->root && child->indexed)
672 UI_WindowNodeRemoveIndexedNode(child->root, child);
673
674 child->next = nullptr;
675 return child;
676 }
677
UI_UpdateRoot(uiNode_t * node,uiNode_t * newRoot)678 void UI_UpdateRoot (uiNode_t* node, uiNode_t* newRoot)
679 {
680 node->root = newRoot;
681 node = node->firstChild;
682 while (node) {
683 UI_UpdateRoot(node, newRoot);
684 node = node->next;
685 }
686 }
687
688 /**
689 * @brief add a node at the end of the node child
690 */
UI_AppendNode(uiNode_t * const node,uiNode_t * newNode)691 void UI_AppendNode (uiNode_t* const node, uiNode_t* newNode)
692 {
693 UI_InsertNode(node, node->lastChild, newNode);
694 }
695
UI_NodeSetPropertyFromRAW(uiNode_t * node,const value_t * property,const void * rawValue,int rawType)696 void UI_NodeSetPropertyFromRAW (uiNode_t* node, const value_t* property, const void* rawValue, int rawType)
697 {
698 if (property->type != rawType) {
699 Com_Printf("UI_NodeSetPropertyFromRAW: type %i expected, but @%s type %i found. Property setter to '%s@%s' skipped\n", rawType, property->string, property->type, UI_GetPath(node), property->string);
700 return;
701 }
702
703 if ((property->type & V_UI_MASK) == V_NOT_UI)
704 Com_SetValue(node, rawValue, property->type, property->ofs, property->size);
705 else if ((property->type & V_UI_MASK) == V_UI_CVAR) {
706 UI_FreeStringProperty(Com_GetValue<void*>(node, property));
707 switch (property->type & V_BASETYPEMASK) {
708 case V_FLOAT: *Com_GetValue<float* >(node, property) = *static_cast<float const*>(rawValue); break;
709 case V_INT: *Com_GetValue<int* >(node, property) = *static_cast<int const*>(rawValue); break;
710 default: Com_GetValue<byte const*>(node, property) = static_cast<byte const*>(rawValue); break;
711 }
712 } else if (property->type == V_UI_ACTION) {
713 Com_GetValue<uiAction_t const*>(node, property) = static_cast<uiAction_t const*>(rawValue);
714 } else if (property->type == V_UI_SPRITEREF) {
715 Com_GetValue<uiSprite_t const*>(node, property) = static_cast<uiSprite_t const*>(rawValue);
716 } else {
717 Com_Error(ERR_FATAL, "UI_NodeSetPropertyFromRAW: Property type '%d' unsupported", property->type);
718 }
719 UI_Node_PropertyChanged(node, property);
720 }
721
722 /**
723 * @brief Set node property
724 */
UI_NodeSetProperty(uiNode_t * node,const value_t * property,const char * value)725 bool UI_NodeSetProperty (uiNode_t* node, const value_t* property, const char* value)
726 {
727 const int specialType = property->type & V_UI_MASK;
728 int result;
729 size_t bytes;
730
731 switch (specialType) {
732 case V_NOT_UI: /* common type */
733 result = Com_ParseValue(node, value, property->type, property->ofs, property->size, &bytes);
734 if (result != RESULT_OK) {
735 Com_Printf("UI_NodeSetProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
736 return false;
737 }
738 UI_Node_PropertyChanged(node, property);
739 return true;
740
741 case V_UI_CVAR: /* cvar */
742 switch ((int)property->type) {
743 case V_UI_CVAR:
744 if (Q_strstart(value, "*cvar:")) {
745 char*& b = Com_GetValue<char*>(node, property);
746 UI_FreeStringProperty(b);
747 b = Mem_PoolStrDup(value, ui_dynStringPool, 0);
748 UI_Node_PropertyChanged(node, property);
749 return true;
750 }
751 break;
752 case V_CVAR_OR_FLOAT:
753 {
754 float f;
755
756 if (Q_strstart(value, "*cvar:")) {
757 char*& b = Com_GetValue<char*>(node, property);
758 UI_FreeStringProperty(b);
759 b = Mem_PoolStrDup(value, ui_dynStringPool, 0);
760 UI_Node_PropertyChanged(node, property);
761 return true;
762 }
763
764 result = Com_ParseValue(&f, value, V_FLOAT, 0, sizeof(f), &bytes);
765 if (result != RESULT_OK) {
766 Com_Printf("UI_NodeSetProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
767 return false;
768 }
769
770 void* const b = Com_GetValue<void*>(node, property);
771 if (char const* const cvar = Q_strstart((char const*)b, "*cvar:"))
772 Cvar_SetValue(cvar, f);
773 else
774 *(float*) b = f;
775 UI_Node_PropertyChanged(node, property);
776 return true;
777 }
778 case V_CVAR_OR_LONGSTRING:
779 case V_CVAR_OR_STRING:
780 {
781 char*& b = Com_GetValue<char*>(node, property);
782 UI_FreeStringProperty(b);
783 b = Mem_PoolStrDup(value, ui_dynStringPool, 0);
784 UI_Node_PropertyChanged(node, property);
785 return true;
786 }
787 }
788 break;
789
790 case V_UI:
791 switch ((int)property->type) {
792 case V_UI_SPRITEREF:
793 {
794 uiSprite_t* sprite = UI_GetSpriteByName(value);
795 Com_GetValue<uiSprite_t const*>(node, property) = sprite;
796 return true;
797 }
798 }
799 break;
800 }
801
802 Com_Printf("UI_NodeSetProperty: Unimplemented type for property '%s@%s'\n", UI_GetPath(node), property->string);
803 return false;
804 }
805
806 /**
807 * @brief Return a string from a node property
808 * @param[in] node Requested node
809 * @param[in] property Requested property
810 * @return Return a string value of a property, else nullptr, if the type is not supported
811 */
UI_GetStringFromNodeProperty(const uiNode_t * node,const value_t * property)812 const char* UI_GetStringFromNodeProperty (const uiNode_t* node, const value_t* property)
813 {
814 const int baseType = property->type & V_UI_MASK;
815 assert(node);
816 assert(property);
817
818 switch (baseType) {
819 case V_NOT_UI: /* common type */
820 return Com_ValueToStr(node, property->type, property->ofs);
821 case V_UI_CVAR:
822 switch ((int)property->type) {
823 case V_CVAR_OR_FLOAT:
824 {
825 const float f = UI_GetReferenceFloat(node, Com_GetValue<void*>(node, property));
826 const int i = f;
827 if (f == i)
828 return va("%i", i);
829 else
830 return va("%f", f);
831 }
832 case V_CVAR_OR_LONGSTRING:
833 case V_CVAR_OR_STRING:
834 case V_UI_CVAR:
835 return UI_GetReferenceString(node, Com_GetValue<char*>(node, property));
836 }
837 break;
838 default:
839 break;
840 }
841
842 Com_Printf("UI_GetStringFromNodeProperty: Unsupported string getter for property type 0x%X (%s@%s)\n", property->type, UI_GetPath(node), property->string);
843 return nullptr;
844 }
845
846 /**
847 * @brief Return a float from a node property
848 * @param[in] node Requested node
849 * @param[in] property Requested property
850 * @return Return the float value of a property, else 0, if the type is not supported
851 * @note If the type is not supported, a waring is reported to the console
852 */
UI_GetFloatFromNodeProperty(const uiNode_t * node,const value_t * property)853 float UI_GetFloatFromNodeProperty (const uiNode_t* node, const value_t* property)
854 {
855 assert(node);
856
857 if (property->type == V_FLOAT) {
858 return Com_GetValue<float>(node, property);
859 } else if ((property->type & V_UI_MASK) == V_UI_CVAR) {
860 void* const b = Com_GetValue<void*>(node, property);
861 if (char const* const cvarName = Q_strstart((char const*)b, "*cvar:")) {
862 const cvar_t* cvar = Cvar_Get(cvarName, "", 0, "UI script cvar property");
863 return cvar->value;
864 } else if (property->type == V_CVAR_OR_FLOAT) {
865 return *(const float*) b;
866 } else if (property->type == V_CVAR_OR_STRING) {
867 return atof((const char*)b);
868 }
869 } else if (property->type == V_INT) {
870 return Com_GetValue<int>(node, property);
871 } else if (property->type == V_BOOL) {
872 return Com_GetValue<bool>(node, property);
873 } else {
874 #ifdef DEBUG
875 Com_Printf("UI_GetFloatFromNodeProperty: Unimplemented float getter for property '%s@%s'. If it should return a float, request it.\n", UI_GetPath(node), property->string);
876 #else
877 Com_Printf("UI_GetFloatFromNodeProperty: Property '%s@%s' can't return a float\n", UI_GetPath(node), property->string);
878 #endif
879 }
880
881 return 0;
882 }
883
884 /**
885 * @brief Invalidate a node and all his parent to request a layout update
886 */
UI_Invalidate(uiNode_t * node)887 void UI_Invalidate (uiNode_t* node)
888 {
889 while (node) {
890 if (node->invalidated)
891 return;
892 node->invalidated = true;
893 node = node->parent;
894 }
895 }
896
897 /**
898 * @brief Validate a node tree
899 */
UI_Validate(uiNode_t * node)900 void UI_Validate (uiNode_t* node)
901 {
902 if (node->invalidated)
903 UI_Node_DoLayout(node);
904 }
905