Lagrange [work/v1.7]

Mobile: Better Settings tap targets; fixed safe area drawing

=> 0e7060d5306fef1f585982cc78223250d3ee8551

diff --git a/src/app.c b/src/app.c
index e597edbe..fa601ac3 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1273,6 +1273,7 @@ void processEvents_App(enum iAppEventMode eventMode) {
                     }
                 }
 #endif
+                const iWidget *oldHover = d->window->hover;
                 iBool wasUsed = processEvent_Window(d->window, &ev);
                 if (!wasUsed) {
                     /* There may be a key bindings for this. */
@@ -1307,6 +1308,11 @@ void processEvents_App(enum iAppEventMode eventMode) {
                     /* Allocated by postCommand_Apps(). */
                     free(ev.user.data1);
                 }
+                /* Update when hover has changed. */
+                if (oldHover != d->window->hover) {
+                    refresh_Widget(oldHover);
+                    refresh_Widget(d->window->hover);
+                }
                 break;
             }
         }
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 802a2d6c..59d62544 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #include "keys.h"
 #include "prefs.h"
 #include "lang.h"
+#include "touch.h"
 #include "app.h"
 
 #include 
@@ -1565,11 +1566,11 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
     }
     switch (processEvent_Click(&d->click, ev)) {
         case none_ClickResult:
-            if (ev->type == SDL_MOUSEBUTTONUP &&
-                deviceType_App() != desktop_AppDeviceType && isFocused_Widget(d)) {
-                setFocus_Widget(NULL);
-                return iTrue;
-            }
+//            if (ev->type == SDL_MOUSEBUTTONUP &&
+//                deviceType_App() != desktop_AppDeviceType && isFocused_Widget(d)) {
+//                setFocus_Widget(NULL);
+//                return iTrue;
+//            }
             break;
         case started_ClickResult: {
             setFocus_Widget(w);
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c
index 46d32e9a..6fa23986 100644
--- a/src/ui/listwidget.c
+++ b/src/ui/listwidget.c
@@ -391,6 +391,7 @@ static void draw_ListWidget_(const iListWidget *d) {
     const int scrollY = pos_SmoothScroll(&d->scrollY);
     iPaint p;
     init_Paint(&p);
+    drawLayerEffects_Widget(w);
     drawBackground_Widget(w);
     alloc_VisBuf(d->visBuf, bounds.size, d->itemHeight);
     /* Update invalid regions/items. */ {
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index 9e2dc4f7..4c183ffa 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -129,6 +129,12 @@ static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd)
         }
         arrange_Widget(mainDetailSplit);
     }
+    else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) {
+        if (focus_Widget() && class_Widget(focus_Widget()) == &Class_InputWidget) {
+            setFocus_Widget(NULL);
+            return iTrue;
+        }
+    }
     return iFalse;
 }
 
@@ -331,7 +337,7 @@ static iWidget *makeValuePaddingWithHeading_(iLabelWidget *heading, iWidget *val
     //setFixedSize_Widget(as_Widget(heading), init_I2(-1, height_Widget(value)));
     setFont_LabelWidget(heading, labelFont_());
     setTextColor_LabelWidget(heading, uiTextStrong_ColorId);
-    if (isInput) {
+    if (isInput && ~value->flags & fixedWidth_WidgetFlag) {
         addChildFlags_Widget(div, iClob(value), expand_WidgetFlag);
     }
     else if (isInstance_Object(value, &Class_LabelWidget) &&
@@ -388,6 +394,27 @@ static size_t countItems_(const iMenuItem *itemsNullTerminated) {
     return num;
 }
 
+static iBool dropdownHeadingHandler_(iWidget *d, const char *cmd) {
+    if (isVisible_Widget(d) &&
+        equal_Command(cmd, "mouse.clicked") && contains_Widget(d, coord_Command(cmd)) &&
+        arg_Command(cmd)) {
+        postCommand_Widget(userData_Object(d),
+                           cstr_String(command_LabelWidget(userData_Object(d))));
+        return iTrue;
+    }
+    return iFalse;
+}
+
+static iBool inputHeadingHandler_(iWidget *d, const char *cmd) {
+    if (isVisible_Widget(d) &&
+        equal_Command(cmd, "mouse.clicked") && contains_Widget(d, coord_Command(cmd)) &&
+        arg_Command(cmd)) {
+        setFocus_Widget(userData_Object(d));
+        return iTrue;
+    }
+    return iFalse;
+}
+
 void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
     iWidget *     widget  = NULL;
     iLabelWidget *heading = NULL;
@@ -434,6 +461,8 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
                             frameless_WidgetFlag, iTrue);
         setId_Widget(as_Widget(drop), id);
         widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label), as_Widget(drop));
+        setCommandHandler_Widget(widget, dropdownHeadingHandler_);
+        setUserData_Object(widget, drop);
     }
     else if (equal_Command(spec, "radio") || equal_Command(spec, "buttons")) {
         const iBool isRadio = equal_Command(spec, "radio");
@@ -504,6 +533,8 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
             }
             widget = makeValuePaddingWithHeading_(heading = makeHeading_Widget(label),
                                                   as_Widget(input));
+            setCommandHandler_Widget(widget, inputHeadingHandler_);
+            setUserData_Object(widget, input);
         }
     }
     else if (equal_Command(spec, "button")) {
diff --git a/src/ui/paint.c b/src/ui/paint.c
index af62f908..71ebb81d 100644
--- a/src/ui/paint.c
+++ b/src/ui/paint.c
@@ -65,10 +65,33 @@ void endTarget_Paint(iPaint *d) {
 
 void setClip_Paint(iPaint *d, iRect rect) {
     //rect = intersect_Rect(rect, rect_Root(get_Root()));
+    addv_I2(&rect.pos, origin_Paint);
     if (isEmpty_Rect(rect)) {
         rect = init_Rect(0, 0, 1, 1);
     }
-    addv_I2(&rect.pos, origin_Paint);
+//    iRect root = rect_Root(get_Root());
+    iRect targetRect = zero_Rect();
+    SDL_Texture *target = SDL_GetRenderTarget(renderer_Paint_(d));
+    if (target) {
+        SDL_QueryTexture(target, NULL, NULL, &targetRect.size.x, &targetRect.size.y);
+        rect = intersect_Rect(rect, targetRect);
+    }
+    else {
+        rect = intersect_Rect(rect, rect_Root(get_Root()));
+    }
+    
+    /*if (rect.pos.x < 0) {
+        adjustEdges_Rect(&rect, 0, 0, 0, -rect.pos.x);
+    }
+    if (rect.pos.y < 0) {
+        adjustEdges_Rect(&rect, -rect.pos.y, 0, 0, 0);
+    }
+    if (right_Rect(rect) > right_Rect(root)) {
+        adjustEdges_Rect(&rect, 0, right_Rect(root) - right_Rect(rect), 0, 0);
+    }
+    if (bottom_Rect(rect) > bottom_Rect(root)) {
+        adjustEdges_Rect(&rect, 0, bottom_Rect(root) - bottom_Rect(rect), 0, 0);
+    }*/
     SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect);
 }
 
diff --git a/src/ui/root.c b/src/ui/root.c
index 7b2b5b15..59f98aa4 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -525,7 +525,9 @@ void updateToolbarColors_Root(iRoot *d) {
 #if defined (iPlatformMobile)
     iWidget *toolBar = findChild_Widget(d->widget, "toolbar");
     if (toolBar) {
-        const iBool isSidebarVisible = isVisible_Widget(findChild_Widget(d->widget, "sidebar"));
+        const iBool isSidebarVisible =
+            isVisible_Widget(findChild_Widget(d->widget, "sidebar")) ||
+            isVisible_Widget(findChild_Widget(d->widget, "sidebar2"));
         const int bg = isSidebarVisible ? uiBackgroundSidebar_ColorId :
                                           tmBannerBackground_ColorId;
         setBackgroundColor_Widget(toolBar, bg);
diff --git a/src/ui/touch.c b/src/ui/touch.c
index f0456acb..5fc8f245 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -254,6 +254,8 @@ static iFloat3 gestureVector_Touch_(const iTouch *d) {
 }
 
 static void update_TouchState_(void *ptr) {
+    iWindow *win = get_Window();
+    const iWidget *oldHover = win->hover;
     iTouchState *d = ptr;
     /* Check for long presses to simulate right clicks. */
     const uint32_t nowTime = SDL_GetTicks();
@@ -293,6 +295,7 @@ static void update_TouchState_(void *ptr) {
                 /* Looks like a possible tap. */
                 dispatchNotification_Touch_(touch, widgetTapBegins_UserEventCode);
                 dispatchMotion_Touch_(touch->pos[0], 0);
+                refresh_Widget(touch->affinity);
                 touch->isTapBegun = iTrue;
             }
             if (!touch->isTapAndHold && nowTime - touch->startTime >= longPressSpanMs_ &&
@@ -363,6 +366,10 @@ static void update_TouchState_(void *ptr) {
     if (!isEmpty_Array(d->touches) || !isEmpty_Array(d->moms)) {
         addTickerRoot_App(update_TouchState_, NULL, ptr);
     }
+    if (oldHover != win->hover) {
+        refresh_Widget(oldHover);
+        refresh_Widget(win->hover);
+    }
 }
 
 #if 0
diff --git a/src/ui/util.c b/src/ui/util.c
index 05d39c01..48ed41a6 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1349,10 +1349,17 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con
                         2)));
 //    finalizeSheet_Mobile(dlg);
     arrange_Widget(dlg);
-    setupSheetTransition_Mobile(dlg, incoming_TransitionFlag | top_TransitionDir);
     if (parent) {
         setFocus_Widget(as_Widget(input));
     }
+    /* Check that the top is in the safe area. */ {
+        int top = top_Rect(bounds_Widget(dlg));
+        int delta = top - top_Rect(safeRect_Root(dlg->root));
+        if (delta < 0) {
+            dlg->rect.pos.y -= delta;
+        }
+    }
+    setupSheetTransition_Mobile(dlg, incoming_TransitionFlag | top_TransitionDir);
     return dlg;
 }
 
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 7665c5bc..0765bf9f 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -89,7 +89,7 @@ static void release_WidgetDrawBuffer(iWidgetDrawBuffer *d) {
 static iRect boundsForDraw_Widget_(const iWidget *d) {
     iRect bounds = bounds_Widget(d);
     if (d->flags & drawBackgroundToBottom_WidgetFlag) {
-        bounds.size.y = iMaxi(bounds.size.y, size_Root(d->root).y - top_Rect(bounds));
+        bounds.size.y = iMax(bounds.size.y, size_Root(d->root).y);
     }
     return bounds;
 }
@@ -1218,7 +1218,7 @@ iLocalDef iBool isDrawn_Widget_(const iWidget *d) {
     return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag;
 }
 
-static void drawLayerEffects_Widget_(const iWidget *d) {
+void drawLayerEffects_Widget(const iWidget *d) {
     /* Layered effects are not buffered, so they are drawn here separately. */
     iAssert(isDrawn_Widget_(d));
     iBool shadowBorder   = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0;
@@ -1248,6 +1248,48 @@ static void drawLayerEffects_Widget_(const iWidget *d) {
         fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget());
         SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
     }
+#if defined (iPlatformAppleMobile)
+    if (d->bgColor >= 0 && d->flags & (drawBackgroundToHorizontalSafeArea_WidgetFlag |
+                                       drawBackgroundToVerticalSafeArea_WidgetFlag)) {
+        iPaint p;
+        init_Paint(&p);
+        const iRect rect     = bounds_Widget(d);
+        const iInt2 rootSize = size_Root(d->root);
+        const iInt2 center   = divi_I2(rootSize, 2);
+        int top = 0, right = 0, bottom = 0, left = 0;
+        if (d->flags & drawBackgroundToHorizontalSafeArea_WidgetFlag) {
+            const iBool isWide = width_Rect(rect) > rootSize.x * 9 / 10;
+            if (isWide || mid_Rect(rect).x < center.x) {
+                left = -left_Rect(rect);
+            }
+            if (isWide || mid_Rect(rect).x > center.x) {
+                right = rootSize.x - right_Rect(rect);
+            }
+        }
+        if (d->flags & drawBackgroundToVerticalSafeArea_WidgetFlag) {
+            if (top_Rect(rect) > center.y) {
+                bottom = rootSize.y - bottom_Rect(rect);
+            }
+            if (bottom_Rect(rect) < center.y) {
+                top = -top_Rect(rect);
+            }
+        }
+        if (top < 0) {
+            fillRect_Paint(&p, (iRect){ init_I2(left_Rect(rect), 0),
+                                        init_I2(width_Rect(rect), top_Rect(rect)) },
+                           d->bgColor);
+        }
+        if (left < 0) {
+            fillRect_Paint(&p, (iRect){ init_I2(0, top_Rect(rect)),
+                init_I2(left_Rect(rect), height_Rect(rect)) }, d->bgColor);
+        }
+        if (right > 0) {
+            fillRect_Paint(&p, (iRect){ init_I2(right_Rect(rect), top_Rect(rect)),
+                init_I2(right, height_Rect(rect)) }, d->bgColor);
+        }
+//        adjustEdges_Rect(&rect, iMin(0, top), iMax(0, right), iMax(0, bottom), iMin(0, left));
+    }
+#endif
 }
 
 void drawBackground_Widget(const iWidget *d) {
@@ -1261,12 +1303,13 @@ void drawBackground_Widget(const iWidget *d) {
     if (d->bgColor >= 0 || d->frameColor >= 0) {
         iRect rect = bounds_Widget(d);
         if (d->flags & drawBackgroundToBottom_WidgetFlag) {
-            rect.size.y = iMax(rect.size.y, size_Root(d->root).y - top_Rect(rect));
+            rect.size.y += size_Root(d->root).y; // = iMax(rect.size.y, size_Root(d->root).y - top_Rect(rect));
         }
         iPaint p;
         init_Paint(&p);
         if (d->bgColor >= 0) {
-#if defined (iPlatformAppleMobile)
+#if 0 && defined (iPlatformAppleMobile)
+            /* TODO: This is part of the unbuffered draw (layer effects). */
             if (d->flags & (drawBackgroundToHorizontalSafeArea_WidgetFlag |
                             drawBackgroundToVerticalSafeArea_WidgetFlag)) {
                 const iInt2 rootSize = size_Root(d->root);
@@ -1289,7 +1332,7 @@ void drawBackground_Widget(const iWidget *d) {
                         top = -top_Rect(rect);
                     }
                 }
-                adjustEdges_Rect(&rect, top, right, bottom, left);
+                adjustEdges_Rect(&rect, iMin(0, top), iMax(0, right), iMax(0, bottom), iMin(0, left));
             }
 #endif
             fillRect_Paint(&p, rect, d->bgColor);
@@ -1339,7 +1382,7 @@ static void addToPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs, iR
     if (isDrawn_Widget_(d)) {
         iRect bounds = bounds_Widget(d);
         if (d->flags & drawBackgroundToBottom_WidgetFlag) {
-            bounds.size.y = size_Root(d->root).y - top_Rect(bounds);
+            bounds.size.y += size_Root(d->root).y; // iMax(bounds.size.y, size_Root(d->root).y - top_Rect(bounds));
         }
         if (isFullyContainedByOther_Rect(bounds, *fullyMasked)) {
             return; /* can't be seen */
@@ -1411,14 +1454,21 @@ void setDrawBufferEnabled_Widget(iWidget *d, iBool enable) {
 
 static void beginBufferDraw_Widget_(const iWidget *d) {
     if (d->drawBuf) {
-//        printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid);
+        printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid);
+        if (d->drawBuf->isValid) {
+            iAssert(!isEqual_I2(d->drawBuf->size, boundsForDraw_Widget_(d).size));
+//            printf("  drawBuf:%dx%d boundsForDraw:%dx%d\n",
+//                   d->drawBuf->size.x, d->drawBuf->size.y,
+//                   boundsForDraw_Widget_(d).size.x,
+//                   boundsForDraw_Widget_(d).size.y);
+        }
         const iRect bounds = bounds_Widget(d);
         SDL_Renderer *render = renderer_Window(get_Window());
         d->drawBuf->oldTarget = SDL_GetRenderTarget(render);
         d->drawBuf->oldOrigin = origin_Paint;
         realloc_WidgetDrawBuffer(d->drawBuf, render, boundsForDraw_Widget_(d).size);
         SDL_SetRenderTarget(render, d->drawBuf->texture);
-        //SDL_SetRenderDrawColor(render, 255, 0, 0, 128);
+//        SDL_SetRenderDrawColor(render, 255, 0, 0, 128);
         SDL_SetRenderDrawColor(render, 0, 0, 0, 0);
         SDL_RenderClear(render);
         origin_Paint = neg_I2(bounds.pos); /* with current visual offset */
@@ -1445,7 +1495,7 @@ void draw_Widget(const iWidget *d) {
         }
         return;
     }
-    drawLayerEffects_Widget_(d);
+    drawLayerEffects_Widget(d);
     if (!d->drawBuf || !checkDrawBuffer_Widget_(d)) {
         beginBufferDraw_Widget_(d);
         drawBackground_Widget(d);
@@ -1755,7 +1805,9 @@ iWidget *focus_Widget(void) {
 }
 
 void setHover_Widget(iWidget *d) {
-    get_Window()->hover = d;
+    iWindow *win = get_Window();
+    iAssert(win);
+    win->hover = d;
 }
 
 iWidget *hover_Widget(void) {
@@ -1850,7 +1902,8 @@ void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) {
     deinit_String(&str);
 }
 
-void refresh_Widget(const iAnyObject *d) {    
+void refresh_Widget(const iAnyObject *d) {
+    if (!d) return;
     /* TODO: Could be widget specific, if parts of the tree are cached. */
     /* TODO: The visbuffer in DocumentWidget and ListWidget could be moved to be a general
        purpose feature of Widget. */
diff --git a/src/ui/widget.h b/src/ui/widget.h
index fd4d8898..b46e5177 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -204,6 +204,7 @@ iAny *  findFocusable_Widget            (const iWidget *startFrom, enum iWidgetF
 iAny *  findOverflowScrollable_Widget   (iWidget *);
 size_t  childCount_Widget               (const iWidget *);
 void    draw_Widget                     (const iWidget *);
+void    drawLayerEffects_Widget         (const iWidget *);
 void    drawBackground_Widget           (const iWidget *);
 void    drawChildren_Widget             (const iWidget *);
 void    drawRoot_Widget                 (const iWidget *); /* root only */
diff --git a/src/ui/window.c b/src/ui/window.c
index 3385f436..8034d858 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -875,7 +875,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
                     }
                 }
             }
-            const iWidget *oldHover = d->hover;
+//            const iWidget *oldHover = d->hover;
             iBool wasUsed = iFalse;
             /* Dispatch first to the mouse-grabbed widget. */
 //            iWidget *widget = d->root.widget;
@@ -929,9 +929,10 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
                     }
                 }
             }
-            if (oldHover != d->hover) {
-                postRefresh_App();
-            }
+//            if (oldHover != d->hover) {
+//                refresh_Widget(oldHover);
+//                refresh_Widget(d->hover);
+//            }
             if (event.type == SDL_MOUSEMOTION) {
                 applyCursor_Window_(d);
             }
@@ -1107,7 +1108,7 @@ void draw_Window(iWindow *d) {
         }
         setCurrent_Root(NULL);
 #if !defined (NDEBUG)
-        draw_Text(defaultBold_FontId, zero_I2(), red_ColorId, "%d", drawCount_);
+        draw_Text(defaultBold_FontId, safeRect_Root(d->roots[0]).pos, red_ColorId, "%d", drawCount_);
         drawCount_ = 0;
 #endif
     }
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.7/cdiff/0e7060d5306fef1f585982cc78223250d3ee8551
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
88.872851 milliseconds
Gemini-to-HTML Time
1.084473 milliseconds

This content has been proxied by September (3851b).