Avalonia Port: MVVM Refactor, macOS Fixes, and UI Improvements#4529
Avalonia Port: MVVM Refactor, macOS Fixes, and UI Improvements#4529Gabriel Dufresne (GabrielDuf) wants to merge 7 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Ports additional UI interactions to MVVM for the Avalonia front-end by replacing code-behind click handlers with RelayCommand bindings, moving toolbar construction and several dialogs/navigation requests into ViewModels, and extracting platform/common styles into separate AXAML resource files (plus adding a {t:Translate} markup extension for inline translations).
Changes:
- Replaced many
Click=/code-behind handlers withCommandbindings and ViewModel-driven requests (navigation, help, share, manage-ignored). - Refactored settings pages to declarative AXAML (setting keys/labels/commands) with supporting Settings* control enhancements (command properties).
- Introduced per-platform/common style resources and a translation markup extension; updated several dialogs/windows to AXAML + ViewModel.
Reviewed changes
Copilot reviewed 78 out of 78 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/UniGetUI.Avalonia/Views/SoftwarePages/SoftwareUpdatesPage.cs | Toolbar actions moved toward ViewModel request commands (share/help/manage ignored). |
| src/UniGetUI.Avalonia/Views/SoftwarePages/PackageBundlesPage.cs | Toolbar actions moved toward ViewModel request commands (share/help). |
| src/UniGetUI.Avalonia/Views/SoftwarePages/InstalledPackagesPage.cs | Toolbar actions moved toward ViewModel request commands (share/help/manage ignored). |
| src/UniGetUI.Avalonia/Views/SoftwarePages/DiscoverSoftwarePage.cs | Toolbar actions moved toward ViewModel request commands (share/help). |
| src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs | Wires ViewModel events for dialogs/navigation; removes sort/search UI handlers; updates sort menu check syncing. |
| src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml | Converts sort/search to commands & translated strings; adds list header select-all; toolbar styling tweaks. |
| src/UniGetUI.Avalonia/Views/SidebarView.axaml.cs | Removes legacy click handlers; routes selection to ViewModel navigation request API. |
| src/UniGetUI.Avalonia/Views/SidebarView.axaml | Sidebar UI refresh (collapse support, tooltips, badges) and command-based navigation. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Updates.axaml.cs | Moves interval items/navigation/restart signaling to ViewModel. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Updates.axaml | Declarative settings wiring via SettingName/Text/commands + {t:Translate}. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsHomepage.axaml.cs | Replaces button click wiring with ViewModel navigation event; minor text tweak. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsHomepage.axaml | Switches settings homepage buttons to command bindings + translated strings. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsBasePage.axaml.cs | Removes direct button wiring; listens to VM back request. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsBasePage.axaml | Back/restart buttons now bind to VM commands; back icon updated. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Operations.axaml.cs | Moves restart/navigation and max-ops logic into ViewModel. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Operations.axaml | Declarative setting bindings + commands for restart/navigation/max-ops update. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Notifications.axaml.cs | Removes imperative settings wiring in code-behind. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Notifications.axaml | Declarative setting bindings + commands and translated strings. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Internet.axaml.cs | Moves credentials/proxy table building into ViewModel; wires restart event. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Internet.axaml | Declarative proxy settings bindings + commands and translated strings. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml.cs | Reduces code-behind; translation usage tweaks; restart signal wiring. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml | Declarative settings bindings/commands + translated strings; icon-cache header binding. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/General.axaml.cs | Moves restart/navigation signaling into ViewModel; keeps language list in code. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/General.axaml | Declarative settings wiring + translated strings and command binding. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Experimental.axaml.cs | Moves restart signaling into ViewModel; sets one label in code-behind. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Experimental.axaml | Declarative experimental settings wiring + translated strings + restart commands. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Backup.axaml.cs | Moves restart signaling/state init into ViewModel. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Backup.axaml | Declarative backup settings wiring + commands and translated strings. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Administrator.axaml.cs | Moves restart signaling into ViewModel; removes imperative wiring. |
| src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Administrator.axaml | Administrator settings converted to declarative bindings/commands; warning banner bound to VM strings. |
| src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs | Removes click handlers in favor of command bindings. |
| src/UniGetUI.Avalonia/Views/MainWindow.axaml | Adds sidebar toggle button; binds sidebar width; binds search submit command. |
| src/UniGetUI.Avalonia/Views/DialogPages/TelemetryDialog.axaml.cs | Removes string initialization moved to AXAML. |
| src/UniGetUI.Avalonia/Views/DialogPages/TelemetryDialog.axaml | Telemetry dialog strings moved to AXAML with {t:Translate}. |
| src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml.cs | Removes direct Close button handler; uses VM close request. |
| src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml | Close button now binds to VM command. |
| src/UniGetUI.Avalonia/Views/DialogPages/OperationOutputWindow.cs | Removes code-only operation output window. |
| src/UniGetUI.Avalonia/Views/DialogPages/OperationOutputWindow.axaml.cs | New AXAML-backed operation output window with VM. |
| src/UniGetUI.Avalonia/Views/DialogPages/OperationOutputWindow.axaml | Defines operation output window UI and bindings. |
| src/UniGetUI.Avalonia/Views/DialogPages/ManageIgnoredUpdatesWindow.axaml.cs | Adds flyout reset click handlers; hooks reset command execution. |
| src/UniGetUI.Avalonia/Views/DialogPages/ManageIgnoredUpdatesWindow.axaml | UI refinements + translated tooltip; flyout theme styling. |
| src/UniGetUI.Avalonia/Views/Controls/Settings/TranslatedTextBlock.cs | Clarifies preferred translation approach (markup extension). |
| src/UniGetUI.Avalonia/Views/Controls/Settings/TextboxCard.cs | Adds ValueChangedCommand; stops auto-translating setters. |
| src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsPageButton.cs | Stops auto-translating setters (expects translated input). |
| src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs | Adds Header/Description/Command styled properties and command execution on click. |
| src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs | Adds StateChangedCommand; improves warning layout/formatting; stops auto-translate in setters. |
| src/UniGetUI.Avalonia/Views/Controls/Settings/ComboboxCard.cs | Adds ValueChangedCommand; stops auto-translating header. |
| src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs | Adds StateChangedCommand; stops auto-translating setters. |
| src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs | Stops auto-translating setters (expects translated input). |
| src/UniGetUI.Avalonia/Views/Controls/Settings/ButtonCard.cs | Relies on base SettingsCard command properties; stops auto-translating setters. |
| src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs | Adds view-mode enum, toolbar builder APIs, sort/search commands, and dialog/navigation request events. |
| src/UniGetUI.Avalonia/ViewModels/SidebarViewModel.cs | Adds collapsible pane state, compact badges, navigation command API, and version label binding. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/UpdatesViewModel.cs | Adds restart/navigation events, interval list, and related commands. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/SettingsHomepageViewModel.cs | Adds navigation commands for each settings section. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/SettingsBasePageViewModel.cs | Adds Back/Restart commands and restart implementation. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/OperationsViewModel.cs | Adds max-ops update logic and navigation/restart commands. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/NotificationsViewModel.cs | Initializes state from settings and adds command to refresh enabled state. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/InternetViewModel.cs | Moves proxy UI construction and restart signaling into ViewModel; adds related commands. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/Interface_PViewModel.cs | Adds ShowRestartRequiredCommand. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/GeneralViewModel.cs | Adds navigation command; exposes restart command. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/ExperimentalViewModel.cs | Adds restart event and command. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/BackupViewModel.cs | Initializes local-backup state; adds command to refresh and request restart. |
| src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/AdministratorViewModel.cs | Adds restart signaling, warning text bindings, and state-refresh commands. |
| src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs | Adds sidebar toggle, banner close commands, and search submit command. |
| src/UniGetUI.Avalonia/ViewModels/DialogPages/PackageDetailsViewModel.cs | Adjusts several label strings; adds close relay command. |
| src/UniGetUI.Avalonia/ViewModels/DialogPages/OperationViewModel.cs | Switches to Toolkit observable properties via base ViewModel. |
| src/UniGetUI.Avalonia/ViewModels/DialogPages/OperationOutputViewModel.cs | New VM for operation output window (title + output text). |
| src/UniGetUI.Avalonia/ViewModels/DialogPages/ManageIgnoredUpdatesViewModel.cs | Text/label updates for ignored-updates management dialog. |
| src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj | Treats style-only AXAML as resources; adds dependent file mapping for new window. |
| src/UniGetUI.Avalonia/Models/PackageCollections.cs | Migrates observable package collection to AvaloniaList; adjusts displayed version string. |
| src/UniGetUI.Avalonia/MarkupExtensions/TranslateExtension.cs | Adds {t:Translate} markup extension for inline translated strings. |
| src/UniGetUI.Avalonia/Infrastructure/AvaloniaOperationRegistry.cs | Uses AvaloniaList for operation view models. |
| src/UniGetUI.Avalonia/Assets/Styles/Styles.macOS.axaml | New macOS-specific theme resources. |
| src/UniGetUI.Avalonia/Assets/Styles/Styles.Windows.axaml | New Windows-specific theme resources. |
| src/UniGetUI.Avalonia/Assets/Styles/Styles.Linux.axaml | New Linux-specific theme resources. |
| src/UniGetUI.Avalonia/Assets/Styles/Styles.Common.axaml | New shared styling (SettingsCard, warning banner, SvgIcon foreground, DataGrid theme include). |
| src/UniGetUI.Avalonia/App.axaml.cs | Loads platform stylesheet dynamically at startup. |
| src/UniGetUI.Avalonia/App.axaml | Removes inline resources/styles; includes shared styles file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <MenuItem x:Name="OrderByAscending_Menu" Header="{t:Translate Ascendant}" Command="{Binding SetSortAscendingCommand}"/> | ||
| <MenuItem x:Name="OrderByDescending_Menu" Header="{t:Translate Descendant}" Command="{Binding SetSortDescendingCommand}"/> |
There was a problem hiding this comment.
The sort direction labels use "Ascendant" / "Descendant", which are incorrect here (the standard UI terms are "Ascending" / "Descending"). This will also make translations inconsistent with existing strings previously used in this menu.
| [ObservableProperty] | ||
| private bool _isPaneOpen = false; | ||
|
|
||
| partial void OnIsPaneOpenChanged(bool value) | ||
| { | ||
| Settings.Set(Settings.K.CollapseNavMenuOnWideScreen, value); | ||
| OnPropertyChanged(nameof(PaneWidth)); | ||
| OnPropertyChanged(nameof(UpdatesBadgeExpandedVisible)); | ||
| OnPropertyChanged(nameof(UpdatesBadgeCompactVisible)); | ||
| OnPropertyChanged(nameof(BundlesBadgeExpandedVisible)); | ||
| OnPropertyChanged(nameof(BundlesBadgeCompactVisible)); | ||
| } |
There was a problem hiding this comment.
Sidebar pane state persistence appears inverted/missing: OnIsPaneOpenChanged stores the open state into Settings.K.CollapseNavMenuOnWideScreen, but that key is used elsewhere as "collapse when true" (e.g. src/UniGetUI/Pages/MainView.xaml.cs:184 opens the pane when the setting is false). Also, SidebarViewModel never initializes IsPaneOpen from this setting, so the user's preference won't be restored on startup. Consider (1) setting IsPaneOpen based on !Settings.Get(CollapseNavMenuOnWideScreen) during construction, and (2) persisting CollapseNavMenuOnWideScreen as !IsPaneOpen (or rename the setting if the semantics have changed).
| private void OnPointerPressed(object? sender, PointerPressedEventArgs e) | ||
| { | ||
| if (_isClickEnabled) | ||
| { | ||
| Click?.Invoke(this, new RoutedEventArgs()); | ||
| var cmd = Command; | ||
| var param = CommandParameter; | ||
| if (cmd?.CanExecute(param) == true) | ||
| cmd.Execute(param); | ||
| } |
There was a problem hiding this comment.
SettingsCard executes its Click/Command on any PointerPressed when IsClickEnabled=true, including right-clicks or other pointer buttons. This can cause accidental navigation/activation and may interfere with context menus. Consider restricting to a primary (left) button press and marking the event handled when you trigger the command.
|
|
||
| public string Title { get; } = CoreTools.Translate("Manage ignored updates"); | ||
| public string Description { get; } = CoreTools.Translate("The packages listed here won't be taken in account when checking for updates. Click the button on their right to stop ignoring their updates."); | ||
| public string Description { get; } = CoreTools.Translate("The packages listed here won't be taken in account when checking for updates. Double-click them or click the button on their right to stop ignoring their updates."); |
There was a problem hiding this comment.
Grammar: "won't be taken in account" should be "won't be taken into account".
| public string Description { get; } = CoreTools.Translate("The packages listed here won't be taken in account when checking for updates. Double-click them or click the button on their right to stop ignoring their updates."); | |
| public string Description { get; } = CoreTools.Translate("The packages listed here won't be taken into account when checking for updates. Double-click them or click the button on their right to stop ignoring their updates."); |
| <settings:CheckboxCard SettingName="DisableUpdatesNotifications" | ||
| Text="{t:Translate Show a notification when an operation fails}" | ||
| CornerRadius="0" | ||
| IsEnabled="{Binding IsNotificationsEnabled}"/> |
There was a problem hiding this comment.
The "operation fails" toggle is wired to SettingName="DisableUpdatesNotifications" (same as the updates toggle), so changing it will persist to the wrong setting and error notifications won't be independently configurable. Bind this card to the dedicated DisableErrorNotifications setting key instead (Settings.K.DisableErrorNotifications).
Full MVVM refactor of the Avalonia port: all user actions are now driven by [RelayCommand] bindings in AXAML, code-behind files are reduced to minimal UI wiring, and several macOS-specific crashes and visual issues have been fixed.
MVVM / Architecture
UI / Style
Bug fixes