diff --git a/src/UniGetUI.Avalonia/App.axaml.cs b/src/UniGetUI.Avalonia/App.axaml.cs
index 52ca50067..6abd79e4e 100644
--- a/src/UniGetUI.Avalonia/App.axaml.cs
+++ b/src/UniGetUI.Avalonia/App.axaml.cs
@@ -1,10 +1,8 @@
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
-using Avalonia.Markup.Xaml.Styling;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading;
@@ -23,25 +21,12 @@ namespace UniGetUI.Avalonia;
public partial class App : Application
{
- [UnconditionalSuppressMessage(
- "Trimming",
- "IL2026",
- Justification = "Platform theme dictionaries are Avalonia resources included in the app package; only the resource URI is selected dynamically.")]
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
#if AVALONIA_DIAGNOSTICS_ENABLED
this.AttachDeveloperTools();
#endif
-
- string platform = OperatingSystem.IsWindows() ? "Windows"
- : OperatingSystem.IsMacOS() ? "macOS"
- : "Linux";
-
- Styles.Add(new StyleInclude(new Uri("avares://UniGetUI.Avalonia/"))
- {
- Source = new Uri($"avares://UniGetUI.Avalonia/Assets/Styles/Styles.{platform}.axaml")
- });
}
public override void OnFrameworkInitializationCompleted()
diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.Common.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.Common.axaml
index d8c151aca..65ab004ad 100644
--- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.Common.axaml
+++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.Common.axaml
@@ -2,8 +2,155 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:UniGetUI.Avalonia.Views.Controls">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.Linux.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.Linux.axaml
deleted file mode 100644
index 5364da18e..000000000
--- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.Linux.axaml
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.Windows.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.Windows.axaml
deleted file mode 100644
index 6154a9ddb..000000000
--- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.Windows.axaml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.macOS.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.macOS.axaml
deleted file mode 100644
index 8d2909c11..000000000
--- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.macOS.axaml
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/UniGetUI.Avalonia/Models/PackageCollections.cs b/src/UniGetUI.Avalonia/Models/PackageCollections.cs
index 21b45e931..125c99342 100644
--- a/src/UniGetUI.Avalonia/Models/PackageCollections.cs
+++ b/src/UniGetUI.Avalonia/Models/PackageCollections.cs
@@ -296,6 +296,40 @@ public enum Sorter
public Sorter CurrentSorter { get; private set; } = Sorter.Name;
private bool _ascending = true;
+ /// Fires when any wrapper's IsChecked changes, or when items are added/removed.
+ public event EventHandler? SelectionStateChanged;
+
+ public ObservablePackageCollection()
+ {
+ CollectionChanged += OnCollectionChanged;
+ }
+
+ private void OnCollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ if (e.OldItems is not null)
+ foreach (PackageWrapper w in e.OldItems) w.PropertyChanged -= OnWrapperPropertyChanged;
+ if (e.NewItems is not null)
+ foreach (PackageWrapper w in e.NewItems) w.PropertyChanged += OnWrapperPropertyChanged;
+ SelectionStateChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void OnWrapperPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(PackageWrapper.IsChecked))
+ SelectionStateChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// Returns the tri-state value for a "select-all" checkbox: true=all, false=none, null=some.
+ public bool? GetSelectionState()
+ {
+ if (Count == 0) return false;
+ int checkedCount = 0;
+ foreach (var w in this) if (w.IsChecked) checkedCount++;
+ if (checkedCount == 0) return false;
+ if (checkedCount == Count) return true;
+ return null;
+ }
+
public List GetPackages() =>
this.Select(w => w.Package).ToList();
diff --git a/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs
index 34fcb3076..4b3ccba2d 100644
--- a/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs
+++ b/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs
@@ -127,6 +127,14 @@ private void OnPageViewModelPropertyChanged(object? sender, System.ComponentMode
}
}
+ // ─── Title bar ───────────────────────────────────────────────────────────
+ // Mirrors WinUI behavior: the version appears next to "UniGetUI" only when
+ // the ShowVersionNumberOnTitlebar setting is enabled (the setting is gated
+ // on restart, so a one-shot read at construction is sufficient).
+ public string TitleBarText { get; } = Settings.Get(Settings.K.ShowVersionNumberOnTitlebar)
+ ? $"UniGetUI {CoreTools.Translate("version {0}", CoreData.VersionName)}"
+ : "UniGetUI";
+
// ─── Banners ─────────────────────────────────────────────────────────────
public InfoBarViewModel UpdatesBanner { get; } = new() { Severity = InfoBarSeverity.Success };
public InfoBarViewModel ErrorBanner { get; } = new() { Severity = InfoBarSeverity.Error };
diff --git a/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs
index 8b6c0daac..310e46ea4 100644
--- a/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs
+++ b/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs
@@ -70,7 +70,16 @@ public class SourceTreeNode : INotifyPropertyChanged
public string? PackageID { get; init; }
public string? Version { get; init; }
public string? Source { get; init; }
- public AvaloniaList Children { get; } = new();
+ public AvaloniaList Children { get; }
+
+ public bool HasChildren => Children.Count > 0;
+
+ public SourceTreeNode()
+ {
+ Children = new AvaloniaList();
+ Children.CollectionChanged += (_, _) =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasChildren)));
+ }
public event PropertyChangedEventHandler? PropertyChanged;
@@ -91,7 +100,21 @@ public bool IsExpanded
public partial class PackagesPageViewModel : ViewModelBase
{
- public double FilterPaneColumnWidth => IsFilterPaneOpen ? 220.0 : 0.0;
+ // Live width of the filter pane. Code-behind keeps this in sync with the GridSplitter
+ // so the toolbar's main button (bound to FilterPaneColumnWidth) tracks resizes.
+ private double _trackedFilterPaneWidth = 220.0;
+ public double TrackedFilterPaneWidth
+ {
+ get => _trackedFilterPaneWidth;
+ set
+ {
+ if (Math.Abs(_trackedFilterPaneWidth - value) < 0.5) return;
+ _trackedFilterPaneWidth = value;
+ if (IsFilterPaneOpen) OnPropertyChanged(nameof(FilterPaneColumnWidth));
+ }
+ }
+
+ public double FilterPaneColumnWidth => IsFilterPaneOpen ? _trackedFilterPaneWidth : 0.0;
partial void OnIsFilterPaneOpenChanged(bool value)
{
OnPropertyChanged(nameof(FilterPaneColumnWidth));
@@ -195,6 +218,11 @@ public PackagesPageViewModel(PackagesPageData data)
SearchBoxPlaceholder = CoreTools.Translate("Search for packages");
AllPackagesChecked = data.PackagesAreCheckedByDefault;
+ FilteredPackages.SelectionStateChanged += (_, _) =>
+ {
+ if (_suppressSelectionRecompute) return;
+ AllPackagesChecked = FilteredPackages.GetSelectionState();
+ };
Loader = data.Loader;
Loader.StartedLoading += Loader_StartedLoading;
@@ -241,12 +269,12 @@ public Button AddToolbarButton(string svgName, string label, Action onClick, boo
var icon = new SvgIcon
{
Path = $"avares://UniGetUI.Avalonia/Assets/Symbols/{svgName}.svg",
- Width = 16,
- Height = 16,
+ Width = 20,
+ Height = 20,
VerticalAlignment = VerticalAlignment.Center,
};
- var content = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 4 };
+ var content = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 6 };
content.Children.Add(icon);
if (showLabel)
{
@@ -260,8 +288,8 @@ public Button AddToolbarButton(string svgName, string label, Action onClick, boo
var btn = new Button
{
- Height = 36,
- Padding = new Thickness(8, 4),
+ Height = 40,
+ Padding = new Thickness(10, 4),
CornerRadius = new CornerRadius(4),
Content = content,
};
@@ -275,13 +303,19 @@ public Button AddToolbarButton(string svgName, string label, Action onClick, boo
/// Adds a thin vertical separator to the toolbar.
public void AddToolbarSeparator()
{
+ object? borderResource = null;
+ Application.Current?.Resources.TryGetResource(
+ "AppBorderBrush",
+ Application.Current?.ActualThemeVariant,
+ out borderResource);
+
var sep = new Separator
{
Width = 1,
- Height = 30,
+ Height = 32,
Margin = new Thickness(4, 4),
- Background = Application.Current?.FindResource("AppBorderBrush") as IBrush
- ?? new SolidColorBrush(Color.FromArgb(60, 255, 255, 255)),
+ Background = borderResource as IBrush
+ ?? new SolidColorBrush(Color.FromArgb(80, 128, 128, 128)),
};
AutomationProperties.SetAccessibilityView(sep, AccessibilityView.Raw);
ToolBarItems.Add(sep);
@@ -559,10 +593,19 @@ partial void OnSearchModeChanged(SearchMode value)
public bool SearchMode_Exact { get => SearchMode == SearchMode.Exact; set { if (value) SearchMode = SearchMode.Exact; } }
public bool SearchMode_Similar { get => SearchMode == SearchMode.Similar; set { if (value) SearchMode = SearchMode.Similar; } }
+ private bool _suppressSelectionRecompute;
partial void OnAllPackagesCheckedChanged(bool? value)
{
- if (value == true) FilteredPackages.SelectAll();
- else if (value == false) FilteredPackages.ClearSelection();
+ _suppressSelectionRecompute = true;
+ try
+ {
+ if (value == true) FilteredPackages.SelectAll();
+ else if (value == false) FilteredPackages.ClearSelection();
+ }
+ finally
+ {
+ _suppressSelectionRecompute = false;
+ }
}
// ─── Sources ──────────────────────────────────────────────────────────────
diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs
index 7cd29d8e6..c54a661a5 100644
--- a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs
+++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs
@@ -15,6 +15,9 @@ public sealed partial class CheckboxButtonCard : SettingsCard
public ToggleSwitch _checkbox;
public TextBlock _textblock;
public Button Button;
+ private readonly TextBlock _stateLabel;
+ private static readonly string EnabledLabel = CoreTools.Translate("Enabled");
+ private static readonly string DisabledLabel = CoreTools.Translate("Disabled");
private bool IS_INVERTED;
private CoreSettings.K setting_name = CoreSettings.K.Unset;
@@ -26,6 +29,7 @@ public CoreSettings.K SettingName
IS_INVERTED = CoreSettings.ResolveKey(value).StartsWith("Disable");
_checkbox.IsChecked = CoreSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion;
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
Button.IsEnabled = (_checkbox.IsChecked ?? false) || _buttonAlwaysOn;
}
}
@@ -70,10 +74,19 @@ public CheckboxButtonCard()
Button = new Button { Margin = new Thickness(0, 8, 0, 0) };
_checkbox = new ToggleSwitch
{
+ // OnContent/OffContent intentionally left null — state label is a
+ // sibling TextBlock to the LEFT of the knob.
+ OnContent = null,
+ OffContent = null,
+ VerticalAlignment = VerticalAlignment.Center,
+ };
+ _stateLabel = new TextBlock
+ {
+ Text = DisabledLabel,
+ VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 0, 8, 0),
- OnContent = new TextBlock { Text = CoreTools.Translate("Enabled") },
- OffContent = new TextBlock { Text = CoreTools.Translate("Disabled") },
};
+ AutomationProperties.SetAccessibilityView(_stateLabel, AccessibilityView.Raw);
_textblock = new TextBlock
{
Margin = new Thickness(2, 0, 0, 0),
@@ -84,7 +97,12 @@ public CheckboxButtonCard()
IS_INVERTED = false;
AutomationProperties.SetAccessibilityView(Button, AccessibilityView.Control);
- Content = _checkbox;
+ Content = new StackPanel
+ {
+ Orientation = Orientation.Horizontal,
+ VerticalAlignment = VerticalAlignment.Center,
+ Children = { _stateLabel, _checkbox },
+ };
Header = _textblock;
Description = Button;
@@ -94,6 +112,7 @@ public CheckboxButtonCard()
StateChanged?.Invoke(this, EventArgs.Empty);
Button.IsEnabled = (_checkbox.IsChecked ?? false) ? true : _buttonAlwaysOn;
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
if (_textblock.Text is not null)
{
AccessibilityAnnouncementService.AnnounceToggle(_textblock.Text, _checkbox.IsChecked ?? false);
@@ -102,4 +121,9 @@ public CheckboxButtonCard()
Button.Click += (s, e) => Click?.Invoke(s, e);
ApplyAutomationMetadata(_checkbox, _textblock.Text);
}
+
+ private void UpdateStateLabel()
+ {
+ _stateLabel.Text = (_checkbox.IsChecked ?? false) ? EnabledLabel : DisabledLabel;
+ }
}
diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs
index a1e24c950..cebe2c0eb 100644
--- a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs
+++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs
@@ -26,6 +26,9 @@ public ICommand? StateChangedCommand
public ToggleSwitch _checkbox;
public TextBlock _textblock;
public TextBlock _warningBlock;
+ private readonly TextBlock _stateLabel;
+ private static readonly string EnabledLabel = CoreTools.Translate("Enabled");
+ private static readonly string DisabledLabel = CoreTools.Translate("Disabled");
protected bool IS_INVERTED;
private CoreSettings.K setting_name = CoreSettings.K.Unset;
@@ -38,6 +41,7 @@ public CoreSettings.K SettingName
IS_INVERTED = CoreSettings.ResolveKey(value).StartsWith("Disable");
_checkbox.IsChecked = CoreSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion;
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
_checkbox.IsCheckedChanged += _checkbox_Toggled;
SyncToggleItemStatus();
}
@@ -77,12 +81,21 @@ public CheckboxCard()
{
_checkbox = new ToggleSwitch
{
- Margin = new Thickness(0, 0, 8, 0),
- OnContent = new TextBlock { Text = CoreTools.Translate("Enabled") },
- OffContent = new TextBlock { Text = CoreTools.Translate("Disabled") },
+ // OnContent/OffContent intentionally left null — the state label is
+ // rendered as a sibling TextBlock to the LEFT of the knob below.
+ OnContent = null,
+ OffContent = null,
+ VerticalAlignment = VerticalAlignment.Center,
};
// Force CheckBox role so macOS VoiceOver exposes checked/unchecked state
AutomationProperties.SetControlTypeOverride(_checkbox, AutomationControlType.CheckBox);
+ _stateLabel = new TextBlock
+ {
+ Text = DisabledLabel,
+ VerticalAlignment = VerticalAlignment.Center,
+ Margin = new Thickness(0, 0, 8, 0),
+ };
+ AutomationProperties.SetAccessibilityView(_stateLabel, AccessibilityView.Raw);
_textblock = new TextBlock
{
VerticalAlignment = VerticalAlignment.Center,
@@ -100,7 +113,12 @@ public CheckboxCard()
IS_INVERTED = false;
AutomationProperties.SetAccessibilityView(_warningBlock, AccessibilityView.Raw);
- Content = _checkbox;
+ Content = new StackPanel
+ {
+ Orientation = Orientation.Horizontal,
+ VerticalAlignment = VerticalAlignment.Center,
+ Children = { _stateLabel, _checkbox },
+ };
Header = new StackPanel
{
Spacing = 4,
@@ -112,11 +130,17 @@ public CheckboxCard()
ApplyAutomationMetadata(_checkbox, _textblock.Text);
}
+ protected void UpdateStateLabel()
+ {
+ _stateLabel.Text = (_checkbox.IsChecked ?? false) ? EnabledLabel : DisabledLabel;
+ }
+
protected virtual void _checkbox_Toggled(object? sender, RoutedEventArgs e)
{
CoreSettings.Set(setting_name, (_checkbox.IsChecked ?? false) ^ IS_INVERTED ^ ForceInversion);
StateChanged?.Invoke(this, EventArgs.Empty);
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
SyncToggleItemStatus();
if (_textblock.Text is not null)
{
@@ -165,6 +189,7 @@ public string KeyName
^ IS_INVERTED
^ ForceInversion;
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
_disableStateChangedEvent = false;
SyncToggleItemStatus();
}
@@ -184,6 +209,7 @@ public CoreSettings.K DictionaryName
^ IS_INVERTED
^ ForceInversion;
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
SyncToggleItemStatus();
}
}
@@ -201,6 +227,7 @@ protected override void _checkbox_Toggled(object? sender, RoutedEventArgs e)
);
StateChanged?.Invoke(this, EventArgs.Empty);
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
SyncToggleItemStatus();
if (_textblock.Text is not null)
{
diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs
index 7332c4432..a0aa8e79d 100644
--- a/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs
+++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs
@@ -25,6 +25,9 @@ public ICommand? StateChangedCommand
public TextBlock _textblock;
public TextBlock _warningBlock;
public ProgressBar _loading; // Avalonia has no ProgressRing; use indeterminate ProgressBar
+ private readonly TextBlock _stateLabel;
+ private static readonly string EnabledLabel = CoreTools.Translate("Enabled");
+ private static readonly string DisabledLabel = CoreTools.Translate("Disabled");
private bool IS_INVERTED;
private SecureSettings.K setting_name = SecureSettings.K.Unset;
@@ -37,6 +40,7 @@ public SecureSettings.K SettingName
IS_INVERTED = SecureSettings.ResolveKey(value).StartsWith("Disable");
_checkbox.IsChecked = SecureSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion;
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
+ UpdateStateLabel();
_checkbox.IsEnabled = true;
}
}
@@ -77,9 +81,17 @@ public SecureCheckboxCard()
{
_checkbox = new ToggleSwitch
{
+ // OnContent/OffContent intentionally left null — the state label is
+ // a sibling TextBlock placed to the LEFT of the knob below.
+ OnContent = null,
+ OffContent = null,
+ VerticalAlignment = VerticalAlignment.Center,
+ };
+ _stateLabel = new TextBlock
+ {
+ Text = DisabledLabel,
+ VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 0, 8, 0),
- OnContent = new TextBlock { Text = CoreTools.Translate("Enabled") },
- OffContent = new TextBlock { Text = CoreTools.Translate("Disabled") },
};
_loading = new ProgressBar
{
@@ -108,7 +120,8 @@ public SecureCheckboxCard()
{
Spacing = 4,
Orientation = Orientation.Horizontal,
- Children = { _loading, _checkbox },
+ VerticalAlignment = VerticalAlignment.Center,
+ Children = { _loading, _stateLabel, _checkbox },
};
Header = new StackPanel
{
@@ -150,6 +163,7 @@ await SecureSettings.TrySet(
cmd.Execute(null);
_textblock.Opacity = (_checkbox.IsChecked ?? false) ? 1 : 0.7;
_checkbox.IsChecked = SecureSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion;
+ UpdateStateLabel();
if (_textblock.Text is not null)
{
AccessibilityAnnouncementService.AnnounceToggle(_textblock.Text, _checkbox.IsChecked ?? false);
@@ -161,8 +175,14 @@ await SecureSettings.TrySet(
{
Logger.Warn(ex);
_checkbox.IsChecked = SecureSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion;
+ UpdateStateLabel();
_loading.IsVisible = false;
_checkbox.IsEnabled = true;
}
}
+
+ private void UpdateStateLabel()
+ {
+ _stateLabel.Text = (_checkbox.IsChecked ?? false) ? EnabledLabel : DisabledLabel;
+ }
}
diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml b/src/UniGetUI.Avalonia/Views/MainWindow.axaml
index c9c8d0d9d..4044c15d8 100644
--- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml
+++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml
@@ -310,7 +310,8 @@
VerticalAlignment="Center"
Orientation="Horizontal"
Margin="65,0,8,0"
- Spacing="8">
+ Spacing="8"
+ Opacity="0.6">
-
@@ -334,7 +335,8 @@
VerticalAlignment="Center"
automation:AutomationProperties.AccessibilityView="Control"
Orientation="Horizontal"
- Spacing="0">
+ Spacing="0"
+ Opacity="0.6">
-
-
+
-
+
-
+ VerticalAlignment="Center">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ IsVisible="{Binding IsExpanded}"
+ Margin="24,0,0,0">
+ GridLinesVisibility="None">
@@ -693,7 +740,10 @@
-
+
@@ -703,7 +753,7 @@
VerticalAlignment="Center">
-
+
@@ -748,17 +803,45 @@
-
-
+
+
+
+
+
+
-
-
+
+
-
-
+
+
diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs
index 815388bce..b490a9e40 100644
--- a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs
+++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs
@@ -95,11 +95,13 @@ or nameof(PackagesPageViewModel.SortAscending))
if (width.IsAbsolute && width.Value >= 100)
{
_savedFilterPaneWidth = width.Value;
+ ViewModel.TrackedFilterPaneWidth = width.Value;
Settings.SetDictionaryItem(Settings.K.SidepanelWidths, ViewModel.PageName, (int)width.Value);
}
else if (width.IsAbsolute && width.Value < 100)
{
_savedFilterPaneWidth = 220;
+ ViewModel.TrackedFilterPaneWidth = 220;
ViewModel.IsFilterPaneOpen = false;
}
});
@@ -126,6 +128,7 @@ or nameof(PackagesPageViewModel.SortAscending))
// Restore per-page filter pane width from settings.
var savedWidth = Settings.GetDictionaryItem(Settings.K.SidepanelWidths, ViewModel.PageName);
if (savedWidth >= 100) _savedFilterPaneWidth = savedWidth;
+ ViewModel.TrackedFilterPaneWidth = _savedFilterPaneWidth;
// Apply the initial filter-pane state (AXAML defaults to 220px open).
UpdateFilterPaneColumn(ViewModel.IsFilterPaneOpen);
diff --git a/src/UniGetUI.Interface.IpcApi/UniGetUI.Interface.IpcApi.csproj b/src/UniGetUI.Interface.IpcApi/UniGetUI.Interface.IpcApi.csproj
index 88db87634..968ac0ebd 100644
--- a/src/UniGetUI.Interface.IpcApi/UniGetUI.Interface.IpcApi.csproj
+++ b/src/UniGetUI.Interface.IpcApi/UniGetUI.Interface.IpcApi.csproj
@@ -1,6 +1,7 @@
-
+
+
diff --git a/src/UniGetUI.Interface.IpcApi/generate-secrets.sh b/src/UniGetUI.Interface.IpcApi/generate-secrets.sh
new file mode 100755
index 000000000..8e939cb6b
--- /dev/null
+++ b/src/UniGetUI.Interface.IpcApi/generate-secrets.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+OUTPUT_PATH="${1:-obj/}"
+
+if [ ! -d "${OUTPUT_PATH}Generated Files" ]; then mkdir -p "${OUTPUT_PATH}Generated Files"; fi
+
+CLIENT_ID="${UNIGETUI_GITHUB_CLIENT_ID}"
+if [ -z "$CLIENT_ID" ]; then CLIENT_ID="CLIENT_ID_UNSET"; fi
+
+cat > "${OUTPUT_PATH}Generated Files/Secrets.Generated.cs" << CSEOF
+// Auto-generated file - do not modify
+namespace UniGetUI.Interface
+{
+ internal static partial class Secrets
+ {
+ public static partial string GetGitHubClientId() => "$CLIENT_ID";
+ }
+}
+CSEOF