Skip to content

Let users reorder the navigation pages by dragging them in the sidebar - #5520

Merged
Gabriel Dufresne (GabrielDuf) merged 2 commits into
mainfrom
feature/reorder-navigation-pages
Oct 15, 2026
Merged

Gabriel Dufresne (GabrielDuf) merged 2 commits into
mainfrom
feature/reorder-navigation-pages

Conversation

@GabrielDuf

Copy link
Copy Markdown
Contributor

The Software Catalog added two pages to the sidebar and pushed the existing ones down, so users lost the Ctrl+number layout they were used to. The order is now theirs to choose; the default stays the current one.

  • Drag a page in the navigation panel to move it (mouse, pen or touch), or press Alt+Up/Down (Option on macOS) on a focused page. Escape cancels a drag. Ctrl/Cmd+number, Ctrl+Tab cycling, the accelerator metadata and the macOS View menu all follow the new order, and keyboard moves are announced to screen readers.
  • The order is stored in the NavigationPageOrder setting. A default order is stored as unset, so users who never reorder follow future default changes. The stored value always lists the catalog pages too, so an order chosen while the catalog is disabled keeps those pages in place when it is re-enabled. Unknown or duplicate names are ignored and missing pages are appended.
  • Settings > User interface gets a "Navigation menu order" card with Reset.
  • The nav ListBox now uses a plain StackPanel: with the default VirtualizingStackPanel, clearing and re-adding the ListBoxItems (which are their own containers) left them detached, so every page vanished after a reorder.
  • Drag positions are measured against the items panel, so scrolling the sidebar mid-drag still drops where the pointer is, and the sidebar only listens to its view model while attached.
  • SvgIcon's static theme brushes are now immutable. The mutable ones were bound to whichever UI thread first rendered an icon, which broke any headless test session that rendered icons after another one.

Tests: SidebarNavigationOrderTests drives the real SidebarView headlessly (mouse drag both ways, touch drag including a scrollable sidebar, Escape, Alt+arrows with focus kept, the announcement, scrolling mid-drag, detach and reattach), and all of them fail if the StackPanel panel is removed. SoftwareCatalogTests covers resolving, merging and serializing the stored order

The Software Catalog added two pages to the sidebar and pushed the existing
ones down, so users lost the Ctrl+number layout they were used to. The order
is now theirs to choose; the default stays the current one.

- Drag a page in the navigation panel to move it (mouse, pen or touch), or
  press Alt+Up/Down (Option on macOS) on a focused page. Escape cancels a
  drag. Ctrl/Cmd+number, Ctrl+Tab cycling, the accelerator metadata and the
  macOS View menu all follow the new order, and keyboard moves are announced
  to screen readers.
- The order is stored in the NavigationPageOrder setting. A default order is
  stored as unset, so users who never reorder follow future default changes.
  The stored value always lists the catalog pages too, so an order chosen
  while the catalog is disabled keeps those pages in place when it is
  re-enabled. Unknown or duplicate names are ignored and missing pages are
  appended.
- Settings > User interface gets a "Navigation menu order" card with Reset.
- The nav ListBox now uses a plain StackPanel: with the default
  VirtualizingStackPanel, clearing and re-adding the ListBoxItems (which are
  their own containers) left them detached, so every page vanished after a
  reorder.
- Drag positions are measured against the items panel, so scrolling the
  sidebar mid-drag still drops where the pointer is, and the sidebar only
  listens to its view model while attached.
- SvgIcon's static theme brushes are now immutable. The mutable ones were
  bound to whichever UI thread first rendered an icon, which broke any
  headless test session that rendered icons after another one.

Tests: SidebarNavigationOrderTests drives the real SidebarView headlessly
(mouse drag both ways, touch drag including a scrollable sidebar, Escape,
Alt+arrows with focus kept, the announcement, scrolling mid-drag, detach and
reattach), and all of them fail if the StackPanel panel is removed.
SoftwareCatalogTests covers resolving, merging and serializing the stored
order.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Pointer handling still allows overlay dismissal to interrupt dragging and stale drag state to affect initiation or placement.

3 open findings
What changed in this PR

Adds customizable sidebar ordering so users can restore their preferred page layout and navigation shortcuts.

Changes:

  • Supports pointer dragging and keyboard reordering with persisted preferences.
  • Synchronizes shortcuts and macOS menus, and adds a reset setting.
  • Adds navigation tests and makes shared icon brushes immutable.
File Description
src/​UniGetUI.Tests/​SoftwareCatalogTests.cs Tests order persistence and shortcut ordering.
src/​UniGetUI.Tests/​SidebarNavigationOrderTests.cs Tests sidebar reordering interactions.
src/​UniGetUI.Core.Settings/​SettingsEngine_Names.cs Registers the navigation-order setting.
src/​UniGetUI.Avalonia/​Views/​SidebarView.axaml.cs Implements reordering and accessibility announcements.
src/​UniGetUI.Avalonia/​Views/​SidebarView.axaml Uses a nonvirtualizing items panel.
src/​UniGetUI.Avalonia/​Views/​Pages/​SettingsPages/​Interface_P.axaml.cs Connects reset and reorder instructions.
src/​UniGetUI.Avalonia/​Views/​Pages/​SettingsPages/​Interface_P.axaml Adds the navigation-order card.
src/​UniGetUI.Avalonia/​Views/​MainWindow.MacMenu.cs Updates menu shortcuts after reordering.
src/​UniGetUI.Avalonia/​Views/​MainWindow.axaml.cs Updates page cycling and exposes reset.
src/​UniGetUI.Avalonia/​Views/​Controls/​SvgIcon.cs Makes shared theme brushes immutable.
src/​UniGetUI.Avalonia/​ViewModels/​SidebarViewModel.cs Exposes reorderable navigation state.
src/​UniGetUI.Avalonia/​ViewModels/​MainWindowViewModel.cs Derives shortcuts from sidebar order.
src/​UniGetUI.Avalonia/​Infrastructure/​SettingsSearchIndex.cs Indexes the new settings card.
src/​UniGetUI.Avalonia/​Infrastructure/​NavigationPageOrder.cs Resolves, merges, and persists ordering.
src/​Languages/​lang_en.json Adds reorder instructions and announcements.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Active drags remain vulnerable to interference from secondary touches and crashes when the navigation order resets.

0 open findings

3 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Cancel active drag before resetting drag state

src/​UniGetUI.Avalonia/​Views/​SidebarView.axaml.cs:159

Resetting the order during an active drag clears _dragItems and _dragTops but leaves _isDragging true. With User interface settings open, holding a touch drag in the docked sidebar and activating Reset with another contact reaches this path. The next drag move or release indexes the empty arrays in UpdateDrag. Cancel the drag and release its captured pointer before rebuilding the items, and add a reset-during-drag regression test.

Medium severity Ignore unrelated pointers during an active drag

src/​UniGetUI.Avalonia/​Views/​SidebarView.axaml.cs:174

A second touch on another row overwrites _dragCandidate and _dragStart while the first finger is dragging. Move and release handlers also accept any pointer, so the second finger can move or commit the first finger's drag while it is still held; the release handler then releases the second pointer's capture instead of the drag owner's. Track the initiating pointer from press through cleanup and ignore unrelated pointer events, including capture loss. Add a two-contact regression test; the current touch helper always uses contact ID 1.

🧠 Review effort: Balanced

@randy-but-a-ro randy-but-a-ro 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.

🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉

Integration Details
{
	"deliveryId": "205af990-c8a8-11f1-8810-891aac94df29",
	"headSha": "b394c66da4dfcd06cc1583caf5c14a2e4b494e7a",
	"reviewer": "copilot-pull-request-reviewer[bot]"
}

@GabrielDuf
Gabriel Dufresne (GabrielDuf) merged commit 986c9e8 into main Oct 15, 2026
7 of 8 checks passed
@GabrielDuf
Gabriel Dufresne (GabrielDuf) deleted the feature/reorder-navigation-pages branch October 15, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants