Skip to content

Add live drag-reorder preview to dash grid with spatial fold/reorder - #108

Open
RobinJ1995 wants to merge 3 commits into
masterfrom
claude/app-drag-drop-reorder-twr4jp
Open

Add live drag-reorder preview to dash grid with spatial fold/reorder#108
RobinJ1995 wants to merge 3 commits into
masterfrom
claude/app-drag-drop-reorder-twr4jp

Conversation

@RobinJ1995

Copy link
Copy Markdown
Owner

Summary

Replaces the pause-to-fold gesture for loose apps and folders with a spatial, live-preview reorder system that mirrors the launcher bar's behavior. Dragging over a cell's centre rings it for folding; dragging over an edge opens a reorder gap. The dragged item becomes an invisible placeholder that flows through the grid as the finger moves, with a smooth reflow animation when the gap position changes.

Key Changes

  • Live reorder preview: When dragging a loose app or folder, the grid shows a real-time preview with the dragged item as an invisible placeholder and other icons flowing around it
  • Spatial fold vs reorder: Distinguishes between folding (drag over cell centre, 30-70% horizontal zone) and reordering (drag over edges/between cells) based on position within the cell
  • Reflow animation: After the preview updates, icons smoothly animate from their old positions to new ones using a DecelerateInterpolator, creating a "make space" feel
  • Preserved member extraction: Folder-member extraction keeps its simpler pause-to-fold gesture unchanged
  • GridAdapter enhancements: Added setHiddenKey() to mark the dragged cell as invisible and setItems() to update the display order during preview
  • DashItem.stableKey: New extension property providing stable string identities for items across drag operations (prefixed by type to avoid collisions)
  • Comprehensive test refactor: Updated tests to use a GeoGrid with deterministic cell geometry instead of mocking pointToPosition, enabling precise targeting of cell centres vs edges

Implementation Details

  • The listener maintains separate state for member drags (pause-to-fold) vs loose app/folder drags (spatial preview)
  • Preview state includes: draggedKey, draggedItem, baseItems (order without dragged item), previewIndex (current gap position), and foldTargetKey (ringed target or null)
  • resolveAndPreview() determines fold vs reorder based on horizontalFraction() within the cell and updates the preview via previewInsertAt()
  • animateReflow() uses ViewTreeObserver.OnPreDrawListener to capture old positions, update the adapter, then animate each icon's translation to its new position
  • Drag state is reset on ACTION_DRAG_ENDED to restore visibility and model order if the drag was cancelled

https://claude.ai/code/session_01D594DCEMgKkkceo5McSkKU

claude added 2 commits July 20, 2026 09:17
Dragging apps to reorder the dash under the custom sort order previously
had no live feedback: the icons stayed put and only jumped into place on
drop, and fold-vs-reorder was distinguished by a dwell timer. This makes
the dash mirror the launcher bar's reorder preview.

The dragged loose app or whole folder now becomes an empty placeholder
cell (GridAdapter.setHiddenKey, keyed by DashItem.stableKey) and the
distinction between reordering and foldering is spatial, like
LauncherDragListener: over a cell's centre (the 0.3-0.7 horizontal band)
rings it for a folder create/add and freezes the gap; over a cell's edge
or the space between cells opens a reorder gap there (custom order only).
The other icons flow around the gap as the finger moves — the listener
reorders the adapter's items live as a preview over the untouched model
and animates each cell from its old slot to its new one with a pre-draw
translate, so they slide rather than jump. The drop commits the previewed
order through DashLayoutRepository.moveItem.

Folder-member extraction keeps its simpler pause-to-fold gesture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D594DCEMgKkkceo5McSkKU
The GeoGrid test harness dereferenced its own `gridAdapter` field from
getChildCount()/getChildAt()/pointToPosition() overrides, but the GridView
super-constructor invokes those before the subclass field is initialised,
so every GeoGrid-based test threw an NPE at construction. Route the
overrides through the null-safe getAdapter() instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D594DCEMgKkkceo5McSkKU

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7e3a1a24e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +164 to +168
if (position == AdapterView.INVALID_POSITION) {
// Past the last icon / empty area: preview an append (custom order only).
this.setFold(grid, null)
if (custom) {
this.previewInsertAt(grid, this.baseItems.size)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid appending from the title padding

When custom ordering is enabled, this treats every INVALID_POSITION as the empty area after the last icon, but the dash grid also reserves the overlaid profile title by adding top padding in ProfilePagerAdapter.onBindViewHolder. Dragging or dropping in that top/title padding has no child under the pointer, so pointToPosition is invalid and the preview moves the item to the end even though the user is above the first row, accidentally corrupting the manual order. Please only append for invalid positions below the last laid-out cell (or otherwise exclude the top padding).

Useful? React with 👍 / 👎.

A dash reorder treated every INVALID_POSITION under the pointer as the
empty area after the last icon and moved the item to the end. But the dash
grid reserves the overlaid profile title as top padding, so dragging or
dropping in that padding above the first row also has no cell under the
pointer — which wrongly appended the item and corrupted the manual order.
Restrict the append to points genuinely below the last laid-out cell.

Reported by Codex review on the PR. Adds tests for both the append and the
padding-guard cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D594DCEMgKkkceo5McSkKU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants