From 2f9e203058df442921fc0151ddc5fe9b68b87935 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 21 Mar 2021 14:29:51 +0200 Subject: iOS: Save to Files; hide toolbar on scroll There is no downloads directory on mobile. Instead, the downloaded file is temporarily cached and given to the iOS document picker to export. Added an option to hide the bottom toolbar while scrolling down. --- src/app.c | 23 ++++++++++++++++++ src/defs.h | 8 +++++++ src/ios.h | 6 +++-- src/ios.m | 41 +++++++++++++++++++++++++++++++- src/prefs.c | 1 + src/prefs.h | 1 + src/ui/documentwidget.c | 63 +++++++++++++++++++++++++++---------------------- src/ui/util.c | 5 +++- src/ui/widget.c | 33 ++++++++++++++++++++++---- src/ui/widget.h | 3 +++ src/ui/window.c | 33 ++++++++++++++++++++------ src/ui/window.h | 1 + 12 files changed, 175 insertions(+), 43 deletions(-) diff --git a/src/app.c b/src/app.c index 0611b6dd..2bf2ad4a 100644 --- a/src/app.c +++ b/src/app.c @@ -275,6 +275,14 @@ static const char *downloadDir_App_(void) { return cstr_String(path); } } +#endif +#if defined (iPlatformAppleMobile) + /* Save to a local cache directory from where the user can export to the cloud. */ + const iString *dlDir = cleanedCStr_Path("~/Library/Caches/Downloads"); + if (!fileExists_FileInfo(dlDir)) { + makeDirs_Path(dlDir); + } + return cstr_String(dlDir); #endif return defaultDownloadDir_App_; } @@ -313,6 +321,11 @@ static void loadPrefs_App_(iApp *d) { d->initialWindowRect = init_Rect( pos.x, pos.y, argLabel_Command(cmd, "width"), argLabel_Command(cmd, "height")); } +#if !defined (LAGRANGE_DOWNLOAD_EDIT) + else if (equal_Command(cmd, "downloads")) { + continue; /* can't change downloads directory */ + } +#endif else { postCommandString_App(&cmdStr); } @@ -1311,6 +1324,8 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { isSelected_Widget(findChild_Widget(d, "prefs.smoothscroll"))); postCommandf_App("imageloadscroll arg:%d", isSelected_Widget(findChild_Widget(d, "prefs.imageloadscroll"))); + postCommandf_App("hidetoolbarscroll arg:%d", + isSelected_Widget(findChild_Widget(d, "prefs.hidetoolbarscroll"))); postCommandf_App("ostheme arg:%d", isSelected_Widget(findChild_Widget(d, "prefs.ostheme"))); postCommandf_App("decodeurls arg:%d", @@ -1592,6 +1607,13 @@ iBool handleCommand_App(const char *cmd) { d->prefs.loadImageInsteadOfScrolling = arg_Command(cmd); return iTrue; } + else if (equal_Command(cmd, "hidetoolbarscroll")) { + d->prefs.hideToolbarOnScroll = arg_Command(cmd); + if (!d->prefs.hideToolbarOnScroll) { + showToolbars_Window(d->window, iTrue); + } + return iTrue; + } else if (equal_Command(cmd, "theme.set")) { const int isAuto = argLabel_Command(cmd, "auto"); d->prefs.theme = arg_Command(cmd); @@ -1864,6 +1886,7 @@ iBool handleCommand_App(const char *cmd) { setToggle_Widget(findChild_Widget(dlg, "prefs.hoverlink"), d->prefs.hoverLink); setToggle_Widget(findChild_Widget(dlg, "prefs.smoothscroll"), d->prefs.smoothScrolling); setToggle_Widget(findChild_Widget(dlg, "prefs.imageloadscroll"), d->prefs.loadImageInsteadOfScrolling); + setToggle_Widget(findChild_Widget(dlg, "prefs.hidetoolbarscroll"), d->prefs.hideToolbarOnScroll); setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); diff --git a/src/defs.h b/src/defs.h index eb8affec..27eacd3b 100644 --- a/src/defs.h +++ b/src/defs.h @@ -82,3 +82,11 @@ enum iFileVersion { #define unhappy_Icon "\U0001f641" #define globe_Icon "\U0001f310" #define magnifyingGlass_Icon "\U0001f50d" + +/* UI labels that depend on the platform */ + +#if defined (iPlatformMobile) +# define saveToDownloads_Label "Save to Files" +#else +# define saveToDownloads_Label "Save to Downloads" +#endif diff --git a/src/ios.h b/src/ios.h index 9490491f..29669a26 100644 --- a/src/ios.h +++ b/src/ios.h @@ -32,7 +32,9 @@ enum iHapticEffect { void setupApplication_iOS (void); void setupWindow_iOS (iWindow *window); -iBool isPhone_iOS (void); -void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); iBool processEvent_iOS (const SDL_Event *); void playHapticEffect_iOS (enum iHapticEffect effect); +void exportDownloadedFile_iOS(const iString *path); + +iBool isPhone_iOS (void); +void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); diff --git a/src/ios.m b/src/ios.m index dbe78fd6..9aca5a19 100644 --- a/src/ios.m +++ b/src/ios.m @@ -120,7 +120,9 @@ API_AVAILABLE(ios(13.0)) /*----------------------------------------------------------------------------------------------*/ -@interface AppState : NSObject +@interface AppState : NSObject { + iString *fileBeingSaved; +} @property (nonatomic, assign) BOOL isHapticsAvailable; @property (nonatomic, strong) NSObject *haptic; @end @@ -129,6 +131,23 @@ static AppState *appState_; @implementation AppState +-(instancetype)init { + self = [super init]; + fileBeingSaved = NULL; + return self; +} + +-(void)setFileBeingSaved:(const iString *)path { + fileBeingSaved = copy_String(path); +} + +-(void)removeSavedFile { + /* The file was copied to an external location, so the cached copy is not needed. */ + remove(cstr_String(fileBeingSaved)); + delete_String(fileBeingSaved); + fileBeingSaved = NULL; +} + -(void)setupHaptics { if (@available(iOS 13.0, *)) { self.isHapticsAvailable = CHHapticEngine.capabilitiesForHardware.supportsHaptics; @@ -145,6 +164,15 @@ static AppState *appState_; } } +- (void)documentPicker:(UIDocumentPickerViewController *)controller +didPickDocumentsAtURLs:(NSArray *)urls { + [self removeSavedFile]; +} + +- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { + [self removeSavedFile]; +} + -(void)keyboardOnScreen:(NSNotification *)notification { NSDictionary *info = notification.userInfo; NSValue *value = info[UIKeyboardFrameEndUserInfoKey]; @@ -257,3 +285,14 @@ iBool processEvent_iOS(const SDL_Event *ev) { } return iFalse; /* allow normal processing */ } + +void exportDownloadedFile_iOS(const iString *path) { + NSURL *url = [NSURL fileURLWithPath:[[NSString alloc] initWithCString:cstr_String(path) + encoding:NSUTF8StringEncoding]]; + UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] + initWithURL:url + inMode:UIDocumentPickerModeExportToService]; + picker.delegate = appState_; + [appState_ setFileBeingSaved:path]; + [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; +} diff --git a/src/prefs.c b/src/prefs.c index b9b59836..a4f12e20 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -36,6 +36,7 @@ void init_Prefs(iPrefs *d) { d->uiScale = 1.0f; /* default set elsewhere */ d->zoomPercent = 100; d->sideIcon = iTrue; + d->hideToolbarOnScroll = iTrue; d->hoverLink = iTrue; d->smoothScrolling = iTrue; d->loadImageInsteadOfScrolling = iFalse; diff --git a/src/prefs.h b/src/prefs.h index 13b91568..130f11e2 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -46,6 +46,7 @@ struct Impl_Prefs { float uiScale; int zoomPercent; iBool sideIcon; + iBool hideToolbarOnScroll; /* Behavior */ iString downloadDir; iBool hoverLink; diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index d9b78a4d..0b9757d4 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -48,6 +48,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "visbuf.h" #include "visited.h" +#if defined (iPlatformAppleMobile) +# include "ios.h" +#endif + #include #include #include @@ -220,6 +224,7 @@ struct Impl_DocumentWidget { SDL_Texture * sideIconBuf; iTextBuf * timestampBuf; iTranslation * translation; + iWidget * phoneToolbar; }; iDefineObjectConstruction(DocumentWidget) @@ -231,6 +236,7 @@ void init_DocumentWidget(iDocumentWidget *d) { setFlags_Widget(w, hover_WidgetFlag, iTrue); init_PersistentDocumentState(&d->mod); d->flags = 0; + d->phoneToolbar = NULL; iZap(d->certExpiry); d->certFingerprint = new_Block(0); d->certFlags = 0; @@ -440,8 +446,13 @@ static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { } static int scrollMax_DocumentWidget_(const iDocumentWidget *d) { - return size_GmDocument(d->doc).y - height_Rect(bounds_Widget(constAs_Widget(d))) + - (hasSiteBanner_GmDocument(d->doc) ? 1 : 2) * d->pageMargin * gap_UI; + int sm = size_GmDocument(d->doc).y - height_Rect(bounds_Widget(constAs_Widget(d))) + + (hasSiteBanner_GmDocument(d->doc) ? 1 : 2) * d->pageMargin * gap_UI; + if (d->phoneToolbar) { + sm += rootSize_Window(get_Window()).y - + top_Rect(boundsWithoutVisualOffset_Widget(d->phoneToolbar)); + } + return sm; } static void invalidateLink_DocumentWidget_(iDocumentWidget *d, iGmLinkId id) { @@ -815,9 +826,10 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode appendFormat_String(src, "\n```\n%s\n```\n" "You can save it as a file to your Downloads folder, though. " - "Press %s or select \"Save to Downloads\" from the menu.", + "Press %s or select \"%s\" from the menu.", cstr_String(meta), - cstr_String(key)); + cstr_String(key), + saveToDownloads_Label); break; } case slowDown_GmStatusCode: @@ -1085,6 +1097,14 @@ static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int dur const int scrollMax = scrollMax_DocumentWidget_(d); if (scrollMax > 0) { destY = iMin(destY, scrollMax); + if (deviceType_App() == phone_AppDeviceType) { + if (destY == scrollMax) { + showToolbars_Window(get_Window(), iTrue); + } + else if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5) { + showToolbars_Window(get_Window(), offset < 0); + } + } } else { destY = 0; @@ -1443,12 +1463,17 @@ static void saveToDownloads_(const iString *url, const iString *mime, const iBlo iFile *f = new_File(savePath); if (open_File(f, writeOnly_FileMode)) { write_File(f, content); + close_File(f); const size_t size = size_Block(content); const iBool isMega = size >= 1000000; +#if defined (iPlatformAppleMobile) + exportDownloadedFile_iOS(savePath); +#else makeMessage_Widget(uiHeading_ColorEscape "FILE SAVED", format_CStr("%s\nSize: %.3f %s", cstr_String(path_File(f)), isMega ? size / 1.0e6f : (size / 1.0e3f), isMega ? "MB" : "KB")); +#endif } else { makeMessage_Widget(uiTextCaution_ColorEscape "ERROR SAVING FILE", @@ -1496,8 +1521,8 @@ static const int homeRowKeys_[] = { static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumentWidget *d, iBool keepCenter) { const int newWidth = documentWidth_DocumentWidget_(d); - if (newWidth == size_GmDocument(d->doc).x) { - return iFalse; /* hasn't changed */ + if (newWidth == size_GmDocument(d->doc).x && !keepCenter /* not a font change */) { + return iFalse; } /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top of the visible area fixed. */ @@ -1526,6 +1551,7 @@ static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumen scrollTo_DocumentWidget_(d, mid_Rect(run->bounds).y, iTrue); } } + return iTrue; } static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { @@ -1533,6 +1559,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { /* Alt/Option key may be involved in window size changes. */ iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iFalse); + d->phoneToolbar = findWidget_App("toolbar"); const iBool keepCenter = equal_Command(cmd, "font.changed"); updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, keepCenter); updateSideIconBuf_DocumentWidget_(d); @@ -2361,36 +2388,16 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e } } else if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { - /* TODO: Maybe clean this up a bit? Wheel events are used for scrolling - but they are calculated differently based on device/mouse/trackpad. */ const iInt2 mouseCoord = mouseCoord_Window(get_Window()); if (isPerPixel_MouseWheelEvent(&ev->wheel)) { stop_Anim(&d->scrollY); iInt2 wheel = init_I2(ev->wheel.x, ev->wheel.y); -//# if defined (iPlatformAppleMobile) -// wheel.x = -wheel.x; -//# else -// /* Wheel mounts are in points. */ -// mulfv_I2(&wheel, get_Window()->pixelRatio); -// /* Only scroll on one axis at a time. */ -// if (iAbs(wheel.x) > iAbs(wheel.y)) { -// wheel.y = 0; -// } -// else { -// wheel.x = 0; -// } -//# endif scroll_DocumentWidget_(d, -wheel.y); scrollWideBlock_DocumentWidget_(d, mouseCoord, -wheel.x, 0); } else { /* Traditional mouse wheel. */ -//#if defined (iPlatformApple) -// /* Disregard wheel acceleration applied by the OS. */ -// const int amount = iSign(ev->wheel.y); -//#else const int amount = ev->wheel.y; -//#endif if (keyMods_Sym(modState_Keys()) == KMOD_PRIMARY) { postCommandf_App("zoom.delta arg:%d", amount > 0 ? 10 : -10); return iTrue; @@ -2513,7 +2520,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e d->contextLink->mediaType != download_GmRunMediaType) { if (isFinished_GmRequest(mediaReq->req)) { pushBack_Array(&items, - &(iMenuItem){ download_Icon " Save to Downloads", + &(iMenuItem){ download_Icon " " saveToDownloads_Label, 0, 0, format_CStr("document.media.save link:%u", @@ -2558,7 +2565,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e &items, (iMenuItem[]){ { "Copy Page Source", 'c', KMOD_PRIMARY, "copy" }, - { download_Icon " Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" } }, + { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" } }, 2); } } diff --git a/src/ui/util.c b/src/ui/util.c index 8fbe5d41..d68ae738 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -986,7 +986,6 @@ static iBool isTwoColumnPage_(iWidget *d) { static iBool isOmittedPref_(const iString *id) { static const char *omittedPrefs[] = { - "prefs.downloads", "prefs.smoothscroll", "prefs.imageloadscroll", "prefs.retainwindow", @@ -1812,6 +1811,10 @@ iWidget *makePreferences_Widget(void) { addChild_Widget(values, iClob(makeToggle_Widget("prefs.smoothscroll"))); addChild_Widget(headings, iClob(makeHeading_Widget("Load image on scroll:"))); addChild_Widget(values, iClob(makeToggle_Widget("prefs.imageloadscroll"))); + if (deviceType_App() == phone_AppDeviceType) { + addChild_Widget(headings, iClob(makeHeading_Widget("Hide toolbar on scroll:"))); + addChild_Widget(values, iClob(makeToggle_Widget("prefs.hidetoolbarscroll"))); + } } /* Window. */ { appendTwoColumnPage_(tabs, "Interface", '2', &headings, &values); diff --git a/src/ui/widget.c b/src/ui/widget.c index 81853c2a..23891999 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -223,6 +223,9 @@ void setVisualOffset_Widget(iWidget *d, int value, uint32_t span, int animFlags) setFlags_Widget(d, visualOffset_WidgetFlag, iTrue); if (span == 0) { init_Anim(&d->visualOffset, value); + if (value == 0) { + setFlags_Widget(d, visualOffset_WidgetFlag, iFalse); /* offset is being reset */ + } } else { setValue_Anim(&d->visualOffset, value, span); @@ -346,6 +349,9 @@ void arrange_Widget(iWidget *d) { else if (d->flags & moveToParentRightEdge_WidgetFlag) { d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); } + else if (d->flags & moveToParentBottomEdge_WidgetFlag) { + d->rect.pos.y = height_Rect(innerRect_Widget_(d->parent)) - height_Rect(d->rect); + } else if (d->flags & centerHorizontal_WidgetFlag) { centerHorizontal_Widget_(d); } @@ -455,8 +461,12 @@ void arrange_Widget(iWidget *d) { iForEach(ObjectList, i, d->children) { iWidget *child = as_Widget(i.object); if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { - if (dirs.x) setWidth_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.x : childSize.x); - if (dirs.y) setHeight_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.y : childSize.y); + if (dirs.x) { + setWidth_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.x : childSize.x); + } + if (dirs.y && ~child->flags & parentCannotResizeHeight_WidgetFlag) { + setHeight_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.y : childSize.y); + } } } } @@ -493,7 +503,8 @@ void arrange_Widget(iWidget *d) { pos.y += child->rect.size.y; } } - else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag) { + else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag && + ~child->flags & moveToParentBottomEdge_WidgetFlag) { child->rect.pos = pos; } else if (d->flags & resizeWidthOfChildren_WidgetFlag) { @@ -522,7 +533,9 @@ void arrange_Widget(iWidget *d) { iForEach(ObjectList, j, d->children) { iWidget *child = as_Widget(j.object); if (child->flags & - (resizeToParentWidth_WidgetFlag | moveToParentLeftEdge_WidgetFlag | + (resizeToParentWidth_WidgetFlag | + moveToParentLeftEdge_WidgetFlag | + moveToParentBottomEdge_WidgetFlag | moveToParentRightEdge_WidgetFlag)) { arrange_Widget(child); } @@ -542,6 +555,10 @@ void arrange_Widget(iWidget *d) { } } } +// if (d->flags & moveToParentBottomEdge_WidgetFlag) { +// /* TODO: Fix this: not DRY. See beginning of method. */ +// d->rect.pos.y = height_Rect(innerRect_Widget_(d->parent)) - height_Rect(d->rect); +// } if (d->flags & centerHorizontal_WidgetFlag) { centerHorizontal_Widget_(d); } @@ -574,6 +591,14 @@ iRect bounds_Widget(const iWidget *d) { return bounds; } +iRect boundsWithoutVisualOffset_Widget(const iWidget *d) { + iRect bounds = d->rect; + for (const iWidget *w = d->parent; w; w = w->parent) { + addv_I2(&bounds.pos, w->rect.pos); + } + return bounds; +} + iInt2 localCoord_Widget(const iWidget *d, iInt2 coord) { for (const iWidget *w = d; w; w = w->parent) { subv_I2(&coord, w->rect.pos); diff --git a/src/ui/widget.h b/src/ui/widget.h index 90ccac8e..ad7ce168 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -110,6 +110,8 @@ enum iWidgetFlag { #define dragged_WidgetFlag iBit64(54) #define hittable_WidgetFlag iBit64(55) #define safePadding_WidgetFlag iBit64(56) /* padded using safe area insets */ +#define moveToParentBottomEdge_WidgetFlag iBit64(57) +#define parentCannotResizeHeight_WidgetFlag iBit64(58) enum iWidgetAddPos { back_WidgetAddPos, @@ -165,6 +167,7 @@ const iString *id_Widget (const iWidget *); int64_t flags_Widget (const iWidget *); iRect bounds_Widget (const iWidget *); /* outer bounds */ iRect innerBounds_Widget (const iWidget *); +iRect boundsWithoutVisualOffset_Widget(const iWidget *); iInt2 localCoord_Widget (const iWidget *, iInt2 coord); iBool contains_Widget (const iWidget *, iInt2 coord); iBool containsExpanded_Widget (const iWidget *, iInt2 coord, int expand); diff --git a/src/ui/window.c b/src/ui/window.c index 1249a0b4..bd2b1493 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -191,7 +191,7 @@ static const iMenuItem navMenuItems_[] = { { add_Icon " New Tab", 't', KMOD_PRIMARY, "tabs.new" }, { "Open Location...", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, { "---", 0, 0, NULL }, - { download_Icon " Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" }, + { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" }, { "Copy Source Text", SDLK_c, KMOD_PRIMARY, "copy" }, { "---", 0, 0, NULL }, { leftHalf_Icon " Toggle Left Sidebar", SDLK_l, KMOD_PRIMARY | KMOD_SHIFT, "sidebar.toggle" }, @@ -259,7 +259,7 @@ static const iMenuItem fileMenuItems_[] = { { "New Tab", SDLK_t, KMOD_PRIMARY, "tabs.new" }, { "Open Location...", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, { "---", 0, 0, NULL }, - { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" }, + { saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" }, }; static const iMenuItem editMenuItems_[] = { @@ -576,7 +576,9 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { if (isPhone) { static const char *buttons[] = { "navbar.back", "navbar.forward", "navbar.sidebar", "navbar.ident", "navbar.home", "navbar.menu" }; - setFlags_Widget(findWidget_App("toolbar"), hidden_WidgetFlag, isLandscape_App()); + iWidget *toolBar = findWidget_App("toolbar"); + setVisualOffset_Widget(toolBar, 0, 0, 0); + setFlags_Widget(toolBar, hidden_WidgetFlag, isLandscape_App()); iForIndices(i, buttons) { iLabelWidget *btn = findChild_Widget(navBar, buttons[i]); setFlags_Widget(as_Widget(btn), hidden_WidgetFlag, isPortrait_App()); @@ -936,7 +938,6 @@ static void setupUserInterface_Window(iWindow *d) { setBackgroundColor_Widget(winBar, uiBackground_ColorId); } #endif - /* Navigation bar. */ { iWidget *navBar = new_Widget(); setId_Widget(navBar, "navbar"); @@ -1119,8 +1120,9 @@ static void setupUserInterface_Window(iWindow *d) { setBackgroundColor_Widget(searchBar, uiBackground_ColorId); setCommandHandler_Widget(searchBar, handleSearchBarCommands_); addChildFlags_Widget( - searchBar, iClob(new_LabelWidget(magnifyingGlass_Icon " Text", NULL)), frameless_WidgetFlag); + searchBar, iClob(new_LabelWidget(magnifyingGlass_Icon, NULL)), frameless_WidgetFlag); iInputWidget *input = new_InputWidget(0); + setHint_InputWidget(input, "Find text on page"); setSelectAllOnFocus_InputWidget(input, iTrue); setEatEscape_InputWidget(input, iFalse); /* unfocus and close with one keypress */ setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), @@ -1133,10 +1135,12 @@ static void setupUserInterface_Window(iWindow *d) { /* Bottom toolbar. */ if (isPhone_iOS()) { iWidget *toolBar = new_Widget(); - addChild_Widget(div, iClob(toolBar)); + addChild_Widget(d->root, iClob(toolBar)); setId_Widget(toolBar, "toolbar"); setCommandHandler_Widget(toolBar, handleToolBarCommands_); - setFlags_Widget(toolBar, collapse_WidgetFlag | resizeWidthOfChildren_WidgetFlag | + setFlags_Widget(toolBar, moveToParentBottomEdge_WidgetFlag | + parentCannotResizeHeight_WidgetFlag | + resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue); setBackgroundColor_Widget(toolBar, tmBannerBackground_ColorId); addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f870", "navigate.back")), frameless_WidgetFlag); @@ -1214,6 +1218,21 @@ static void setupUserInterface_Window(iWindow *d) { updateMetrics_Window_(d); } +void showToolbars_Window(iWindow *d, iBool show) { + if (isLandscape_App()) return; + iWidget *toolBar = findChild_Widget(d->root, "toolbar"); + if (!toolBar) return; + const int height = rootSize_Window(d).y - top_Rect(boundsWithoutVisualOffset_Widget(toolBar)); + if (show && !isVisible_Widget(toolBar)) { + setFlags_Widget(toolBar, hidden_WidgetFlag, iFalse); + setVisualOffset_Widget(toolBar, 0, 200, easeOut_AnimFlag); + } + else if (!show && isVisible_Widget(toolBar)) { + setFlags_Widget(toolBar, hidden_WidgetFlag, iTrue); + setVisualOffset_Widget(toolBar, height, 200, easeOut_AnimFlag); + } +} + static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) { iInt2 *size = &d->root->rect.size; const iInt2 oldSize = *size; diff --git a/src/ui/window.h b/src/ui/window.h index cd2e3814..7303acb2 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -91,6 +91,7 @@ void setCursor_Window (iWindow *, int cursor); void setSnap_Window (iWindow *, int snapMode); void setKeyboardHeight_Window(iWindow *, int height); void dismissPortraitPhoneSidebars_Window (iWindow *); +void showToolbars_Window (iWindow *, iBool show); iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *); uint32_t id_Window (const iWindow *); -- cgit v1.2.3