diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-04 12:23:59 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-04 12:23:59 +0200 |
commit | d3fd40c8ef38ba5eeaeaa474166a66ca36f2edf7 (patch) | |
tree | 6d2baeb7562d2a2baac3b45821fcd9799fee6c44 | |
parent | 8d255fca904c55fb8c15631c6845c8a87f1a0c6a (diff) |
SidebarWidget: Mobile bookmark editing mode
ListWidget can use drag handles on items.
-rw-r--r-- | po/en.po | 6 | ||||
-rw-r--r-- | src/ui/listwidget.c | 29 | ||||
-rw-r--r-- | src/ui/listwidget.h | 2 | ||||
-rw-r--r-- | src/ui/mobile.c | 3 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 50 |
5 files changed, 83 insertions, 7 deletions
@@ -419,6 +419,12 @@ msgstr "Identities" | |||
419 | msgid "sidebar.outline" | 419 | msgid "sidebar.outline" |
420 | msgstr "Outline" | 420 | msgstr "Outline" |
421 | 421 | ||
422 | msgid "sidebar.action.bookmarks.newfolder" | ||
423 | msgstr "New Folder" | ||
424 | |||
425 | msgid "sidebar.action.bookmarks.edit" | ||
426 | msgstr "Edit" | ||
427 | |||
422 | # This label should be fairly short so it fits in a button in the sidebar. | 428 | # This label should be fairly short so it fits in a button in the sidebar. |
423 | msgid "sidebar.action.feeds.markallread" | 429 | msgid "sidebar.action.feeds.markallread" |
424 | msgstr "Read All" | 430 | msgstr "Read All" |
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index d12afc4c..ccc5b104 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c | |||
@@ -87,6 +87,7 @@ void init_ListWidget(iListWidget *d) { | |||
87 | d->hoverItem = iInvalidPos; | 87 | d->hoverItem = iInvalidPos; |
88 | d->dragItem = iInvalidPos; | 88 | d->dragItem = iInvalidPos; |
89 | d->dragOrigin = zero_I2(); | 89 | d->dragOrigin = zero_I2(); |
90 | d->dragHandleWidth = 0; | ||
90 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 91 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
91 | init_IntSet(&d->invalidItems); | 92 | init_IntSet(&d->invalidItems); |
92 | d->visBuf = new_VisBuf(); | 93 | d->visBuf = new_VisBuf(); |
@@ -192,6 +193,13 @@ void setScrollMode_ListWidget(iListWidget *d, enum iScrollMode mode) { | |||
192 | d->scrollMode = mode; | 193 | d->scrollMode = mode; |
193 | } | 194 | } |
194 | 195 | ||
196 | void setDragHandleWidth_ListWidget(iListWidget *d, int dragHandleWidth) { | ||
197 | d->dragHandleWidth = dragHandleWidth; | ||
198 | if (dragHandleWidth == 0) { | ||
199 | setFlags_Widget(as_Widget(d), touchDrag_WidgetFlag, iFalse); /* mobile drag handles */ | ||
200 | } | ||
201 | } | ||
202 | |||
195 | void scrollOffset_ListWidget(iListWidget *d, int offset) { | 203 | void scrollOffset_ListWidget(iListWidget *d, int offset) { |
196 | moveSpan_SmoothScroll(&d->scrollY, offset, 0); | 204 | moveSpan_SmoothScroll(&d->scrollY, offset, 0); |
197 | } | 205 | } |
@@ -353,6 +361,7 @@ static iBool endDrag_ListWidget_(iListWidget *d, iInt2 endPos) { | |||
353 | if (d->dragItem == iInvalidPos) { | 361 | if (d->dragItem == iInvalidPos) { |
354 | return iFalse; | 362 | return iFalse; |
355 | } | 363 | } |
364 | setFlags_Widget(as_Widget(d), touchDrag_WidgetFlag, iFalse); /* mobile drag handles */ | ||
356 | stop_Anim(&d->scrollY.pos); | 365 | stop_Anim(&d->scrollY.pos); |
357 | enum iDragDestination dstKind; | 366 | enum iDragDestination dstKind; |
358 | const size_t index = resolveDragDestination_ListWidget_(d, endPos, &dstKind); | 367 | const size_t index = resolveDragDestination_ListWidget_(d, endPos, &dstKind); |
@@ -441,6 +450,17 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
441 | } | 450 | } |
442 | } | 451 | } |
443 | if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { | 452 | if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { |
453 | if (d->dragHandleWidth) { | ||
454 | if (d->dragItem == iInvalidPos) { | ||
455 | const iInt2 wpos = coord_MouseWheelEvent(&ev->wheel); | ||
456 | if (contains_Widget(w, wpos) && | ||
457 | wpos.x >= right_Rect(boundsWithoutVisualOffset_Widget(w)) - d->dragHandleWidth) { | ||
458 | setFlags_Widget(w, touchDrag_WidgetFlag, iTrue); | ||
459 | printf("[%p] touch drag started\n", d); | ||
460 | return iTrue; | ||
461 | } | ||
462 | } | ||
463 | } | ||
444 | if (isScrollDisabled_ListWidget_(d, ev)) { | 464 | if (isScrollDisabled_ListWidget_(d, ev)) { |
445 | if (ev->wheel.which == SDL_TOUCH_MOUSEID) { | 465 | if (ev->wheel.which == SDL_TOUCH_MOUSEID) { |
446 | /* TODO: Could generalize this selection of the scrollable parent. */ | 466 | /* TODO: Could generalize this selection of the scrollable parent. */ |
@@ -498,7 +518,9 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
498 | return iTrue; | 518 | return iTrue; |
499 | } | 519 | } |
500 | redrawHoverItem_ListWidget_(d); | 520 | redrawHoverItem_ListWidget_(d); |
501 | if (contains_Rect(itemRect_ListWidget(d, d->hoverItem), pos_Click(&d->click)) && | 521 | if (contains_Rect(adjusted_Rect(itemRect_ListWidget(d, d->hoverItem), |
522 | zero_I2(), init_I2(-d->dragHandleWidth, 0)), | ||
523 | pos_Click(&d->click)) && | ||
502 | d->hoverItem != iInvalidPos) { | 524 | d->hoverItem != iInvalidPos) { |
503 | postCommand_Widget(w, "list.clicked arg:%zu item:%p", | 525 | postCommand_Widget(w, "list.clicked arg:%zu item:%p", |
504 | d->hoverItem, constHoverItem_ListWidget(d)); | 526 | d->hoverItem, constHoverItem_ListWidget(d)); |
@@ -598,8 +620,9 @@ static void draw_ListWidget_(const iListWidget *d) { | |||
598 | } | 620 | } |
599 | setClip_Paint(&p, bounds_Widget(w)); | 621 | setClip_Paint(&p, bounds_Widget(w)); |
600 | draw_VisBuf(d->visBuf, addY_I2(topLeft_Rect(bounds), -scrollY), ySpan_Rect(bounds)); | 622 | draw_VisBuf(d->visBuf, addY_I2(topLeft_Rect(bounds), -scrollY), ySpan_Rect(bounds)); |
601 | const iInt2 mousePos = mouseCoord_Window(get_Window(), 0); | 623 | const iBool isMobile = (deviceType_App() != desktop_AppDeviceType); |
602 | if (d->dragItem != iInvalidPos && contains_Rect(bounds, mousePos)) { | 624 | const iInt2 mousePos = mouseCoord_Window(get_Window(), isMobile ? SDL_TOUCH_MOUSEID : 0); |
625 | if (d->dragItem != iInvalidPos && (isMobile || contains_Rect(bounds, mousePos))) { | ||
603 | iInt2 pos = add_I2(mousePos, d->dragOrigin); | 626 | iInt2 pos = add_I2(mousePos, d->dragOrigin); |
604 | const iListItem *item = constAt_PtrArray(&d->items, d->dragItem); | 627 | const iListItem *item = constAt_PtrArray(&d->items, d->dragItem); |
605 | const iRect itemRect = { init_I2(left_Rect(bounds), pos.y), | 628 | const iRect itemRect = { init_I2(left_Rect(bounds), pos.y), |
diff --git a/src/ui/listwidget.h b/src/ui/listwidget.h index 081109e8..c5d412dd 100644 --- a/src/ui/listwidget.h +++ b/src/ui/listwidget.h | |||
@@ -66,6 +66,7 @@ struct Impl_ListWidget { | |||
66 | size_t hoverItem; | 66 | size_t hoverItem; |
67 | size_t dragItem; | 67 | size_t dragItem; |
68 | iInt2 dragOrigin; /* offset from mouse to drag item's top-left corner */ | 68 | iInt2 dragOrigin; /* offset from mouse to drag item's top-left corner */ |
69 | int dragHandleWidth; | ||
69 | iClick click; | 70 | iClick click; |
70 | iIntSet invalidItems; | 71 | iIntSet invalidItems; |
71 | iVisBuf *visBuf; | 72 | iVisBuf *visBuf; |
@@ -90,6 +91,7 @@ int scrollPos_ListWidget (const iListWidget *); | |||
90 | 91 | ||
91 | void setScrollPos_ListWidget (iListWidget *, int pos); | 92 | void setScrollPos_ListWidget (iListWidget *, int pos); |
92 | void setScrollMode_ListWidget (iListWidget *, enum iScrollMode mode); | 93 | void setScrollMode_ListWidget (iListWidget *, enum iScrollMode mode); |
94 | void setDragHandleWidth_ListWidget(iListWidget *, int dragHandleWidth); | ||
93 | void scrollToItem_ListWidget (iListWidget *, size_t index, uint32_t span); | 95 | void scrollToItem_ListWidget (iListWidget *, size_t index, uint32_t span); |
94 | void scrollOffset_ListWidget (iListWidget *, int offset); | 96 | void scrollOffset_ListWidget (iListWidget *, int offset); |
95 | void scrollOffsetSpan_ListWidget (iListWidget *, int offset, uint32_t span); | 97 | void scrollOffsetSpan_ListWidget (iListWidget *, int offset, uint32_t span); |
diff --git a/src/ui/mobile.c b/src/ui/mobile.c index ab282a86..633b33b8 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c | |||
@@ -243,6 +243,9 @@ static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) { | |||
243 | else if (findWidget_App("upload")) { | 243 | else if (findWidget_App("upload")) { |
244 | postCommand_App("upload.cancel"); | 244 | postCommand_App("upload.cancel"); |
245 | } | 245 | } |
246 | else if (findWidget_App("bmed.sidebar")) { | ||
247 | postCommand_App("bmed.cancel"); | ||
248 | } | ||
246 | else if (findWidget_App("ident")) { | 249 | else if (findWidget_App("ident")) { |
247 | postCommand_Widget(topPanel, "ident.cancel"); | 250 | postCommand_Widget(topPanel, "ident.cancel"); |
248 | } | 251 | } |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 48802ddf..b7e3b19a 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -117,7 +117,7 @@ struct Impl_SidebarWidget { | |||
117 | iCertListWidget * certList; | 117 | iCertListWidget * certList; |
118 | iWidget * actions; /* below the list, area for buttons */ | 118 | iWidget * actions; /* below the list, area for buttons */ |
119 | int midHeight; /* on portrait phone, the height for the middle state */ | 119 | int midHeight; /* on portrait phone, the height for the middle state */ |
120 | iBool isBeingDraggedVertically; /* on portrait phone, sidebar can be dragged up/down */ | 120 | iBool isEditing; /* mobile edit mode */ |
121 | int modeScroll[max_SidebarMode]; | 121 | int modeScroll[max_SidebarMode]; |
122 | iLabelWidget * modeButtons[max_SidebarMode]; | 122 | iLabelWidget * modeButtons[max_SidebarMode]; |
123 | int maxButtonLabelWidth; | 123 | int maxButtonLabelWidth; |
@@ -235,7 +235,18 @@ static iBool isBookmarkFolded_SidebarWidget_(const iSidebarWidget *d, const iBoo | |||
235 | } | 235 | } |
236 | 236 | ||
237 | static iBool isSlidingSheet_SidebarWidget_(const iSidebarWidget *d) { | 237 | static iBool isSlidingSheet_SidebarWidget_(const iSidebarWidget *d) { |
238 | return isPortraitPhone_App();// && scrollPos_ListWidget(d->list) <= 0; | 238 | return isPortraitPhone_App(); |
239 | } | ||
240 | |||
241 | static void setMobileEditMode_SidebarWidget_(iSidebarWidget *d, iBool editing) { | ||
242 | iWidget *w = as_Widget(d); | ||
243 | d->isEditing = editing; | ||
244 | setFlags_Widget(findChild_Widget(w, "sidebar.close"), hidden_WidgetFlag, editing); | ||
245 | setFlags_Widget(child_Widget(d->actions, 0), hidden_WidgetFlag, !editing); | ||
246 | setTextCStr_LabelWidget(child_Widget(as_Widget(d->actions), 2), | ||
247 | editing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}"); | ||
248 | setDragHandleWidth_ListWidget(d->list, editing ? itemHeight_ListWidget(d->list) * 3 / 2 : 0); | ||
249 | arrange_Widget(d->actions); | ||
239 | } | 250 | } |
240 | 251 | ||
241 | static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) { | 252 | static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) { |
@@ -482,6 +493,14 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
482 | { "---", 0, 0, NULL }, | 493 | { "---", 0, 0, NULL }, |
483 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } }, | 494 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } }, |
484 | 6); | 495 | 6); |
496 | if (isMobile) { | ||
497 | addActionButton_SidebarWidget_(d, "${sidebar.action.bookmarks.newfolder}", | ||
498 | "bookmarks.addfolder", !d->isEditing ? hidden_WidgetFlag : 0); | ||
499 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); | ||
500 | iLabelWidget *btn = addActionButton_SidebarWidget_(d, | ||
501 | d->isEditing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}", | ||
502 | "sidebar.bookmarks.edit", 0); | ||
503 | } | ||
485 | break; | 504 | break; |
486 | } | 505 | } |
487 | case history_SidebarMode: { | 506 | case history_SidebarMode: { |
@@ -545,7 +564,6 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
545 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); | 564 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); |
546 | iLabelWidget *btn = addActionButton_SidebarWidget_(d, "${sidebar.action.history.clear}", | 565 | iLabelWidget *btn = addActionButton_SidebarWidget_(d, "${sidebar.action.history.clear}", |
547 | "history.clear confirm:1", 0); | 566 | "history.clear confirm:1", 0); |
548 | setFont_LabelWidget(btn, uiLabelBigBold_FontId); | ||
549 | } | 567 | } |
550 | break; | 568 | break; |
551 | } | 569 | } |
@@ -740,7 +758,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
740 | d->mode = -1; | 758 | d->mode = -1; |
741 | d->feedsMode = all_FeedsMode; | 759 | d->feedsMode = all_FeedsMode; |
742 | d->midHeight = 0; | 760 | d->midHeight = 0; |
743 | d->isBeingDraggedVertically = iFalse; | 761 | d->isEditing = iFalse; |
744 | d->numUnreadEntries = 0; | 762 | d->numUnreadEntries = 0; |
745 | d->buttonFont = uiLabel_FontId; /* wiil be changed later */ | 763 | d->buttonFont = uiLabel_FontId; /* wiil be changed later */ |
746 | d->itemFonts[0] = uiContent_FontId; | 764 | d->itemFonts[0] = uiContent_FontId; |
@@ -918,6 +936,12 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
918 | break; | 936 | break; |
919 | } | 937 | } |
920 | case bookmarks_SidebarMode: | 938 | case bookmarks_SidebarMode: |
939 | if (d->isEditing) { | ||
940 | d->contextItem = item; | ||
941 | d->contextIndex = itemIndex; | ||
942 | postCommand_Widget(d, "bookmark.edit"); | ||
943 | break; | ||
944 | } | ||
921 | if (isEmpty_String(&item->url)) /* a folder */ { | 945 | if (isEmpty_String(&item->url)) /* a folder */ { |
922 | if (contains_IntSet(d->closedFolders, item->id)) { | 946 | if (contains_IntSet(d->closedFolders, item->id)) { |
923 | remove_IntSet(d->closedFolders, item->id); | 947 | remove_IntSet(d->closedFolders, item->id); |
@@ -1174,6 +1198,9 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * | |||
1174 | } | 1198 | } |
1175 | else { | 1199 | else { |
1176 | setVisualOffset_Widget(w, bottom_Rect(rect_Root(w->root)) - w->rect.pos.y, 300, animFlags); | 1200 | setVisualOffset_Widget(w, bottom_Rect(rect_Root(w->root)) - w->rect.pos.y, 300, animFlags); |
1201 | if (d->isEditing) { | ||
1202 | setMobileEditMode_SidebarWidget_(d, iFalse); | ||
1203 | } | ||
1177 | } | 1204 | } |
1178 | showToolbar_Root(w->root, isHiding); | 1205 | showToolbar_Root(w->root, isHiding); |
1179 | } | 1206 | } |
@@ -1189,6 +1216,10 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * | |||
1189 | refresh_Widget(w->parent); | 1216 | refresh_Widget(w->parent); |
1190 | return iTrue; | 1217 | return iTrue; |
1191 | } | 1218 | } |
1219 | else if (equal_Command(cmd, "bookmarks.edit")) { | ||
1220 | setMobileEditMode_SidebarWidget_(d, !d->isEditing); | ||
1221 | invalidate_ListWidget(d->list); | ||
1222 | } | ||
1192 | return iFalse; | 1223 | return iFalse; |
1193 | } | 1224 | } |
1194 | 1225 | ||
@@ -1853,6 +1884,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
1853 | &Class_SidebarWidget); | 1884 | &Class_SidebarWidget); |
1854 | const iBool isMenuVisible = isVisible_Widget(sidebar->menu); | 1885 | const iBool isMenuVisible = isVisible_Widget(sidebar->menu); |
1855 | const iBool isDragging = constDragItem_ListWidget(list) == d; | 1886 | const iBool isDragging = constDragItem_ListWidget(list) == d; |
1887 | const iBool isEditing = sidebar->isEditing; /* only on mobile */ | ||
1856 | const iBool isPressing = isMouseDown_ListWidget(list) && !isDragging; | 1888 | const iBool isPressing = isMouseDown_ListWidget(list) && !isDragging; |
1857 | const iBool isHover = | 1889 | const iBool isHover = |
1858 | (!isMenuVisible && | 1890 | (!isMenuVisible && |
@@ -2006,6 +2038,16 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2006 | drawRange_Text(font, textPos, fg, range_String(&d->label)); | 2038 | drawRange_Text(font, textPos, fg, range_String(&d->label)); |
2007 | const int metaFont = uiLabel_FontId; | 2039 | const int metaFont = uiLabel_FontId; |
2008 | const int metaIconWidth = 4.5f * gap_UI; | 2040 | const int metaIconWidth = 4.5f * gap_UI; |
2041 | if (isEditing) { | ||
2042 | iRect dragRect = { | ||
2043 | addX_I2(topRight_Rect(itemRect), -itemHeight * 3 / 2), | ||
2044 | init_I2(itemHeight * 3 / 2, itemHeight) | ||
2045 | }; | ||
2046 | fillRect_Paint(p, dragRect, bg); | ||
2047 | drawVLine_Paint(p, topLeft_Rect(dragRect), height_Rect(dragRect), uiSeparator_ColorId); | ||
2048 | drawCentered_Text(uiContent_FontId, dragRect, iTrue, uiAnnotation_ColorId, menu_Icon); | ||
2049 | adjustEdges_Rect(&itemRect, 0, -width_Rect(dragRect), 0, 0); | ||
2050 | } | ||
2009 | const iInt2 metaPos = | 2051 | const iInt2 metaPos = |
2010 | init_I2(right_Rect(itemRect) - | 2052 | init_I2(right_Rect(itemRect) - |
2011 | length_String(&d->meta) * | 2053 | length_String(&d->meta) * |