Reorder the tree rows when the assembly list is sorted - #4138
Merged
Merged
Conversation
Sorting reorders the assembly list in place and reports it as a Move, which nothing downstream could act on: the tree node's handler had cases for Add, Remove and Reset only, and neither the child collection nor the flattener had a move at all. The rows therefore kept their pre-sort order until something else forced a rebuild. Moving the node rather than removing and re-inserting it keeps its identity, so an expanded subtree stays expanded and its row is not rebuilt. A run longer than one row has to be reported the way the consumer reads it: Avalonia's VirtualizingStackPanel applies a ranged move by removing OldItems.Count rows and re-inserting them at NewStartingIndex - (Count - 1), so a run reported by its final start index lands short by its own length. For a single row - a collapsed node, and every move a sort makes - the two readings coincide. Assisted-by: Claude:claude-opus-5[1m]:Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sorting the assembly list reorders it in place and reports each step as a
Move. Nothing downstream could act on that:AssemblyListTreeNode's handlerhad cases for
Add,RemoveandResetonly, and neitherSharpTreeNodeCollectionnorTreeFlattenerhad a move at all, so the rows kepttheir pre-sort order until something else forced a rebuild.
This adds the move to all three layers:
SharpTreeNodeCollection.Move(oldIndex, newIndex)reorders the backing listand raises a single
NotifyCollectionChangedAction.Move.SharpTreeNode.OnChildrenChangedhandles it without the attach/detach work theother actions need - the node keeps its parent and its visibility - and moves
the node's run in the flat list in one step.
TreeFlattener.NodesMovedraises one rangedMovetowards theItemsSource.AssemblyListTreeNodereorders its children instead of ignoring thenotification.
Moving the node rather than removing and re-inserting it keeps its identity, so
an expanded subtree stays expanded and its row is not rebuilt.
The one thing worth a second opinion
A run longer than one row has to be reported the way the consumer reads it:
Avalonia's
VirtualizingStackPanelapplies a ranged move by removingOldItems.Countrows and re-inserting them atNewStartingIndex - (Count - 1).A run reported by its final start index therefore lands one run-length short -
the headless test in this PR renders
A, C, B, Cinstead ofA, C, B, B1ifNodesMovedreports the start. It now reports the row the run ends on for aforward move, which is documented at the call site. For a single row - a
collapsed node, and every move a sort makes - the two readings coincide.
Also worth knowing: a
Movedoes not carry the selection.SelectionNodeBase.OnSourceCollectionChangedin Avalonia handlesMoveasremove-then-add and drops the selection of the moved items. Restoring it would be
a
TreeSelectionBinderchange and is not part of this PR.Tests
Five model tests in
FlatListTreeNodeTests(event shape, no-op move, flattenedorder, expanded-run move, ranged-event indices) and one headless rendering test in
SharpTreeViewTests. FullILSpy.Testsrun: 1283 tests, 0 failed, 3 skipped.Opened by an AI agent (Claude) on Siegfried's behalf.