4 #include <Carbon/Carbon.h>
10 #include <unordered_map>
11 #include <unordered_set>
41 using std::unordered_map;
42 using std::unordered_set;
66 bool GUI::disableTabFocusControl =
false;
88 for (
const auto& [screenId, screen]:
screens) {
100 if (
id != screen->
getId()) {
101 Console::println(
"GUI::addScreen(): screen id '" +
id +
"' must be '" + screen->
getId() +
"'");
110 auto screensIt =
screens.find(
id);
111 if (screensIt !=
screens.end()) {
113 auto screen = screensIt->second;
126 vector<string> entitiesToRemove;
127 for (
const auto& [screenId, screen]:
screens) {
128 entitiesToRemove.push_back(screenId);
130 for (
auto i = 0; i < entitiesToRemove.size(); i++) {
157 auto screenIt =
screens.find(screenId);
158 if (screenIt ==
screens.end())
return;
164 auto screen = screenIt->second;
165 screen->setGUI(
this);
166 screen->setConditionsMet();
167 if (screenIdx == -1) {
179 screen->initializeMiniScript();
184 auto screenIt =
screens.find(screenId);
185 if (screenIt ==
screens.end())
return;
188 screenIt->second->setGUI(
nullptr);
210 if (screen->isEnabled() ==
false)
214 if (screen->isPopUp() ==
true)
237 if (focussedNode !=
nullptr) {
243 if (focussedNode->getController() !=
nullptr) focussedNode->getController()->onFocusLost();
244 focussedNode->getScreenNode()->forwardUnfocus(focussedNode);
252 if (focussedNode !=
nullptr) {
258 focussedNode->getBorder().topColor = focussedNodeScreen->getFoccussedBorderColor();
259 focussedNode->getBorder().leftColor = focussedNodeScreen->getFoccussedBorderColor();
260 focussedNode->getBorder().bottomColor = focussedNodeScreen->getFoccussedBorderColor();
261 focussedNode->getBorder().rightColor = focussedNodeScreen->getFoccussedBorderColor();
262 if (focussedNode->getController() !=
nullptr) focussedNode->getController()->onFocusGained();
263 focussedNode->getScreenNode()->forwardFocus(focussedNode);
271 if (focussedNode == newFoccussedNode) {
278 if (newFoccussedNode !=
nullptr)
focusNode();
288 auto focussedNodeIdx = -1;
294 auto focussedNextNodeIdx = (focussedNodeIdx + 1) %
focusableNodes.size();
297 newFocussedNode->scrollToNodeX();
298 newFocussedNode->scrollToNodeY();
309 auto focussedNodeIdx = -1;
315 int focussedPreviousNodeIdx = (focussedNodeIdx - 1) %
focusableNodes.size();
316 if (focussedPreviousNodeIdx < 0) focussedPreviousNodeIdx +=
focusableNodes.size();
319 newFocussedNode->scrollToNodeX();
320 newFocussedNode->scrollToNodeY();
334 if (screen->isEnabled() ==
false)
continue;
336 screen->invalidateLayouts();
337 screen->scrollToNodes();
347 if (screen->isEnabled() ==
false)
continue;
358 auto nodeId = node->
getId();
365 void GUI::handleMouseEvent(
GUINode* node,
GUIMouseEvent* event,
const unordered_set<string>& mouseOutCandidateEventNodeIds,
const unordered_set<string>& mouseOutClickCandidateEventNodeIds, unordered_set<string>& mousePressedEventNodeIds,
bool floatingNodes)
368 unordered_set<string> mouseEventNodeIgnore;
369 unordered_set<string> mouseEventNodeIds;
376 if (event->
getType() == GUIMouseEvent::MOUSEEVENT_DRAGGED) {
379 if (event->
getType() == GUIMouseEvent::MOUSEEVENT_RELEASED &&
384 if (floatingNodes ==
true) {
393 mouseEventNodeIds.insert(eventNodeId);
398 mouseEventNodeIds.insert(eventNodeId);
403 map<string, string> sortedMouseEventNodeIds;
404 for (
const auto& eventNodeId: mouseEventNodeIds) {
407 if (eventNode ==
nullptr)
continue;
409 sortedMouseEventNodeIds[to_string(eventNode->isEventBelongingToNode(event) !=
true) +
"_" + eventNode->getHierarchicalId()] = eventNodeId;
411 vector<string> sortedMouseEventNodeIdsVector;
412 for (
const auto& [sortedMouseEventNodeId, sortedMouseEventNode]: sortedMouseEventNodeIds) {
413 sortedMouseEventNodeIdsVector.push_back(sortedMouseEventNode);
415 reverse(sortedMouseEventNodeIdsVector.begin(), sortedMouseEventNodeIdsVector.end());
418 for (
const auto& eventNodeId: sortedMouseEventNodeIdsVector) {
421 if (eventNode ==
nullptr)
continue;
424 if (floatingNodes ==
false && eventNode->flow == GUINode_Flow::FLOATING)
continue;
427 if (event->
getType() == GUIMouseEvent::MOUSEEVENT_DRAGGED && event->
isProcessed() ==
true)
break;
430 auto controllerNode = eventNode;
431 if (controllerNode->getController() ==
nullptr) {
434 if (controllerNode ==
nullptr)
continue;
437 if (eventNode->getScreenNode()->getGUI() ==
nullptr)
continue;
444 if (event->
getType() == GUIMouseEvent::MOUSEEVENT_PRESSED) {
445 for (
const auto& eventNodeId: mouseEventNodeIds) {
451 mouseEventNodeIds.clear();
470 case (GUIKeyboardEvent::KEYCODE_TAB):
472 if (event->
getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED) {
479 event->setProcessed(
true);
482 case (GUIKeyboardEvent::KEYCODE_LEFT_ALT):
483 case (GUIKeyboardEvent::KEYCODE_RIGHT_ALT):
485 altDown =
event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED;
488 case (GUIKeyboardEvent::KEYCODE_LEFT_CONTROL):
489 case (GUIKeyboardEvent::KEYCODE_RIGHT_CONTROL):
491 controlDown =
event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED;
494 case (GUIKeyboardEvent::KEYCODE_LEFT_SHIFT):
495 case (GUIKeyboardEvent::KEYCODE_RIGHT_SHIFT):
497 shiftDown =
event->getType() == GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED;
510 unordered_map<string, unordered_set<string>> _mouseOutCandidateEventNodeIds;
511 unordered_map<string, unordered_set<string>> _mouseOutClickCandidateEventNodeIds;
520 map<string, GUINode*> tooltipFloatingNodes;
521 map<string, GUINode*> tooltipNodes;
524 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
525 auto screen = renderScreensCopy[i];
528 if (screen->isEnabled() ==
false)
continue;
531 const auto& floatingNodes = screen->getFloatingNodes();
532 for (
auto j = 0; j < floatingNodes.size(); j++) {
534 unordered_set<string> eventNodeIds;
535 unordered_set<string> eventFloatingNodeIds;
538 auto floatingNode = floatingNodes[j];
540 floatingNode->determineMouseEventNodes(&
lastMouseEvent, floatingNode->flow == GUINode_Flow::FLOATING, eventNodeIds, eventFloatingNodeIds, GUINode::DETERMINEMOUSEEVENTNODES_FLAG_TOOLTIP);
543 for (
const auto& eventFloatingNodeId: eventFloatingNodeIds) {
544 auto node = screen->getNodeById(eventFloatingNodeId);
545 tooltipFloatingNodes[node->getHierarchicalId()] = node;
547 for (
const auto& eventNodeId: eventNodeIds) {
548 auto node = screen->getNodeById(eventNodeId);
549 tooltipFloatingNodes[node->getHierarchicalId()] = node;
553 if (screen->isPopUp() ==
true)
break;
557 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
559 unordered_set<string> eventNodeIds;
560 unordered_set<string> eventFloatingNodeIds;
563 auto screen = renderScreensCopy[i];
564 if (screen->isEnabled() ==
false)
continue;
567 screen->determineMouseEventNodes(&
lastMouseEvent, screen->flow == GUINode_Flow::FLOATING, eventNodeIds, eventFloatingNodeIds, GUINode::DETERMINEMOUSEEVENTNODES_FLAG_TOOLTIP);
570 for (
const auto& eventFloatingNodeId: eventFloatingNodeIds) {
571 auto node = screen->getNodeById(eventFloatingNodeId);
572 tooltipNodes[node->getHierarchicalId()] = node;
574 for (
const auto& eventNodeId: eventNodeIds) {
575 auto node = screen->getNodeById(eventNodeId);
576 tooltipNodes[node->getHierarchicalId()] = node;
580 if (screen->isPopUp() ==
true)
break;
584 if (tooltipFloatingNodes.empty() ==
false) {
585 vector<GUINode*> tooltipFloatingNodesVector;
586 for (
const auto& [tooltipFloatingNodeId, tooltipFloatingNode]: tooltipFloatingNodes) {
587 tooltipFloatingNodesVector.push_back(tooltipFloatingNode);
589 reverse(tooltipFloatingNodesVector.begin(), tooltipFloatingNodesVector.end());
591 auto node = tooltipFloatingNodesVector[0];
594 if (tooltipNodes.empty() ==
false) {
595 vector<GUINode*> tooltipNodesVector;
596 for (
const auto& [tooltipNodeId, tooltipNode]: tooltipNodes) {
597 tooltipNodesVector.push_back(tooltipNode);
599 reverse(tooltipNodesVector.begin(), tooltipNodesVector.end());
602 auto node = tooltipNodesVector[0];
614 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
616 auto screen = renderScreensCopy[i];
619 screen->forwardTooltipCloseRequest();
629 if (event.getType() == GUIMouseEvent::MOUSEEVENT_MOVED) {
633 if (event.getType() == GUIMouseEvent::MOUSEEVENT_PRESSED || event.getType() == GUIMouseEvent::MOUSEEVENT_RELEASED) {
639 if (event.getType() == GUIMouseEvent::MOUSEEVENT_DRAGGED) {
640 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
641 auto screen = renderScreensCopy[i];
650 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
651 auto screen = renderScreensCopy[i];
654 if (screen->isEnabled() ==
false)
continue;
657 auto processed =
false;
658 const auto& floatingNodes = screen->getFloatingNodes();
659 for (
auto j = 0; j < floatingNodes.size(); j++) {
660 auto floatingNode = floatingNodes[j];
665 if (event.isProcessed() ==
true)
break;
670 if ((event.getType() == GUIMouseEvent::MOUSEEVENT_MOVED || floatingNode->isEventBelongingToNode(&event) ==
true) &&
671 floatingNode->flow == GUINode_Flow::FLOATING &&
672 floatingNode->conditionsMet ==
true &&
673 ((floatingNode->showOn.getConditions().empty() ==
false && floatingNode->showOn.has(
"always") ==
false) ||
674 (floatingNode->hideOn.getConditions().empty() ==
false && floatingNode->showOn.has(
"never") ==
false))) processed =
true;
677 if (event.isProcessed() ==
true)
break;
679 if (processed ==
true)
event.setProcessed(
true);
681 if (screen->isPopUp() ==
true)
break;
686 if (event.isProcessed() ==
false) {
687 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
688 auto screen = renderScreensCopy[i];
689 if (screen->isEnabled() ==
false)
continue;
691 if (screen->isPopUp() ==
true)
break;
696 if (event.getType() == GUIMouseEvent::MOUSEEVENT_RELEASED) {
697 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
698 auto screen = renderScreensCopy[i];
705 if (event.getButton() == GUIMouseEvent::MOUSEEVENT_BUTTON_LEFT && event.isProcessed() ==
false)
setFoccussedNode(
nullptr);
723 for (
int i = renderScreensCopy.size() - 1; i >= 0; i--) {
724 auto screen = renderScreensCopy[i];
725 if (screen->isEnabled() ==
false)
continue;
727 if (screen->getInputEventHandler() !=
nullptr) {
728 screen->getInputEventHandler()->handleInputEvents();
730 screen->forwardEvents();
731 if (screen->getMiniScript() !=
nullptr) {
734 if (screen->isPopUp() ==
true)
break;
738 for (
auto screen: renderScreensCopy) {
739 screen->clearEvents();
743 if (clearEvents ==
true) {
749 for (
int i = 0; i < renderScreensCopy.size(); i++) {
750 auto screen = renderScreensCopy[i];
752 auto screenMiniScript = screen->getMiniScript();
753 if (screenMiniScript ==
nullptr)
continue;
755 if (screenMiniScript->isPopped() ==
true) {
760 auto nextScreen = screenMiniScript->releaseNextScreenNode();
761 if (nextScreen ==
nullptr)
continue;
766 addScreen(nextScreen->getId(), nextScreen);
775 guiKeyboardEvent.
setTime(Time::getCurrentMillis());
776 guiKeyboardEvent.
setType(GUIKeyboardEvent::KEYBOARDEVENT_KEY_TYPED);
788 void GUI::onKeyDown (
int key,
int keyCode,
int x,
int y,
bool repeat,
int modifiers) {
791 guiKeyboardEvent.
setTime(Time::getCurrentMillis());
792 guiKeyboardEvent.
setType(GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED);
807 guiKeyboardEvent.
setTime(Time::getCurrentMillis());
808 guiKeyboardEvent.
setType(GUIKeyboardEvent::KEYBOARDEVENT_KEY_RELEASED);
822 guiMouseEvent.
setTime(Time::getCurrentMillis());
823 guiMouseEvent.
setType(GUIMouseEvent::MOUSEEVENT_DRAGGED);
826 guiMouseEvent.
setX(x);
827 guiMouseEvent.
setY(y);
841 guiMouseEvent.
setTime(Time::getCurrentMillis());
842 guiMouseEvent.
setType(GUIMouseEvent::MOUSEEVENT_MOVED);
845 guiMouseEvent.
setX(x);
846 guiMouseEvent.
setY(y);
863 guiMouseEvent.
setTime(Time::getCurrentMillis());
864 guiMouseEvent.
setType(state ==
MOUSE_BUTTON_DOWN?GUIMouseEvent::MOUSEEVENT_PRESSED:GUIMouseEvent::MOUSEEVENT_RELEASED);
867 guiMouseEvent.
setX(x);
868 guiMouseEvent.
setY(y);
885 guiMouseEvent.
setTime(Time::getCurrentMillis());
886 guiMouseEvent.
setType(GUIMouseEvent::MOUSEEVENT_WHEEL_MOVED);
889 guiMouseEvent.
setX(x);
890 guiMouseEvent.
setY(y);
893 guiMouseEvent.
setWheelY(direction * 1.0f);
903 bool isControlDown =
false;
904 bool isAltDown =
false;
905 bool isShiftDown =
false;
906 #if defined(__APPLE__)
909 #define IS_KEY_DOWN(key, var) \
911 uint8_t index = key / 32 ; \
912 uint8_t shift = key % 32 ; \
913 var = keys[index].bigEndianValue & (1 << shift); \
915 IS_KEY_DOWN(kVK_Command, isControlDown);
916 IS_KEY_DOWN(kVK_Option, isAltDown);
917 IS_KEY_DOWN(kVK_Shift, isShiftDown);
920 if (isControlDown ==
false &&
921 isAltDown ==
false &&
922 isShiftDown ==
false) {
927 guiKeyboardEvent.
setTime(Time::getCurrentMillis());
928 guiKeyboardEvent.
setType(GUIKeyboardEvent::KEYBOARDEVENT_KEY_PRESSED);
942 auto screenNodeWidthConstrained =
width;
943 auto screenNodeHeightConstrained =
height;
945 auto minRatio = 1.0f;
952 screenNodeHeightConstrained = (int)((
float)screenNodeHeightConstrained * minRatio);
957 screenNodeWidthConstrained = (int)((
float)screenNodeWidthConstrained * minRatio);
960 if ((screenNode->
sizeConstraints.
minWidth > 0 && screenNodeWidthConstrained < screenNode->sizeConstraints.minWidth) ||
962 if (screenNode->
sizeConstraints.
minWidth > 0 && screenNodeWidthConstrained < screenNode->sizeConstraints.minWidth) {
965 screenNodeHeightConstrained = (int)((
float)screenNodeHeightConstrained * ratio / minRatio);
967 if (screenNode->
sizeConstraints.
minHeight > 0 && screenNodeHeightConstrained < screenNode->sizeConstraints.minHeight) {
970 screenNodeWidthConstrained = (int)((
float)screenNodeWidthConstrained * ratio / minRatio);
974 screenNode->
setScreenSize(screenNodeWidthConstrained, screenNodeHeightConstrained);
void doneGUIMode()
Set up GUI mode rendering.
void initGUIMode()
Set up GUI mode rendering.
unordered_map< string, unordered_set< string > > mousePressedEventNodeIds
GUIColor unfocussedNodeBorderTopColor
void onMouseMoved(int x, int y) override
On mouse moved.
GUIElementNode * getFocussedNode()
vector< GUIScreenNode * > renderScreens
string focussedNodeScreenId
unordered_map< string, unordered_set< string > > mouseOutCandidateEventNodeIds
void onMouseDragged(int x, int y) override
On mouse dragged.
void focusPreviousNode()
Focus next node.
static STATIC_DLL_IMPEXT bool disableTabFocusControl
bool isHavingMouseInteraction(GUINode *node)
Reports if node has currently mouse interaction like dragging or pressing.
void removeRenderScreen(const string &screenId)
Remove render screen.
void handleEvents(bool clearEvents=true)
Handle screen events.
vector< GUIScreenNode * > focusableScreenNodes
void addRenderScreen(const string &screenId, int screenIdx=-1)
Add render screen.
vector< GUIElementNode * > focusableNodes
void onMouseButton(int button, int state, int x, int y) override
On mouse moved.
void handleMouseEvent(GUINode *node, GUIMouseEvent *event, const unordered_set< string > &mouseOutCandidateEventNodeIds, const unordered_set< string > &mouseOutClickCandidateEventNodeIds, unordered_set< string > &mousePressedEventNodeIds, bool floatingNodes)
Handle mouse event for given node.
unordered_map< string, bool > mouseIsDragging
void determineFocussedNodes()
Determine focussed nodes.
void onMouseWheel(int button, int direction, int x, int y) override
On mouse wheel.
GUIScreenNode * getScreen(const string &id)
Get screen.
unordered_map< string, GUIScreenNode * > screens
GUIRenderer * guiRenderer
void reshape(int width, int height)
Reshape.
string focussedNodeNodeId
void unfocusNode()
Unfocus current focussed node.
void unsetMouseStates()
Render screens change.
void render()
Render GUIs.
void onKeyUp(int key, int keyCode, int x, int y) override
On key up.
vector< GUIKeyboardEvent > keyboardEvents
static constexpr int64_t TOOLTIP_TIME
void setFoccussedNode(GUIElementNode *newFoccussedNode)
Set focussed node.
void onKeyDown(int key, int keyCode, int x, int y, bool repeat, int modifiers) override
On key down.
unordered_map< string, unordered_set< string > > mouseOutClickCandidateEventNodeIds
void focusNode()
Focus current focussed node.
void fakeKeyboardModifierEvent()
Fake a keyboard modifier event.
GUIColor unfocussedNodeBorderRightColor
void removeScreen(const string &id)
Removes an screen.
void addScreen(const string &id, GUIScreenNode *screen)
Add screen.
void onChar(int key, int x, int y) override
On char.
void invalidateFocussedNode()
Invalidate focussed node.
void reshapeScreen(GUIScreenNode *screenNode)
Reshape screen.
unordered_map< string, unordered_set< string > > mouseDraggingEventNodeIds
void focusNextNode()
Focus next node.
GUIColor unfocussedNodeBorderLeftColor
void reset()
Removes all screens and caches.
GUIMouseEvent lastMouseEvent
void handleKeyboardEvent(GUIKeyboardEvent *event)
Handle mouse event for given node.
void resetRenderScreens()
Reset render screens.
GUIColor unfocussedNodeBorderBottomColor
vector< GUIMouseEvent > mouseEvents
int32_t getKeyCode() const
void setKeyCode(int32_t code)
Set key code.
void setAltDown(bool altDown)
Set alt down.
void setProcessed(bool processed)
Set event processed.
void setType(GUIKeyboardEventType type)
Set type.
void setControlDown(bool controlDown)
Set control down.
GUIKeyboardEventType getType() const
void setKeyChar(int32_t keyChar)
Set key char.
void setTime(int64_t time)
Time in milliseconds.
void setMetaDown(bool metaDown)
Set meta down.
void setRepeat(bool repeat)
Set repeat.
void setShiftDown(bool shiftDown)
Set shift down.
GUIMouseEventType getType() const
void setYUnscaled(int yUnscaled)
Set y unscaled.
void setAltDown(bool altDown)
Set alt down.
void setWheelX(float wheelX)
Set up wheel x.
void setProcessed(bool processed)
Set processed.
void setControlDown(bool controlDown)
Set control down.
void setWheelZ(float wheelZ)
Set up wheel z.
void setXUnscaled(int xUnscaled)
Set x unscaled.
void setTime(int64_t time)
Set time.
void setButton(int button)
Set button.
void setShiftDown(bool shiftDown)
Set shift down.
void setType(GUIMouseEventType type)
Set type.
void setWheelY(float wheelY)
Set up wheel y.
void handleKeyboardEvent(GUIKeyboardEvent *event)
Handle keyboard event.
GUINodeConditions & getActiveConditions()
GUI element node conditions.
bool add(const string &condition)
Add a condition.
bool remove(const string &condition)
Remove a condition.
GUI node controller base class.
virtual void handleMouseEvent(GUINode *node, GUIMouseEvent *event)=0
Handle mouse event.
virtual void determineMouseEventNodes(GUIMouseEvent *event, bool floatingNode, unordered_set< string > &eventNodeIds, unordered_set< string > &eventFloatingNodeIds, int flags=DETERMINEMOUSEEVENTNODES_FLAG_NONE)
Determine mouse event nodes.
GUINodeController * getController()
int getGUIEffectOffsetX()
GUIParentNode * getParentControllerNode()
GUIScreenNode * getScreenNode()
int getGUIEffectOffsetY()
GUI screen node that represents a screen that can be rendered via GUI system.
GUIScreenNode_SizeConstraints sizeConstraints
void setScreenSize(int width, int height)
Set screen size.
GUINode * getNodeById(const string &nodeId)
Get GUI node by id.
void initRendering()
Init rendering.
void doneRendering()
Done rendering.
void setGUI(GUI *gui)
Set GUI.
Properties class, which helps out with storeing or loading key value pairs from/to property files.
std::exception Exception
Exception base class.