summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-05 22:02:36 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-05 22:02:36 +0200
commite5305a5e45a0f8a4e7ea2a670b4afd2c34f7840c (patch)
tree29ab06ec8585ecd1ef90800b86878f1874f67034 /src
parent8a8040bffff335395ce37d7fe2c9801db05b4b0a (diff)
Mobile: Pull to refresh
Diffstat (limited to 'src')
-rw-r--r--src/ui/documentwidget.c48
-rw-r--r--src/ui/util.c101
-rw-r--r--src/ui/util.h2
3 files changed, 125 insertions, 26 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 0ef80690..823c37ac 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -649,7 +649,8 @@ static const iGmRun *lastVisibleLink_DocumentWidget_(const iDocumentWidget *d) {
649static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { 649static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) {
650 const int docSize = pageHeight_DocumentWidget_(d); // size_GmDocument(d->doc).y; 650 const int docSize = pageHeight_DocumentWidget_(d); // size_GmDocument(d->doc).y;
651 if (docSize) { 651 if (docSize) {
652 return pos_SmoothScroll(&d->scrollY) / (float) docSize; 652 float pos = pos_SmoothScroll(&d->scrollY) / (float) docSize;
653 return iMax(pos, 0.0f);
653 } 654 }
654 return 0; 655 return 0;
655} 656}
@@ -2179,15 +2180,24 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
2179 insertChildAfter_Widget(buttons, iClob(lineBreak), 0); 2180 insertChildAfter_Widget(buttons, iClob(lineBreak), 0);
2180 } 2181 }
2181 else { 2182 else {
2183#if !defined (iPlatformAppleMobile)
2182 lineBreak = new_LabelWidget("${dlg.input.linebreak}", "text.insert arg:10"); 2184 lineBreak = new_LabelWidget("${dlg.input.linebreak}", "text.insert arg:10");
2185#endif
2186 }
2187 if (lineBreak) {
2188 setFlags_Widget(as_Widget(lineBreak), frameless_WidgetFlag, iTrue);
2189 setTextColor_LabelWidget(lineBreak, uiTextDim_ColorId);
2183 } 2190 }
2184 setFlags_Widget(as_Widget(lineBreak), frameless_WidgetFlag, iTrue);
2185 setTextColor_LabelWidget(lineBreak, uiTextDim_ColorId);
2186 } 2191 }
2187 setId_Widget(addChildPosFlags_Widget(buttons, 2192 iWidget *counter = (iWidget *) new_LabelWidget("", NULL);
2188 iClob(new_LabelWidget("", NULL)), 2193 setId_Widget(counter, "valueinput.counter");
2189 front_WidgetAddPos, frameless_WidgetFlag), 2194 setFlags_Widget(counter, frameless_WidgetFlag, iTrue);
2190 "valueinput.counter"); 2195 if (deviceType_App() == desktop_AppDeviceType) {
2196 addChildPos_Widget(buttons, iClob(counter), front_WidgetAddPos);
2197 }
2198 else {
2199 insertChildAfter_Widget(buttons, iClob(counter), 0);
2200 }
2191 if (lineBreak && deviceType_App() != desktop_AppDeviceType) { 2201 if (lineBreak && deviceType_App() != desktop_AppDeviceType) {
2192 addChildPos_Widget(buttons, iClob(lineBreak), front_WidgetAddPos); 2202 addChildPos_Widget(buttons, iClob(lineBreak), front_WidgetAddPos);
2193 } 2203 }
@@ -2207,6 +2217,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
2207 /* Keep scroll position when reloading the same page. */ 2217 /* Keep scroll position when reloading the same page. */
2208 reset_SmoothScroll(&d->scrollY); 2218 reset_SmoothScroll(&d->scrollY);
2209 } 2219 }
2220 d->scrollY.pullActionTriggered = 0;
2210 pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); 2221 pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue);
2211 iRelease(d->doc); /* new content incoming */ 2222 iRelease(d->doc); /* new content incoming */
2212 d->doc = new_GmDocument(); 2223 d->doc = new_GmDocument();
@@ -3698,6 +3709,10 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3698 return iTrue; 3709 return iTrue;
3699 } 3710 }
3700 else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { 3711 else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) {
3712 if (isCommand_Widget(w, ev, "pullaction")) {
3713 postCommand_Widget(w, "navigate.reload");
3714 return iTrue;
3715 }
3701 if (!handleCommand_DocumentWidget_(d, command_UserEvent(ev))) { 3716 if (!handleCommand_DocumentWidget_(d, command_UserEvent(ev))) {
3702 /* Base class commands. */ 3717 /* Base class commands. */
3703 return processEvent_Widget(w, ev); 3718 return processEvent_Widget(w, ev);
@@ -5132,6 +5147,25 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
5132 if (colorTheme_App() == pureWhite_ColorTheme) { 5147 if (colorTheme_App() == pureWhite_ColorTheme) {
5133 drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId); 5148 drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId);
5134 } 5149 }
5150 /* Pull action indicator. */
5151 if (deviceType_App() != desktop_AppDeviceType) {
5152 float pullPos = pullActionPos_SmoothScroll(&d->scrollY);
5153 /* Account for the part where the indicator isn't yet visible. */
5154 pullPos = (pullPos - 0.2f) / 0.8f;
5155 iRect indRect = initCentered_Rect(init_I2(mid_Rect(bounds).x,
5156 top_Rect(bounds) - 5 * gap_UI -
5157 pos_SmoothScroll(&d->scrollY)),
5158 init_I2(25 * gap_UI, 2 * gap_UI));
5159 setClip_Paint(&ctx.paint, clipBounds);
5160 int color = pullPos < 1.0f ? tmBannerItemFrame_ColorId : tmBannerItemText_ColorId;
5161 drawRect_Paint(&ctx.paint, indRect, color);
5162 if (pullPos > 0) {
5163 shrink_Rect(&indRect, divi_I2(gap2_UI, 2));
5164 indRect.size.x *= pullPos;
5165 fillRect_Paint(&ctx.paint, indRect, color);
5166 }
5167 unsetClip_Paint(&ctx.paint);
5168 }
5135 drawChildren_Widget(w); 5169 drawChildren_Widget(w);
5136 if (d->flags & drawDownloadCounter_DocumentWidgetFlag && isRequestOngoing_DocumentWidget(d)) { 5170 if (d->flags & drawDownloadCounter_DocumentWidgetFlag && isRequestOngoing_DocumentWidget(d)) {
5137 const int font = uiLabelLarge_FontId; 5171 const int font = uiLabelLarge_FontId;
diff --git a/src/ui/util.c b/src/ui/util.c
index de838769..8aedef99 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -479,12 +479,14 @@ void init_SmoothScroll(iSmoothScroll *d, iWidget *owner, iSmoothScrollNotifyFunc
479 reset_SmoothScroll(d); 479 reset_SmoothScroll(d);
480 d->widget = owner; 480 d->widget = owner;
481 d->notify = notify; 481 d->notify = notify;
482 d->pullActionTriggered = 0;
482} 483}
483 484
484void reset_SmoothScroll(iSmoothScroll *d) { 485void reset_SmoothScroll(iSmoothScroll *d) {
485 init_Anim(&d->pos, 0); 486 init_Anim(&d->pos, 0);
486 d->max = 0; 487 d->max = 0;
487 d->overscroll = (deviceType_App() != desktop_AppDeviceType ? 100 * gap_UI : 0); 488 d->overscroll = (deviceType_App() != desktop_AppDeviceType ? 100 * gap_UI : 0);
489 d->pullActionTriggered = 0;
488} 490}
489 491
490void setMax_SmoothScroll(iSmoothScroll *d, int max) { 492void setMax_SmoothScroll(iSmoothScroll *d, int max) {
@@ -518,6 +520,29 @@ iBool isFinished_SmoothScroll(const iSmoothScroll *d) {
518 return isFinished_Anim(&d->pos); 520 return isFinished_Anim(&d->pos);
519} 521}
520 522
523iLocalDef int pullActionThreshold_SmoothScroll_(const iSmoothScroll *d) {
524 return d->overscroll * 6 / 10;
525}
526
527float pullActionPos_SmoothScroll(const iSmoothScroll *d) {
528 if (d->pullActionTriggered >= 1) {
529 return 1.0f;
530 }
531 float pos = overscroll_SmoothScroll_(d);
532 if (pos >= 0.0f) {
533 return 0.0f;
534 }
535 pos = -pos / (float) pullActionThreshold_SmoothScroll_(d);
536 return iMin(pos, 1.0f);
537}
538
539static void checkPullAction_SmoothScroll_(iSmoothScroll *d) {
540 if (d->pullActionTriggered == 1 && d->widget) {
541 postCommand_Widget(d->widget, "pullaction");
542 d->pullActionTriggered = 2; /* pending handling */
543 }
544}
545
521void moveSpan_SmoothScroll(iSmoothScroll *d, int offset, uint32_t span) { 546void moveSpan_SmoothScroll(iSmoothScroll *d, int offset, uint32_t span) {
522#if !defined (iPlatformMobile) 547#if !defined (iPlatformMobile)
523 if (!prefs_App()->smoothScrolling) { 548 if (!prefs_App()->smoothScrolling) {
@@ -525,6 +550,14 @@ void moveSpan_SmoothScroll(iSmoothScroll *d, int offset, uint32_t span) {
525 } 550 }
526#endif 551#endif
527 int destY = targetValue_Anim(&d->pos) + offset; 552 int destY = targetValue_Anim(&d->pos) + offset;
553 if (destY < -pullActionThreshold_SmoothScroll_(d)) {
554 if (d->pullActionTriggered == 0) {
555 d->pullActionTriggered = iTrue;
556#if defined (iPlatformAppleMobile)
557 playHapticEffect_iOS(tap_HapticEffect);
558#endif
559 }
560 }
528 if (destY < -d->overscroll) { 561 if (destY < -d->overscroll) {
529 destY = -d->overscroll; 562 destY = -d->overscroll;
530 } 563 }
@@ -552,6 +585,7 @@ void moveSpan_SmoothScroll(iSmoothScroll *d, int offset, uint32_t span) {
552 // printf("remaining: %f dur: %d\n", remaining, duration); 585 // printf("remaining: %f dur: %d\n", remaining, duration);
553 d->pos.bounce = (osDelta < 0 ? -1 : 1) * 586 d->pos.bounce = (osDelta < 0 ? -1 : 1) *
554 iMini(5 * d->overscroll, remaining * remaining * 0.00005f); 587 iMini(5 * d->overscroll, remaining * remaining * 0.00005f);
588 checkPullAction_SmoothScroll_(d);
555 } 589 }
556 } 590 }
557 if (d->notify) { 591 if (d->notify) {
@@ -570,6 +604,7 @@ iBool processEvent_SmoothScroll(iSmoothScroll *d, const SDL_Event *ev) {
570 moveSpan_SmoothScroll(d, -osDelta, 100 * sqrt(iAbs(osDelta) / gap_UI)); 604 moveSpan_SmoothScroll(d, -osDelta, 100 * sqrt(iAbs(osDelta) / gap_UI));
571 d->pos.flags = easeOut_AnimFlag | muchSofter_AnimFlag; 605 d->pos.flags = easeOut_AnimFlag | muchSofter_AnimFlag;
572 } 606 }
607 checkPullAction_SmoothScroll_(d);
573 return iTrue; 608 return iTrue;
574 } 609 }
575 return iFalse; 610 return iFalse;
@@ -1622,6 +1657,11 @@ static void updateValueInputWidth_(iWidget *dlg) {
1622 } 1657 }
1623 /* Adjust the maximum number of visible lines. */ 1658 /* Adjust the maximum number of visible lines. */
1624 int footer = 6 * gap_UI + get_MainWindow()->keyboardHeight; 1659 int footer = 6 * gap_UI + get_MainWindow()->keyboardHeight;
1660#if defined (iPlatformAppleMobile)
1661 if (deviceType_App() == phone_AppDeviceType) {
1662 footer -= 12 * gap_UI; /* A little suspect, this... Check the math? */
1663 }
1664#endif
1625 iWidget *buttons = findChild_Widget(dlg, "dialogbuttons"); 1665 iWidget *buttons = findChild_Widget(dlg, "dialogbuttons");
1626 if (buttons) { 1666 if (buttons) {
1627 footer += height_Widget(buttons); 1667 footer += height_Widget(buttons);
@@ -1695,8 +1735,8 @@ iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) {
1695 } 1735 }
1696 int fonts[2] = { uiLabel_FontId, uiLabelBold_FontId }; 1736 int fonts[2] = { uiLabel_FontId, uiLabelBold_FontId };
1697 if (deviceType_App() == phone_AppDeviceType) { 1737 if (deviceType_App() == phone_AppDeviceType) {
1698 fonts[0] = uiLabelMedium_FontId; 1738 fonts[0] = uiLabelBig_FontId;
1699 fonts[1] = uiLabelMediumBold_FontId; 1739 fonts[1] = uiLabelBigBold_FontId;
1700 } 1740 }
1701 for (size_t i = 0; i < numActions; i++) { 1741 for (size_t i = 0; i < numActions; i++) {
1702 const char *label = actions[i].label; 1742 const char *label = actions[i].label;
@@ -1738,6 +1778,10 @@ iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) {
1738 setId_Widget(as_Widget(button), "default"); 1778 setId_Widget(as_Widget(button), "default");
1739 } 1779 }
1740 setFlags_Widget(as_Widget(button), alignLeft_WidgetFlag | drawKey_WidgetFlag, isDefault); 1780 setFlags_Widget(as_Widget(button), alignLeft_WidgetFlag | drawKey_WidgetFlag, isDefault);
1781 if (deviceType_App() != desktop_AppDeviceType) {
1782 setFlags_Widget(as_Widget(button), frameless_WidgetFlag | noBackground_WidgetFlag, iTrue);
1783 setTextColor_LabelWidget(button, uiTextAction_ColorId);
1784 }
1741 setFont_LabelWidget(button, isDefault ? fonts[1] : fonts[0]); 1785 setFont_LabelWidget(button, isDefault ? fonts[1] : fonts[0]);
1742 } 1786 }
1743 return div; 1787 return div;
@@ -1753,9 +1797,12 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con
1753 if (parent) { 1797 if (parent) {
1754 addChild_Widget(parent, iClob(dlg)); 1798 addChild_Widget(parent, iClob(dlg));
1755 } 1799 }
1756 setId_Widget( 1800 if (deviceType_App() != phone_AppDeviceType) {
1757 addChildFlags_Widget(dlg, iClob(new_LabelWidget(title, NULL)), frameless_WidgetFlag), 1801 setId_Widget(
1758 "valueinput.title"); 1802 addChildFlags_Widget(dlg, iClob(new_LabelWidget(title, NULL)),
1803 frameless_WidgetFlag),
1804 "valueinput.title");
1805 }
1759 iLabelWidget *promptLabel; 1806 iLabelWidget *promptLabel;
1760 setId_Widget(addChildFlags_Widget( 1807 setId_Widget(addChildFlags_Widget(
1761 dlg, iClob(promptLabel = new_LabelWidget(prompt, NULL)), frameless_WidgetFlag 1808 dlg, iClob(promptLabel = new_LabelWidget(prompt, NULL)), frameless_WidgetFlag
@@ -1775,15 +1822,25 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con
1775 } 1822 }
1776 setId_Widget(as_Widget(input), "input"); 1823 setId_Widget(as_Widget(input), "input");
1777 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); 1824 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
1778 addChild_Widget(dlg, 1825 /* On mobile, the actions are laid out a bit differently: buttons on top, on opposite edges. */
1779 iClob(makeDialogButtons_Widget( 1826 iArray actions;
1780 (iMenuItem[]){ { "${cancel}", SDLK_ESCAPE, 0, "valueinput.cancel" }, 1827 init_Array(&actions, sizeof(iMenuItem));
1781 { acceptLabel, 1828 pushBack_Array(&actions, &(iMenuItem){ "${cancel}", SDLK_ESCAPE, 0, "valueinput.cancel" });
1782 SDLK_RETURN, 1829 if (deviceType_App() != desktop_AppDeviceType) {
1783 acceptKeyMod_ReturnKeyBehavior(prefs_App()->returnKey), 1830 pushBack_Array(&actions, &(iMenuItem){ "---" });
1784 "valueinput.accept" } }, 1831 }
1785 2))); 1832 pushBack_Array(&actions, &(iMenuItem){
1786// finalizeSheet_Mobile(dlg); 1833 acceptLabel,
1834 SDLK_RETURN,
1835 acceptKeyMod_ReturnKeyBehavior(prefs_App()->returnKey),
1836 "valueinput.accept"
1837 });
1838 addChildPos_Widget(dlg,
1839 iClob(makeDialogButtons_Widget(constData_Array(&actions),
1840 size_Array(&actions))),
1841 deviceType_App() != desktop_AppDeviceType ?
1842 front_WidgetAddPos : back_WidgetAddPos);
1843 deinit_Array(&actions);
1787 arrange_Widget(dlg); 1844 arrange_Widget(dlg);
1788 if (parent) { 1845 if (parent) {
1789 setFocus_Widget(as_Widget(input)); 1846 setFocus_Widget(as_Widget(input));
@@ -2396,7 +2453,7 @@ iWidget *makePreferences_Widget(void) {
2396 }; 2453 };
2397 const iMenuItem colorPanelItems[] = { 2454 const iMenuItem colorPanelItems[] = {
2398 { "title id:heading.prefs.colors" }, 2455 { "title id:heading.prefs.colors" },
2399 { "heading id:heading.prefs.uitheme" }, 2456 //{ "heading id:heading.prefs.uitheme" },
2400 { "toggle id:prefs.ostheme" }, 2457 { "toggle id:prefs.ostheme" },
2401 { "radio id:prefs.theme", 0, 0, (const void *) themeItems }, 2458 { "radio id:prefs.theme", 0, 0, (const void *) themeItems },
2402 { "radio id:prefs.accent", 0, 0, (const void *) accentItems }, 2459 { "radio id:prefs.accent", 0, 0, (const void *) accentItems },
@@ -2414,11 +2471,16 @@ iWidget *makePreferences_Widget(void) {
2414 { "dropdown id:prefs.font.body", 0, 0, (const void *) constData_Array(makeFontItems_("body")) }, 2471 { "dropdown id:prefs.font.body", 0, 0, (const void *) constData_Array(makeFontItems_("body")) },
2415 { "dropdown id:prefs.font.mono", 0, 0, (const void *) constData_Array(makeFontItems_("mono")) }, 2472 { "dropdown id:prefs.font.mono", 0, 0, (const void *) constData_Array(makeFontItems_("mono")) },
2416 { "buttons id:prefs.mono", 0, 0, (const void *) monoFontItems }, 2473 { "buttons id:prefs.mono", 0, 0, (const void *) monoFontItems },
2417 { "dropdown id:prefs.font.monodoc", 0, 0, (const void *) constData_Array(makeFontItems_("monodoc")) },
2418 { "padding" }, 2474 { "padding" },
2419 { "toggle id:prefs.font.smooth" }, 2475 { "dropdown id:prefs.font.monodoc", 0, 0, (const void *) constData_Array(makeFontItems_("monodoc")) },
2420 { "padding" }, 2476// { "padding" },
2421 { "dropdown id:prefs.font.ui", 0, 0, (const void *) constData_Array(makeFontItems_("ui")) }, 2477 { "heading id:prefs.gemtext.ansi" },
2478 { "toggle id:prefs.gemtext.ansi.fg" },
2479 { "toggle id:prefs.gemtext.ansi.bg" },
2480 { "toggle id:prefs.gemtext.ansi.fontstyle" },
2481// { "toggle id:prefs.font.smooth" },
2482// { "padding" },
2483// { "dropdown id:prefs.font.ui", 0, 0, (const void *) constData_Array(makeFontItems_("ui")) },
2422 { "padding" }, 2484 { "padding" },
2423 { "button text:" fontpack_Icon " " uiTextAction_ColorEscape "${menu.fonts}", 0, 0, "!open url:about:fonts" }, 2485 { "button text:" fontpack_Icon " " uiTextAction_ColorEscape "${menu.fonts}", 0, 0, "!open url:about:fonts" },
2424 { NULL } 2486 { NULL }
@@ -3048,6 +3110,7 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) {
3048 { format_CStr("title id:feedcfg.heading text:%s", headingText) }, 3110 { format_CStr("title id:feedcfg.heading text:%s", headingText) },
3049 { "input id:feedcfg.title text:${dlg.feed.title}" }, 3111 { "input id:feedcfg.title text:${dlg.feed.title}" },
3050 { "radio id:dlg.feed.entrytype", 0, 0, (const void *) typeItems }, 3112 { "radio id:dlg.feed.entrytype", 0, 0, (const void *) typeItems },
3113 { "padding" },
3051 { "toggle id:feedcfg.ignoreweb text:${dlg.feed.ignoreweb}" }, 3114 { "toggle id:feedcfg.ignoreweb text:${dlg.feed.ignoreweb}" },
3052 { NULL } 3115 { NULL }
3053 }, actions, iElemCount(actions)); 3116 }, actions, iElemCount(actions));
diff --git a/src/ui/util.h b/src/ui/util.h
index 7ee94f1d..023fa7ae 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -184,6 +184,7 @@ struct Impl_SmoothScroll {
184 int max; 184 int max;
185 int overscroll; 185 int overscroll;
186 iWidget *widget; 186 iWidget *widget;
187 int pullActionTriggered;
187 iSmoothScrollNotifyFunc notify; 188 iSmoothScrollNotifyFunc notify;
188}; 189};
189 190
@@ -197,6 +198,7 @@ iBool processEvent_SmoothScroll (iSmoothScroll *, const SDL_Event *ev);
197 198
198float pos_SmoothScroll (const iSmoothScroll *); 199float pos_SmoothScroll (const iSmoothScroll *);
199iBool isFinished_SmoothScroll (const iSmoothScroll *); 200iBool isFinished_SmoothScroll (const iSmoothScroll *);
201float pullActionPos_SmoothScroll (const iSmoothScroll *); /* 0...1 */
200 202
201/*-----------------------------------------------------------------------------------------------*/ 203/*-----------------------------------------------------------------------------------------------*/
202 204