Lagrange [work/v1.7]

Mobile: Many UI improvements; Upload UI

=> 1410bbde7779efe3a20f603523547c8b8f55b6a1

diff --git a/po/en.po b/po/en.po
index dd3af388..546a9489 100644
--- a/po/en.po
+++ b/po/en.po
@@ -251,6 +251,10 @@ msgstr "Show Feed Entries"
 msgid "menu.preferences"
 msgstr "Preferences…"
 
+# used for Preferences on mobile
+msgid "menu.settings"
+msgstr "Settings…"
+
 msgid "menu.help"
 msgstr "Help"
 
@@ -906,7 +910,7 @@ msgid "heading.lookup.other"
 msgstr "OTHER"
 
 msgid "menu.page.upload"
-msgstr "Upload Page with Titan"
+msgstr "Upload Page with Titan…"
 
 msgid "heading.upload"
 msgstr "UPLOAD WITH TITAN"
@@ -953,6 +957,18 @@ msgstr "Set the Titan server port to use for this URL.\nThe port is saved in the
 msgid "dlg.uploadport.set"
 msgstr "Set Port"
 
+# used on mobile
+msgid "dlg.upload.text"
+msgstr "Upload Plain Text"
+
+# used on mobile
+msgid "dlg.upload.file"
+msgstr "Upload a File"
+
+# used on mobile
+msgid "dlg.upload.pickfile"
+msgstr "Select File…"
+
 msgid "heading.translate"
 msgstr "TRANSLATE PAGE"
 
@@ -1151,6 +1167,10 @@ msgstr "SPECIAL TAGS"
 msgid "heading.prefs"
 msgstr "PREFERENCES"
 
+# used on mobile
+msgid "heading.settings"
+msgstr "SETTINGS"
+
 msgid "heading.prefs.certs"
 msgstr "CERTIFICATES"
 
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 9d87657f..ba87d002 100644
Binary files a/res/lang/de.bin and b/res/lang/de.bin differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 9fe4cfb3..a7dfb866 100644
Binary files a/res/lang/en.bin and b/res/lang/en.bin differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index 1effaf3d..7e7398d6 100644
Binary files a/res/lang/es.bin and b/res/lang/es.bin differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index fcaa8cc6..607e52fd 100644
Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index 32104f44..955695ed 100644
Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index 5b64a182..61a18efc 100644
Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index 6d568972..06ea7979 100644
Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index 69ad06f8..5fc5e24a 100644
Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 67babbcd..1718f647 100644
Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index a7f7639b..60b7b600 100644
Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ
diff --git a/res/lang/tok.bin b/res/lang/tok.bin
index b7476101..3298f0e8 100644
Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 03eb8b43..8c32a0c5 100644
Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index c0378db8..68f7d3bc 100644
Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ
diff --git a/src/app.c b/src/app.c
index a52552c0..e597edbe 100644
--- a/src/app.c
+++ b/src/app.c
@@ -2382,7 +2382,8 @@ iBool handleCommand_App(const char *cmd) {
             setUrl_UploadWidget(upload, url);
             setResponseViewer_UploadWidget(upload, document_App());
             addChild_Widget(get_Root()->widget, iClob(upload));
-            finalizeSheet_Mobile(as_Widget(upload));
+//            finalizeSheet_Mobile(as_Widget(upload));
+            setupSheetTransition_Mobile(as_Widget(upload), iTrue);
             postRefresh_App();
             return iTrue;
         }
@@ -2761,7 +2762,9 @@ iBool handleCommand_App(const char *cmd) {
         iCertImportWidget *imp = new_CertImportWidget();
         setPageContent_CertImportWidget(imp, sourceContent_DocumentWidget(document_App()));
         addChild_Widget(get_Root()->widget, iClob(imp));
-        finalizeSheet_Mobile(as_Widget(imp));
+//        finalizeSheet_Mobile(as_Widget(imp));
+        arrange_Widget(as_Widget(imp));
+        setupSheetTransition_Mobile(as_Widget(imp), iTrue);
         postRefresh_App();
         return iTrue;
     }
diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c
index 2e60c71f..65cb6654 100644
--- a/src/ui/certimportwidget.c
+++ b/src/ui/certimportwidget.c
@@ -104,61 +104,83 @@ static iBool tryImport_CertImportWidget_(iCertImportWidget *d, const iBlock *dat
 
 void init_CertImportWidget(iCertImportWidget *d) {
     iWidget *w = as_Widget(d);
+    const iMenuItem actions[] = {
+#if defined (iPlatformAppleMobile)
+        { "${dlg.certimport.pickfile}", 0, 0, "certimport.pickfile" },
+        { "---" },
+#endif
+        { "${cancel}" },
+        { uiTextAction_ColorEscape "${dlg.certimport.import}",
+          SDLK_RETURN, KMOD_PRIMARY,
+          "certimport.accept" }
+    };
     init_Widget(w);
     setId_Widget(w, "certimport");
     d->cert = NULL;
-    /* This should behave similar to sheets. */ 
-    useSheetStyle_Widget(w);
-    addChildFlags_Widget(
-        w,
-        iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.certimport}", NULL)),
-        frameless_WidgetFlag);
-    d->info = addChildFlags_Widget(w, iClob(new_LabelWidget(infoText_, NULL)), frameless_WidgetFlag);
-    addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
-    d->crtLabel = new_LabelWidget("", NULL); {
-        setFont_LabelWidget(d->crtLabel, uiContent_FontId);
-        addChildFlags_Widget(w, iClob(d->crtLabel), 0);
-        setFrameColor_Widget(as_Widget(d->crtLabel), uiTextCaution_ColorId);
+    if (isUsingPanelLayout_Mobile()) {
+        initPanels_Mobile(w, NULL, (iMenuItem[]){
+            { "title id:heading.certimport" },
+            { format_CStr("label id:certimport.info text:%s", infoText_) },
+            //{ "padding" },
+            { "label id:certimport.crt nowrap:1 frame:1" },
+            { "padding arg:0.25" },
+            { "label id:certimport.key nowrap:1 frame:1" },
+            { "heading text:${dlg.certimport.notes}" },
+            { "input id:certimport.notes hint:hint.certimport.description noheading:1" },
+            { NULL }
+        }, actions, iElemCount(actions));
+        d->info     = findChild_Widget(w, "certimport.info");
+        d->crtLabel = findChild_Widget(w, "certimport.crt");
+        d->keyLabel = findChild_Widget(w, "certimport.key");
+        d->notes    = findChild_Widget(w, "certimport.notes");
+        setFixedSize_Widget(as_Widget(d->crtLabel), init_I2(-1, gap_UI * 12));
+        setFixedSize_Widget(as_Widget(d->keyLabel), init_I2(-1, gap_UI * 12));
     }
-    d->keyLabel = new_LabelWidget("", NULL); {
-        setFont_LabelWidget(d->keyLabel, uiContent_FontId);
+    else {
+        /* This should behave similar to sheets. */ 
+        useSheetStyle_Widget(w);
+        addChildFlags_Widget(
+            w,
+            iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.certimport}", NULL)),
+            frameless_WidgetFlag);
+        d->info = addChildFlags_Widget(w, iClob(new_LabelWidget(infoText_, NULL)), frameless_WidgetFlag);
         addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
-        addChildFlags_Widget(w, iClob(d->keyLabel), 0);
-        setFrameColor_Widget(as_Widget(d->keyLabel), uiTextCaution_ColorId);
-    }
-    addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
-    /* TODO: Use makeTwoColumnWidget_() */
-    iWidget *page = new_Widget(); {
-        setFlags_Widget(page, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
-        iWidget *headings = addChildFlags_Widget(
-            page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
-        iWidget *values = addChildFlags_Widget(
-            page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
-//        addChild_Widget(headings, iClob(makeHeading_Widget("${dlg.certimport.notes}")));
-//        addChild_Widget(values, iClob(d->notes = new_InputWidget(0)));
-//        setHint_InputWidget(d->notes, "${hint.certimport.description}");
-        addTwoColumnDialogInputField_Widget(
-            headings,
-            values,
-            "${dlg.certimport.notes}",
-            "",
-            iClob(d->notes = newHint_InputWidget(0, "${hint.certimport.description}")));
-        as_Widget(d->notes)->rect.size.x = gap_UI * 70;
+        d->crtLabel = new_LabelWidget("", NULL); {
+            setFont_LabelWidget(d->crtLabel, uiContent_FontId);
+            addChildFlags_Widget(w, iClob(d->crtLabel), 0);
+        }
+        d->keyLabel = new_LabelWidget("", NULL); {
+            setFont_LabelWidget(d->keyLabel, uiContent_FontId);
+            addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
+            addChildFlags_Widget(w, iClob(d->keyLabel), 0);
+        }
+        addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
+        /* TODO: Use makeTwoColumnWidget_() */
+        iWidget *page = new_Widget(); {
+            setFlags_Widget(page, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
+            iWidget *headings = addChildFlags_Widget(
+                page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
+            iWidget *values = addChildFlags_Widget(
+                page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
+            addTwoColumnDialogInputField_Widget(
+                headings,
+                values,
+                "${dlg.certimport.notes}",
+                "",
+                iClob(d->notes = newHint_InputWidget(0, "${hint.certimport.description}")));
+            as_Widget(d->notes)->rect.size.x = gap_UI * 70;
+        }
+        addChild_Widget(w, iClob(page));
+        arrange_Widget(w);
+        setFixedSize_Widget(as_Widget(d->crtLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
+        setFixedSize_Widget(as_Widget(d->keyLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
+        /* Buttons. */
+        addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
+        iWidget *buttons = makeDialogButtons_Widget(actions, iElemCount(actions));
+        addChild_Widget(w, iClob(buttons));
     }
-    addChild_Widget(w, iClob(page));
-    arrange_Widget(w);
-    setFixedSize_Widget(as_Widget(d->crtLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
-    setFixedSize_Widget(as_Widget(d->keyLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
-    /* Buttons. */
-    addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
-    iWidget *buttons = makeDialogButtons_Widget(
-        (iMenuItem[]){ { "${cancel}" },
-                       { uiTextAction_ColorEscape "${dlg.certimport.import}",
-                         SDLK_RETURN,
-                         KMOD_PRIMARY,
-                         "certimport.accept" } },
-        2);
-    addChild_Widget(w, iClob(buttons));
+    setFrameColor_Widget(as_Widget(d->crtLabel), uiTextCaution_ColorId);
+    setFrameColor_Widget(as_Widget(d->keyLabel), uiTextCaution_ColorId);
     if (deviceType_App() != desktop_AppDeviceType) {
         /* Try auto-pasting. */
         postCommand_App("certimport.paste");
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 83f2ea6a..4b3c2db0 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -2852,7 +2852,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
             setUrl_UploadWidget(upload, d->mod.url);
             setResponseViewer_UploadWidget(upload, d);
             addChild_Widget(get_Root()->widget, iClob(upload));
-            finalizeSheet_Mobile(as_Widget(upload));
+//            finalizeSheet_Mobile(as_Widget(upload));
+            setupSheetTransition_Mobile(as_Widget(upload), iTrue);
             postRefresh_App();
         }
         return iTrue;
@@ -3679,16 +3680,10 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                             { "---" },
                             { book_Icon " ${menu.page.import}", 0, 0, "bookmark.links confirm:1" },
                             { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" },
-#if defined (iPlatformMobile)
-                            { "---" },
-                            { "${menu.page.copyurl}", 0, 0, "document.copylink" } },
-                        14);
-#else
                             { upload_Icon " ${menu.page.upload}", 0, 0, "document.upload" },
                             { "---" },
                             { "${menu.page.copyurl}", 0, 0, "document.copylink" } },
                         15);
-#endif
                     if (isEmpty_Range(&d->selectMark)) {
                         pushBackN_Array(
                             &items,
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index 03595d1a..ec324d02 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -244,6 +244,9 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int
             }
         }
     }
+    if (d->forceFg >= 0) {
+        *fg = d->forceFg;
+    }
     if (isPress) {
         *bg = uiBackgroundPressed_ColorId | permanent_ColorId;
         if (isButton) {
@@ -257,9 +260,6 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int
             *fg = isDark_ColorTheme(colorTheme_App()) ? white_ColorId : black_ColorId;
         }
     }
-    if (d->forceFg >= 0) {
-        *fg = d->forceFg;
-    }
 }
 
 iLocalDef int iconPadding_LabelWidget_(const iLabelWidget *d) {
@@ -318,6 +318,10 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
     }
     setClip_Paint(&p, rect);
     const int iconPad = iconPadding_LabelWidget_(d);
+    const int iconColor = isCaution ? uiTextCaution_ColorId
+                          : flags & (disabled_WidgetFlag | pressed_WidgetFlag) ? fg
+                          : isHover                                            ? uiIconHover_ColorId
+                                                                               : uiIcon_ColorId;
     if (d->icon && d->icon != 0x20) { /* no need to draw an empty icon */
         iString str;
         initUnicodeN_String(&str, &d->icon, 1);
@@ -331,10 +335,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
                                -gap_UI / 8)),
                 init_I2(iconPad, lineHeight_Text(d->font)) },
             iTrue,
-            isCaution                                            ? uiTextCaution_ColorId
-            : flags & (disabled_WidgetFlag | pressed_WidgetFlag) ? fg
-            : isHover                                            ? uiIconHover_ColorId
-                                                                 : uiIcon_ColorId,
+            iconColor,
             "%s",
             cstr_String(&str));
         deinit_String(&str);
@@ -387,7 +388,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
         drawCentered_Text(d->font,
                           (iRect){ addX_I2(topRight_Rect(chRect), -iconPad),
                                    init_I2(chSize, height_Rect(chRect)) },
-                          iTrue, uiSeparator_ColorId, rightAngle_Icon);
+                          iTrue, iconColor /*uiSeparator_ColorId*/, rightAngle_Icon);
     }
     unsetClip_Paint(&p);
 }
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index daa1fa1a..6ea672e6 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -90,15 +90,15 @@ static void unselectAllPanelButtons_(iWidget *topPanel) {
 
 static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd) {
     if (equal_Command(cmd, "window.resized")) {
-        const iBool isPortrait = (deviceType_App() == phone_AppDeviceType && isPortrait_App());
-        const iRect safeRoot   = safeRect_Root(mainDetailSplit->root);
-        setPos_Widget(mainDetailSplit, topLeft_Rect(safeRoot));
-        setFixedSize_Widget(mainDetailSplit, safeRoot.size);
+        const iBool  isPortrait   = (deviceType_App() == phone_AppDeviceType && isPortrait_App());
+        const iRect  safeRoot     = safeRect_Root(mainDetailSplit->root);
         iWidget *    sheet        = parent_Widget(mainDetailSplit);
         iWidget *    navi         = findChild_Widget(sheet, "panel.navi");
         iWidget *    detailStack  = findChild_Widget(mainDetailSplit, "detailstack");
         const size_t numPanels    = childCount_Widget(detailStack);
         const iBool  isSideBySide = isSideBySideLayout_() && numPanels > 0;
+        setPos_Widget(mainDetailSplit, topLeft_Rect(safeRoot));
+        setFixedSize_Widget(mainDetailSplit, safeRoot.size);
         setFlags_Widget(mainDetailSplit, arrangeHorizontal_WidgetFlag, isSideBySide);
         setFlags_Widget(detailStack, expand_WidgetFlag, isSideBySide);
         setFlags_Widget(detailStack, hidden_WidgetFlag, numPanels == 0);
@@ -172,7 +172,16 @@ static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) {
         }
         unselectAllPanelButtons_(topPanel);
         if (!wasClosed) {
-            postCommand_App("prefs.dismiss");
+            /* TODO: Should come up with a more general-purpose approach here. */
+            if (findWidget_App("prefs")) {
+                postCommand_App("prefs.dismiss");
+            }
+            else if (findWidget_App("upload")) {
+                postCommand_App("upload.cancel");
+            }
+            else {
+                postCommand_App("cancel");
+            }
         }
         return iTrue;
     }
@@ -503,11 +512,19 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
     else if (equal_Command(spec, "label")) {
         iLabelWidget *lab = new_LabelWidget(label, NULL);
         widget = as_Widget(lab);
-        setWrap_LabelWidget(lab, iTrue);
-        setFlags_Widget(widget, fixedHeight_WidgetFlag | frameless_WidgetFlag, iTrue);
+        setId_Widget(widget, id);
+        setWrap_LabelWidget(lab, !argLabel_Command(spec, "nowrap"));
+        setFlags_Widget(widget,
+                        fixedHeight_WidgetFlag |
+                            (!argLabel_Command(spec, "frame") ? frameless_WidgetFlag : 0),
+                        iTrue);
     }
     else if (equal_Command(spec, "padding")) {
-        widget = makePadding_Widget(lineHeight_Text(labelFont_()) * 1.5f);
+        float height = 1.5f;
+        if (hasLabel_Command(spec, "arg")) {
+            height *= argfLabel_Command(spec, "arg");
+        }
+        widget = makePadding_Widget(lineHeight_Text(labelFont_()) * height);
     }
     /* Apply common styling to the heading. */
     if (heading) {
@@ -539,7 +556,7 @@ static const iMenuItem *findDialogCancelAction_(const iMenuItem *items, size_t n
         return NULL;
     }
     for (size_t i = 0; i < n; i++) {
-        if (!iCmpStr(items[i].label, "${cancel}")) {
+        if (!iCmpStr(items[i].label, "${cancel}") || !iCmpStr(items[i].label, "${close}")) {
             return &items[i];
         }
     }
@@ -556,13 +573,20 @@ iWidget *makePanelsParent_Mobile(iWidget *parentWidget,
                                  const char *id,
                                  const iMenuItem *itemsNullTerminated,
                                  const iMenuItem *actions, size_t numActions) {
+    iWidget *panels = new_Widget();
+    setId_Widget(panels, id);
+    initPanels_Mobile(panels, parentWidget, itemsNullTerminated, actions, numActions);
+    return panels;
+}
+
+void initPanels_Mobile(iWidget *panels, iWidget *parentWidget, 
+                       const iMenuItem *itemsNullTerminated,
+                       const iMenuItem *actions, size_t numActions) {
     /* A multipanel widget has a top panel and one or more detail panels. In a horizontal layout,
        the detail panels slide in from the right and cover the top panel. In a landscape layout,
        the detail panels are always visible on the side. */
-    iWidget *sheet = new_Widget();
-    setId_Widget(sheet, id);
-    setBackgroundColor_Widget(sheet, uiBackground_ColorId);
-    setFlags_Widget(sheet,
+    setBackgroundColor_Widget(panels, uiBackground_ColorId);
+    setFlags_Widget(panels,
                     resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag |
                         frameless_WidgetFlag | focusRoot_WidgetFlag | commandOnClick_WidgetFlag |
                         overflowScrollable_WidgetFlag | leftEdgeDraggable_WidgetFlag,
@@ -572,7 +596,7 @@ iWidget *makePanelsParent_Mobile(iWidget *parentWidget,
         setCommandHandler_Widget(mainDetailSplit, mainDetailSplitHandler_);
         setFlags_Widget(mainDetailSplit, resizeHeightOfChildren_WidgetFlag, iFalse);
         setId_Widget(mainDetailSplit, "mdsplit");
-        addChild_Widget(sheet, iClob(mainDetailSplit));
+        addChild_Widget(panels, iClob(mainDetailSplit));
     }
     /* The panel roots. */
     iWidget *topPanel = new_Widget(); {
@@ -591,7 +615,6 @@ iWidget *makePanelsParent_Mobile(iWidget *parentWidget,
         setFlags_Widget(detailStack, collapse_WidgetFlag | resizeWidthOfChildren_WidgetFlag, iTrue);
         addChild_Widget(mainDetailSplit, iClob(detailStack));
     }
-    addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_()))));
     /* Slide top panel with detail panels. */ {
         setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue);
         topPanel->offsetRef = detailStack;
@@ -612,15 +635,17 @@ iWidget *makePanelsParent_Mobile(iWidget *parentWidget,
         checkIcon_LabelWidget(naviBack);
         setId_Widget(as_Widget(naviBack), "panel.back");
         setFont_LabelWidget(naviBack, labelFont_());
-        addChildFlags_Widget(sheet, iClob(navi),
+        addChildFlags_Widget(panels, iClob(navi),
                              drawBackgroundToVerticalSafeArea_WidgetFlag |
                                  arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
                                  resizeToParentWidth_WidgetFlag | arrangeVertical_WidgetFlag);        
     }
+    iBool haveDetailPanels = iFalse;
     /* Create panel contents based on provided items. */
     for (size_t i = 0; itemsNullTerminated[i].label; i++) {
         const iMenuItem *item = &itemsNullTerminated[i];
         if (equal_Command(item->label, "panel")) {
+            haveDetailPanels = iTrue;
             const char *id = cstr_Rangecc(range_Command(item->label, "id"));
             const iString *label = hasLabel_Command(item->label, "text")
                                        ? collect_String(suffix_Command(item->label, "text"))
@@ -655,10 +680,12 @@ iWidget *makePanelsParent_Mobile(iWidget *parentWidget,
             setFont_LabelWidget(naviBack, labelBoldFont_());            
         }
         else if (defaultItem && defaultItem != cancelItem) {
-            updateTextCStr_LabelWidget(naviBack, cancelItem->label);
-            setCommand_LabelWidget(naviBack, collectNewCStr_String(cancelItem->command
-                                                                    ? cancelItem->command
-                                                                    : "cancel"));
+            if (!haveDetailPanels) {
+                updateTextCStr_LabelWidget(naviBack, cancelItem->label);
+                setCommand_LabelWidget(naviBack, collectNewCStr_String(cancelItem->command
+                                                                       ? cancelItem->command
+                                                                       : "cancel"));
+            }
             iLabelWidget *defaultButton = new_LabelWidget(defaultItem->label, defaultItem->command);
             setFont_LabelWidget(defaultButton, labelBoldFont_());
             setFlags_Widget(as_Widget(defaultButton),
@@ -689,16 +716,21 @@ iWidget *makePanelsParent_Mobile(iWidget *parentWidget,
             }
             makePanelItem_Mobile(
                 topPanel,
-                &(iMenuItem){ format_CStr("button text:%s", act->label), 0, 0, act->command });
+                &(iMenuItem){ format_CStr("button text:" uiTextAction_ColorEscape "%s", act->label),
+                              0,
+                              0,
+                              act->command });
         }
     }
     /* Finalize the layout. */
-    addChild_Widget(parentWidget, iClob(sheet));
+    if (parentWidget) {
+        addChild_Widget(parentWidget, iClob(panels));
+    }
     mainDetailSplitHandler_(mainDetailSplit, "window.resized"); /* make it resize the split */
-    updatePanelSheetMetrics_(sheet);
-    arrange_Widget(sheet);
+    updatePanelSheetMetrics_(panels);
+    arrange_Widget(panels);
     postCommand_App("widget.overflow"); /* with the correct dimensions */    
-    return sheet;
+    printTree_Widget(panels);
 }
 
 #if 0
@@ -1130,7 +1162,9 @@ void setupMenuTransition_Mobile(iWidget *sheet, iBool isIncoming) {
     }
 }
 
-void setupSheetTransition_Mobile(iWidget *sheet, iBool isIncoming) {
+void setupSheetTransition_Mobile(iWidget *sheet, int flags) {
+    const iBool isIncoming = (flags & incoming_TransitionFlag) != 0;
+    const int   dir        = flags & dirMask_TransitionFlag;
     if (!isUsingPanelLayout_Mobile()) {
         if (prefs_App()->uiAnimations) {
             setFlags_Widget(sheet, horizontalOffset_WidgetFlag, iFalse);
@@ -1144,17 +1178,51 @@ void setupSheetTransition_Mobile(iWidget *sheet, iBool isIncoming) {
         }
         return;
     }
-    if(isSideBySideLayout_()) {
+    if (isSideBySideLayout_()) {
+        /* TODO: Landscape transitions? */
         return;
     }
-    setFlags_Widget(sheet, horizontalOffset_WidgetFlag, iTrue);
+    setFlags_Widget(sheet,
+                    horizontalOffset_WidgetFlag,
+                    dir == right_TransitionDir || dir == left_TransitionDir);
     if (isIncoming) {
-        setVisualOffset_Widget(sheet, size_Root(sheet->root).x, 0, 0);
+        switch (dir) {
+            case right_TransitionDir:
+                setVisualOffset_Widget(sheet, size_Root(sheet->root).x, 0, 0);
+                break;
+            case left_TransitionDir:
+                setVisualOffset_Widget(sheet, -size_Root(sheet->root).x, 0, 0);
+                break;
+            case top_TransitionDir:
+                setVisualOffset_Widget(
+                    sheet, -bottom_Rect(boundsWithoutVisualOffset_Widget(sheet)), 0, 0);
+                break;
+            case bottom_TransitionDir:
+                setVisualOffset_Widget(sheet, height_Widget(sheet), 0, 0);
+                break;
+        }
         setVisualOffset_Widget(sheet, 0, 200, easeOut_AnimFlag);
-    }
+    }        
     else {
-        const iBool wasDragged = iAbs(value_Anim(&sheet->visualOffset)) > 0;
-        setVisualOffset_Widget(sheet, size_Root(sheet->root).x, wasDragged ? 100 : 200,
-                               wasDragged ? 0 : easeIn_AnimFlag);
+        switch (dir) {
+            case right_TransitionDir: {
+                const iBool wasDragged = iAbs(value_Anim(&sheet->visualOffset)) > 0;
+                setVisualOffset_Widget(sheet, size_Root(sheet->root).x, wasDragged ? 100 : 200,
+                                       wasDragged ? 0 : easeIn_AnimFlag);
+                break;
+            }
+            case left_TransitionDir:
+                setVisualOffset_Widget(sheet, -size_Root(sheet->root).x, 200, easeIn_AnimFlag);
+                break;
+            case top_TransitionDir:
+                setVisualOffset_Widget(sheet,
+                                       -bottom_Rect(boundsWithoutVisualOffset_Widget(sheet)),
+                                       200,
+                                       easeIn_AnimFlag);
+                break;
+            case bottom_TransitionDir:
+                setVisualOffset_Widget(sheet, height_Widget(sheet), 200, easeIn_AnimFlag);
+                break;
+        }
     }
 }
diff --git a/src/ui/mobile.h b/src/ui/mobile.h
index 30679c7c..e1131953 100644
--- a/src/ui/mobile.h
+++ b/src/ui/mobile.h
@@ -35,8 +35,23 @@ iWidget *   makePanelsParent_Mobile     (iWidget *parent,
                                          const char *id,                                         
                                          const iMenuItem *itemsNullTerminated,
                                          const iMenuItem *actions, size_t numActions);
+void        initPanels_Mobile           (iWidget *panels, iWidget *parentWidget,
+                                         const iMenuItem *itemsNullTerminated,
+                                         const iMenuItem *actions, size_t numActions);
+
+enum iTransitionFlags {
+    incoming_TransitionFlag = iBit(1),
+    dirMask_TransitionFlag  = iBit(2) | iBit(3),
+};
+
+enum iTransitionDir {
+    right_TransitionDir  = 0,
+    bottom_TransitionDir = 2,
+    left_TransitionDir   = 4,
+    top_TransitionDir    = 6,
+};
 
-void        setupMenuTransition_Mobile  (iWidget *menu, iBool isIncoming);
-void        setupSheetTransition_Mobile (iWidget *sheet, iBool isIncoming);
+void        setupMenuTransition_Mobile  (iWidget *menu,  iBool isIncoming);
+void        setupSheetTransition_Mobile (iWidget *sheet, int flags);
 
 void        finalizeSheet_Mobile        (iWidget *sheet);
diff --git a/src/ui/root.c b/src/ui/root.c
index eae8e4bb..a792e93d 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -118,7 +118,7 @@ static const iMenuItem phoneNavMenuItems_[] = {
     { "${menu.downloads}", 0, 0, "downloads.open" },
     { "${menu.feeds.entrylist}", 0, 0, "!open url:about:feeds" },
     { "---" },
-    { gear_Icon " Settings...", SDLK_COMMA, KMOD_PRIMARY, "preferences" },
+    { gear_Icon " ${menu.settings}", SDLK_COMMA, KMOD_PRIMARY, "preferences" },
 };
 #endif /* Mobile */
 
@@ -1163,20 +1163,12 @@ void createUserInterface_Root(iRoot *d) {
                         { star_Icon " ${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" },
                         { book_Icon " ${menu.page.import}", 0, 0, "bookmark.links confirm:1" },
                         { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" },
-#if defined (iPlatformMobile)
-                        { "---" },
-                        { "${menu.page.copyurl}", 0, 0, "document.copylink" },
-                        { "${menu.page.copysource}", 'c', KMOD_PRIMARY, "copy" },
-                        { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" } },
-                    11);
-#else
                         { upload_Icon " ${menu.page.upload}", 0, 0, "document.upload" },
                         { "---" },
                         { "${menu.page.copyurl}", 0, 0, "document.copylink" },
                         { "${menu.page.copysource}", 'c', KMOD_PRIMARY, "copy" },
                         { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" } },
                     12);
-#endif
                 setId_Widget(as_Widget(pageMenuButton), "pagemenubutton");
                 setFont_LabelWidget(pageMenuButton, uiContentBold_FontId);
                 setAlignVisually_LabelWidget(pageMenuButton, iTrue);
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index 4c72c60a..fb8aaf0a 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -81,91 +81,122 @@ void init_UploadWidget(iUploadWidget *d) {
     iWidget *w = as_Widget(d);
     init_Widget(w);
     setId_Widget(w, "upload");
-    useSheetStyle_Widget(w);
     init_String(&d->originalUrl);
     init_String(&d->url);
     d->viewer = NULL;
     d->request = NULL;
     init_String(&d->filePath);
     d->fileSize = 0;
-    addChildFlags_Widget(w,
-                         iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.upload}", NULL)),
-                         frameless_WidgetFlag);
-    d->info = addChildFlags_Widget(w, iClob(new_LabelWidget("", NULL)),
-                                   frameless_WidgetFlag | resizeToParentWidth_WidgetFlag |
-                                   fixedHeight_WidgetFlag);
-    setWrap_LabelWidget(d->info, iTrue);
-    /* Tabs for input data. */
-    iWidget *tabs = makeTabs_Widget(w);
-    /* Make the tabs support vertical expansion based on content. */ {
-        setFlags_Widget(tabs, resizeHeightOfChildren_WidgetFlag, iFalse);
-        setFlags_Widget(tabs, arrangeHeight_WidgetFlag, iTrue);
-        iWidget *tabPages = findChild_Widget(tabs, "tabs.pages");
-        setFlags_Widget(tabPages, resizeHeightOfChildren_WidgetFlag, iFalse);
-        setFlags_Widget(tabPages, arrangeHeight_WidgetFlag, iTrue);
+    const iMenuItem actions[] = {
+        { "${upload.port}", 0, 0, "upload.setport" },
+        { "---" },
+        { "${close}", SDLK_ESCAPE, 0, "upload.cancel" },
+        { uiTextAction_ColorEscape "${dlg.upload.send}", SDLK_RETURN, KMOD_PRIMARY, "upload.accept" }
+    };
+    if (isUsingPanelLayout_Mobile()) {
+        const iMenuItem textItems[] = {
+            { "title id:heading.upload.text" },
+            { "input id:upload.text noheading:1" },
+            { NULL }        
+        };
+        const iMenuItem fileItems[] = {
+            { "title id:heading.upload.file" },
+            { "button text:" uiTextAction_ColorEscape "${dlg.upload.pickfile}", 0, 0, "upload.pickfile" },            
+            { "heading id:upload.file.name" },
+            { "label id:upload.filepathlabel" },
+            { "heading id:upload.file.size" },
+            { "label id:upload.filesizelabel" },
+            { "padding" },
+            { "input id:upload.mime" },
+            { "label id:upload.counter text:" },
+            { NULL }        
+        };
+        initPanels_Mobile(w, NULL, (iMenuItem[]){
+            { "title id:heading.upload" },
+            { "label id:upload.info" },
+//            { "padding" },
+            { "panel id:dlg.upload.text icon:0x1f5b9", 0, 0, (const void *) textItems },
+            { "panel id:dlg.upload.file icon:0x1f4c1", 0, 0, (const void *) fileItems },
+            { "padding" },
+            { "input id:upload.token hint:hint.upload.token" },
+            { NULL }
+        }, actions, iElemCount(actions));
+        d->info          = findChild_Widget(w, "upload.info");
+        d->input         = findChild_Widget(w, "upload.text");
+        d->filePathLabel = findChild_Widget(w, "upload.file.name");
+        d->fileSizeLabel = findChild_Widget(w, "upload.file.size");
+        d->mime          = findChild_Widget(w, "upload.mime");
+        d->token         = findChild_Widget(w, "upload.token");
+        d->counter       = findChild_Widget(w, "upload.counter");
     }
-    iWidget *headings, *values;
-    setBackgroundColor_Widget(findChild_Widget(tabs, "tabs.buttons"), uiBackgroundSidebar_ColorId);
-    setId_Widget(tabs, "upload.tabs");
-//    const int bigGap = lineHeight_Text(uiLabel_FontId) * 3 / 4;
-    /* Text input. */ {
-        //appendTwoColumnTabPage_Widget(tabs, "${heading.upload.text}", '1', &headings, &values);
-        iWidget *page = new_Widget();
-        setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue);
-        d->input = new_InputWidget(0);
-        setId_Widget(as_Widget(d->input), "upload.text");
-        setFont_InputWidget(d->input, monospace_FontId);
-        setLineLimits_InputWidget(d->input, 7, 20);
-        setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */        
-        setHint_InputWidget(d->input, "${hint.upload.text}");
-        setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1));
-        addChild_Widget(page, iClob(d->input));
-        appendFramelessTabPage_Widget(tabs, iClob(page), "${heading.upload.text}", '1', 0);
-    }
-    /* File content. */ {
-        appendTwoColumnTabPage_Widget(tabs, "${heading.upload.file}", '2', &headings, &values);        
-//        iWidget *pad = addChild_Widget(headings, iClob(makePadding_Widget(0)));
-//        iWidget *hint = addChild_Widget(values, iClob(new_LabelWidget("${upload.file.drophint}", NULL)));
-//        pad->sizeRef = hint;
-        addChildFlags_Widget(headings, iClob(new_LabelWidget("${upload.file.name}", NULL)), frameless_WidgetFlag);
-        d->filePathLabel = addChildFlags_Widget(values, iClob(new_LabelWidget(uiTextAction_ColorEscape "${upload.file.drophere}", NULL)), frameless_WidgetFlag);
-        addChildFlags_Widget(headings, iClob(new_LabelWidget("${upload.file.size}", NULL)), frameless_WidgetFlag);
-        d->fileSizeLabel = addChildFlags_Widget(values, iClob(new_LabelWidget("\u2014", NULL)), frameless_WidgetFlag);
-        d->mime = new_InputWidget(0);
-        setFixedSize_Widget(as_Widget(d->mime), init_I2(70 * gap_UI, -1));
-        addTwoColumnDialogInputField_Widget(headings, values, "${upload.mime}", "upload.mime", iClob(d->mime));
-    }
-    /* Token. */ {
-        addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
-        iWidget *page = makeTwoColumns_Widget(&headings, &values);
-        d->token = addTwoColumnDialogInputField_Widget(
-            headings, values, "${upload.token}", "upload.token", iClob(new_InputWidget(0)));
-        setHint_InputWidget(d->token, "${hint.upload.token}");
-        setFixedSize_Widget(as_Widget(d->token), init_I2(50 * gap_UI, -1));
-        addChild_Widget(w, iClob(page));
-    }
-    /* Buttons. */ {
-        addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
-        iWidget *buttons =
-            makeDialogButtons_Widget((iMenuItem[]){ { "${upload.port}", 0, 0, "upload.setport" },
-                                                    { "---" },
-                                                    { "${close}", SDLK_ESCAPE, 0, "upload.cancel" },
-                                                    { uiTextAction_ColorEscape "${dlg.upload.send}",
-                                                      SDLK_RETURN,
-                                                      KMOD_PRIMARY,
-                                                      "upload.accept" } },
-                                     4);
-        setId_Widget(insertChildAfterFlags_Widget(buttons,
-                                             iClob(d->counter = new_LabelWidget("", NULL)),
-                                             0, frameless_WidgetFlag),
-                     "upload.counter");
-        addChild_Widget(w, iClob(buttons));
+    else {
+        useSheetStyle_Widget(w);
+        addChildFlags_Widget(w,
+                             iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.upload}", NULL)),
+                             frameless_WidgetFlag);
+        d->info = addChildFlags_Widget(w, iClob(new_LabelWidget("", NULL)),
+                                       frameless_WidgetFlag | resizeToParentWidth_WidgetFlag |
+                                       fixedHeight_WidgetFlag);
+        setWrap_LabelWidget(d->info, iTrue);
+        /* Tabs for input data. */
+        iWidget *tabs = makeTabs_Widget(w);
+        /* Make the tabs support vertical expansion based on content. */ {
+            setFlags_Widget(tabs, resizeHeightOfChildren_WidgetFlag, iFalse);
+            setFlags_Widget(tabs, arrangeHeight_WidgetFlag, iTrue);
+            iWidget *tabPages = findChild_Widget(tabs, "tabs.pages");
+            setFlags_Widget(tabPages, resizeHeightOfChildren_WidgetFlag, iFalse);
+            setFlags_Widget(tabPages, arrangeHeight_WidgetFlag, iTrue);
+        }
+        iWidget *headings, *values;
+        setBackgroundColor_Widget(findChild_Widget(tabs, "tabs.buttons"), uiBackgroundSidebar_ColorId);
+        setId_Widget(tabs, "upload.tabs");
+        /* Text input. */ {
+            iWidget *page = new_Widget();
+            setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue);
+            d->input = new_InputWidget(0);
+            setId_Widget(as_Widget(d->input), "upload.text");
+            setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1));
+            addChild_Widget(page, iClob(d->input));
+            appendFramelessTabPage_Widget(tabs, iClob(page), "${heading.upload.text}", '1', 0);
+        }
+        /* File content. */ {
+            appendTwoColumnTabPage_Widget(tabs, "${heading.upload.file}", '2', &headings, &values);        
+            addChildFlags_Widget(headings, iClob(new_LabelWidget("${upload.file.name}", NULL)), frameless_WidgetFlag);
+            d->filePathLabel = addChildFlags_Widget(values, iClob(new_LabelWidget(uiTextAction_ColorEscape "${upload.file.drophere}", NULL)), frameless_WidgetFlag);
+            addChildFlags_Widget(headings, iClob(new_LabelWidget("${upload.file.size}", NULL)), frameless_WidgetFlag);
+            d->fileSizeLabel = addChildFlags_Widget(values, iClob(new_LabelWidget("\u2014", NULL)), frameless_WidgetFlag);
+            d->mime = new_InputWidget(0);
+            setFixedSize_Widget(as_Widget(d->mime), init_I2(70 * gap_UI, -1));
+            addTwoColumnDialogInputField_Widget(headings, values, "${upload.mime}", "upload.mime", iClob(d->mime));
+        }
+        /* Token. */ {
+            addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
+            iWidget *page = makeTwoColumns_Widget(&headings, &values);
+            d->token = addTwoColumnDialogInputField_Widget(
+                headings, values, "${upload.token}", "upload.token", iClob(new_InputWidget(0)));
+            setHint_InputWidget(d->token, "${hint.upload.token}");
+            setFixedSize_Widget(as_Widget(d->token), init_I2(50 * gap_UI, -1));
+            addChild_Widget(w, iClob(page));
+        }
+        /* Buttons. */ {
+            addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
+            iWidget *buttons = makeDialogButtons_Widget(actions, iElemCount(actions));
+            setId_Widget(insertChildAfterFlags_Widget(buttons,
+                                                 iClob(d->counter = new_LabelWidget("", NULL)),
+                                                 0, frameless_WidgetFlag),
+                         "upload.counter");
+            addChild_Widget(w, iClob(buttons));
+        }
+        resizeToLargestPage_Widget(tabs);
+        arrange_Widget(w);
+        setFixedSize_Widget(as_Widget(d->token), init_I2(width_Widget(tabs) - left_Rect(parent_Widget(d->token)->rect), -1));
+        setFlags_Widget(as_Widget(d->token), expand_WidgetFlag, iTrue);
+        setFocus_Widget(as_Widget(d->input));
     }
-    resizeToLargestPage_Widget(tabs);
-    arrange_Widget(w);
-    setFixedSize_Widget(as_Widget(d->token), init_I2(width_Widget(tabs) - left_Rect(parent_Widget(d->token)->rect), -1));
-    setFlags_Widget(as_Widget(d->token), expand_WidgetFlag, iTrue);
-    setFocus_Widget(as_Widget(d->input));
+    setFont_InputWidget(d->input, monospace_FontId);
+    setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */
+    setLineLimits_InputWidget(d->input, 7, 20);
+    setHint_InputWidget(d->input, "${hint.upload.text}");
     setBackupFileName_InputWidget(d->input, "uploadbackup.txt");
     updateInputMaxHeight_UploadWidget_(d);
 }
@@ -201,6 +232,7 @@ static void setUrlPort_UploadWidget_(iUploadWidget *d, const iString *url, uint1
     appendFormat_String(&d->url, ":%u", overridePort ? overridePort : titanPortForUrl_(url));
     appendRange_String(&d->url, (iRangecc){ parts.path.start, constEnd_String(url) });
     setText_LabelWidget(d->info, &d->url);
+    arrange_Widget(as_Widget(d));
 }
 
 void setUrl_UploadWidget(iUploadWidget *d, const iString *url) {
@@ -233,7 +265,7 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
     if (isResize_UserEvent(ev)) {
         updateInputMaxHeight_UploadWidget_(d);
     }
-    if (isCommand_Widget(w, ev, "upload.cancel")) {
+    if (equal_Command(cmd, "upload.cancel")) {
         setupSheetTransition_Mobile(w, iFalse);
         destroy_Widget(w);
         return iTrue;
diff --git a/src/ui/util.c b/src/ui/util.c
index b875e260..6069e800 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1061,6 +1061,7 @@ iWidget *removeTabPage_Widget(iWidget *tabs, size_t index) {
 }
 
 void resizeToLargestPage_Widget(iWidget *tabs) {
+    if (!tabs) return;
 //    puts("RESIZE TO LARGEST PAGE ...");
     iWidget *pages = findChild_Widget(tabs, "tabs.pages");
     iForEach(ObjectList, i, children_Widget(pages)) {
@@ -1216,7 +1217,7 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) {
                 postCommandf_App("valueinput.cancelled id:%s", cstr_String(id_Widget(dlg)));
                 setId_Widget(dlg, ""); /* no further commands to emit */
             }
-            setupSheetTransition_Mobile(dlg, iFalse);
+            setupSheetTransition_Mobile(dlg, top_TransitionDir);
             destroy_Widget(dlg);
             return iTrue;
         }
@@ -1225,13 +1226,13 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) {
     else if (equal_Command(cmd, "valueinput.cancel")) {
         postCommandf_App("valueinput.cancelled id:%s", cstr_String(id_Widget(dlg)));
         setId_Widget(dlg, ""); /* no further commands to emit */
-        setupSheetTransition_Mobile(dlg, iFalse);
+        setupSheetTransition_Mobile(dlg, top_TransitionDir);
         destroy_Widget(dlg);
         return iTrue;
     }
     else if (equal_Command(cmd, "valueinput.accept")) {
         acceptValueInput_(dlg);
-        setupSheetTransition_Mobile(dlg, iFalse);        
+        setupSheetTransition_Mobile(dlg, top_TransitionDir);        
         destroy_Widget(dlg);
         return iTrue;
     }
@@ -1345,7 +1346,9 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con
                                          acceptKeyMod_ReturnKeyBehavior(prefs_App()->returnKey),
                                          "valueinput.accept" } },
                         2)));
-    finalizeSheet_Mobile(dlg);
+//    finalizeSheet_Mobile(dlg);
+    arrange_Widget(dlg);
+    setupSheetTransition_Mobile(dlg, incoming_TransitionFlag | top_TransitionDir);
     if (parent) {
         setFocus_Widget(as_Widget(input));
     }
@@ -1915,6 +1918,7 @@ iWidget *makePreferences_Widget(void) {
             { NULL }
         };
         iWidget *dlg = makePanels_Mobile("prefs", (iMenuItem[]){
+            { "title id:heading.settings" },
             { "panel text:" gear_Icon " ${heading.prefs.general}", 0, 0, (const void *) generalPanelItems },
             { "panel icon:0x1f5a7 id:heading.prefs.network", 0, 0, (const void *) networkPanelItems },
             { "panel text:" person_Icon " ${sidebar.identities}", 0, 0, (const void *) identityPanelItems },
@@ -2405,7 +2409,7 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) {
         arrange_Widget(dlg);
         as_Widget(input)->rect.size.x = 100 * gap_UI - headings->rect.size.x;
         addChild_Widget(get_Root()->widget, iClob(dlg));
-        finalizeSheet_Mobile(dlg);
+//        finalizeSheet_Mobile(dlg);
     }
     /* Initialize. */ {
         const iBookmark *bm  = bookmarkId ? get_Bookmarks(bookmarks_App(), bookmarkId) : NULL;
@@ -2419,6 +2423,7 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) {
                         iTrue);
         setCommandHandler_Widget(dlg, handleFeedSettingCommands_);
     }
+    setupSheetTransition_Mobile(dlg, incoming_TransitionFlag);
     return dlg;
 }
 
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 4fd8f066..659a00cc 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -452,7 +452,7 @@ static void arrange_Widget_(iWidget *d) {
     else if (d->flags & centerHorizontal_WidgetFlag) {
         centerHorizontal_Widget_(d);
     }
-    if (d->flags & resizeToParentWidth_WidgetFlag) {
+    if (d->flags & resizeToParentWidth_WidgetFlag && d->parent) {
         iRect childBounds = zero_Rect();
         if (flags_Widget(d->parent) & arrangeWidth_WidgetFlag) {
             /* Can't go narrower than what the children require, though. */
@@ -462,7 +462,7 @@ static void arrange_Widget_(iWidget *d) {
         setWidth_Widget_(d, iMaxi(width_Rect(innerRect_Widget_(d->parent)),
                                   width_Rect(childBounds)));
     }
-    if (d->flags & resizeToParentHeight_WidgetFlag) {
+    if (d->flags & resizeToParentHeight_WidgetFlag && d->parent) {
         TRACE(d, "resize to parent height");
         setHeight_Widget_(d, height_Rect(innerRect_Widget_(d->parent)));
     }
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.7/cdiff/1410bbde7779efe3a20f603523547c8b8f55b6a1
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
130.646655 milliseconds
Gemini-to-HTML Time
2.1347 milliseconds

This content has been proxied by September (ba2dc).