summaryrefslogtreecommitdiff
path: root/src/ui/widget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/widget.c')
-rw-r--r--src/ui/widget.c93
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
51void init_Widget(iWidget *d) { 51void 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
185iWidget *root_Widget(const iWidget *d) {
186 return d ? d->root->widget : NULL;
187}
188
185void showCollapsed_Widget(iWidget *d, iBool show) { 189void 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
353static void centerHorizontal_Widget_(iWidget *d) { 357static 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
736void unhover_Widget(void) { 740void unhover_Widget(void) {
741 /* TODO: Which root? */
737 get_Root()->hover = NULL; 742 get_Root()->hover = NULL;
738} 743}
739 744
740iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { 745iBool 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
846static iBool scrollOverflow_Widget_(iWidget *d, int delta) { 851static 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
1264iBool isFocused_Widget(const iAnyObject *d) { 1269iBool 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
1269iBool isHover_Widget(const iAnyObject *d) { 1274iBool 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
1274iBool isSelected_Widget(const iAnyObject *d) { 1279iBool isSelected_Widget(const iAnyObject *d) {
@@ -1314,13 +1319,15 @@ iBool isAffectedByVisualOffset_Widget(const iWidget *d) {
1314} 1319}
1315 1320
1316void setFocus_Widget(iWidget *d) { 1321void 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
1334void setHover_Widget(iWidget *d) { 1341void 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
1338iWidget *hover_Widget(void) { 1350iWidget *hover_Widget(void) {
@@ -1380,7 +1392,7 @@ static const iWidget *findFocusRoot_Widget_(const iWidget *d) {
1380} 1392}
1381 1393
1382iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir) { 1394iAny *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
1394void setMouseGrab_Widget(iWidget *d) { 1406void 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
1401iWidget *mouseGrab_Widget(void) { 1414iWidget *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
1405void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) { 1424void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) {
@@ -1433,7 +1452,7 @@ void refresh_Widget(const iAnyObject *d) {
1433} 1452}
1434 1453
1435void raise_Widget(iWidget *d) { 1454void 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);