1 /**
2 * @file
3 */
4
5 /*
6 Copyright (C) 2002-2013 UFO: Alien Invasion.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24
25 #include "ui_main.h"
26 #include "ui_internal.h"
27 #include "ui_actions.h"
28 #include "ui_input.h"
29 #include "ui_internal.h"
30 #include "ui_nodes.h"
31 #include "ui_node.h"
32 #include "ui_parse.h"
33 #include "ui_draw.h"
34 #include "ui_dragndrop.h"
35 #include "ui_timer.h"
36
37 #include "../input/cl_keys.h"
38 #include "../input/cl_input.h"
39 #include "../cl_shared.h"
40 #include "../battlescape/cl_localentity.h"
41 #include "../battlescape/cl_camera.h"
42 #include "../battlescape/cl_actor.h"
43 #include "../battlescape/cl_battlescape.h"
44
45 /**
46 * @brief save the node with the focus
47 */
48 static uiNode_t* focusNode;
49
50 /**
51 * @brief save the current hovered node (first node under the mouse)
52 * @sa UI_GetHoveredNode
53 * @sa UI_MouseMove
54 * @sa UI_CheckMouseMove
55 */
56 static uiNode_t* hoveredNode;
57
58 /**
59 * @brief save the previous hovered node
60 */
61 static uiNode_t* oldHoveredNode;
62
63 /**
64 * @brief save old position of the mouse
65 */
66 static int oldMousePosX, oldMousePosY;
67
68 /**
69 * @brief save the captured node
70 * @sa UI_SetMouseCapture
71 * @sa UI_GetMouseCapture
72 * @sa UI_MouseRelease
73 * @todo think about replacing it by a boolean. When capturedNode != nullptr => hoveredNode == capturedNode
74 * it create unneed case
75 */
76 static uiNode_t* capturedNode;
77
78 /**
79 * @brief Store node which receive a mouse down event
80 */
81 static uiNode_t* pressedNode;
82
83 /**
84 * @brief X position of the mouse when pressedNode != nullptr
85 */
86 static int pressedNodeLocationX;
87
88 /**
89 * @brief Y position of the mouse when pressedNode != nullptr
90 */
91 static int pressedNodeLocationY;
92
93 /**
94 * @brief Button pressed when pressedNode != nullptr
95 */
96 static int pressedNodeButton;
97
98 /**
99 * @brief Value of pressedNodeLocationX when event already sent
100 */
101 #define UI_STARTDRAG_ALREADY_SENT -1
102
103 /**
104 * @brief Value of the delay to wait before sending a long press event
105 * This value is used for Qt 4.7. Maybe we can use a cvar
106 */
107 #define LONGPRESS_DELAY 800
108
109 /**
110 * @brief Timer used to manage long press event
111 */
112 static uiTimer_t* longPressTimer;
113
114
115 /**
116 * @brief Execute the current focused action node
117 * @note Action nodes are nodes with click defined
118 * @sa Key_Event
119 * @sa UI_FocusNextActionNode
120 */
UI_FocusExecuteActionNode(void)121 static bool UI_FocusExecuteActionNode (void)
122 {
123 #if 0 /**< @todo need a cleanup */
124 if (IN_GetMouseSpace() != MS_UI)
125 return false;
126
127 if (UI_GetMouseCapture())
128 return false;
129
130 if (focusNode) {
131 if (focusNode->onClick) {
132 UI_ExecuteEventActions(focusNode, focusNode->onClick);
133 }
134 UI_ExecuteEventActions(focusNode, focusNode->onMouseLeave);
135 focusNode = nullptr;
136 return true;
137 }
138 #endif
139 return false;
140 }
141
142 #if 0 /**< @todo need a cleanup */
143 /**
144 * @sa UI_FocusExecuteActionNode
145 * @note node must be in a window
146 */
147 static uiNode_t* UI_GetNextActionNode (uiNode_t* node)
148 {
149 if (node)
150 node = node->next;
151 while (node) {
152 if (UI_CheckVisibility(node) && !node->invis
153 && ((node->onClick && node->onMouseEnter) || node->onMouseEnter))
154 return node;
155 node = node->next;
156 }
157 return nullptr;
158 }
159 #endif
160
161 /**
162 * @brief Set the focus to the next action node
163 * @note Action nodes are nodes with click defined
164 * @sa Key_Event
165 * @sa UI_FocusExecuteActionNode
166 */
UI_FocusNextActionNode(void)167 static bool UI_FocusNextActionNode (void)
168 {
169 #if 0 /**< @todo need a cleanup */
170 static int i = UI_MAX_WINDOWSTACK + 1; /* to cycle between all windows */
171
172 if (IN_GetMouseSpace() != MS_UI)
173 return false;
174
175 if (UI_GetMouseCapture())
176 return false;
177
178 if (i >= ui_global.windowStackPos)
179 i = UI_GetLastFullScreenWindow();
180
181 assert(i >= 0);
182
183 if (focusNode) {
184 uiNode_t* node = UI_GetNextActionNode(focusNode);
185 if (node)
186 return UI_FocusSetNode(node);
187 }
188
189 while (i < ui_global.windowStackPos) {
190 uiNode_t* window;
191 window = ui_global.windowStack[i++];
192 if (UI_FocusSetNode(UI_GetNextActionNode(window->firstChild)))
193 return true;
194 }
195 i = UI_GetLastFullScreenWindow();
196
197 /* no node to focus */
198 UI_RemoveFocus();
199 #endif
200 return false;
201 }
202
203 /**
204 * @brief request the focus for a node
205 */
UI_RequestFocus(uiNode_t * node)206 void UI_RequestFocus (uiNode_t* node)
207 {
208 uiNode_t* tmp;
209 assert(node);
210 if (node == focusNode)
211 return;
212
213 /* invalidate the data before calling the event */
214 tmp = focusNode;
215 focusNode = nullptr;
216
217 /* lost the focus */
218 if (tmp) {
219 UI_Node_FocusLost(tmp);
220 }
221
222 /* get the focus */
223 focusNode = node;
224 UI_Node_FocusGained(focusNode);
225 }
226
227 /**
228 * @brief check if a node got the focus
229 */
UI_HasFocus(const uiNode_t * node)230 bool UI_HasFocus (const uiNode_t* node)
231 {
232 return node == focusNode;
233 }
234
235 /**
236 * @sa UI_FocusExecuteActionNode
237 * @sa UI_FocusNextActionNode
238 * @sa IN_Frame
239 * @sa Key_Event
240 */
UI_RemoveFocus(void)241 void UI_RemoveFocus (void)
242 {
243 uiNode_t* tmp;
244
245 if (UI_GetMouseCapture())
246 return;
247
248 if (!focusNode)
249 return;
250
251 /* invalidate the data before calling the event */
252 tmp = focusNode;
253 focusNode = nullptr;
254
255 /* callback the lost of the focus */
256 UI_Node_FocusLost(tmp);
257 }
258
UI_AllocStaticKeyBinding(void)259 static uiKeyBinding_t* UI_AllocStaticKeyBinding (void)
260 {
261 uiKeyBinding_t* result;
262 if (ui_global.numKeyBindings >= UI_MAX_KEYBINDING)
263 Com_Error(ERR_FATAL, "UI_AllocStaticKeyBinding: UI_MAX_KEYBINDING hit");
264
265 result = &ui_global.keyBindings[ui_global.numKeyBindings];
266 ui_global.numKeyBindings++;
267
268 OBJZERO(*result);
269 return result;
270 }
271
UI_GetKeyBindingCount(void)272 int UI_GetKeyBindingCount (void)
273 {
274 return ui_global.numKeyBindings;
275 }
276
UI_GetKeyBindingByIndex(int index)277 uiKeyBinding_t* UI_GetKeyBindingByIndex (int index)
278 {
279 return &ui_global.keyBindings[index];
280 }
281
282 /**
283 * @brief Set a binding from a key to a node to active.
284 *
285 * This command create a relation between a key and a node.
286 *
287 * The relation is stored to the node (to display the shortcut into tooltip)
288 * and to parent window of the node (to faster search of all available shortcuts).
289 *
290 * The storage on the node is not a list, then if there is more than one shortcut
291 * to a node we can't display all shortcut to tooltip, but the binding will still
292 * work.
293 *
294 * If the parent window is inherited, the binding is dup to others extend
295 * windows and the relation is flagged as "inherited".
296 *
297 * @param path Path to a node, or a node mathod
298 * @param key The key number to use (see for example the K_* names are matched up.)
299 * @param inherited True if this binding is inherited from another binding.
300 *
301 * @todo check: only one binding per nodes
302 * @todo check: key per window must be unique
303 * @todo check: key used into UI_KeyPressed can't be used
304 */
UI_SetKeyBindingEx(const char * path,int key,const char * description,bool inherited)305 static void UI_SetKeyBindingEx (const char* path, int key, const char* description, bool inherited)
306 {
307 uiNode_t* node;
308 uiKeyBinding_t* binding;
309 const value_t* property = nullptr;
310 int windowId;
311 char newPath[256];
312
313 UI_ReadNodePath(path, nullptr, &node, &property);
314 if (node == nullptr) {
315 Com_Printf("UI_SetKeyBinding: node \"%s\" not found.\n", path);
316 return;
317 }
318
319 if (property != nullptr && property->type != V_UI_NODEMETHOD)
320 Com_Error(ERR_FATAL, "UI_SetKeyBinding: Only node and method are supported. Property @%s not found in path \"%s\".", property->string, path);
321
322 /* init and link the keybinding */
323 binding = UI_AllocStaticKeyBinding();
324 binding->node = node;
325 binding->property = property;
326 binding->key = key;
327 binding->inherited = inherited;
328 node->key = binding;
329
330 if (Q_strnull(description))
331 Com_Printf("Warning: Empty description for UI keybinding: %s (%s)\n", path, Key_KeynumToString(key));
332 else
333 binding->description = Mem_PoolStrDup(description, ui_dynPool, 0);
334
335 UI_WindowNodeRegisterKeyBinding(node->root, binding);
336
337 /* search and update windows extend node->root */
338 for (windowId = 0; windowId < ui_global.numWindows; windowId++) {
339 uiNode_t* window = ui_global.windows[windowId];
340
341 /* skip window which are not direct extends of the main window */
342 if (window->super != node->root)
343 continue;
344
345 /* create a new patch from the new windows */
346 newPath[0] = '\0';
347 Q_strcat(newPath, sizeof(newPath), "%s%s", window->name, path + strlen(node->root->name));
348 UI_SetKeyBindingEx(newPath, key, description, true);
349 }
350 }
351
352 /**
353 * @brief Set a binding from a key to a node to active.
354 *
355 * @param path Path to a node, or a node mathod
356 * @param key The key number to use (see for example the K_* names are matched up.)
357 *
358 * @todo check: only one binding per nodes?
359 * @todo check: key per window must be unique
360 * @todo check: key used into UI_KeyPressed can't be used
361 */
UI_SetKeyBinding(const char * path,int key,const char * description)362 void UI_SetKeyBinding (const char* path, int key, const char* description)
363 {
364 UI_SetKeyBindingEx(path, key, description, false);
365 }
366
367 /**
368 * @brief Check if a key binding exists for a window and execute it
369 */
UI_KeyPressedInWindow(unsigned int key,const uiNode_t * window)370 static bool UI_KeyPressedInWindow (unsigned int key, const uiNode_t* window)
371 {
372 uiNode_t* node;
373 const uiKeyBinding_t* binding;
374
375 /* search requested key binding */
376 binding = UI_WindowNodeGetKeyBinding(window, key);
377 if (!binding)
378 return false;
379
380 /* check node visibility */
381 node = binding->node;
382 while (node) {
383 if (node->disabled || node->invis)
384 return false;
385 node = node->parent;
386 }
387
388 /* execute event */
389 node = binding->node;
390 if (binding->property == nullptr)
391 UI_Node_Activate(node);
392 else if (binding->property->type == V_UI_NODEMETHOD) {
393 uiCallContext_t newContext;
394 uiNodeMethod_t func = (uiNodeMethod_t) binding->property->ofs;
395 newContext.source = node;
396 newContext.useCmdParam = false;
397 func(node, &newContext);
398 } else
399 Com_Printf("UI_KeyPressedInWindow: @%s not supported.", binding->property->string);
400
401 return true;
402 }
403
404 /**
405 * @brief Called by the client when the user released a key
406 * @param[in] key key code, either K_ value or lowercase ascii
407 * @param[in] unicode translated meaning of keypress in unicode
408 * @return true, if we used the event
409 */
UI_KeyRelease(unsigned int key,unsigned short unicode)410 bool UI_KeyRelease (unsigned int key, unsigned short unicode)
411 {
412 /* translate event into the node with focus */
413 if (focusNode) {
414 return UI_Node_KeyReleased(focusNode, key, unicode);
415 }
416
417 return false;
418 }
419
420 /**
421 * @brief Called by the client when the user type a key
422 * @param[in] key key code, either K_ value or lowercase ascii
423 * @param[in] unicode translated meaning of keypress in unicode
424 * @return true, if we used the event
425 * @todo think about what we should do when the mouse is captured
426 */
UI_KeyPressed(unsigned int key,unsigned short unicode)427 bool UI_KeyPressed (unsigned int key, unsigned short unicode)
428 {
429 int windowId;
430 int lastWindowId;
431
432 if (UI_DNDIsDragging()) {
433 if (key == K_ESCAPE) {
434 UI_DNDAbort();
435 return true;
436 }
437 return false;
438 }
439
440 if (key == K_ESCAPE && CL_BattlescapeRunning()
441 && selActor && CL_ActorFireModeActivated(selActor->actorMode)) {
442 /* Cancel firing with Escape, needed for Android, where right mouse click is bound to multitouch, which is non-obvious */
443 CL_ActorSetMode(selActor, M_MOVE);
444 return true;
445 }
446
447 /* translate event into the node with focus */
448 if (focusNode && UI_Node_KeyPressed(focusNode, key, unicode)) {
449 return true;
450 }
451
452 /* else use common behaviour */
453 switch (key) {
454 case K_TAB:
455 if (UI_FocusNextActionNode())
456 return true;
457 break;
458 case K_ENTER:
459 case K_KP_ENTER:
460 if (UI_FocusExecuteActionNode())
461 return true;
462 break;
463 case K_ESCAPE:
464 if (UI_GetMouseCapture() != nullptr) {
465 UI_MouseRelease();
466 return true;
467 }
468 UI_PopWindowWithEscKey();
469 return true;
470 }
471
472 lastWindowId = UI_GetLastFullScreenWindow();
473 if (lastWindowId < 0)
474 return false;
475
476 /* check "active" window from top to down */
477 for (windowId = ui_global.windowStackPos - 1; windowId >= lastWindowId; windowId--) {
478 const uiNode_t* window = ui_global.windowStack[windowId];
479 if (!window)
480 return false;
481 if (UI_KeyPressedInWindow(key, window))
482 return true;
483 if (UI_WindowIsModal(window))
484 break;
485 }
486
487 return false;
488 }
489
490 /**
491 * @brief Release all captured input (keyboard or mouse)
492 */
UI_ReleaseInput(void)493 void UI_ReleaseInput (void)
494 {
495 UI_RemoveFocus();
496 UI_MouseRelease();
497 if (UI_DNDIsDragging())
498 UI_DNDAbort();
499 }
500
501 /**
502 * @brief Return the captured node
503 * @return Return a node, else nullptr
504 */
UI_GetMouseCapture(void)505 uiNode_t* UI_GetMouseCapture (void)
506 {
507 return capturedNode;
508 }
509
510 /**
511 * @brief Captured the mouse into a node
512 */
UI_SetMouseCapture(uiNode_t * node)513 void UI_SetMouseCapture (uiNode_t* node)
514 {
515 assert(capturedNode == nullptr);
516 assert(node != nullptr);
517 capturedNode = node;
518 }
519
520 /**
521 * @brief Release the captured node
522 */
UI_MouseRelease(void)523 void UI_MouseRelease (void)
524 {
525 uiNode_t* tmp = capturedNode;
526
527 if (capturedNode == nullptr)
528 return;
529
530 capturedNode = nullptr;
531 UI_Node_CapturedMouseLost(tmp);
532 UI_InvalidateMouse();
533 }
534
UI_ResetInput(void)535 void UI_ResetInput (void)
536 {
537 hoveredNode = nullptr;
538 oldHoveredNode = nullptr;
539 focusNode = nullptr;
540 capturedNode = nullptr;
541 pressedNode = nullptr;
542 longPressTimer = nullptr;
543 }
544
545 /**
546 * @brief Get the current hovered node
547 * @return A node, else nullptr if the mouse hover nothing
548 */
UI_GetHoveredNode(void)549 uiNode_t* UI_GetHoveredNode (void)
550 {
551 return hoveredNode;
552 }
553
554 /**
555 * @brief Force to invalidate the mouse position and then to update hover nodes...
556 */
UI_InvalidateMouse(void)557 void UI_InvalidateMouse (void)
558 {
559 oldMousePosX = -1;
560 oldMousePosY = -1;
561 }
562
563 /**
564 * @brief Call mouse move only if the mouse position change
565 */
UI_CheckMouseMove(void)566 bool UI_CheckMouseMove (void)
567 {
568 /* is hovered node no more draw */
569 if (hoveredNode && (hoveredNode->invis || !UI_CheckVisibility(hoveredNode)))
570 UI_InvalidateMouse();
571
572 if (mousePosX != oldMousePosX || mousePosY != oldMousePosY) {
573 oldMousePosX = mousePosX;
574 oldMousePosY = mousePosY;
575 UI_MouseMove(mousePosX, mousePosY);
576 return true;
577 }
578
579 return false;
580 }
581
582 /**
583 * @brief Is called every time the mouse position change
584 */
UI_MouseMove(int x,int y)585 void UI_MouseMove (int x, int y)
586 {
587 if (UI_DNDIsDragging())
588 return;
589
590 if (pressedNode && !capturedNode) {
591 if (pressedNodeLocationX != UI_STARTDRAG_ALREADY_SENT) {
592 UI_TimerStop(longPressTimer);
593 int dist = abs(pressedNodeLocationX - x) + abs(pressedNodeLocationY - y);
594 if (dist > 4) {
595 uiNode_t* node = pressedNode;
596 while (node) {
597 if (UI_Node_StartDragging(node, pressedNodeLocationX, pressedNodeLocationY, x, y, pressedNodeButton))
598 break;
599 node = node->parent;
600 }
601 pressedNodeLocationX = UI_STARTDRAG_ALREADY_SENT;
602 }
603 }
604 }
605
606 /* send the captured move mouse event */
607 if (capturedNode) {
608 UI_Node_CapturedMouseMove(capturedNode, x, y);
609 return;
610 }
611
612 hoveredNode = UI_GetNodeAtPosition(x, y);
613
614 /* update houvered node by sending messages */
615 if (oldHoveredNode != hoveredNode) {
616 uiNode_t* commonNode = hoveredNode;
617 uiNode_t* node;
618
619 /* search the common node */
620 while (commonNode) {
621 node = oldHoveredNode;
622 while (node) {
623 if (node == commonNode)
624 break;
625 node = node->parent;
626 }
627 if (node != nullptr)
628 break;
629 commonNode = commonNode->parent;
630 }
631
632 /* send 'leave' event from old node to common node */
633 node = oldHoveredNode;
634 while (node != commonNode) {
635 UI_Node_MouseLeave(node);
636 node = node->parent;
637 }
638 if (oldHoveredNode)
639 oldHoveredNode->state = false;
640
641 /* send 'enter' event from common node to new node */
642 while (commonNode != hoveredNode) {
643 /** @todo we can remove that loop with an array allocation */
644 node = hoveredNode;
645 while (node->parent != commonNode)
646 node = node->parent;
647 commonNode = node;
648 UI_Node_MouseEnter(node);
649 }
650 if (hoveredNode) {
651 hoveredNode->state = true;
652 UI_Node_MouseEnter(node);
653 }
654 }
655
656 oldHoveredNode = hoveredNode;
657
658 /* send the move event */
659 if (hoveredNode)
660 UI_Node_MouseMove(hoveredNode, x, y);
661 }
662
663 #define UI_IsMouseInvalidate() (oldMousePosX == -1)
664
665 /**
666 * @brief Is called every time one clicks on a window/screen. Then checks if anything needs to be executed in the area of the click
667 * (e.g. button-commands, inventory-handling, geoscape-stuff, etc...)
668 * @sa UI_ExecuteEventActions
669 * @sa UI_RightClick
670 * @sa Key_Message
671 */
UI_LeftClick(int x,int y)672 static void UI_LeftClick (int x, int y)
673 {
674 if (UI_IsMouseInvalidate())
675 return;
676
677 /* send it to the captured mouse node */
678 if (capturedNode) {
679 UI_Node_LeftClick(capturedNode, x, y);
680 return;
681 }
682
683 /* if we click outside a dropdown window, we close it */
684 /** @todo need to refactoring it with the focus code (cleaner) */
685 /** @todo at least should be moved on the mouse down event (when the focus should change) */
686 /** @todo this code must be on mouse down */
687 if (!pressedNode && ui_global.windowStackPos != 0) {
688 uiNode_t* window = ui_global.windowStack[ui_global.windowStackPos - 1];
689 if (UI_WindowIsDropDown(window)) {
690 UI_PopWindow();
691 }
692 }
693
694 const bool disabled = (pressedNode == nullptr) || (pressedNode->disabled) || (pressedNode->parent && pressedNode->parent->disabled);
695 if (!disabled) {
696 UI_Node_LeftClick(pressedNode, x, y);
697 }
698 }
699
700 /**
701 * @sa GEO_ResetAction
702 * @sa UI_ExecuteEventActions
703 * @sa UI_LeftClick
704 * @sa UI_MiddleClick
705 * @sa UI_MouseWheel
706 */
UI_RightClick(int x,int y)707 static void UI_RightClick (int x, int y)
708 {
709 if (UI_IsMouseInvalidate())
710 return;
711
712 /* send it to the captured mouse node */
713 if (capturedNode) {
714 UI_Node_RightClick(capturedNode, x, y);
715 return;
716 }
717
718 const bool disabled = (pressedNode == nullptr) || (pressedNode->disabled) || (pressedNode->parent && pressedNode->parent->disabled);
719 if (!disabled) {
720 UI_Node_RightClick(pressedNode, x, y);
721 }
722 }
723
724 /**
725 * @sa UI_LeftClick
726 * @sa UI_RightClick
727 * @sa UI_MouseWheel
728 */
UI_MiddleClick(int x,int y)729 static void UI_MiddleClick (int x, int y)
730 {
731 if (UI_IsMouseInvalidate())
732 return;
733
734 /* send it to the captured mouse node */
735 if (capturedNode) {
736 UI_Node_MiddleClick(capturedNode, x, y);
737 return;
738 }
739
740 const bool disabled = (pressedNode == nullptr) || (pressedNode->disabled) || (pressedNode->parent && pressedNode->parent->disabled);
741 if (!disabled) {
742 UI_Node_MiddleClick(pressedNode, x, y);
743 }
744 }
745
746 /**
747 * @brief Called when we are in UI mode and scroll via mousewheel
748 * @note The geoscape zooming code is here, too (also in @see CL_ParseInput)
749 * @sa UI_LeftClick
750 * @sa UI_RightClick
751 * @sa UI_MiddleClick
752 */
UI_MouseScroll(int deltaX,int deltaY)753 void UI_MouseScroll (int deltaX, int deltaY)
754 {
755 uiNode_t* node;
756
757 /* send it to the captured mouse node */
758 if (capturedNode) {
759 UI_Node_Scroll(capturedNode, deltaX, deltaY);
760 return;
761 }
762
763 node = hoveredNode;
764
765 while (node) {
766 if (UI_Node_Scroll(node, deltaX, deltaY)) {
767 break;
768 }
769 node = node->parent;
770 }
771 }
772
773 /**
774 * Callback function waked up to send long click event
775 */
UI_LongPressCallback(uiNode_t *,uiTimer_t * timer)776 static void UI_LongPressCallback (uiNode_t* , uiTimer_t* timer)
777 {
778 UI_TimerStop(timer);
779
780 /* make sure the event still make sense */
781 if (pressedNode == nullptr)
782 return;
783
784 uiNode_t* node = pressedNode;
785 while (node) {
786 if (UI_Node_MouseLongPress(node, pressedNodeLocationX, pressedNodeLocationY, pressedNodeButton))
787 break;
788 node = node->parent;
789 }
790 }
791
792 /**
793 * @brief Called when we are in UI mode and down a mouse button
794 * @sa UI_LeftClick
795 * @sa UI_RightClick
796 * @sa UI_MiddleClick
797 */
UI_MouseDown(int x,int y,int button)798 void UI_MouseDown (int x, int y, int button)
799 {
800 /* disable old long click event */
801 if (longPressTimer)
802 UI_TimerStop(longPressTimer);
803
804 uiNode_t* node;
805
806 /* captured or hover node */
807 node = capturedNode ? capturedNode : hoveredNode;
808
809 if (node != nullptr) {
810 UI_MoveWindowOnTop(node->root);
811 UI_Node_MouseDown(node, x, y, button);
812 }
813
814 /* select clickableNode on button up, and detect multipress button */
815 if (pressedNode == nullptr) {
816 pressedNode = node;
817 pressedNodeLocationX = x;
818 pressedNodeLocationY = y;
819 pressedNodeButton = button;
820 /** @todo find a way to create the timer when UI loading and remove "if (longPressTimer)" */
821 if (longPressTimer == nullptr) {
822 longPressTimer = UI_AllocTimer(nullptr, LONGPRESS_DELAY, UI_LongPressCallback);
823 }
824 UI_TimerStart(longPressTimer);
825 } else {
826 pressedNode = nullptr;
827 }
828 }
829
830 /**
831 * @brief Called when we are in UI mode and up a mouse button
832 * @sa UI_LeftClick
833 * @sa UI_RightClick
834 * @sa UI_MiddleClick
835 */
UI_MouseUp(int x,int y,int button)836 void UI_MouseUp (int x, int y, int button)
837 {
838 /* disable long click event */
839 if (longPressTimer)
840 UI_TimerStop(longPressTimer);
841
842 /* send click event */
843 /** @todo it is really need to clean up this subfunctions */
844 if (pressedNode || capturedNode) {
845 switch (button) {
846 case K_MOUSE1:
847 UI_LeftClick(x, y);
848 break;
849 case K_MOUSE2:
850 UI_RightClick(x, y);
851 break;
852 case K_MOUSE3:
853 UI_MiddleClick(x, y);
854 break;
855 }
856 }
857
858 /* captured or hovered node */
859 uiNode_t* node = nullptr;
860 if (capturedNode) {
861 node = capturedNode;
862 } else if (pressedNode == hoveredNode) {
863 node = hoveredNode;
864 }
865
866 pressedNode = nullptr;
867 if (node == nullptr)
868 return;
869
870 UI_Node_MouseUp(node, x, y, button);
871 }
872