diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-30 07:24:54 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-30 07:24:54 +0300 |
commit | 9978d46dfe90f7701b90652a7f8500cdd46eaf31 (patch) | |
tree | 839a16825c9162063f81e04e3b52f4c6bd524c8a /src/ui | |
parent | de77a35bdac544e186546c9daefe2f023f07d4f7 (diff) |
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.
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/listwidget.c | 33 | ||||
-rw-r--r-- | 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) { | |||
321 | setHoverItem_ListWidget(d, hover); | 321 | setHoverItem_ListWidget(d, hover); |
322 | } | 322 | } |
323 | 323 | ||
324 | static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dstPos, iBool *isOnto) { | 324 | enum iDragDestination { |
325 | before_DragDestination, | ||
326 | on_DragDestination, | ||
327 | after_DragDestination | ||
328 | }; | ||
329 | |||
330 | static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dstPos, | ||
331 | enum iDragDestination *dstKind) { | ||
325 | size_t index = itemIndex_ListWidget(d, dstPos); | 332 | size_t index = itemIndex_ListWidget(d, dstPos); |
326 | const iListItem *item = constItem_ListWidget(d, index); | 333 | const iListItem *item = constItem_ListWidget(d, index); |
327 | if (!item) { | 334 | if (!item) { |
@@ -333,15 +340,17 @@ static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dst | |||
333 | if (item->isDropTarget) { | 340 | if (item->isDropTarget) { |
334 | const int pad = size_Range(&span) / 4; | 341 | const int pad = size_Range(&span) / 4; |
335 | if (dstPos.y >= span.start + pad && dstPos.y < span.end - pad) { | 342 | if (dstPos.y >= span.start + pad && dstPos.y < span.end - pad) { |
336 | *isOnto = iTrue; | 343 | *dstKind = on_DragDestination; |
337 | return index; | 344 | return index; |
338 | } | 345 | } |
339 | } | 346 | } |
347 | int delta = dstPos.y - top_Rect(rect); | ||
340 | if (dstPos.y - span.start > span.end - dstPos.y) { | 348 | if (dstPos.y - span.start > span.end - dstPos.y) { |
341 | index++; | 349 | index++; |
350 | delta -= d->itemHeight; | ||
342 | } | 351 | } |
343 | index = iMin(index, numItems_ListWidget(d)); | 352 | index = iMin(index, numItems_ListWidget(d)); |
344 | *isOnto = iFalse; | 353 | *dstKind = delta < 0 && index > 0 ? after_DragDestination : before_DragDestination; |
345 | return index; | 354 | return index; |
346 | } | 355 | } |
347 | 356 | ||
@@ -350,14 +359,16 @@ static iBool endDrag_ListWidget_(iListWidget *d, iInt2 endPos) { | |||
350 | return iFalse; | 359 | return iFalse; |
351 | } | 360 | } |
352 | stop_Anim(&d->scrollY.pos); | 361 | stop_Anim(&d->scrollY.pos); |
353 | iBool isOnto; | 362 | enum iDragDestination dstKind; |
354 | const size_t index = resolveDragDestination_ListWidget_(d, endPos, &isOnto); | 363 | const size_t index = resolveDragDestination_ListWidget_(d, endPos, &dstKind); |
355 | if (index != d->dragItem) { | 364 | if (index != d->dragItem) { |
356 | if (isOnto) { | 365 | if (dstKind == on_DragDestination) { |
357 | postCommand_Widget(d, "list.dragged arg:%zu onto:%zu", d->dragItem, index); | 366 | postCommand_Widget(d, "list.dragged arg:%zu onto:%zu", d->dragItem, index); |
358 | } | 367 | } |
359 | else { | 368 | else { |
360 | postCommand_Widget(d, "list.dragged arg:%zu before:%zu", d->dragItem, index); | 369 | postCommand_Widget(d, "list.dragged arg:%zu %s:%zu", d->dragItem, |
370 | dstKind == after_DragDestination ? "after" : "before", | ||
371 | dstKind == after_DragDestination ? index - 1 : index); | ||
361 | } | 372 | } |
362 | } | 373 | } |
363 | invalidateItem_ListWidget(d, d->dragItem); | 374 | invalidateItem_ListWidget(d, d->dragItem); |
@@ -571,19 +582,19 @@ static void draw_ListWidget_(const iListWidget *d) { | |||
571 | const iRect itemRect = { init_I2(left_Rect(bounds), pos.y), | 582 | const iRect itemRect = { init_I2(left_Rect(bounds), pos.y), |
572 | init_I2(d->visBuf->texSize.x, d->itemHeight) }; | 583 | init_I2(d->visBuf->texSize.x, d->itemHeight) }; |
573 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | 584 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); |
574 | iBool dstOnto; | 585 | enum iDragDestination dstKind; |
575 | const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstOnto); | 586 | const size_t dstIndex = resolveDragDestination_ListWidget_(d, mousePos, &dstKind); |
576 | if (dstIndex != d->dragItem) { | 587 | if (dstIndex != d->dragItem) { |
577 | const iRect dstRect = itemRect_ListWidget(d, dstIndex); | 588 | const iRect dstRect = itemRect_ListWidget(d, dstIndex); |
578 | p.alpha = 0xff; | 589 | p.alpha = 0xff; |
579 | if (dstOnto) { | 590 | if (dstKind == on_DragDestination) { |
580 | drawRectThickness_Paint(&p, dstRect, gap_UI / 2, uiTextAction_ColorId); | 591 | drawRectThickness_Paint(&p, dstRect, gap_UI / 2, uiTextAction_ColorId); |
581 | } | 592 | } |
582 | else if (dstIndex != d->dragItem + 1) { | 593 | else if (dstIndex != d->dragItem + 1) { |
583 | fillRect_Paint(&p, (iRect){ addY_I2(dstRect.pos, -gap_UI / 4), | 594 | fillRect_Paint(&p, (iRect){ addY_I2(dstRect.pos, -gap_UI / 4), |
584 | init_I2(width_Rect(dstRect), gap_UI / 2) }, | 595 | init_I2(width_Rect(dstRect), gap_UI / 2) }, |
585 | uiTextAction_ColorId); | 596 | uiTextAction_ColorId); |
586 | } | 597 | } |
587 | } | 598 | } |
588 | p.alpha = 0x80; | 599 | p.alpha = 0x80; |
589 | setOpacity_Text(0.5f); | 600 | 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 * | |||
1095 | return iFalse; | 1095 | return iFalse; |
1096 | } | 1096 | } |
1097 | 1097 | ||
1098 | static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t beforeIndex) { | 1098 | static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t dstIndex, |
1099 | iBool isBefore) { | ||
1099 | const iSidebarItem *movingItem = item_ListWidget(d->list, index); | 1100 | const iSidebarItem *movingItem = item_ListWidget(d->list, index); |
1100 | const iBool isLast = (beforeIndex == numItems_ListWidget(d->list)); | 1101 | const iBool isLast = (dstIndex == numItems_ListWidget(d->list)); |
1101 | const iSidebarItem *dstItem = item_ListWidget(d->list, | 1102 | const iSidebarItem *dstItem = item_ListWidget(d->list, |
1102 | isLast ? numItems_ListWidget(d->list) - 1 | 1103 | isLast ? numItems_ListWidget(d->list) - 1 |
1103 | : beforeIndex); | 1104 | : dstIndex); |
1105 | if (isLast && isBefore) isBefore = iFalse; | ||
1104 | const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id); | 1106 | const iBookmark *dst = get_Bookmarks(bookmarks_App(), dstItem->id); |
1105 | if (hasParent_Bookmark(dst, movingItem->id)) { | 1107 | if (hasParent_Bookmark(dst, movingItem->id) || hasTag_Bookmark(dst, remote_BookmarkTag)) { |
1108 | /* Can't move a folder inside itself, and remote bookmarks cannot be reordered. */ | ||
1106 | return; | 1109 | return; |
1107 | } | 1110 | } |
1108 | reorder_Bookmarks(bookmarks_App(), movingItem->id, dst->order + (isLast ? 1 : 0)); | 1111 | reorder_Bookmarks(bookmarks_App(), movingItem->id, dst->order + (isBefore ? 0 : 1)); |
1109 | get_Bookmarks(bookmarks_App(), movingItem->id)->parentId = dst->parentId; | 1112 | get_Bookmarks(bookmarks_App(), movingItem->id)->parentId = dst->parentId; |
1110 | updateItems_SidebarWidget_(d); | 1113 | updateItems_SidebarWidget_(d); |
1111 | /* Don't confuse the user: keep the dragged item in hover state. */ | 1114 | /* Don't confuse the user: keep the dragged item in hover state. */ |
1112 | setHoverItem_ListWidget(d->list, index < beforeIndex ? beforeIndex - 1 : beforeIndex); | 1115 | setHoverItem_ListWidget(d->list, dstIndex + (isBefore ? 0 : 1) + (index < dstIndex ? -1 : 0)); |
1113 | postCommandf_App("bookmarks.changed nosidebar:%p", d); /* skip this sidebar since we updated already */ | 1116 | postCommandf_App("bookmarks.changed nosidebar:%p", d); /* skip this sidebar since we updated already */ |
1114 | } | 1117 | } |
1115 | 1118 | ||
@@ -1243,17 +1246,19 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1243 | } | 1246 | } |
1244 | else if (isCommand_Widget(w, ev, "list.dragged")) { | 1247 | else if (isCommand_Widget(w, ev, "list.dragged")) { |
1245 | iAssert(d->mode == bookmarks_SidebarMode); | 1248 | iAssert(d->mode == bookmarks_SidebarMode); |
1246 | if (hasLabel_Command(cmd, "before")) { | 1249 | if (hasLabel_Command(cmd, "onto")) { |
1247 | bookmarkMoved_SidebarWidget_(d, | ||
1248 | argU32Label_Command(cmd, "arg"), | ||
1249 | argU32Label_Command(cmd, "before")); | ||
1250 | } | ||
1251 | else { | ||
1252 | /* Dragged onto a folder. */ | 1250 | /* Dragged onto a folder. */ |
1253 | bookmarkMovedOntoFolder_SidebarWidget_(d, | 1251 | bookmarkMovedOntoFolder_SidebarWidget_(d, |
1254 | argU32Label_Command(cmd, "arg"), | 1252 | argU32Label_Command(cmd, "arg"), |
1255 | argU32Label_Command(cmd, "onto")); | 1253 | argU32Label_Command(cmd, "onto")); |
1256 | } | 1254 | } |
1255 | else { | ||
1256 | const iBool isBefore = hasLabel_Command(cmd, "before"); | ||
1257 | bookmarkMoved_SidebarWidget_(d, | ||
1258 | argU32Label_Command(cmd, "arg"), | ||
1259 | argU32Label_Command(cmd, isBefore ? "before" : "after"), | ||
1260 | isBefore); | ||
1261 | } | ||
1257 | return iTrue; | 1262 | return iTrue; |
1258 | } | 1263 | } |
1259 | else if (isCommand_Widget(w, ev, "menu.closed")) { | 1264 | else if (isCommand_Widget(w, ev, "menu.closed")) { |