summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-04 12:23:59 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-04 12:23:59 +0200
commitd3fd40c8ef38ba5eeaeaa474166a66ca36f2edf7 (patch)
tree6d2baeb7562d2a2baac3b45821fcd9799fee6c44
parent8d255fca904c55fb8c15631c6845c8a87f1a0c6a (diff)
SidebarWidget: Mobile bookmark editing mode
ListWidget can use drag handles on items.
-rw-r--r--po/en.po6
-rw-r--r--src/ui/listwidget.c29
-rw-r--r--src/ui/listwidget.h2
-rw-r--r--src/ui/mobile.c3
-rw-r--r--src/ui/sidebarwidget.c50
5 files changed, 83 insertions, 7 deletions
diff --git a/po/en.po b/po/en.po
index 08683659..0ac1dd2e 100644
--- a/po/en.po
+++ b/po/en.po
@@ -419,6 +419,12 @@ msgstr "Identities"
419msgid "sidebar.outline" 419msgid "sidebar.outline"
420msgstr "Outline" 420msgstr "Outline"
421 421
422msgid "sidebar.action.bookmarks.newfolder"
423msgstr "New Folder"
424
425msgid "sidebar.action.bookmarks.edit"
426msgstr "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.
423msgid "sidebar.action.feeds.markallread" 429msgid "sidebar.action.feeds.markallread"
424msgstr "Read All" 430msgstr "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
196void 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
195void scrollOffset_ListWidget(iListWidget *d, int offset) { 203void 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
91void setScrollPos_ListWidget (iListWidget *, int pos); 92void setScrollPos_ListWidget (iListWidget *, int pos);
92void setScrollMode_ListWidget (iListWidget *, enum iScrollMode mode); 93void setScrollMode_ListWidget (iListWidget *, enum iScrollMode mode);
94void setDragHandleWidth_ListWidget(iListWidget *, int dragHandleWidth);
93void scrollToItem_ListWidget (iListWidget *, size_t index, uint32_t span); 95void scrollToItem_ListWidget (iListWidget *, size_t index, uint32_t span);
94void scrollOffset_ListWidget (iListWidget *, int offset); 96void scrollOffset_ListWidget (iListWidget *, int offset);
95void scrollOffsetSpan_ListWidget (iListWidget *, int offset, uint32_t span); 97void 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
237static iBool isSlidingSheet_SidebarWidget_(const iSidebarWidget *d) { 237static iBool isSlidingSheet_SidebarWidget_(const iSidebarWidget *d) {
238 return isPortraitPhone_App();// && scrollPos_ListWidget(d->list) <= 0; 238 return isPortraitPhone_App();
239}
240
241static 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
241static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepActions) { 252static 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) *