From 9978d46dfe90f7701b90652a7f8500cdd46eaf31 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 30 Sep 2021 07:24:54 +0300 Subject: ListWidget: Dragging before/after an item It's useful to make a distinction whether a drag ended before or after an item, even if it's still referring to the same boundary between items. This allows bookmarks to be reordered inside a folder so that an item is moved to the bottom of a folder, or out of the folder following it in order. --- src/ui/listwidget.c | 33 ++++++++++++++++++++++----------- src/ui/sidebarwidget.c | 29 +++++++++++++++++------------ 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index 652393ff..6bb53f2b 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c @@ -321,7 +321,14 @@ static void updateHover_ListWidget_(iListWidget *d, const iInt2 mouse) { setHoverItem_ListWidget(d, hover); } -static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dstPos, iBool *isOnto) { +enum iDragDestination { + before_DragDestination, + on_DragDestination, + after_DragDestination +}; + +static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dstPos, + enum iDragDestination *dstKind) { size_t index = itemIndex_ListWidget(d, dstPos); const iListItem *item = constItem_ListWidget(d, index); if (!item) { @@ -333,15 +340,17 @@ static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dst if (item->isDropTarget) { const int pad = size_Range(&span) / 4; if (dstPos.y >= span.start + pad && dstPos.y < span.end - pad) { - *isOnto = iTrue; + *dstKind = on_DragDestination; return index; } } + int delta = dstPos.y - top_Rect(rect); if (dstPos.y - span.start > span.end - dstPos.y) { index++; + delta -= d->itemHeight; } index = iMin(index, numItems_ListWidget(d)); - *isOnto = iFalse; + *dstKind = delta < 0 && index > 0 ? after_DragDestination : before_DragDestination; return index; } @@ -350,14 +359,16 @@ static iBool endDrag_ListWidget_(iListWidget *d, iInt2 endPos) { return iFalse; } stop_Anim(&d->scrollY.pos); - iBool isOnto; - const size_t index = resolveDragDestination_ListWidget_(d, endPos, &isOnto); + enum iDragDestination dstKind; + const size_t index = resolveDragDestination_ListWidget_(d, endPos, &dstKind); if (index != d->dragItem) { - if (isOnto) { + if (dstKind == on_DragDestination) { postCommand_Widget(d, "list.dragged arg:%zu onto:%zu", d->dragItem, index); } else { - postCommand_Widget(d, "list.dragged arg:%zu before:%zu", d->dragItem, index); + postCommand_Widget(d, "list.dragged arg:%zu %s:%zu", d->dragItem, + dstKind == after_DragDestination ? "after" : "before", + dstKind == after_DragDestination ? index - 1 : index); } } invalidateItem_ListWidget(d, d->dragItem); @@ -571,19 +582,19 @@ static void draw_ListWidget_(const iListWidget *d) { const iRect itemRect = { init_I2(left_Rect(bounds), pos.y), init_I2(d->visBuf->texSize.x, d->itemHeight) }; SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); - iBool dstOnto; - const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstOnto); + enum iDragDestination dstKind; + const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstKind); if (dstIndex != d->dragItem) { const iRect dstRect = itemRect_ListWidget(d, dstIndex); p.alpha = 0xff; - if (dstOnto) { + if (dstKind == on_DragDestination) { drawRectThickness_Paint(&p, dstRect, gap_UI / 2, uiTextAction_ColorId); } else if (dstIndex != d->dragItem + 1) { fillRect_Paint(&p, (iRect){ addY_I2(dstRect.pos, -gap_UI / 4), init_I2(width_Rect(dstRect), gap_UI / 2) }, uiTextAction_ColorId); - } + } } p.alpha = 0x80; setOpacity_Text(0.5f); diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index aaa6e9e9..4d229c59 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -1095,21 +1095,24 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * return iFalse; } -static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t beforeIndex) { +static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t dstIndex, + iBool isBefore) { const iSidebarItem *movingItem = item_ListWidget(d->list, index); - const iBool isLast = (beforeIndex == numItems_ListWidget(d->list)); + const iBool isLast = (dstIndex == numItems_ListWidget(d->list)); const iSidebarItem *dstItem = item_ListWidget(d->list, isLast ? numItems_ListWidget(d->list) - 1 - : beforeIndex); + : dstIndex); + if (isLast && isBefore) isBefore = iFalse; const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id); - if (hasParent_Bookmark(dst, movingItem->id)) { + if (hasParent_Bookmark(dst, movingItem->id) || hasTag_Bookmark(dst, remote_BookmarkTag)) { + /* Can't move a folder inside itself, and remote bookmarks cannot be reordered. */ return; } - reorder_Bookmarks(bookmarks_App(), movingItem->id, dst->order + (isLast ? 1 : 0)); + reorder_Bookmarks(bookmarks_App(), movingItem->id, dst->order + (isBefore ? 0 : 1)); get_Bookmarks(bookmarks_App(), movingItem->id)->parentId = dst->parentId; updateItems_SidebarWidget_(d); /* Don't confuse the user: keep the dragged item in hover state. */ - setHoverItem_ListWidget(d->list, index < beforeIndex ? beforeIndex - 1 : beforeIndex); + setHoverItem_ListWidget(d->list, dstIndex + (isBefore ? 0 : 1) + (index < dstIndex ? -1 : 0)); postCommandf_App("bookmarks.changed nosidebar:%p", d); /* skip this sidebar since we updated already */ } @@ -1243,17 +1246,19 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) } else if (isCommand_Widget(w, ev, "list.dragged")) { iAssert(d->mode == bookmarks_SidebarMode); - if (hasLabel_Command(cmd, "before")) { - bookmarkMoved_SidebarWidget_(d, - argU32Label_Command(cmd, "arg"), - argU32Label_Command(cmd, "before")); - } - else { + if (hasLabel_Command(cmd, "onto")) { /* Dragged onto a folder. */ bookmarkMovedOntoFolder_SidebarWidget_(d, argU32Label_Command(cmd, "arg"), argU32Label_Command(cmd, "onto")); } + else { + const iBool isBefore = hasLabel_Command(cmd, "before"); + bookmarkMoved_SidebarWidget_(d, + argU32Label_Command(cmd, "arg"), + argU32Label_Command(cmd, isBefore ? "before" : "after"), + isBefore); + } return iTrue; } else if (isCommand_Widget(w, ev, "menu.closed")) { -- cgit v1.2.3