summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-04 07:40:56 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-04 07:40:56 +0200
commit723fcbf263bd2f5ff6c259a47091ebf685b50723 (patch)
tree5ed11977ee09fa542ed62a12c73117a27140d04e /src
parentad86fbe2cffa2bea07d004782e711932c5c91a79 (diff)
Mobile: Sidebar is now a vertically sliding panel
Switched the phone sidebar to use the iOS half/full-height sliding sheet design. This is better for finger reachability and for retaining access to the current page.
Diffstat (limited to 'src')
-rw-r--r--src/ui/documentwidget.c6
-rw-r--r--src/ui/listwidget.c35
-rw-r--r--src/ui/listwidget.h8
-rw-r--r--src/ui/root.c106
-rw-r--r--src/ui/sidebarwidget.c271
-rw-r--r--src/ui/sidebarwidget.h1
-rw-r--r--src/ui/touch.c37
-rw-r--r--src/ui/touch.h1
-rw-r--r--src/ui/widget.c1
-rw-r--r--src/ui/widget.h5
10 files changed, 340 insertions, 131 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 95286566..0ef80690 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -2620,9 +2620,9 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2620 /* The temporary "swipeIn" will display the previous page until the finger is lifted. */ 2620 /* The temporary "swipeIn" will display the previous page until the finger is lifted. */
2621 iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); 2621 iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein");
2622 if (!swipeIn) { 2622 if (!swipeIn) {
2623 const iBool sidebarSwipe = (isPortraitPhone_App() && 2623 const iBool sidebarSwipe = iFalse; /* && (isPortraitPhone_App() &&
2624 d->flags & openedFromSidebar_DocumentWidgetFlag && 2624 d->flags & openedFromSidebar_DocumentWidgetFlag &&
2625 !isVisible_Widget(findWidget_App("sidebar"))); 2625 !isVisible_Widget(findWidget_App("sidebar"))); */
2626 swipeIn = new_DocumentWidget(); 2626 swipeIn = new_DocumentWidget();
2627 setId_Widget(as_Widget(swipeIn), "swipein"); 2627 setId_Widget(as_Widget(swipeIn), "swipein");
2628 setFlags_Widget(as_Widget(swipeIn), 2628 setFlags_Widget(as_Widget(swipeIn),
@@ -3235,6 +3235,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
3235 return iTrue; 3235 return iTrue;
3236 } 3236 }
3237 else if (equal_Command(cmd, "navigate.back") && document_App() == d) { 3237 else if (equal_Command(cmd, "navigate.back") && document_App() == d) {
3238#if 0
3238 if (isPortraitPhone_App()) { 3239 if (isPortraitPhone_App()) {
3239 if (d->flags & openedFromSidebar_DocumentWidgetFlag && 3240 if (d->flags & openedFromSidebar_DocumentWidgetFlag &&
3240 !isVisible_Widget(findWidget_App("sidebar"))) { 3241 !isVisible_Widget(findWidget_App("sidebar"))) {
@@ -3247,6 +3248,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
3247 } 3248 }
3248 d->flags &= ~openedFromSidebar_DocumentWidgetFlag; 3249 d->flags &= ~openedFromSidebar_DocumentWidgetFlag;
3249 } 3250 }
3251#endif
3250 if (d->request) { 3252 if (d->request) {
3251 postCommandf_Root(w->root, 3253 postCommandf_Root(w->root,
3252 "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); 3254 "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url));
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c
index 82e4e451..d12afc4c 100644
--- a/src/ui/listwidget.c
+++ b/src/ui/listwidget.c
@@ -81,6 +81,7 @@ void init_ListWidget(iListWidget *d) {
81 setThumb_ScrollWidget(d->scroll, 0, 0); 81 setThumb_ScrollWidget(d->scroll, 0, 0);
82 init_SmoothScroll(&d->scrollY, w, scrollBegan_ListWidget_); 82 init_SmoothScroll(&d->scrollY, w, scrollBegan_ListWidget_);
83 d->itemHeight = 0; 83 d->itemHeight = 0;
84 d->scrollMode = normal_ScrollMode;
84 d->noHoverWhileScrolling = iFalse; 85 d->noHoverWhileScrolling = iFalse;
85 init_PtrArray(&d->items); 86 init_PtrArray(&d->items);
86 d->hoverItem = iInvalidPos; 87 d->hoverItem = iInvalidPos;
@@ -187,6 +188,10 @@ void setScrollPos_ListWidget(iListWidget *d, int pos) {
187 refresh_Widget(as_Widget(d)); 188 refresh_Widget(as_Widget(d));
188} 189}
189 190
191void setScrollMode_ListWidget(iListWidget *d, enum iScrollMode mode) {
192 d->scrollMode = mode;
193}
194
190void scrollOffset_ListWidget(iListWidget *d, int offset) { 195void scrollOffset_ListWidget(iListWidget *d, int offset) {
191 moveSpan_SmoothScroll(&d->scrollY, offset, 0); 196 moveSpan_SmoothScroll(&d->scrollY, offset, 0);
192} 197}
@@ -366,12 +371,28 @@ static iBool endDrag_ListWidget_(iListWidget *d, iInt2 endPos) {
366 return iTrue; 371 return iTrue;
367} 372}
368 373
374static iBool isScrollDisabled_ListWidget_(const iListWidget *d, const SDL_Event *ev) {
375 int dir = 0;
376 if (ev->type == SDL_MOUSEWHEEL) {
377 dir = iSign(ev->wheel.y);
378 }
379 switch (d->scrollMode) {
380 case disabledAtTopBothDirections_ScrollMode:
381 return scrollPos_ListWidget(d) <= 0;
382 case disabledAtTopUpwards_ScrollMode:
383 return scrollPos_ListWidget(d) <= 0 && dir > 0;
384 default:
385 break;
386 }
387 return iFalse;
388}
389
369static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { 390static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) {
370 iWidget *w = as_Widget(d); 391 iWidget *w = as_Widget(d);
371 if (isMetricsChange_UserEvent(ev)) { 392 if (isMetricsChange_UserEvent(ev)) {
372 invalidate_ListWidget(d); 393 invalidate_ListWidget(d);
373 } 394 }
374 else if (processEvent_SmoothScroll(&d->scrollY, ev)) { 395 else if (!isScrollDisabled_ListWidget_(d, ev) && processEvent_SmoothScroll(&d->scrollY, ev)) {
375 return iTrue; 396 return iTrue;
376 } 397 }
377 else if (isCommand_SDLEvent(ev)) { 398 else if (isCommand_SDLEvent(ev)) {
@@ -420,6 +441,18 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) {
420 } 441 }
421 } 442 }
422 if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { 443 if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) {
444 if (isScrollDisabled_ListWidget_(d, ev)) {
445 if (ev->wheel.which == SDL_TOUCH_MOUSEID) {
446 /* TODO: Could generalize this selection of the scrollable parent. */
447 extern iWidgetClass Class_SidebarWidget;
448 iWidget *sidebar = findParentClass_Widget(w, &Class_SidebarWidget);
449 if (sidebar) {
450 transferAffinity_Touch(w, sidebar);
451 d->noHoverWhileScrolling = iTrue;
452 }
453 }
454 return iFalse;
455 }
423 int amount = -ev->wheel.y; 456 int amount = -ev->wheel.y;
424 if (isPerPixel_MouseWheelEvent(&ev->wheel)) { 457 if (isPerPixel_MouseWheelEvent(&ev->wheel)) {
425 stop_Anim(&d->scrollY.pos); 458 stop_Anim(&d->scrollY.pos);
diff --git a/src/ui/listwidget.h b/src/ui/listwidget.h
index 7e6624a0..081109e8 100644
--- a/src/ui/listwidget.h
+++ b/src/ui/listwidget.h
@@ -51,6 +51,12 @@ iDeclareObjectConstruction(ListWidget)
51 51
52iDeclareType(VisBuf) 52iDeclareType(VisBuf)
53 53
54enum iScrollMode {
55 normal_ScrollMode,
56 disabledAtTopBothDirections_ScrollMode,
57 disabledAtTopUpwards_ScrollMode,
58};
59
54struct Impl_ListWidget { 60struct Impl_ListWidget {
55 iWidget widget; 61 iWidget widget;
56 iScrollWidget *scroll; 62 iScrollWidget *scroll;
@@ -63,6 +69,7 @@ struct Impl_ListWidget {
63 iClick click; 69 iClick click;
64 iIntSet invalidItems; 70 iIntSet invalidItems;
65 iVisBuf *visBuf; 71 iVisBuf *visBuf;
72 enum iScrollMode scrollMode;
66 iBool noHoverWhileScrolling; 73 iBool noHoverWhileScrolling;
67}; 74};
68 75
@@ -82,6 +89,7 @@ int itemHeight_ListWidget (const iListWidget *);
82int scrollPos_ListWidget (const iListWidget *); 89int scrollPos_ListWidget (const iListWidget *);
83 90
84void setScrollPos_ListWidget (iListWidget *, int pos); 91void setScrollPos_ListWidget (iListWidget *, int pos);
92void setScrollMode_ListWidget (iListWidget *, enum iScrollMode mode);
85void scrollToItem_ListWidget (iListWidget *, size_t index, uint32_t span); 93void scrollToItem_ListWidget (iListWidget *, size_t index, uint32_t span);
86void scrollOffset_ListWidget (iListWidget *, int offset); 94void scrollOffset_ListWidget (iListWidget *, int offset);
87void scrollOffsetSpan_ListWidget (iListWidget *, int offset, uint32_t span); 95void scrollOffsetSpan_ListWidget (iListWidget *, int offset, uint32_t span);
diff --git a/src/ui/root.c b/src/ui/root.c
index f722df94..65fc11d1 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -487,11 +487,23 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
487 else if (deviceType_App() == phone_AppDeviceType && equal_Command(cmd, "window.resized")) { 487 else if (deviceType_App() == phone_AppDeviceType && equal_Command(cmd, "window.resized")) {
488 /* Place the sidebar next to or under doctabs depending on orientation. */ 488 /* Place the sidebar next to or under doctabs depending on orientation. */
489 iSidebarWidget *sidebar = findChild_Widget(root, "sidebar"); 489 iSidebarWidget *sidebar = findChild_Widget(root, "sidebar");
490 iSidebarWidget *sidebar2 = findChild_Widget(root, "sidebar2");
491 removeChild_Widget(parent_Widget(sidebar), sidebar); 490 removeChild_Widget(parent_Widget(sidebar), sidebar);
492 // setBackgroundColor_Widget(findChild_Widget(as_Widget(sidebar), "buttons"), 491 if (isLandscape_App()) {
493 // isPortrait_App() ? uiBackgroundUnfocusedSelection_ColorId 492 addChildPos_Widget(findChild_Widget(root, "tabs.content"), iClob(sidebar), front_WidgetAddPos);
494 // : uiBackgroundSidebar_ColorId); 493 setWidth_SidebarWidget(sidebar, 73.0f);
494 setFlags_Widget(as_Widget(sidebar), fixedHeight_WidgetFlag, iFalse);
495 }
496 else {
497 addChild_Widget(root, iClob(sidebar));
498 setWidth_SidebarWidget(sidebar, (float) width_Widget(root) / (float) gap_UI);
499 const int midHeight = height_Widget(root) / 2;// + lineHeight_Text(uiLabelLarge_FontId);
500 setMidHeight_SidebarWidget(sidebar, midHeight);
501 setFixedSize_Widget(as_Widget(sidebar), init_I2(-1, midHeight));
502 setPos_Widget(as_Widget(sidebar), init_I2(0, height_Widget(root) - midHeight));
503 }
504#if 0
505 iSidebarWidget *sidebar = findChild_Widget(root, "sidebar");
506 iSidebarWidget *sidebar2 = findChild_Widget(root, "sidebar2");
495 setFlags_Widget(findChild_Widget(as_Widget(sidebar), "buttons"), 507 setFlags_Widget(findChild_Widget(as_Widget(sidebar), "buttons"),
496 borderTop_WidgetFlag, 508 borderTop_WidgetFlag,
497 isPortrait_App()); 509 isPortrait_App());
@@ -507,6 +519,7 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
507 setWidth_SidebarWidget(sidebar, (float) width_Widget(root) / (float) gap_UI); 519 setWidth_SidebarWidget(sidebar, (float) width_Widget(root) / (float) gap_UI);
508 setWidth_SidebarWidget(sidebar2, (float) width_Widget(root) / (float) gap_UI); 520 setWidth_SidebarWidget(sidebar2, (float) width_Widget(root) / (float) gap_UI);
509 } 521 }
522#endif
510 return iFalse; 523 return iFalse;
511 } 524 }
512 else if (handleCommand_App(cmd)) { 525 else if (handleCommand_App(cmd)) {
@@ -609,14 +622,14 @@ void updatePadding_Root(iRoot *d) {
609 } 622 }
610 } 623 }
611#endif 624#endif
612 if (toolBar) { 625// if (toolBar) {
613 /* TODO: get this from toolBar height, but it's buggy for some reason */ 626 /* TODO: get this from toolBar height, but it's buggy for some reason */
614 const int sidebarBottomPad = isPortrait_App() ? 11 * gap_UI + bottom : 0; 627// const int sidebarBottomPad = isPortrait_App() ? 11 * gap_UI + bottom : 0;
615 setPadding_Widget(findChild_Widget(d->widget, "sidebar"), 0, 0, 0, sidebarBottomPad); 628// setPadding_Widget(findChild_Widget(d->widget, "sidebar"), 0, 0, 0, sidebarBottomPad);
616 setPadding_Widget(findChild_Widget(d->widget, "sidebar2"), 0, 0, 0, sidebarBottomPad); 629 //setPadding_Widget(findChild_Widget(d->widget, "sidebar2"), 0, 0, 0, sidebarBottomPad);
617 /* TODO: There seems to be unrelated layout glitch in the sidebar where its children 630 /* TODO: There seems to be unrelated layout glitch in the sidebar where its children
618 are not arranged correctly until it's hidden and reshown. */ 631 are not arranged correctly until it's hidden and reshown. */
619 } 632// }
620 /* Note that `handleNavBarCommands_` also adjusts padding and spacing. */ 633 /* Note that `handleNavBarCommands_` also adjusts padding and spacing. */
621} 634}
622 635
@@ -1015,30 +1028,17 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) {
1015 } 1028 }
1016 else if (equal_Command(cmd, "toolbar.showview")) { 1029 else if (equal_Command(cmd, "toolbar.showview")) {
1017 /* TODO: Clean this up. */ 1030 /* TODO: Clean this up. */
1018 iWidget *sidebar = findWidget_App("sidebar"); 1031// iWidget *sidebar = findWidget_App("sidebar");
1019 iWidget *sidebar2 = findWidget_App("sidebar2"); 1032// iWidget *sidebar2 = findWidget_App("sidebar2");
1020 dismissSidebar_(sidebar2, "toolbar.ident"); 1033// dismissSidebar_(sidebar2, "toolbar.ident");
1021 const iBool isVisible = isVisible_Widget(sidebar); 1034// const iBool isVisible = isVisible_Widget(sidebar);
1022 // setFlags_Widget(findChild_Widget(toolBar, "toolbar.view"), noBackground_WidgetFlag,
1023 // isVisible);
1024 /* If a sidebar hasn't been shown yet, it's height is zero. */ 1035 /* If a sidebar hasn't been shown yet, it's height is zero. */
1025 const int viewHeight = size_Root(get_Root()).y; 1036// const int viewHeight = size_Root(get_Root()).y;
1026 if (arg_Command(cmd) >= 0) { 1037 if (arg_Command(cmd) >= 0) {
1027 postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd)); 1038 postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd));
1028// if (!isVisible) {
1029// setVisualOffset_Widget(sidebar, viewHeight, 0, 0);
1030// setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag);
1031// }
1032 } 1039 }
1033 else { 1040 else {
1034 postCommandf_App("sidebar.toggle"); 1041 postCommandf_App("sidebar.toggle");
1035// if (isVisible) {
1036// setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag);
1037// }
1038// else {
1039// setVisualOffset_Widget(sidebar, viewHeight, 0, 0);
1040// setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag);
1041// }
1042 } 1042 }
1043 return iTrue; 1043 return iTrue;
1044 } 1044 }
@@ -1110,41 +1110,22 @@ void updateMetrics_Root(iRoot *d) {
1110 setFixedSize_Widget(appClose, appMin->rect.size); 1110 setFixedSize_Widget(appClose, appMin->rect.size);
1111 setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y)); 1111 setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y));
1112 } 1112 }
1113 iWidget *navBar = findChild_Widget(d->widget, "navbar"); 1113 iWidget *navBar = findChild_Widget(d->widget, "navbar");
1114// iWidget *lock = findChild_Widget(navBar, "navbar.lock"); 1114 iWidget *url = findChild_Widget(d->widget, "url");
1115 iWidget *url = findChild_Widget(d->widget, "url"); 1115 iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed");
1116 iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed"); 1116 iWidget *embedPad = findChild_Widget(navBar, "url.embedpad");
1117 iWidget *embedPad = findChild_Widget(navBar, "url.embedpad"); 1117 iWidget *urlButtons = findChild_Widget(navBar, "url.buttons");
1118 iWidget *urlButtons = findChild_Widget(navBar, "url.buttons"); 1118 iLabelWidget *idName = findChild_Widget(d->widget, "toolbar.name");
1119 setPadding_Widget(as_Widget(url), 0, gap_UI, 0, gap_UI); 1119 setPadding_Widget(as_Widget(url), 0, gap_UI, 0, gap_UI);
1120 navBar->rect.size.y = 0; /* recalculate height based on children (FIXME: shouldn't be needed) */ 1120 navBar->rect.size.y = 0; /* recalculate height based on children (FIXME: shouldn't be needed) */
1121// updateSize_LabelWidget((iLabelWidget *) lock);
1122// updateSize_LabelWidget((iLabelWidget *) findChild_Widget(navBar, "reload"));
1123// arrange_Widget(urlButtons);
1124 setFixedSize_Widget(embedPad, init_I2(width_Widget(urlButtons) + gap_UI / 2, 1)); 1121 setFixedSize_Widget(embedPad, init_I2(width_Widget(urlButtons) + gap_UI / 2, 1));
1125// setContentPadding_InputWidget((iInputWidget *) url, width_Widget(lock) * 0.75,
1126// width_Widget(lock) * 0.75);
1127 rightEmbed->rect.pos.y = gap_UI; 1122 rightEmbed->rect.pos.y = gap_UI;
1128 updatePadding_Root(d); 1123 updatePadding_Root(d);
1129 arrange_Widget(d->widget); 1124 arrange_Widget(d->widget);
1130 updateUrlInputContentPadding_(navBar); 1125 updateUrlInputContentPadding_(navBar);
1131 /* Position the toolbar identity name label manually. */ { 1126 if (idName) {
1132 iLabelWidget *idName = findChild_Widget(d->widget, "toolbar.name"); 1127 setFixedSize_Widget(as_Widget(idName),
1133 if (idName) { 1128 init_I2(-1, 2 * gap_UI + lineHeight_Text(uiLabelTiny_FontId)));
1134 const iWidget *toolBar = findChild_Widget(d->widget, "toolbar");
1135 const iWidget *viewButton = findChild_Widget(d->widget, "toolbar.view");
1136 const iWidget *idButton = findChild_Widget(toolBar, "toolbar.ident");
1137// const int font = uiLabelTiny_FontId;
1138 setFixedSize_Widget(as_Widget(idName), init_I2(-1, 2 * gap_UI + lineHeight_Text(uiLabelTiny_FontId)));
1139// setFont_LabelWidget(idName, font);
1140 /*setPos_Widget(as_Widget(idName),
1141 windowToLocal_Widget(as_Widget(idName),
1142 init_I2(left_Rect(bounds_Widget(idButton)),
1143 bottom_Rect(bounds_Widget(viewButton)) -
1144 lineHeight_Text(font) - gap_UI / 2)));
1145 setFixedSize_Widget(as_Widget(idName), init_I2(width_Widget(idButton),
1146 lineHeight_Text(font)));*/
1147 }
1148 } 1129 }
1149 postRefresh_App(); 1130 postRefresh_App();
1150} 1131}
@@ -1168,11 +1149,9 @@ void createUserInterface_Root(iRoot *d) {
1168 setFlags_Widget( 1149 setFlags_Widget(
1169 root, resizeChildren_WidgetFlag | fixedSize_WidgetFlag | focusRoot_WidgetFlag, iTrue); 1150 root, resizeChildren_WidgetFlag | fixedSize_WidgetFlag | focusRoot_WidgetFlag, iTrue);
1170 setCommandHandler_Widget(root, handleRootCommands_); 1151 setCommandHandler_Widget(root, handleRootCommands_);
1171
1172 iWidget *div = makeVDiv_Widget(); 1152 iWidget *div = makeVDiv_Widget();
1173 setId_Widget(div, "navdiv"); 1153 setId_Widget(div, "navdiv");
1174 addChild_Widget(root, iClob(div)); 1154 addChild_Widget(root, iClob(div));
1175
1176#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 1155#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
1177 /* Window title bar. */ 1156 /* Window title bar. */
1178 if (prefs_App()->customFrame) { 1157 if (prefs_App()->customFrame) {
@@ -1446,20 +1425,19 @@ void createUserInterface_Root(iRoot *d) {
1446 "newtab"); 1425 "newtab");
1447 } 1426 }
1448 /* Sidebars. */ { 1427 /* Sidebars. */ {
1449 iWidget *content = findChild_Widget(root, "tabs.content");
1450 iSidebarWidget *sidebar1 = new_SidebarWidget(left_SidebarSide); 1428 iSidebarWidget *sidebar1 = new_SidebarWidget(left_SidebarSide);
1451 addChildPos_Widget(content, iClob(sidebar1), front_WidgetAddPos);
1452 if (deviceType_App() != phone_AppDeviceType) { 1429 if (deviceType_App() != phone_AppDeviceType) {
1430 /* Sidebars are next to the tab content. */
1431 iWidget *content = findChild_Widget(root, "tabs.content");
1432 addChildPos_Widget(content, iClob(sidebar1), front_WidgetAddPos);
1453 iSidebarWidget *sidebar2 = new_SidebarWidget(right_SidebarSide); 1433 iSidebarWidget *sidebar2 = new_SidebarWidget(right_SidebarSide);
1454 addChildPos_Widget(content, iClob(sidebar2), back_WidgetAddPos); 1434 addChildPos_Widget(content, iClob(sidebar2), back_WidgetAddPos);
1455 } 1435 }
1456#if 0
1457 else { 1436 else {
1458 /* The identities sidebar is always in the main area. */ 1437 /* Sidebar is a slide-over. */
1459 addChild_Widget(findChild_Widget(root, "stack"), iClob(sidebar2)); 1438 addChild_Widget(/*findChild_Widget(root, "stack")*/ root, iClob(sidebar1));
1460 setFlags_Widget(as_Widget(sidebar2), hidden_WidgetFlag, iTrue); 1439 setFlags_Widget(as_Widget(sidebar1), hidden_WidgetFlag, iTrue);
1461 } 1440 }
1462#endif
1463 } 1441 }
1464 /* Lookup results. */ { 1442 /* Lookup results. */ {
1465 iLookupWidget *lookup = new_LookupWidget(); 1443 iLookupWidget *lookup = new_LookupWidget();
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 13fc33b1..401c5d25 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
39#include "paint.h" 39#include "paint.h"
40#include "root.h" 40#include "root.h"
41#include "scrollwidget.h" 41#include "scrollwidget.h"
42#include "touch.h"
42#include "util.h" 43#include "util.h"
43#include "visited.h" 44#include "visited.h"
44 45
@@ -99,13 +100,15 @@ struct Impl_SidebarWidget {
99 iListWidget * list; 100 iListWidget * list;
100 iCertListWidget * certList; 101 iCertListWidget * certList;
101 iWidget * actions; /* below the list, area for buttons */ 102 iWidget * actions; /* below the list, area for buttons */
103 int midHeight; /* on portrait phone, the height for the middle state */
104 iBool isBeingDraggedVertically; /* on portrait phone, sidebar can be dragged up/down */
102 int modeScroll[max_SidebarMode]; 105 int modeScroll[max_SidebarMode];
103 iLabelWidget * modeButtons[max_SidebarMode]; 106 iLabelWidget * modeButtons[max_SidebarMode];
104 int maxButtonLabelWidth; 107 int maxButtonLabelWidth;
105 float widthAsGaps; 108 float widthAsGaps;
106 int buttonFont; 109 int buttonFont;
107 int itemFonts[2]; 110 int itemFonts[2];
108 size_t numUnreadEntries; 111 size_t numUnreadEntries;
109 iWidget * resizer; 112 iWidget * resizer;
110 iWidget * menu; /* context menu for an item */ 113 iWidget * menu; /* context menu for an item */
111 iWidget * modeMenu; /* context menu for the sidebar mode (no item) */ 114 iWidget * modeMenu; /* context menu for the sidebar mode (no item) */
@@ -194,9 +197,9 @@ static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const cha
194 //(deviceType_App() != desktop_AppDeviceType ? 197 //(deviceType_App() != desktop_AppDeviceType ?
195 // extraPadding_WidgetFlag : 0) | 198 // extraPadding_WidgetFlag : 0) |
196 flags); 199 flags);
197 setFont_LabelWidget(btn, deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide 200 setFont_LabelWidget(btn, /*deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide
198 ? uiLabelBig_FontId 201 ? uiLabelBig_FontId : */
199 : d->buttonFont); 202 d->buttonFont);
200 checkIcon_LabelWidget(btn); 203 checkIcon_LabelWidget(btn);
201 return btn; 204 return btn;
202} 205}
@@ -211,7 +214,12 @@ static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBoo
211 return iFalse; 214 return iFalse;
212} 215}
213 216
217static iBool isSlidingSheet_SidebarWidget_(const iSidebarWidget *d) {
218 return isPortraitPhone_App();// && scrollPos_ListWidget(d->list) <= 0;
219}
220
214static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) { 221static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) {
222 const iBool isMobile = (deviceType_App() != desktop_AppDeviceType);
215 clear_ListWidget(d->list); 223 clear_ListWidget(d->list);
216 releaseChildren_Widget(d->blank); 224 releaseChildren_Widget(d->blank);
217 if (!keepActions) { 225 if (!keepActions) {
@@ -299,9 +307,10 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct
299 } 307 }
300 /* Actions. */ 308 /* Actions. */
301 if (!keepActions) { 309 if (!keepActions) {
302 addActionButton_SidebarWidget_( 310 addActionButton_SidebarWidget_(d,
303 d, check_Icon " ${sidebar.action.feeds.markallread}", "feeds.markallread", expand_WidgetFlag | 311 check_Icon " ${sidebar.action.feeds.markallread}",
304 tight_WidgetFlag); 312 "feeds.markallread",
313 expand_WidgetFlag | tight_WidgetFlag);
305 updateSize_LabelWidget(addChildFlags_Widget(d->actions, 314 updateSize_LabelWidget(addChildFlags_Widget(d->actions,
306 iClob(new_LabelWidget("${sidebar.action.show}", NULL)), 315 iClob(new_LabelWidget("${sidebar.action.show}", NULL)),
307 frameless_WidgetFlag | tight_WidgetFlag)); 316 frameless_WidgetFlag | tight_WidgetFlag));
@@ -617,6 +626,10 @@ void setClosedFolders_SidebarWidget(iSidebarWidget *d, const iIntSet *closedFold
617 } 626 }
618} 627}
619 628
629void setMidHeight_SidebarWidget(iSidebarWidget *d, int midHeight) {
630 d->midHeight = midHeight;
631}
632
620enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { 633enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) {
621 return d ? d->mode : 0; 634 return d ? d->mode : 0;
622} 635}
@@ -686,6 +699,8 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
686 d->side = side; 699 d->side = side;
687 d->mode = -1; 700 d->mode = -1;
688 d->feedsMode = all_FeedsMode; 701 d->feedsMode = all_FeedsMode;
702 d->midHeight = 0;
703 d->isBeingDraggedVertically = iFalse;
689 d->numUnreadEntries = 0; 704 d->numUnreadEntries = 0;
690 d->buttonFont = uiLabel_FontId; /* wiil be changed later */ 705 d->buttonFont = uiLabel_FontId; /* wiil be changed later */
691 d->itemFonts[0] = uiContent_FontId; 706 d->itemFonts[0] = uiContent_FontId;
@@ -703,15 +718,24 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
703 iWidget *vdiv = makeVDiv_Widget(); 718 iWidget *vdiv = makeVDiv_Widget();
704 addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag); 719 addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag);
705 iZap(d->modeButtons); 720 iZap(d->modeButtons);
706 d->resizer = NULL; 721 d->resizer = NULL;
707 d->list = NULL; 722 d->list = NULL;
708 d->certList = NULL; 723 d->certList = NULL;
709 d->actions = NULL; 724 d->actions = NULL;
710 d->closedFolders = new_IntSet(); 725 d->closedFolders = new_IntSet();
711 /* On a phone, the right sidebar is not used. */ 726 /* On a phone, the right sidebar is not used. */
712 const iBool isPhone = deviceType_App() == phone_AppDeviceType; 727 const iBool isPhone = (deviceType_App() == phone_AppDeviceType);
713 //if (!isPhone || d->side == left_SidebarSide) { 728 if (isPhone) {
714 iWidget *buttons = new_Widget(); 729 iLabelWidget *closeButton =
730 addChildFlags_Widget(vdiv,
731 iClob(new_LabelWidget("${sidebar.close}", "sidebar.toggle")),
732 collapse_WidgetFlag | alignRight_WidgetFlag |
733 extraPadding_WidgetFlag | frameless_WidgetFlag);
734 as_Widget(closeButton)->flags2 |= slidingSheetDraggable_WidgetFlag2; /* phone */
735 setId_Widget(as_Widget(closeButton), "sidebar.close");
736 setFont_LabelWidget(closeButton, uiLabelBigBold_FontId);
737 }
738 iWidget *buttons = new_Widget();
715 setId_Widget(buttons, "buttons"); 739 setId_Widget(buttons, "buttons");
716 setDrawBufferEnabled_Widget(buttons, iTrue); 740 setDrawBufferEnabled_Widget(buttons, iTrue);
717 for (int i = 0; i < max_SidebarMode; i++) { 741 for (int i = 0; i < max_SidebarMode; i++) {
@@ -725,28 +749,14 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
725 tightModeLabels_[i], 749 tightModeLabels_[i],
726 format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))), 750 format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))),
727 frameless_WidgetFlag | noBackground_WidgetFlag); 751 frameless_WidgetFlag | noBackground_WidgetFlag);
752 as_Widget(d->modeButtons[i])->flags2 |= slidingSheetDraggable_WidgetFlag2; /* phone */
728 } 753 }
729 setButtonFont_SidebarWidget(d, isPhone ? uiLabelBig_FontId : uiLabel_FontId); 754 setButtonFont_SidebarWidget(d, isPhone ? uiLabelBig_FontId : uiLabel_FontId);
730 addChildFlags_Widget(vdiv, 755 addChildFlags_Widget(vdiv,
731 iClob(buttons), 756 iClob(buttons),
732 arrangeHorizontal_WidgetFlag | 757 arrangeHorizontal_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
733 resizeWidthOfChildren_WidgetFlag | 758 arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag);
734 arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag); // |
735// drawBackgroundToHorizontalSafeArea_WidgetFlag);
736 setBackgroundColor_Widget(buttons, uiBackgroundSidebar_ColorId); 759 setBackgroundColor_Widget(buttons, uiBackgroundSidebar_ColorId);
737// }
738#if 0
739 else {
740 iLabelWidget *heading = new_LabelWidget(person_Icon " ${sidebar.identities}", NULL);
741 checkIcon_LabelWidget(heading);
742 setBackgroundColor_Widget(as_Widget(heading), uiBackgroundSidebar_ColorId);
743 setTextColor_LabelWidget(heading, uiTextSelected_ColorId);
744 setFont_LabelWidget(addChildFlags_Widget(vdiv, iClob(heading), borderTop_WidgetFlag |
745 alignLeft_WidgetFlag | frameless_WidgetFlag |
746 drawBackgroundToHorizontalSafeArea_WidgetFlag),
747 uiLabelLargeBold_FontId);
748 }
749#endif
750 iWidget *content = new_Widget(); 760 iWidget *content = new_Widget();
751 setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue); 761 setFlags_Widget(content, resizeChildren_WidgetFlag, iTrue);
752 iWidget *listAndActions = makeVDiv_Widget(); 762 iWidget *listAndActions = makeVDiv_Widget();
@@ -756,15 +766,17 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
756 d->list = new_ListWidget(); 766 d->list = new_ListWidget();
757 setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); 767 setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI);
758 addChild_Widget(listArea, iClob(d->list)); 768 addChild_Widget(listArea, iClob(d->list));
759 d->certList = new_CertListWidget(); 769 if (!isPhone) {
760 setPadding_Widget(as_Widget(d->certList), 0, gap_UI, 0, gap_UI); 770 d->certList = new_CertListWidget();
761 addChild_Widget(listArea, iClob(d->certList)); 771 setPadding_Widget(as_Widget(d->certList), 0, gap_UI, 0, gap_UI);
772 addChild_Widget(listArea, iClob(d->certList));
773 }
762 addChildFlags_Widget(listAndActions, 774 addChildFlags_Widget(listAndActions,
763 iClob(listArea), 775 iClob(listArea),
764 expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag); 776 expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag);
765 setId_Widget(addChildPosFlags_Widget(listAndActions, 777 setId_Widget(addChildPosFlags_Widget(listAndActions,
766 iClob(d->actions = new_Widget()), 778 iClob(d->actions = new_Widget()),
767 isPhone ? front_WidgetAddPos : back_WidgetAddPos, 779 /*isPhone ? front_WidgetAddPos :*/ back_WidgetAddPos,
768 arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | 780 arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag |
769 resizeWidthOfChildren_WidgetFlag), // | 781 resizeWidthOfChildren_WidgetFlag), // |
770// drawBackgroundToHorizontalSafeArea_WidgetFlag), 782// drawBackgroundToHorizontalSafeArea_WidgetFlag),
@@ -891,7 +903,7 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) {
891// updateMetrics_SidebarWidget_(d); 903// updateMetrics_SidebarWidget_(d);
892 updateItemHeight_SidebarWidget_(d); 904 updateItemHeight_SidebarWidget_(d);
893 } 905 }
894 setButtonFont_SidebarWidget(d, isPortrait_App() ? uiLabelBig_FontId : uiLabel_FontId); 906 setButtonFont_SidebarWidget(d, isPortrait_App() ? uiLabelMedium_FontId : uiLabel_FontId);
895 } 907 }
896 const iBool isTight = 908 const iBool isTight =
897 (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth); 909 (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth);
@@ -982,6 +994,52 @@ iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *c
982 return iFalse; 994 return iFalse;
983} 995}
984 996
997static void animateSlidingSheetHeight_SidebarWidget_(iAny *sidebar) {
998 iWidget *d = sidebar;
999 const int oldSize = d->rect.size.y;
1000 const int newSize = bottom_Rect(safeRect_Root(d->root)) - top_Rect(bounds_Widget(d));
1001 if (oldSize != newSize) {
1002 d->rect.size.y = newSize;
1003 arrange_Widget(d);
1004 }
1005// printf("[%p] %u: %d animating %d\n", d, window_Widget(d)->frameTime,
1006// (flags_Widget(sidebar) & visualOffset_WidgetFlag) != 0,
1007// newSize);
1008 if (!isFinished_Anim(&d->visualOffset)) {
1009 addTicker_App(animateSlidingSheetHeight_SidebarWidget_, sidebar);
1010 }
1011}
1012
1013enum iSlidingSheetPos {
1014 top_SlidingSheetPos,
1015 middle_SlidingSheetPos,
1016 bottom_SlidingSheetPos,
1017};
1018
1019static void setSlidingSheetPos_SidebarWidget_(iSidebarWidget *d, enum iSlidingSheetPos slide) {
1020 iWidget *w = as_Widget(d);
1021 const int pos = w->rect.pos.y;
1022 const iRect safeRect = safeRect_Root(w->root);
1023 if (slide == top_SlidingSheetPos) {
1024 w->rect.pos.y = top_Rect(safeRect);
1025 w->rect.size.y = height_Rect(safeRect);
1026 setVisualOffset_Widget(w, pos - w->rect.pos.y, 0, 0);
1027 setVisualOffset_Widget(w, 0, 200, easeOut_AnimFlag | softer_AnimFlag);
1028 setScrollMode_ListWidget(d->list, disabledAtTopUpwards_ScrollMode);
1029 }
1030 else if (slide == bottom_SlidingSheetPos) {
1031 postCommand_Widget(w, "sidebar.toggle");
1032 }
1033 else {
1034 w->rect.size.y = d->midHeight;
1035 w->rect.pos.y = height_Rect(safeRect) - w->rect.size.y;
1036 setVisualOffset_Widget(w, pos - w->rect.pos.y, 0, 0);
1037 setVisualOffset_Widget(w, 0, 200, easeOut_AnimFlag | softer_AnimFlag);
1038 setScrollMode_ListWidget(d->list, disabledAtTopBothDirections_ScrollMode);
1039 }
1040 animateSlidingSheetHeight_SidebarWidget_(d);
1041}
1042
985static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *cmd) { 1043static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *cmd) {
986 iWidget *w = as_Widget(d); 1044 iWidget *w = as_Widget(d);
987 if (equal_Command(cmd, "width")) { 1045 if (equal_Command(cmd, "width")) {
@@ -1011,35 +1069,58 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *
1011 argLabel_Command(cmd, "noanim") == 0 && 1069 argLabel_Command(cmd, "noanim") == 0 &&
1012 (d->side == left_SidebarSide || deviceType_App() != phone_AppDeviceType); 1070 (d->side == left_SidebarSide || deviceType_App() != phone_AppDeviceType);
1013 int visX = 0; 1071 int visX = 0;
1072 int visY = 0;
1014 if (isVisible_Widget(w)) { 1073 if (isVisible_Widget(w)) {
1015 visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); 1074 visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect);
1075 visY = top_Rect(bounds_Widget(w)) - top_Rect(w->root->widget->rect);
1016 } 1076 }
1017 setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); 1077 const iBool isHiding = isVisible_Widget(w);
1078 setFlags_Widget(w, hidden_WidgetFlag, isHiding);
1018 /* Safe area inset for mobile. */ 1079 /* Safe area inset for mobile. */
1019 const int safePad = (d->side == left_SidebarSide ? left_Rect(safeRect_Root(w->root)) : 0); 1080 const int safePad = (d->side == left_SidebarSide ? left_Rect(safeRect_Root(w->root)) : 0);
1020 if (isVisible_Widget(w)) { 1081 const int animFlags = easeOut_AnimFlag | softer_AnimFlag;
1021 setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse); 1082 if (!isPortraitPhone_App()) {
1022 w->rect.size.x = d->widthAsGaps * gap_UI; 1083 if (!isHiding) {
1023 invalidate_ListWidget(d->list); 1084 setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse);
1024 if (isAnimated) { 1085 w->rect.size.x = d->widthAsGaps * gap_UI;
1086 invalidate_ListWidget(d->list);
1087 if (isAnimated) {
1088 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue);
1089 setVisualOffset_Widget(
1090 w, (d->side == left_SidebarSide ? -1 : 1) * (w->rect.size.x + safePad), 0, 0);
1091 setVisualOffset_Widget(w, 0, 300, animFlags);
1092 }
1093 }
1094 else if (isAnimated) {
1025 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); 1095 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue);
1026 setVisualOffset_Widget( 1096 if (d->side == right_SidebarSide) {
1027 w, (d->side == left_SidebarSide ? -1 : 1) * (w->rect.size.x + safePad), 0, 0); 1097 setVisualOffset_Widget(w, visX, 0, 0);
1028 setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag); 1098 setVisualOffset_Widget(
1099 w, visX + w->rect.size.x + safePad, 300, animFlags);
1100 }
1101 else {
1102 setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue);
1103 setVisualOffset_Widget(
1104 w, -w->rect.size.x - safePad, 300, animFlags);
1105 }
1029 } 1106 }
1107 setScrollMode_ListWidget(d->list, normal_ScrollMode);
1030 } 1108 }
1031 else if (isAnimated) { 1109 else {
1032 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); 1110 /* Portrait phone sidebar works differently: it slides up from the bottom. */
1033 if (d->side == right_SidebarSide) { 1111 setFlags_Widget(w, horizontalOffset_WidgetFlag, iFalse);
1034 setVisualOffset_Widget(w, visX, 0, 0); 1112 if (!isHiding) {
1035 setVisualOffset_Widget( 1113 invalidate_ListWidget(d->list);
1036 w, visX + w->rect.size.x + safePad, 300, easeOut_AnimFlag | softer_AnimFlag); 1114 w->rect.pos.y = height_Rect(safeRect_Root(w->root)) - d->midHeight;
1115 setVisualOffset_Widget(w, bottom_Rect(rect_Root(w->root)) - w->rect.pos.y, 0, 0);
1116 setVisualOffset_Widget(w, 0, 300, animFlags);
1117 animateSlidingSheetHeight_SidebarWidget_(d);
1118 setScrollMode_ListWidget(d->list, disabledAtTopBothDirections_ScrollMode);
1037 } 1119 }
1038 else { 1120 else {
1039 setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); 1121 setVisualOffset_Widget(w, bottom_Rect(rect_Root(w->root)) - w->rect.pos.y, 300, animFlags);
1040 setVisualOffset_Widget(
1041 w, -w->rect.size.x - safePad, 300, easeOut_AnimFlag | softer_AnimFlag);
1042 } 1122 }
1123 showToolbar_Root(w->root, isHiding);
1043 } 1124 }
1044 updateToolbarColors_Root(w->root); 1125 updateToolbarColors_Root(w->root);
1045 arrange_Widget(w->parent); 1126 arrange_Widget(w->parent);
@@ -1097,13 +1178,32 @@ static size_t numBookmarks_(const iPtrArray *bmList) {
1097 return num; 1178 return num;
1098} 1179}
1099 1180
1181static iRangei SlidingSheetMiddleRegion_SidebarWidget_(const iSidebarWidget *d) {
1182 const iWidget *w = constAs_Widget(d);
1183 const iRect safeRect = safeRect_Root(w->root);
1184 const int midY = bottom_Rect(safeRect) - d->midHeight;
1185 const int topHalf = (top_Rect(safeRect) + midY) / 2;
1186 const int bottomHalf = (bottom_Rect(safeRect) + midY * 2) / 3;
1187 return (iRangei){ topHalf, bottomHalf };
1188}
1189
1190static void gotoNearestSlidingSheetPos_SidebarWidget_(iSidebarWidget *d) {
1191 const iRangei midRegion = SlidingSheetMiddleRegion_SidebarWidget_(d);
1192 const int pos = top_Rect(d->widget.rect);
1193 setSlidingSheetPos_SidebarWidget_(d, pos < midRegion.start
1194 ? top_SlidingSheetPos
1195 : pos > midRegion.end ? bottom_SlidingSheetPos
1196 : middle_SlidingSheetPos);
1197}
1198
1100static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { 1199static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) {
1101 iWidget *w = as_Widget(d); 1200 iWidget *w = as_Widget(d);
1102 /* Handle commands. */ 1201 /* Handle commands. */
1103 if (isResize_UserEvent(ev)) { 1202 if (isResize_UserEvent(ev)) {
1104 checkModeButtonLayout_SidebarWidget_(d); 1203 checkModeButtonLayout_SidebarWidget_(d);
1105 if (deviceType_App() == phone_AppDeviceType && d->side == left_SidebarSide) { 1204 if (deviceType_App() == phone_AppDeviceType) { // && d->side == left_SidebarSide) {
1106 setFlags_Widget(w, rightEdgeDraggable_WidgetFlag, isPortrait_App()); 1205// setFlags_Widget(w, rightEdgeDraggable_WidgetFlag, isPortrait_App());
1206 setFlags_Widget(findChild_Widget(w, "sidebar.close"), hidden_WidgetFlag, isLandscape_App());
1107 /* In landscape, visibility of the toolbar is controlled separately. */ 1207 /* In landscape, visibility of the toolbar is controlled separately. */
1108 if (isVisible_Widget(w)) { 1208 if (isVisible_Widget(w)) {
1109 postCommand_Widget(w, "sidebar.toggle"); 1209 postCommand_Widget(w, "sidebar.toggle");
@@ -1117,6 +1217,10 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1117 setFlags_Widget(as_Widget(d->list), 1217 setFlags_Widget(as_Widget(d->list),
1118 drawBackgroundToHorizontalSafeArea_WidgetFlag, 1218 drawBackgroundToHorizontalSafeArea_WidgetFlag,
1119 isLandscape_App()); 1219 isLandscape_App());
1220 setFlags_Widget(w,
1221 drawBackgroundToBottom_WidgetFlag,
1222 isPortrait_App());
1223 setBackgroundColor_Widget(w, isPortrait_App() ? uiBackgroundSidebar_ColorId : none_ColorId);
1120 return iFalse; 1224 return iFalse;
1121 } 1225 }
1122 } 1226 }
@@ -1572,6 +1676,61 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1572 return iTrue; 1676 return iTrue;
1573 } 1677 }
1574 } 1678 }
1679 if (isSlidingSheet_SidebarWidget_(d)) {
1680 if (ev->type == SDL_MOUSEWHEEL) {
1681 enum iWidgetTouchMode touchMode = widgetMode_Touch(w);
1682 if (touchMode == momentum_WidgetTouchMode) {
1683 /* We don't do momentum. */
1684 float swipe = stopWidgetMomentum_Touch(w);
1685// printf("swipe: %f\n", swipe);
1686 const iRangei midRegion = SlidingSheetMiddleRegion_SidebarWidget_(d);
1687 const int pos = top_Rect(w->rect);
1688 if (swipe < 500) {
1689 gotoNearestSlidingSheetPos_SidebarWidget_(d);
1690 }
1691 else if (swipe > 6500 && ev->wheel.y > 0) {
1692 /* Fast swipe down will dismiss. */
1693 setSlidingSheetPos_SidebarWidget_(d, bottom_SlidingSheetPos);
1694 }
1695 else if (ev->wheel.y < 0) {
1696 setSlidingSheetPos_SidebarWidget_(d, top_SlidingSheetPos);
1697 }
1698 else if (pos < (midRegion.start + midRegion.end) / 2) {
1699 setSlidingSheetPos_SidebarWidget_(d, middle_SlidingSheetPos);
1700 }
1701 else {
1702 setSlidingSheetPos_SidebarWidget_(d, bottom_SlidingSheetPos);
1703 }
1704 }
1705 else if (touchMode == touch_WidgetTouchMode) {
1706 /* Move with the finger. */
1707 adjustEdges_Rect(&w->rect, ev->wheel.y, 0, 0, 0);
1708 /* Upon reaching the top, scrolling is switched back to the list. */
1709 const iRect rootRect = safeRect_Root(w->root);
1710 const int top = top_Rect(rootRect);
1711 if (w->rect.pos.y < top) {
1712 setScrollMode_ListWidget(d->list, disabledAtTopUpwards_ScrollMode);
1713 setScrollPos_ListWidget(d->list, top - w->rect.pos.y);
1714 transferAffinity_Touch(w, as_Widget(d->list));
1715 w->rect.pos.y = top;
1716 w->rect.size.y = height_Rect(rootRect);
1717 }
1718 else {
1719 setScrollMode_ListWidget(d->list, disabledAtTopBothDirections_ScrollMode);
1720 }
1721 arrange_Widget(w);
1722 refresh_Widget(w);
1723 }
1724 else {
1725 return iFalse;
1726 }
1727 return iTrue;
1728 }
1729 if (ev->type == SDL_USEREVENT && ev->user.code == widgetTouchEnds_UserEventCode) {
1730 gotoNearestSlidingSheetPos_SidebarWidget_(d);
1731 return iTrue;
1732 }
1733 }
1575 if (ev->type == SDL_MOUSEBUTTONDOWN && 1734 if (ev->type == SDL_MOUSEBUTTONDOWN &&
1576 contains_Widget(as_Widget(d->list), init_I2(ev->button.x, ev->button.y))) { 1735 contains_Widget(as_Widget(d->list), init_I2(ev->button.x, ev->button.y))) {
1577 if (hoverItem_ListWidget(d->list) || isVisible_Widget(d->menu)) { 1736 if (hoverItem_ListWidget(d->list) || isVisible_Widget(d->menu)) {
diff --git a/src/ui/sidebarwidget.h b/src/ui/sidebarwidget.h
index 81c6681f..2a930a60 100644
--- a/src/ui/sidebarwidget.h
+++ b/src/ui/sidebarwidget.h
@@ -54,6 +54,7 @@ iBool setMode_SidebarWidget (iSidebarWidget *, enum iSidebar
54void setWidth_SidebarWidget (iSidebarWidget *, float widthAsGaps); 54void setWidth_SidebarWidget (iSidebarWidget *, float widthAsGaps);
55iBool setButtonFont_SidebarWidget (iSidebarWidget *, int font); 55iBool setButtonFont_SidebarWidget (iSidebarWidget *, int font);
56void setClosedFolders_SidebarWidget (iSidebarWidget *, const iIntSet *closedFolders); 56void setClosedFolders_SidebarWidget (iSidebarWidget *, const iIntSet *closedFolders);
57void setMidHeight_SidebarWidget (iSidebarWidget *, int midHeight); /* phone layout */
57 58
58enum iSidebarMode mode_SidebarWidget (const iSidebarWidget *); 59enum iSidebarMode mode_SidebarWidget (const iSidebarWidget *);
59enum iFeedsMode feedsMode_SidebarWidget (const iSidebarWidget *); 60enum iFeedsMode feedsMode_SidebarWidget (const iSidebarWidget *);
diff --git a/src/ui/touch.c b/src/ui/touch.c
index 195d1dff..aee5a383 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -589,9 +589,18 @@ iBool processEvent_Touch(const SDL_Event *ev) {
589 divvf_F3(&touch->accum, 6); 589 divvf_F3(&touch->accum, 6);
590 divfv_I2(&pixels, 6); 590 divfv_I2(&pixels, 6);
591 /* Allow scrolling a scrollable widget. */ 591 /* Allow scrolling a scrollable widget. */
592 iWidget *flow = findOverflowScrollable_Widget(touch->affinity); 592 if (touch->affinity && touch->affinity->flags2 & slidingSheetDraggable_WidgetFlag2) {
593 if (flow) { 593 extern iWidgetClass Class_SidebarWidget; /* The only type of sliding sheet for now. */
594 touch->affinity = flow; 594 iWidget *slider = findParentClass_Widget(touch->affinity, &Class_SidebarWidget);
595 if (slider) {
596 touch->affinity = slider;
597 }
598 }
599 else {
600 iWidget *flow = findOverflowScrollable_Widget(touch->affinity);
601 if (flow) {
602 touch->affinity = flow;
603 }
595 } 604 }
596 } 605 }
597 else { 606 else {
@@ -617,11 +626,13 @@ iBool processEvent_Touch(const SDL_Event *ev) {
617 if (touch->axis == y_TouchAxis) { 626 if (touch->axis == y_TouchAxis) {
618 pixels.x = 0; 627 pixels.x = 0;
619 } 628 }
620// printf("%p (%s) py: %i wy: %f acc: %f edge: %d\n", 629#if 0
621// touch->affinity, 630 printf("%p (%s) py: %i wy: %f acc: %f edge: %d\n",
622// class_Widget(touch->affinity)->name, 631 touch->affinity,
623// pixels.y, y_F3(amount), y_F3(touch->accum), 632 class_Widget(touch->affinity)->name,
624// touch->edge); 633 pixels.y, y_F3(amount), y_F3(touch->accum),
634 touch->edge);
635#endif
625 if (pixels.x || pixels.y) { 636 if (pixels.x || pixels.y) {
626 //setFocus_Widget(NULL); 637 //setFocus_Widget(NULL);
627 dispatchMotion_Touch_(touch->startPos /*pos[0]*/, 0); 638 dispatchMotion_Touch_(touch->startPos /*pos[0]*/, 0);
@@ -801,6 +812,16 @@ void widgetDestroyed_Touch(iWidget *widget) {
801 } 812 }
802} 813}
803 814
815void transferAffinity_Touch(iWidget *src, iWidget *dst) {
816 iTouchState *d = touchState_();
817 iForEach(Array, i, d->touches) {
818 iTouch *touch = i.value;
819 if (touch->affinity == src) {
820 touch->affinity = dst;
821 }
822 }
823}
824
804iInt2 latestPosition_Touch(void) { 825iInt2 latestPosition_Touch(void) {
805 return touchState_()->currentTouchPos; 826 return touchState_()->currentTouchPos;
806} 827}
diff --git a/src/ui/touch.h b/src/ui/touch.h
index e048224a..c9c76d86 100644
--- a/src/ui/touch.h
+++ b/src/ui/touch.h
@@ -39,6 +39,7 @@ void update_Touch (void);
39float stopWidgetMomentum_Touch (const iWidget *widget); 39float stopWidgetMomentum_Touch (const iWidget *widget);
40enum iWidgetTouchMode widgetMode_Touch (const iWidget *widget); 40enum iWidgetTouchMode widgetMode_Touch (const iWidget *widget);
41void widgetDestroyed_Touch (iWidget *widget); 41void widgetDestroyed_Touch (iWidget *widget);
42void transferAffinity_Touch (iWidget *src, iWidget *dst);
42 43
43iInt2 latestPosition_Touch (void); /* valid during processing of current event */ 44iInt2 latestPosition_Touch (void); /* valid during processing of current event */
44iBool isHovering_Touch (void); /* stationary touch or a long-press drag ongoing */ 45iBool isHovering_Touch (void); /* stationary touch or a long-press drag ongoing */
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 210fe899..0d20cca9 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -124,6 +124,7 @@ void init_Widget(iWidget *d) {
124 init_String(&d->id); 124 init_String(&d->id);
125 d->root = get_Root(); /* never changes after this */ 125 d->root = get_Root(); /* never changes after this */
126 d->flags = 0; 126 d->flags = 0;
127 d->flags2 = 0;
127 d->rect = zero_Rect(); 128 d->rect = zero_Rect();
128 d->minSize = zero_I2(); 129 d->minSize = zero_I2();
129 d->sizeRef = NULL; 130 d->sizeRef = NULL;
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 4025f5c5..35be1bcb 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -123,6 +123,10 @@ enum iWidgetFlag {
123#define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */ 123#define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */
124#define nativeMenu_WidgetFlag iBit64(64) 124#define nativeMenu_WidgetFlag iBit64(64)
125 125
126enum iWidgetFlag2 {
127 slidingSheetDraggable_WidgetFlag2 = iBit(1),
128};
129
126enum iWidgetAddPos { 130enum iWidgetAddPos {
127 back_WidgetAddPos, 131 back_WidgetAddPos,
128 front_WidgetAddPos, 132 front_WidgetAddPos,
@@ -139,6 +143,7 @@ struct Impl_Widget {
139 iObject object; 143 iObject object;
140 iString id; 144 iString id;
141 int64_t flags; 145 int64_t flags;
146 int flags2;
142 iRect rect; 147 iRect rect;
143 iInt2 minSize; 148 iInt2 minSize;
144 iWidget * sizeRef; 149 iWidget * sizeRef;