Skip to content

Add drag-n-drop reordering for clothing layers#317

Merged
RyeMutt merged 2 commits into
AlchemyViewer:developfrom
gwigz:gwigz/dnd-cof-layers
Jun 15, 2026
Merged

Add drag-n-drop reordering for clothing layers#317
RyeMutt merged 2 commits into
AlchemyViewer:developfrom
gwigz:gwigz/dnd-cof-layers

Conversation

@gwigz

@gwigz gwigz commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

This also includes changes for scroll containers to preserve and restore scroll position during layout and resize. Without it, the scroll would jump and place the first selected item at the bottom of the scroll area, instead of holding position while the selection remained visible.

CleanShot.2026-06-11.at.9.19.51.mp4

Ported from secondlife/viewer#5918.

- Drag-and-drop reordering for clothing layers with visual insertion indicator
- Scroll containers should also better preserve and restore scroll positions during layout and resize
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 59ef3f7f-6a79-4fb7-bba7-4b57acc4693e

📥 Commits

Reviewing files that changed from the base of the PR and between a96333e and 3c8d3a6.

📒 Files selected for processing (2)
  • indra/llui/llflatlistview.cpp
  • indra/newview/llappearancemgr.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • indra/newview/llappearancemgr.cpp

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added drag-to-reorder for clothing items in the appearance list, with clear visual feedback and support for multi-row layer moves.
    • Reordering is constrained within the same wearable type to keep layers consistent.
  • Bug Fixes
    • Improved scroll behavior by preserving scrollbar position during panel resizing.
    • Fixed wearable identification when multiple worn instances share the same asset, improving reorder reliability.
  • Documentation
    • Added a tooltip hint explaining how to drag or use the arrow controls to reorder layers.

Walkthrough

Adds drag-to-reorder support for clothing layers in the COF wearables panel. LLFlatListView gains a full reorder state machine with visual insertion indicator and auto-scroll. LLScrollContainer gains scroll position preservation on resize. LLAgentWearables and LLAppearanceMgr gain index-based wearable movement and COF persistence APIs, wired into LLCOFWearables with same-type validation, a tooltip, and an XML attribute.

Changes

Clothing Layer Drag-to-Reorder

Layer / File(s) Summary
LLFlatListView reorder contract: Params, types, and member state
indra/llui/llflatlistview.h
Adds allow_reorder/drag_indicator_color Params fields, reorder_signal_t/reorder_validate_signal_t callback typedefs, public setters (setAllowReorder, setReorderCallback, setReorderValidateCallback), virtual mouse overrides, private lifecycle method declarations, and all private drag-state member variables.
LLFlatListView reorder state machine and draw integration
indra/llui/llflatlistview.cpp
Initializes reorder state in constructor; adds reorder-cancel to clear() and removeItemPair(); updates reshape() for overlap-based selection preservation; expands draw() with auto-scroll and insertion indicator; modifies onItemMouseClick() to arm reorder and defer selection collapse; guards right-click from triggering drag; implements armReorderDrag, updateReorderDrag, finishReorderDrag, cancelReorderDrag, getInsertIndexAt, constrainInsertIndex, drawReorderIndicator, handleHover, and handleMouseUp.
LLScrollContainer scroll position preservation on resize
indra/llui/llscrollcontainer.h, indra/llui/llscrollcontainer.cpp
Introduces mStoredDocPos[ORIENTATION_COUNT] and preserveScrollbarMetrics() to snapshot and restore scrollbar doc position around doc/page size updates in both reshape() and updateScroll().
LLAgentWearables: linked-item lookup fix and moveWearableToIndex
indra/newview/llagentwearables.h, indra/newview/llagentwearables.cpp
Fixes getWearableIdxFromItem() to match by linked item UUID first (asset UUID fallback); adds moveWearableToIndex() which clamps the target index and performs step-by-step adjacent swaps.
LLAppearanceMgr: reorderWearable, reorderWearableGroup, and persistWearableOrder
indra/newview/llappearancemgr.h, indra/newview/llappearancemgr.cpp
Refactors moveWearable to delegate to reorderWearable; adds single-item reorder, full-group reorder, and a persistWearableOrder helper that rewrites COF sort-index descriptions and triggers a single appearance update.
LLCOFWearables reorder callbacks, tooltip, and XML wiring
indra/newview/llcofwearables.h, indra/newview/llcofwearables.cpp, indra/newview/skins/default/xui/en/panel_cof_wearables.xml, indra/newview/skins/default/xui/en/strings.xml
Registers canReorderClothing/onClothingReordered callbacks on mClothing; gates reorder to same wearable type; persists via reorderWearableGroup; sets ReorderClothingTooltip on eligible rows; enables allow_reorder="true" in XML; adds tooltip string.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LLCOFWearables
  participant LLFlatListView
  participant LLAppearanceMgr
  participant LLAgentWearables
  participant Inventory

  rect rgba(70, 130, 180, 0.5)
    note over User,LLFlatListView: Drag interaction
    User->>LLFlatListView: mouse down on clothing row
    LLFlatListView->>LLFlatListView: armReorderDrag (capture mouse)
    User->>LLFlatListView: handleHover (move past threshold)
    LLFlatListView->>LLFlatListView: mIsReordering=true, updateReorderDrag, drawReorderIndicator
    User->>LLFlatListView: handleMouseUp
    LLFlatListView->>LLCOFWearables: reorder_validate_signal (canReorderClothing)
    LLCOFWearables-->>LLFlatListView: same wearable type → allowed
    LLFlatListView->>LLCOFWearables: reorder_signal (onClothingReordered, new_index)
  end

  rect rgba(60, 179, 113, 0.5)
    note over LLCOFWearables,Inventory: Persistence
    LLCOFWearables->>LLAppearanceMgr: reorderWearableGroup(type, ordered_link_ids)
    LLAppearanceMgr->>LLAgentWearables: moveWearableToIndex per item
    LLAgentWearables->>LLAgentWearables: swapWearables() steps
    LLAppearanceMgr->>LLAppearanceMgr: persistWearableOrder(type)
    LLAppearanceMgr->>Inventory: update_inventory_item (rewrite sort-index desc)
    LLAppearanceMgr->>LLAppearanceMgr: wearableUpdated + markOutfitDirty
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A bunny hops through layers of cloth,
dragging and dropping without any froth.
The list learns to shuffle, the scroll holds its place,
each wearable finds its own rightful space.
Persist the new order, the COF agrees —
reorder your outfit with effortless ease! 🎽

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete. While it provides context about the changes and links to the source, it lacks required sections from the template such as related issues, checklist items, and testing confirmation. Add the missing template sections: link to related issues, complete the checklist items confirming testing and code quality checks, and include information about dependent changes and documentation updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add drag-n-drop reordering for clothing layers' directly and clearly summarizes the main functionality added in this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gwigz

gwigz commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

need to re-test this, and check it doesn't circumvent RLV too

@gwigz

gwigz commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

re-ordering seems fine

also no RLV areas look related, just detaching which is already guarded

@gwigz gwigz marked this pull request as ready for review June 14, 2026 13:48

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
indra/llui/llflatlistview.cpp (1)

690-778: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don’t arm reordering on modified selection clicks.

armReorderDrag() runs before the MASK_SHIFT/MASK_CONTROL selection paths, so a shift-click or ctrl-click can still turn into a reorder if the pointer crosses the drag threshold before mouse-up. That makes selection gestures unexpectedly mutate list order.

Suggested guard
-    armReorderDrag(item_pair);
+    if (!(mask & (MASK_SHIFT | MASK_CONTROL)))
+    {
+        armReorderDrag(item_pair);
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@indra/llui/llflatlistview.cpp` around lines 690 - 778, The
armReorderDrag(item_pair) call happens unconditionally at the start of the
function before checking for modified selection clicks (MASK_SHIFT or
MASK_CONTROL), which allows shift-click and ctrl-click selection gestures to
potentially trigger unintended reordering if the pointer crosses the drag
threshold. Add a guard condition before the armReorderDrag(item_pair) call to
prevent arming reorder drag when MASK_SHIFT or MASK_CONTROL modifiers are
active, ensuring that modified selection operations do not mutate list order.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@indra/llui/llflatlistview.cpp`:
- Around line 912-932: The mReorderCallback call is passing mReorderInsertIndex
(the group's insertion point) as the new index for moved_value (the grabbed
row), but these may not align if the grabbed row is not the first item in the
dragged group. Replace the callback invocation to calculate and pass the actual
final index of moved_value within mItemPairs instead of using
mReorderInsertIndex, ensuring the reported index matches the grabbed row's
actual position in the reordered list.

In `@indra/newview/llappearancemgr.cpp`:
- Around line 4568-4582: The reorderWearableGroup function starts mutating
wearable state immediately in the loop by calling moveWearableToIndex, so if a
later validation check fails (like an invalid link or wrong wearable type), the
function returns false after partial mutations have been applied, leaving the
order corrupted. Add a pre-validation loop before the reordering loop that
iterates through all ordered_link_ids entries and validates each one by calling
gInventory.getItem and checking link->getWearableType, only proceeding to the
actual moveWearableToIndex mutations if all validations pass.

---

Outside diff comments:
In `@indra/llui/llflatlistview.cpp`:
- Around line 690-778: The armReorderDrag(item_pair) call happens
unconditionally at the start of the function before checking for modified
selection clicks (MASK_SHIFT or MASK_CONTROL), which allows shift-click and
ctrl-click selection gestures to potentially trigger unintended reordering if
the pointer crosses the drag threshold. Add a guard condition before the
armReorderDrag(item_pair) call to prevent arming reorder drag when MASK_SHIFT or
MASK_CONTROL modifiers are active, ensuring that modified selection operations
do not mutate list order.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cf412f3f-e018-4094-86e6-46d538555335

📥 Commits

Reviewing files that changed from the base of the PR and between 753e934 and a96333e.

📒 Files selected for processing (12)
  • indra/llui/llflatlistview.cpp
  • indra/llui/llflatlistview.h
  • indra/llui/llscrollcontainer.cpp
  • indra/llui/llscrollcontainer.h
  • indra/newview/llagentwearables.cpp
  • indra/newview/llagentwearables.h
  • indra/newview/llappearancemgr.cpp
  • indra/newview/llappearancemgr.h
  • indra/newview/llcofwearables.cpp
  • indra/newview/llcofwearables.h
  • indra/newview/skins/default/xui/en/panel_cof_wearables.xml
  • indra/newview/skins/default/xui/en/strings.xml

Comment thread indra/llui/llflatlistview.cpp
Comment thread indra/newview/llappearancemgr.cpp
@RyeMutt RyeMutt added this to the NEXT milestone Jun 15, 2026
@RyeMutt RyeMutt merged commit bdac91b into AlchemyViewer:develop Jun 15, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants