summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-24 07:36:08 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-24 07:36:08 +0300
commit446df26adb969dcb4dd9cf5e171fbb9453a8e27c (patch)
tree562c647f7c9dc907d34e263d44c9a414a80a8017
parent27ebfc904b9b3eac3ef5479ed0d0ba166fac1543 (diff)
SidebarWidget: Reordering bookmarks manually
One can now drag and drop bookmarks to change their order in the list.
-rw-r--r--src/bookmarks.c14
-rw-r--r--src/bookmarks.h10
-rw-r--r--src/ui/listwidget.c53
-rw-r--r--src/ui/listwidget.h1
-rw-r--r--src/ui/sidebarwidget.c31
-rw-r--r--src/ui/util.c2
6 files changed, 91 insertions, 20 deletions
diff --git a/src/bookmarks.c b/src/bookmarks.c
index 94f4be4e..616e4632 100644
--- a/src/bookmarks.c
+++ b/src/bookmarks.c
@@ -402,6 +402,20 @@ iBookmark *get_Bookmarks(iBookmarks *d, uint32_t id) {
402 return (iBookmark *) value_Hash(&d->bookmarks, id); 402 return (iBookmark *) value_Hash(&d->bookmarks, id);
403} 403}
404 404
405void reorder_Bookmarks(iBookmarks *d, uint32_t id, int newOrder) {
406 lock_Mutex(d->mtx);
407 iForEach(Hash, i, &d->bookmarks) {
408 iBookmark *bm = (iBookmark *) i.value;
409 if (id_Bookmark(bm) == id) {
410 bm->order = newOrder;
411 }
412 else if (bm->order >= newOrder) {
413 bm->order++;
414 }
415 }
416 unlock_Mutex(d->mtx);
417}
418
405iBool filterTagsRegExp_Bookmarks(void *regExp, const iBookmark *bm) { 419iBool filterTagsRegExp_Bookmarks(void *regExp, const iBookmark *bm) {
406 iRegExpMatch m; 420 iRegExpMatch m;
407 init_RegExpMatch(&m); 421 init_RegExpMatch(&m);
diff --git a/src/bookmarks.h b/src/bookmarks.h
index dc7eca9a..0de930d7 100644
--- a/src/bookmarks.h
+++ b/src/bookmarks.h
@@ -32,6 +32,8 @@ iDeclareType(GmRequest)
32iDeclareType(Bookmark) 32iDeclareType(Bookmark)
33iDeclareTypeConstruction(Bookmark) 33iDeclareTypeConstruction(Bookmark)
34 34
35/* TODO: Make the special internal tags a bitfield, separate from user's tags. */
36
35#define headings_BookmarkTag "headings" 37#define headings_BookmarkTag "headings"
36#define homepage_BookmarkTag "homepage" 38#define homepage_BookmarkTag "homepage"
37#define linkSplit_BookmarkTag "linksplit" 39#define linkSplit_BookmarkTag "linksplit"
@@ -78,16 +80,18 @@ iDeclareTypeConstruction(Bookmarks)
78 80
79void clear_Bookmarks (iBookmarks *); 81void clear_Bookmarks (iBookmarks *);
80void load_Bookmarks (iBookmarks *, const char *dirPath); 82void load_Bookmarks (iBookmarks *, const char *dirPath);
83void save_Bookmarks (const iBookmarks *, const char *dirPath);
84
81uint32_t add_Bookmarks (iBookmarks *, const iString *url, const iString *title, 85uint32_t add_Bookmarks (iBookmarks *, const iString *url, const iString *title,
82 const iString *tags, iChar icon); 86 const iString *tags, iChar icon);
83iBool remove_Bookmarks (iBookmarks *, uint32_t id); 87iBool remove_Bookmarks (iBookmarks *, uint32_t id);
84iBookmark * get_Bookmarks (iBookmarks *, uint32_t id); 88iBookmark * get_Bookmarks (iBookmarks *, uint32_t id);
89void reorder_Bookmarks (iBookmarks *, uint32_t id, int newOrder);
90iBool updateBookmarkIcon_Bookmarks(iBookmarks *, const iString *url, iChar icon);
85void fetchRemote_Bookmarks (iBookmarks *); 91void fetchRemote_Bookmarks (iBookmarks *);
86void requestFinished_Bookmarks (iBookmarks *, iGmRequest *req); 92void requestFinished_Bookmarks (iBookmarks *, iGmRequest *req);
87iBool updateBookmarkIcon_Bookmarks(iBookmarks *, const iString *url, iChar icon);
88iChar siteIcon_Bookmarks (const iBookmarks *, const iString *url);
89 93
90void save_Bookmarks (const iBookmarks *, const char *dirPath); 94iChar siteIcon_Bookmarks (const iBookmarks *, const iString *url);
91uint32_t findUrl_Bookmarks (const iBookmarks *, const iString *url); /* O(n) */ 95uint32_t findUrl_Bookmarks (const iBookmarks *, const iString *url); /* O(n) */
92 96
93typedef iBool (*iBookmarksFilterFunc) (void *context, const iBookmark *); 97typedef iBool (*iBookmarksFilterFunc) (void *context, const iBookmark *);
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c
index 6a1372ab..a34f3d03 100644
--- a/src/ui/listwidget.c
+++ b/src/ui/listwidget.c
@@ -277,7 +277,7 @@ size_t hoverItemIndex_ListWidget(const iListWidget *d) {
277 return d->hoverItem; 277 return d->hoverItem;
278} 278}
279 279
280static void setHoverItem_ListWidget_(iListWidget *d, size_t index) { 280void setHoverItem_ListWidget(iListWidget *d, size_t index) {
281 if (index < size_PtrArray(&d->items)) { 281 if (index < size_PtrArray(&d->items)) {
282 const iListItem *item = at_PtrArray(&d->items, index); 282 const iListItem *item = at_PtrArray(&d->items, index);
283 if (item->isSeparator) { 283 if (item->isSeparator) {
@@ -294,7 +294,7 @@ static void setHoverItem_ListWidget_(iListWidget *d, size_t index) {
294 294
295void updateMouseHover_ListWidget(iListWidget *d) { 295void updateMouseHover_ListWidget(iListWidget *d) {
296 const iInt2 mouse = mouseCoord_Window(get_Window(), 0); 296 const iInt2 mouse = mouseCoord_Window(get_Window(), 0);
297 setHoverItem_ListWidget_(d, itemIndex_ListWidget(d, mouse)); 297 setHoverItem_ListWidget(d, itemIndex_ListWidget(d, mouse));
298} 298}
299 299
300void sort_ListWidget(iListWidget *d, int (*cmp)(const iListItem **item1, const iListItem **item2)) { 300void sort_ListWidget(iListWidget *d, int (*cmp)(const iListItem **item1, const iListItem **item2)) {
@@ -318,19 +318,18 @@ static void updateHover_ListWidget_(iListWidget *d, const iInt2 mouse) {
318 contains_Widget(constAs_Widget(d), mouse)) { 318 contains_Widget(constAs_Widget(d), mouse)) {
319 hover = itemIndex_ListWidget(d, mouse); 319 hover = itemIndex_ListWidget(d, mouse);
320 } 320 }
321 setHoverItem_ListWidget_(d, hover); 321 setHoverItem_ListWidget(d, hover);
322} 322}
323 323
324static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dstPos, iBool *isOnto) { 324static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dstPos, iBool *isOnto) {
325 size_t index = itemIndex_ListWidget(d, dstPos); 325 size_t index = itemIndex_ListWidget(d, dstPos);
326 const iRect rect = itemRect_ListWidget(d, index);
327 const iListItem *item = constItem_ListWidget(d, index); 326 const iListItem *item = constItem_ListWidget(d, index);
328 const iRangei span = ySpan_Rect(rect);
329 if (!item) { 327 if (!item) {
330 item = constItem_ListWidget( 328 index = (dstPos.y < mid_Rect(bounds_Widget(constAs_Widget(d))).y ? 0 : (numItems_ListWidget(d) - 1));
331 d, 329 item = constItem_ListWidget(d, index);
332 dstPos.y < mid_Rect(bounds_Widget(constAs_Widget(d))).y ? 0 : (numItems_ListWidget(d) - 1));
333 } 330 }
331 const iRect rect = itemRect_ListWidget(d, index);
332 const iRangei span = ySpan_Rect(rect);
334 if (item->isDropTarget) { 333 if (item->isDropTarget) {
335 const int pad = size_Range(&span) / 4; 334 const int pad = size_Range(&span) / 4;
336 if (dstPos.y >= span.start + pad && dstPos.y < span.end) { 335 if (dstPos.y >= span.start + pad && dstPos.y < span.end) {
@@ -350,6 +349,7 @@ static iBool endDrag_ListWidget_(iListWidget *d, iInt2 endPos) {
350 if (d->dragItem == iInvalidPos) { 349 if (d->dragItem == iInvalidPos) {
351 return iFalse; 350 return iFalse;
352 } 351 }
352 stop_Anim(&d->scrollY.pos);
353 iBool isOnto; 353 iBool isOnto;
354 const size_t index = resolveDragDestination_ListWidget_(d, endPos, &isOnto); 354 const size_t index = resolveDragDestination_ListWidget_(d, endPos, &isOnto);
355 if (isOnto) { 355 if (isOnto) {
@@ -385,14 +385,34 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) {
385 d->noHoverWhileScrolling = iFalse; 385 d->noHoverWhileScrolling = iFalse;
386 } 386 }
387 if (ev->type == SDL_MOUSEMOTION) { 387 if (ev->type == SDL_MOUSEMOTION) {
388 const iInt2 mousePos = init_I2(ev->motion.x, ev->motion.y);
388 if (ev->motion.state == 0 /* not dragging */) { 389 if (ev->motion.state == 0 /* not dragging */) {
389 if (ev->motion.which != SDL_TOUCH_MOUSEID) { 390 if (ev->motion.which != SDL_TOUCH_MOUSEID) {
390 d->noHoverWhileScrolling = iFalse; 391 d->noHoverWhileScrolling = iFalse;
391 } 392 }
392 updateHover_ListWidget_(d, init_I2(ev->motion.x, ev->motion.y)); 393 updateHover_ListWidget_(d, mousePos);
393 } 394 }
394 else if (d->dragItem != iInvalidPos) { 395 else if (d->dragItem != iInvalidPos) {
395 refresh_Widget(d); 396 /* Start scrolling if near the ends. */
397 const int zone = d->itemHeight;
398 const iRect bounds = bounds_Widget(w);
399 float scrollSpeed = 0.0f;
400 if (mousePos.y > bottom_Rect(bounds) - zone) {
401 scrollSpeed = (mousePos.y - bottom_Rect(bounds) + zone) / (float) zone;
402 }
403 else if (mousePos.y < top_Rect(bounds) + zone) {
404 scrollSpeed = -(top_Rect(bounds) + zone - mousePos.y) / (float) zone;
405 }
406 scrollSpeed = iClamp(scrollSpeed, -1.0f, 1.0f);
407 if (iAbs(scrollSpeed) < 0.001f) {
408 stop_Anim(&d->scrollY.pos);
409 refresh_Widget(d);
410 }
411 else {
412 setValueSpeed_Anim(&d->scrollY.pos, scrollSpeed < 0 ? 0 : scrollMax_ListWidget_(d),
413 iAbs(scrollSpeed * gap_UI * 100));
414 refreshWhileScrolling_ListWidget_(d);
415 }
396 } 416 }
397 } 417 }
398 if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { 418 if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) {
@@ -416,7 +436,12 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) {
416 redrawHoverItem_ListWidget_(d); 436 redrawHoverItem_ListWidget_(d);
417 return iTrue; 437 return iTrue;
418 case aborted_ClickResult: 438 case aborted_ClickResult:
419 endDrag_ListWidget_(d, pos_Click(&d->click)); 439// endDrag_ListWidget_(d, pos_Click(&d->click));
440 if (d->dragItem != iInvalidPos) {
441 stop_Anim(&d->scrollY.pos);
442 invalidateItem_ListWidget(d, d->dragItem);
443 d->dragItem = iInvalidPos;
444 }
420 redrawHoverItem_ListWidget_(d); 445 redrawHoverItem_ListWidget_(d);
421 break; 446 break;
422 case drag_ClickResult: 447 case drag_ClickResult:
@@ -436,7 +461,7 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) {
436 return iTrue; 461 return iTrue;
437 } 462 }
438 redrawHoverItem_ListWidget_(d); 463 redrawHoverItem_ListWidget_(d);
439 if (contains_Rect(innerBounds_Widget(w), pos_Click(&d->click)) && 464 if (contains_Rect(itemRect_ListWidget(d, d->hoverItem), pos_Click(&d->click)) &&
440 d->hoverItem != iInvalidPos) { 465 d->hoverItem != iInvalidPos) {
441 postCommand_Widget(w, "list.clicked arg:%zu item:%p", 466 postCommand_Widget(w, "list.clicked arg:%zu item:%p",
442 d->hoverItem, constHoverItem_ListWidget(d)); 467 d->hoverItem, constHoverItem_ListWidget(d));
@@ -542,14 +567,10 @@ static void draw_ListWidget_(const iListWidget *d) {
542 const iListItem *item = constAt_PtrArray(&d->items, d->dragItem); 567 const iListItem *item = constAt_PtrArray(&d->items, d->dragItem);
543 const iRect itemRect = { init_I2(left_Rect(bounds), pos.y), init_I2(d->visBuf->texSize.x, d->itemHeight) }; 568 const iRect itemRect = { init_I2(left_Rect(bounds), pos.y), init_I2(d->visBuf->texSize.x, d->itemHeight) };
544 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); 569 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
545// setOpacity_Text(0.25f);
546// SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
547// setOpacity_Text(1.0f);
548 iBool dstOnto; 570 iBool dstOnto;
549 const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstOnto); 571 const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstOnto);
550 if (dstIndex != d->dragItem && dstIndex != d->dragItem + 1) { 572 if (dstIndex != d->dragItem && dstIndex != d->dragItem + 1) {
551 const iRect dstRect = itemRect_ListWidget(d, dstIndex); 573 const iRect dstRect = itemRect_ListWidget(d, dstIndex);
552// SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
553 p.alpha = 0xff; 574 p.alpha = 0xff;
554 if (dstOnto) { 575 if (dstOnto) {
555 fillRect_Paint(&p, dstRect, uiTextAction_ColorId); 576 fillRect_Paint(&p, dstRect, uiTextAction_ColorId);
diff --git a/src/ui/listwidget.h b/src/ui/listwidget.h
index e586a003..dfa24c07 100644
--- a/src/ui/listwidget.h
+++ b/src/ui/listwidget.h
@@ -66,6 +66,7 @@ void scrollToItem_ListWidget (iListWidget *, size_t index);
66void scrollOffset_ListWidget (iListWidget *, int offset); 66void scrollOffset_ListWidget (iListWidget *, int offset);
67void updateVisible_ListWidget (iListWidget *); 67void updateVisible_ListWidget (iListWidget *);
68void updateMouseHover_ListWidget (iListWidget *); 68void updateMouseHover_ListWidget (iListWidget *);
69void setHoverItem_ListWidget (iListWidget *, size_t index);
69 70
70void sort_ListWidget (iListWidget *, int (*cmp)(const iListItem **item1, const iListItem **item2)); 71void sort_ListWidget (iListWidget *, int (*cmp)(const iListItem **item1, const iListItem **item2));
71 72
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 3463f1a5..20e43153 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -332,6 +332,7 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
332 iRegExp *homeTag = iClob(new_RegExp("\\b" homepage_BookmarkTag "\\b", caseSensitive_RegExpOption)); 332 iRegExp *homeTag = iClob(new_RegExp("\\b" homepage_BookmarkTag "\\b", caseSensitive_RegExpOption));
333 iRegExp *subTag = iClob(new_RegExp("\\b" subscribed_BookmarkTag "\\b", caseSensitive_RegExpOption)); 333 iRegExp *subTag = iClob(new_RegExp("\\b" subscribed_BookmarkTag "\\b", caseSensitive_RegExpOption));
334 iRegExp *remoteSourceTag = iClob(new_RegExp("\\b" remoteSource_BookmarkTag "\\b", caseSensitive_RegExpOption)); 334 iRegExp *remoteSourceTag = iClob(new_RegExp("\\b" remoteSource_BookmarkTag "\\b", caseSensitive_RegExpOption));
335 iRegExp *remoteTag = iClob(new_RegExp("\\b" remote_BookmarkTag "\\b", caseSensitive_RegExpOption));
335 iRegExp *linkSplitTag = iClob(new_RegExp("\\b" linkSplit_BookmarkTag "\\b", caseSensitive_RegExpOption)); 336 iRegExp *linkSplitTag = iClob(new_RegExp("\\b" linkSplit_BookmarkTag "\\b", caseSensitive_RegExpOption));
336 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTree_Bookmark_, NULL, NULL)) { 337 iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), cmpTree_Bookmark_, NULL, NULL)) {
337 const iBookmark *bm = i.ptr; 338 const iBookmark *bm = i.ptr;
@@ -352,6 +353,10 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
352 appendChar_String(&item->meta, 0x1f3e0); 353 appendChar_String(&item->meta, 0x1f3e0);
353 } 354 }
354 init_RegExpMatch(&m); 355 init_RegExpMatch(&m);
356 if (matchString_RegExp(remoteTag, &bm->tags, &m)) {
357 item->listItem.isDraggable = iFalse;
358 }
359 init_RegExpMatch(&m);
355 if (matchString_RegExp(remoteSourceTag, &bm->tags, &m)) { 360 if (matchString_RegExp(remoteSourceTag, &bm->tags, &m)) {
356 appendChar_String(&item->meta, 0x2913); 361 appendChar_String(&item->meta, 0x2913);
357 item->isBold = iTrue; 362 item->isBold = iTrue;
@@ -555,6 +560,7 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
555#endif 560#endif
556 arrange_Widget(d->actions); 561 arrange_Widget(d->actions);
557 arrange_Widget(as_Widget(d)); 562 arrange_Widget(as_Widget(d));
563 updateMouseHover_ListWidget(d->list);
558} 564}
559 565
560static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) { 566static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) {
@@ -1000,6 +1006,19 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *
1000 return iFalse; 1006 return iFalse;
1001} 1007}
1002 1008
1009static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t beforeIndex) {
1010 const iSidebarItem *movingItem = item_ListWidget(d->list, index);
1011 const iBool isLast = (beforeIndex == numItems_ListWidget(d->list));
1012 const iSidebarItem *dstItem = item_ListWidget(d->list,
1013 isLast ? numItems_ListWidget(d->list) - 1
1014 : beforeIndex);
1015 const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id);
1016 reorder_Bookmarks(bookmarks_App(), movingItem->id, dst->order + (isLast ? 1 : 0));
1017 updateItems_SidebarWidget_(d);
1018 /* Don't confuse the user: keep the dragged item in hover state. */
1019 setHoverItem_ListWidget(d->list, index < beforeIndex ? beforeIndex - 1 : beforeIndex);
1020}
1021
1003static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { 1022static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) {
1004 iWidget *w = as_Widget(d); 1023 iWidget *w = as_Widget(d);
1005 /* Handle commands. */ 1024 /* Handle commands. */
@@ -1107,6 +1126,18 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1107 d, pointerLabel_Command(cmd, "item"), argU32Label_Command(cmd, "arg")); 1126 d, pointerLabel_Command(cmd, "item"), argU32Label_Command(cmd, "arg"));
1108 return iTrue; 1127 return iTrue;
1109 } 1128 }
1129 else if (isCommand_Widget(w, ev, "list.dragged")) {
1130 iAssert(d->mode == bookmarks_SidebarMode);
1131 if (hasLabel_Command(cmd, "before")) {
1132 bookmarkMoved_SidebarWidget_(d,
1133 argU32Label_Command(cmd, "arg"),
1134 argU32Label_Command(cmd, "before"));
1135 }
1136 else {
1137 /* Dragged onto a folder. */
1138 }
1139 return iTrue;
1140 }
1110 else if (isCommand_Widget(w, ev, "menu.closed")) { 1141 else if (isCommand_Widget(w, ev, "menu.closed")) {
1111 // invalidateItem_ListWidget(d->list, d->contextIndex); 1142 // invalidateItem_ListWidget(d->list, d->contextIndex);
1112 } 1143 }
diff --git a/src/ui/util.c b/src/ui/util.c
index 2b6ff929..7fa5d675 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -337,7 +337,7 @@ void setValue_Anim(iAnim *d, float to, uint32_t span) {
337} 337}
338 338
339void setValueSpeed_Anim(iAnim *d, float to, float unitsPerSecond) { 339void setValueSpeed_Anim(iAnim *d, float to, float unitsPerSecond) {
340 if (iAbs(d->to - to) > 0.0001f) { 340 if (iAbs(d->to - to) > 0.0001f || !isFinished_Anim(d)) {
341 const uint32_t now = SDL_GetTicks(); 341 const uint32_t now = SDL_GetTicks();
342 const float from = valueAt_Anim_(d, now); 342 const float from = valueAt_Anim_(d, now);
343 const float delta = to - from; 343 const float delta = to - from;