diff options
Diffstat (limited to 'src/ui/widget.c')
-rw-r--r-- | src/ui/widget.c | 93 |
1 files changed, 56 insertions, 37 deletions
diff --git a/src/ui/widget.c b/src/ui/widget.c index 2d9bf45d..5d924b57 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -50,6 +50,7 @@ iDefineObjectConstruction(Widget) | |||
50 | 50 | ||
51 | void init_Widget(iWidget *d) { | 51 | void init_Widget(iWidget *d) { |
52 | init_String(&d->id); | 52 | init_String(&d->id); |
53 | d->root = get_Root(); /* never changes after this */ | ||
53 | d->flags = 0; | 54 | d->flags = 0; |
54 | d->rect = zero_Rect(); | 55 | d->rect = zero_Rect(); |
55 | d->minSize = zero_I2(); | 56 | d->minSize = zero_I2(); |
@@ -81,7 +82,7 @@ void deinit_Widget(iWidget *d) { | |||
81 | //#endif | 82 | //#endif |
82 | deinit_String(&d->id); | 83 | deinit_String(&d->id); |
83 | if (d->flags & keepOnTop_WidgetFlag) { | 84 | if (d->flags & keepOnTop_WidgetFlag) { |
84 | removeAll_PtrArray(onTop_Root(get_Root()), d); | 85 | removeAll_PtrArray(onTop_Root(d->root), d); |
85 | } | 86 | } |
86 | if (d->flags & visualOffset_WidgetFlag) { | 87 | if (d->flags & visualOffset_WidgetFlag) { |
87 | removeTicker_App(visualOffsetAnimation_Widget_, d); | 88 | removeTicker_App(visualOffsetAnimation_Widget_, d); |
@@ -95,10 +96,10 @@ static void aboutToBeDestroyed_Widget_(iWidget *d) { | |||
95 | return; | 96 | return; |
96 | } | 97 | } |
97 | if (flags_Widget(d) & keepOnTop_WidgetFlag) { | 98 | if (flags_Widget(d) & keepOnTop_WidgetFlag) { |
98 | removeOne_PtrArray(onTop_Root(get_Root()), d); | 99 | removeOne_PtrArray(onTop_Root(d->root), d); |
99 | } | 100 | } |
100 | if (isHover_Widget(d)) { | 101 | if (isHover_Widget(d)) { |
101 | get_Root()->hover = NULL; | 102 | d->root->hover = NULL; |
102 | } | 103 | } |
103 | iForEach(ObjectList, i, d->children) { | 104 | iForEach(ObjectList, i, d->children) { |
104 | aboutToBeDestroyed_Widget_(as_Widget(i.object)); | 105 | aboutToBeDestroyed_Widget_(as_Widget(i.object)); |
@@ -111,11 +112,10 @@ void destroy_Widget(iWidget *d) { | |||
111 | postRefresh_App(); | 112 | postRefresh_App(); |
112 | } | 113 | } |
113 | aboutToBeDestroyed_Widget_(d); | 114 | aboutToBeDestroyed_Widget_(d); |
114 | iRoot *root = get_Root(); | 115 | if (!d->root->pendingDestruction) { |
115 | if (!root->pendingDestruction) { | 116 | d->root->pendingDestruction = new_PtrSet(); |
116 | root->pendingDestruction = new_PtrSet(); | ||
117 | } | 117 | } |
118 | insert_PtrSet(root->pendingDestruction, d); | 118 | insert_PtrSet(d->root->pendingDestruction, d); |
119 | } | 119 | } |
120 | } | 120 | } |
121 | 121 | ||
@@ -139,7 +139,7 @@ void setFlags_Widget(iWidget *d, int64_t flags, iBool set) { | |||
139 | } | 139 | } |
140 | iChangeFlags(d->flags, flags, set); | 140 | iChangeFlags(d->flags, flags, set); |
141 | if (flags & keepOnTop_WidgetFlag) { | 141 | if (flags & keepOnTop_WidgetFlag) { |
142 | iPtrArray *onTop = onTop_Root(get_Root()); | 142 | iPtrArray *onTop = onTop_Root(d->root); |
143 | if (set) { | 143 | if (set) { |
144 | iAssert(indexOf_PtrArray(onTop, d) == iInvalidPos); | 144 | iAssert(indexOf_PtrArray(onTop, d) == iInvalidPos); |
145 | pushBack_PtrArray(onTop, d); | 145 | pushBack_PtrArray(onTop, d); |
@@ -182,12 +182,16 @@ void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) { | |||
182 | d->padding[3] = bottom; | 182 | d->padding[3] = bottom; |
183 | } | 183 | } |
184 | 184 | ||
185 | iWidget *root_Widget(const iWidget *d) { | ||
186 | return d ? d->root->widget : NULL; | ||
187 | } | ||
188 | |||
185 | void showCollapsed_Widget(iWidget *d, iBool show) { | 189 | void showCollapsed_Widget(iWidget *d, iBool show) { |
186 | const iBool isVisible = !(d->flags & hidden_WidgetFlag); | 190 | const iBool isVisible = !(d->flags & hidden_WidgetFlag); |
187 | if ((isVisible && !show) || (!isVisible && show)) { | 191 | if ((isVisible && !show) || (!isVisible && show)) { |
188 | setFlags_Widget(d, hidden_WidgetFlag, !show); | 192 | setFlags_Widget(d, hidden_WidgetFlag, !show); |
189 | /* The entire UI may be affected, if parents are resized due to the (un)collapsing. */ | 193 | /* The entire UI may be affected, if parents are resized due to the (un)collapsing. */ |
190 | arrange_Widget(get_Window()->root.widget); | 194 | arrange_Widget(root_Widget(d)); |
191 | postRefresh_App(); | 195 | postRefresh_App(); |
192 | } | 196 | } |
193 | } | 197 | } |
@@ -352,7 +356,7 @@ static size_t numArrangedChildren_Widget_(const iWidget *d) { | |||
352 | 356 | ||
353 | static void centerHorizontal_Widget_(iWidget *d) { | 357 | static void centerHorizontal_Widget_(iWidget *d) { |
354 | d->rect.pos.x = ((d->parent ? width_Rect(innerRect_Widget_(d->parent)) | 358 | d->rect.pos.x = ((d->parent ? width_Rect(innerRect_Widget_(d->parent)) |
355 | : size_Root(get_Root()).x) - | 359 | : size_Root(d->root).x) - |
356 | width_Rect(d->rect)) / | 360 | width_Rect(d->rect)) / |
357 | 2; | 361 | 2; |
358 | TRACE(d, "center horizontally: %d", d->rect.pos.x); | 362 | TRACE(d, "center horizontally: %d", d->rect.pos.x); |
@@ -706,7 +710,7 @@ iBool containsExpanded_Widget(const iWidget *d, iInt2 coord, int expand) { | |||
706 | const iRect bounds = { | 710 | const iRect bounds = { |
707 | zero_I2(), | 711 | zero_I2(), |
708 | addY_I2(d->rect.size, | 712 | addY_I2(d->rect.size, |
709 | d->flags & drawBackgroundToBottom_WidgetFlag ? size_Root(get_Root()).y : 0) | 713 | d->flags & drawBackgroundToBottom_WidgetFlag ? size_Root(d->root).y : 0) |
710 | }; | 714 | }; |
711 | return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, | 715 | return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, |
712 | localCoord_Widget(d, coord)); | 716 | localCoord_Widget(d, coord)); |
@@ -734,24 +738,25 @@ static iBool filterEvent_Widget_(const iWidget *d, const SDL_Event *ev) { | |||
734 | } | 738 | } |
735 | 739 | ||
736 | void unhover_Widget(void) { | 740 | void unhover_Widget(void) { |
741 | /* TODO: Which root? */ | ||
737 | get_Root()->hover = NULL; | 742 | get_Root()->hover = NULL; |
738 | } | 743 | } |
739 | 744 | ||
740 | iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | 745 | iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { |
746 | iAssert(d->root == get_Root()); | ||
741 | if (!d->parent) { | 747 | if (!d->parent) { |
742 | //setCurrent_Root(d); | ||
743 | if (ev->type == SDL_MOUSEMOTION) { | 748 | if (ev->type == SDL_MOUSEMOTION) { |
744 | /* Hover widget may change. */ | 749 | /* Hover widget may change. */ |
745 | setHover_Widget(NULL); | 750 | setHover_Widget(NULL); |
746 | } | 751 | } |
747 | if (get_Root()->focus && isKeyboardEvent_(ev)) { | 752 | if (d->root->focus && isKeyboardEvent_(ev)) { |
748 | /* Root dispatches keyboard events directly to the focused widget. */ | 753 | /* Root dispatches keyboard events directly to the focused widget. */ |
749 | if (dispatchEvent_Widget(get_Root()->focus, ev)) { | 754 | if (dispatchEvent_Widget(d->root->focus, ev)) { |
750 | return iTrue; | 755 | return iTrue; |
751 | } | 756 | } |
752 | } | 757 | } |
753 | /* Root offers events first to widgets on top. */ | 758 | /* Root offers events first to widgets on top. */ |
754 | iReverseForEach(PtrArray, i, get_Root()->onTop) { | 759 | iReverseForEach(PtrArray, i, d->root->onTop) { |
755 | iWidget *widget = *i.value; | 760 | iWidget *widget = *i.value; |
756 | if (isVisible_Widget(widget) && dispatchEvent_Widget(widget, ev)) { | 761 | if (isVisible_Widget(widget) && dispatchEvent_Widget(widget, ev)) { |
757 | #if 0 | 762 | #if 0 |
@@ -775,7 +780,7 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
775 | } | 780 | } |
776 | } | 781 | } |
777 | else if (ev->type == SDL_MOUSEMOTION && | 782 | else if (ev->type == SDL_MOUSEMOTION && |
778 | (!get_Root()->hover || hasParent_Widget(d, get_Root()->hover)) && | 783 | (!d->root->hover || hasParent_Widget(d, d->root->hover)) && |
779 | flags_Widget(d) & hover_WidgetFlag && ~flags_Widget(d) & hidden_WidgetFlag && | 784 | flags_Widget(d) & hover_WidgetFlag && ~flags_Widget(d) & hidden_WidgetFlag && |
780 | ~flags_Widget(d) & disabled_WidgetFlag) { | 785 | ~flags_Widget(d) & disabled_WidgetFlag) { |
781 | if (contains_Widget(d, init_I2(ev->motion.x, ev->motion.y))) { | 786 | if (contains_Widget(d, init_I2(ev->motion.x, ev->motion.y))) { |
@@ -793,7 +798,7 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
793 | handle the events first. */ | 798 | handle the events first. */ |
794 | iReverseForEach(ObjectList, i, d->children) { | 799 | iReverseForEach(ObjectList, i, d->children) { |
795 | iWidget *child = as_Widget(i.object); | 800 | iWidget *child = as_Widget(i.object); |
796 | if (child == get_Root()->focus && isKeyboardEvent_(ev)) { | 801 | if (child == d->root->focus && isKeyboardEvent_(ev)) { |
797 | continue; /* Already dispatched. */ | 802 | continue; /* Already dispatched. */ |
798 | } | 803 | } |
799 | if (isVisible_Widget(child) && child->flags & keepOnTop_WidgetFlag) { | 804 | if (isVisible_Widget(child) && child->flags & keepOnTop_WidgetFlag) { |
@@ -845,8 +850,8 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
845 | 850 | ||
846 | static iBool scrollOverflow_Widget_(iWidget *d, int delta) { | 851 | static iBool scrollOverflow_Widget_(iWidget *d, int delta) { |
847 | iRect bounds = bounds_Widget(d); | 852 | iRect bounds = bounds_Widget(d); |
848 | const iInt2 rootSize = size_Root(get_Root()); | 853 | const iInt2 rootSize = size_Root(d->root); |
849 | const iRect winRect = safeRect_Root(get_Root()); | 854 | const iRect winRect = safeRect_Root(d->root); |
850 | const int yTop = top_Rect(winRect); | 855 | const int yTop = top_Rect(winRect); |
851 | const int yBottom = bottom_Rect(winRect); | 856 | const int yBottom = bottom_Rect(winRect); |
852 | //const int safeBottom = rootSize.y - yBottom; | 857 | //const int safeBottom = rootSize.y - yBottom; |
@@ -979,14 +984,14 @@ void drawBackground_Widget(const iWidget *d) { | |||
979 | break; | 984 | break; |
980 | } | 985 | } |
981 | fillRect_Paint(&p, | 986 | fillRect_Paint(&p, |
982 | rect_Root(get_Root()), | 987 | rect_Root(d->root), |
983 | fadeColor); | 988 | fadeColor); |
984 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 989 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); |
985 | } | 990 | } |
986 | if (d->bgColor >= 0 || d->frameColor >= 0) { | 991 | if (d->bgColor >= 0 || d->frameColor >= 0) { |
987 | iRect rect = bounds_Widget(d); | 992 | iRect rect = bounds_Widget(d); |
988 | if (d->flags & drawBackgroundToBottom_WidgetFlag) { | 993 | if (d->flags & drawBackgroundToBottom_WidgetFlag) { |
989 | rect.size.y = size_Root(get_Root()).y - top_Rect(rect); | 994 | rect.size.y = size_Root(d->root).y - top_Rect(rect); |
990 | } | 995 | } |
991 | iPaint p; | 996 | iPaint p; |
992 | init_Paint(&p); | 997 | init_Paint(&p); |
@@ -994,7 +999,7 @@ void drawBackground_Widget(const iWidget *d) { | |||
994 | #if defined (iPlatformAppleMobile) | 999 | #if defined (iPlatformAppleMobile) |
995 | if (d->flags & (drawBackgroundToHorizontalSafeArea_WidgetFlag | | 1000 | if (d->flags & (drawBackgroundToHorizontalSafeArea_WidgetFlag | |
996 | drawBackgroundToVerticalSafeArea_WidgetFlag)) { | 1001 | drawBackgroundToVerticalSafeArea_WidgetFlag)) { |
997 | const iInt2 rootSize = size_Root(get_Root()); | 1002 | const iInt2 rootSize = size_Root(d->root); |
998 | const iInt2 center = divi_I2(rootSize, 2); | 1003 | const iInt2 center = divi_I2(rootSize, 2); |
999 | int top = 0, right = 0, bottom = 0, left = 0; | 1004 | int top = 0, right = 0, bottom = 0, left = 0; |
1000 | if (d->flags & drawBackgroundToHorizontalSafeArea_WidgetFlag) { | 1005 | if (d->flags & drawBackgroundToHorizontalSafeArea_WidgetFlag) { |
@@ -1058,7 +1063,7 @@ void drawChildren_Widget(const iWidget *d) { | |||
1058 | } | 1063 | } |
1059 | /* Root draws the on-top widgets on top of everything else. */ | 1064 | /* Root draws the on-top widgets on top of everything else. */ |
1060 | if (!d->parent) { | 1065 | if (!d->parent) { |
1061 | iConstForEach(PtrArray, i, onTop_Root(get_Root())) { | 1066 | iConstForEach(PtrArray, i, onTop_Root(d->root)) { |
1062 | const iWidget *top = *i.value; | 1067 | const iWidget *top = *i.value; |
1063 | draw_Widget(top); | 1068 | draw_Widget(top); |
1064 | } | 1069 | } |
@@ -1175,7 +1180,7 @@ iAny *hitChild_Widget(const iWidget *d, iInt2 coord) { | |||
1175 | } | 1180 | } |
1176 | /* Check for on-top widgets first. */ | 1181 | /* Check for on-top widgets first. */ |
1177 | if (!d->parent) { | 1182 | if (!d->parent) { |
1178 | iReverseForEach(PtrArray, i, onTop_Root(get_Root())) { | 1183 | iReverseForEach(PtrArray, i, onTop_Root(d->root)) { |
1179 | iWidget *child = i.ptr; | 1184 | iWidget *child = i.ptr; |
1180 | // printf("ontop: %s (%s) hidden:%d hittable:%d\n", cstr_String(id_Widget(child)), | 1185 | // printf("ontop: %s (%s) hidden:%d hittable:%d\n", cstr_String(id_Widget(child)), |
1181 | // class_Widget(child)->name, | 1186 | // class_Widget(child)->name, |
@@ -1263,12 +1268,12 @@ iBool isDisabled_Widget(const iAnyObject *d) { | |||
1263 | 1268 | ||
1264 | iBool isFocused_Widget(const iAnyObject *d) { | 1269 | iBool isFocused_Widget(const iAnyObject *d) { |
1265 | iAssert(isInstance_Object(d, &Class_Widget)); | 1270 | iAssert(isInstance_Object(d, &Class_Widget)); |
1266 | return get_Root()->focus == d; | 1271 | return ((const iWidget *) d)->root->focus == d; |
1267 | } | 1272 | } |
1268 | 1273 | ||
1269 | iBool isHover_Widget(const iAnyObject *d) { | 1274 | iBool isHover_Widget(const iAnyObject *d) { |
1270 | iAssert(isInstance_Object(d, &Class_Widget)); | 1275 | iAssert(isInstance_Object(d, &Class_Widget)); |
1271 | return get_Root()->hover == d; | 1276 | return ((const iWidget *) d)->root->hover == d; |
1272 | } | 1277 | } |
1273 | 1278 | ||
1274 | iBool isSelected_Widget(const iAnyObject *d) { | 1279 | iBool isSelected_Widget(const iAnyObject *d) { |
@@ -1314,13 +1319,15 @@ iBool isAffectedByVisualOffset_Widget(const iWidget *d) { | |||
1314 | } | 1319 | } |
1315 | 1320 | ||
1316 | void setFocus_Widget(iWidget *d) { | 1321 | void setFocus_Widget(iWidget *d) { |
1317 | if (get_Root()->focus != d) { | 1322 | iRoot *root = d ? d->root : get_Root(); |
1318 | if (get_Root()->focus) { | 1323 | if (root->focus != d) { |
1319 | iAssert(!contains_PtrSet(get_Root()->pendingDestruction, get_Root()->focus)); | 1324 | if (root->focus) { |
1320 | postCommand_Widget(get_Root()->focus, "focus.lost"); | 1325 | iAssert(!contains_PtrSet(root->pendingDestruction, root->focus)); |
1326 | postCommand_Widget(root->focus, "focus.lost"); | ||
1321 | } | 1327 | } |
1322 | get_Root()->focus = d; | 1328 | root->focus = d; |
1323 | if (d) { | 1329 | if (d) { |
1330 | iAssert(root == d->root); | ||
1324 | iAssert(flags_Widget(d) & focusable_WidgetFlag); | 1331 | iAssert(flags_Widget(d) & focusable_WidgetFlag); |
1325 | postCommand_Widget(d, "focus.gained"); | 1332 | postCommand_Widget(d, "focus.gained"); |
1326 | } | 1333 | } |
@@ -1332,7 +1339,12 @@ iWidget *focus_Widget(void) { | |||
1332 | } | 1339 | } |
1333 | 1340 | ||
1334 | void setHover_Widget(iWidget *d) { | 1341 | void setHover_Widget(iWidget *d) { |
1335 | get_Root()->hover = d; | 1342 | if (d) { |
1343 | d->root->hover = d; | ||
1344 | } | ||
1345 | else { | ||
1346 | get_Root()->hover = NULL; | ||
1347 | } | ||
1336 | } | 1348 | } |
1337 | 1349 | ||
1338 | iWidget *hover_Widget(void) { | 1350 | iWidget *hover_Widget(void) { |
@@ -1380,7 +1392,7 @@ static const iWidget *findFocusRoot_Widget_(const iWidget *d) { | |||
1380 | } | 1392 | } |
1381 | 1393 | ||
1382 | iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir) { | 1394 | iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir) { |
1383 | const iWidget *root = findFocusRoot_Widget_(get_Window()->root.widget); | 1395 | const iWidget *root = findFocusRoot_Widget_(get_Root()->widget); |
1384 | iAssert(root != NULL); | 1396 | iAssert(root != NULL); |
1385 | iBool getNext = (startFrom ? iFalse : iTrue); | 1397 | iBool getNext = (startFrom ? iFalse : iTrue); |
1386 | const iWidget *found = findFocusable_Widget_(root, startFrom, &getNext, focusDir); | 1398 | const iWidget *found = findFocusable_Widget_(root, startFrom, &getNext, focusDir); |
@@ -1392,14 +1404,21 @@ iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusD | |||
1392 | } | 1404 | } |
1393 | 1405 | ||
1394 | void setMouseGrab_Widget(iWidget *d) { | 1406 | void setMouseGrab_Widget(iWidget *d) { |
1395 | if (get_Root()->mouseGrab != d) { | 1407 | iRoot *root = d ? d->root : get_Root(); |
1396 | get_Root()->mouseGrab = d; | 1408 | if (root->mouseGrab != d) { |
1409 | root->mouseGrab = d; | ||
1397 | SDL_CaptureMouse(d != NULL); | 1410 | SDL_CaptureMouse(d != NULL); |
1398 | } | 1411 | } |
1399 | } | 1412 | } |
1400 | 1413 | ||
1401 | iWidget *mouseGrab_Widget(void) { | 1414 | iWidget *mouseGrab_Widget(void) { |
1402 | return get_Root()->mouseGrab; | 1415 | iWindow *win = get_Window(); |
1416 | iForIndices(i, win->roots) { | ||
1417 | if (win->roots[i] && win->roots[i]->mouseGrab) { | ||
1418 | return win->roots[i]->mouseGrab; | ||
1419 | } | ||
1420 | } | ||
1421 | return NULL; | ||
1403 | } | 1422 | } |
1404 | 1423 | ||
1405 | void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) { | 1424 | void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) { |
@@ -1433,7 +1452,7 @@ void refresh_Widget(const iAnyObject *d) { | |||
1433 | } | 1452 | } |
1434 | 1453 | ||
1435 | void raise_Widget(iWidget *d) { | 1454 | void raise_Widget(iWidget *d) { |
1436 | iPtrArray *onTop = onTop_Root(get_Root()); | 1455 | iPtrArray *onTop = onTop_Root(d->root); |
1437 | if (d->flags & keepOnTop_WidgetFlag) { | 1456 | if (d->flags & keepOnTop_WidgetFlag) { |
1438 | iAssert(indexOf_PtrArray(onTop, d) != iInvalidPos); | 1457 | iAssert(indexOf_PtrArray(onTop, d) != iInvalidPos); |
1439 | removeOne_PtrArray(onTop, d); | 1458 | removeOne_PtrArray(onTop, d); |