summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-30 07:24:54 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-30 07:24:54 +0300
commit9978d46dfe90f7701b90652a7f8500cdd46eaf31 (patch)
tree839a16825c9162063f81e04e3b52f4c6bd524c8a
parentde77a35bdac544e186546c9daefe2f023f07d4f7 (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.
-rw-r--r--src/ui/listwidget.c33
-rw-r--r--src/ui/sidebarwidget.c29
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
324static size_t resolveDragDestination_ListWidget_(const iListWidget *d, iInt2 dstPos, iBool *isOnto) { 324enum iDragDestination {
325 before_DragDestination,
326 on_DragDestination,
327 after_DragDestination
328};
329
330static 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
1098static void bookmarkMoved_SidebarWidget_(iSidebarWidget *d, size_t index, size_t beforeIndex) { 1098static 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")) {