From ec54a320d2225b127ecabcaed386c14e3445d170 Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 09:13:59 -0400 Subject: [PATCH 01/21] Add native vibrancy backdrop to the macOS main window --- src/UniGetUI.Avalonia/App.axaml.cs | 5 +- .../Assets/Styles/Styles.MacGlass.axaml | 78 +++++++++ .../Assets/Styles/Styles.MacGlass.axaml.cs | 12 ++ .../Infrastructure/MacGlassWindowHelper.cs | 156 ++++++++++++++++++ .../Views/MainWindow.axaml.cs | 1 + 5 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml create mode 100644 src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml.cs create mode 100644 src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs diff --git a/src/UniGetUI.Avalonia/App.axaml.cs b/src/UniGetUI.Avalonia/App.axaml.cs index 26d398000c..4496d9a733 100644 --- a/src/UniGetUI.Avalonia/App.axaml.cs +++ b/src/UniGetUI.Avalonia/App.axaml.cs @@ -35,9 +35,12 @@ public override void Initialize() // Windows 11 Mica look is opt-in per environment: only merge the translucent // surface overrides when Mica is actually usable (Win11 + transparency on). - // macOS, Linux, Windows 10, and transparency-off all keep the solid Styles.Common look. + // macOS gets its own vibrancy overrides (Styles.MacGlass) unless Reduce transparency is on; + // Linux, Windows 10, and transparency-off all keep the solid Styles.Common look. if (MicaWindowHelper.IsMicaEnabled()) ApplyWindowsMicaStyling(); + else if (MacGlassWindowHelper.IsGlassEnabled()) + Resources.MergedDictionaries.Add(new MacGlassStyles()); #if AVALONIA_DIAGNOSTICS_ENABLED this.AttachDeveloperTools(); #endif diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml new file mode 100644 index 0000000000..159bc08479 --- /dev/null +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml.cs b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml.cs new file mode 100644 index 0000000000..aa1a904046 --- /dev/null +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace UniGetUI.Avalonia.Assets.Styles; + +public partial class MacGlassStyles : ResourceDictionary +{ + public MacGlassStyles() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs b/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs new file mode 100644 index 0000000000..054f7698e4 --- /dev/null +++ b/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs @@ -0,0 +1,156 @@ +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using UniGetUI.Avalonia.Assets.Styles; +using UniGetUI.Core.Logging; + +namespace UniGetUI.Avalonia.Infrastructure; + +/// macOS counterpart of : native vibrancy (AcrylicBlur) behind a +/// transparent window. Skipped under "Reduce transparency", keeping the solid Styles.Common look. +internal static partial class MacGlassWindowHelper +{ + private const string ObjCRuntime = "/usr/lib/libobjc.A.dylib"; + + // AppKit NSVisualEffectMaterial / NSVisualEffectBlendingMode values. + private const long NSVisualEffectMaterialUnderWindowBackground = 21; + private const long NSVisualEffectBlendingModeBehindWindow = 0; + + private static bool? _reduceTransparency; + private static bool _glassConfirmedUnavailable; + + public static bool IsGlassEnabled() + => OperatingSystem.IsMacOS() + && !_glassConfirmedUnavailable + && !ShouldReduceTransparency(); + + /// Requests the vibrancy backdrop. Call before the window is shown. + public static void Apply(Window window) + { + if (!IsGlassEnabled()) + return; + + window.TransparencyLevelHint = new[] { WindowTransparencyLevel.AcrylicBlur }; + window.Bind(Window.BackgroundProperty, window.GetResourceObservable("MacGlassWindowTint")); + window.Opened += (_, _) => OnOpened(window); + } + + private static void OnOpened(Window window) + { + // A transparent window without a granted backdrop shows whatever is behind it unblurred, + // so drop back to the solid surfaces for the whole session if the request was refused. + if (window.ActualTransparencyLevel != WindowTransparencyLevel.AcrylicBlur) + { + Logger.Warn($"macOS vibrancy was not granted (got {window.ActualTransparencyLevel}); using solid surfaces"); + FallBackToSolid(window); + return; + } + + try + { + UseAdaptiveMaterial(window); + } + catch (Exception ex) + { + Logger.Warn("Could not switch the macOS vibrancy material; keeping Avalonia's default"); + Logger.Warn(ex); + } + } + + private static void FallBackToSolid(Window window) + { + _glassConfirmedUnavailable = true; + + if (Application.Current is { } app) + { + for (int i = app.Resources.MergedDictionaries.Count - 1; i >= 0; i--) + { + if (app.Resources.MergedDictionaries[i] is MacGlassStyles) + app.Resources.MergedDictionaries.RemoveAt(i); + } + } + + window.TransparencyLevelHint = new[] { WindowTransparencyLevel.None }; + window.ClearValue(Window.BackgroundProperty); + if (window.TryFindResource("AppWindowBackground", window.ActualThemeVariant, out object? bg) && bg is IBrush brush) + window.Background = brush; + } + + // Avalonia creates its behind-window NSVisualEffectView with the deprecated, always-light + // NSVisualEffectMaterialLight. A semantic material follows the window's light/dark appearance. + private static void UseAdaptiveMaterial(Window window) + { + nint nsWindow = GetNSWindow(window); + if (nsWindow == 0) + return; + + nint effectViewClass = objc_getClass("NSVisualEffectView"); + nint contentView = objc_msgSend_IntPtr(nsWindow, sel_registerName("contentView")); + nint subviews = objc_msgSend_IntPtr(contentView, sel_registerName("subviews")); + long count = objc_msgSend_IntPtr(subviews, sel_registerName("count")); + + for (long i = 0; i < count; i++) + { + nint view = objc_msgSend_IntPtr_Long(subviews, sel_registerName("objectAtIndex:"), i); + if (!objc_msgSend_Bool_IntPtr(view, sel_registerName("isKindOfClass:"), effectViewClass) + || objc_msgSend_IntPtr(view, sel_registerName("blendingMode")) != NSVisualEffectBlendingModeBehindWindow) + continue; + + objc_msgSend_IntPtr_Long(view, sel_registerName("setMaterial:"), NSVisualEffectMaterialUnderWindowBackground); + } + } + + private static nint GetNSWindow(Window window) + { + if (window.TryGetPlatformHandle() is not { Handle: not 0 } handle) + return 0; + + return handle.HandleDescriptor == "NSWindow" + ? handle.Handle + : objc_msgSend_IntPtr(handle.Handle, sel_registerName("window")); + } + + // [NSWorkspace sharedWorkspace].accessibilityDisplayShouldReduceTransparency, read once per session. + private static bool ShouldReduceTransparency() + { + if (_reduceTransparency is { } cached) + return cached; + + bool reduce = false; + try + { + nint workspace = objc_msgSend_IntPtr(objc_getClass("NSWorkspace"), sel_registerName("sharedWorkspace")); + if (workspace != 0) + reduce = objc_msgSend_Bool(workspace, sel_registerName("accessibilityDisplayShouldReduceTransparency")); + } + catch (Exception ex) + { + Logger.Warn("Could not read the macOS Reduce transparency setting; assuming it is off"); + Logger.Warn(ex); + } + + _reduceTransparency = reduce; + return reduce; + } + + [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] + private static partial nint objc_getClass(string name); + + [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] + private static partial nint sel_registerName(string name); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + private static partial nint objc_msgSend_IntPtr(nint receiver, nint selector); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + private static partial nint objc_msgSend_IntPtr_Long(nint receiver, nint selector, long arg); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + [return: MarshalAs(UnmanagedType.U1)] + private static partial bool objc_msgSend_Bool(nint receiver, nint selector); + + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + [return: MarshalAs(UnmanagedType.U1)] + private static partial bool objc_msgSend_Bool_IntPtr(nint receiver, nint selector, nint arg); +} diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs index 5c04322ffd..0b38808491 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs @@ -575,6 +575,7 @@ private void SetupTitleBar() ExtendClientAreaToDecorationsHint = true; ExtendClientAreaTitleBarHeightHint = 44; ApplyMacTitleBarControlSizes(); + MacGlassWindowHelper.Apply(this); // Drive height/inset from code rather than the AXAML bindings: in fullscreen the native // title bar is hidden and WindowDecorationMargin collapses to 0, which would clip the From 9a4912e96d37d43bc4dda146f175ec291528b82a Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 09:36:18 -0400 Subject: [PATCH 02/21] Float the macOS sidebar as a glass panel over an opaque window --- .../Assets/Styles/Styles.MacGlass.axaml | 29 +++++++- .../Infrastructure/MacGlassWindowHelper.cs | 11 ++- src/UniGetUI.Avalonia/Views/MainWindow.axaml | 47 ++++++++++++- .../Views/MainWindow.axaml.cs | 67 ++++++++++++++++++- 4 files changed, 144 insertions(+), 10 deletions(-) diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml index 159bc08479..d30fd32775 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml @@ -3,11 +3,24 @@ x:Class="UniGetUI.Avalonia.Assets.Styles.MacGlassStyles"> + 18 + - - + + + + + + + + + + + + 0 4 16 0 #1A000000 @@ -41,7 +54,17 @@ - + + + + + + + + + + + 0 6 20 0 #4D000000 diff --git a/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs b/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs index 054f7698e4..1e6008ec24 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs @@ -11,10 +11,13 @@ namespace UniGetUI.Avalonia.Infrastructure; /// transparent window. Skipped under "Reduce transparency", keeping the solid Styles.Common look. internal static partial class MacGlassWindowHelper { + /// Class set on a window while its glass look is active; layout and styles key off it. + public const string GLASS_CLASS = "mac-glass"; + private const string ObjCRuntime = "/usr/lib/libobjc.A.dylib"; // AppKit NSVisualEffectMaterial / NSVisualEffectBlendingMode values. - private const long NSVisualEffectMaterialUnderWindowBackground = 21; + private const long NSVisualEffectMaterialSidebar = 7; private const long NSVisualEffectBlendingModeBehindWindow = 0; private static bool? _reduceTransparency; @@ -32,7 +35,8 @@ public static void Apply(Window window) return; window.TransparencyLevelHint = new[] { WindowTransparencyLevel.AcrylicBlur }; - window.Bind(Window.BackgroundProperty, window.GetResourceObservable("MacGlassWindowTint")); + window.Background = Brushes.Transparent; + window.Classes.Add(GLASS_CLASS); window.Opened += (_, _) => OnOpened(window); } @@ -72,6 +76,7 @@ private static void FallBackToSolid(Window window) } window.TransparencyLevelHint = new[] { WindowTransparencyLevel.None }; + window.Classes.Remove(GLASS_CLASS); window.ClearValue(Window.BackgroundProperty); if (window.TryFindResource("AppWindowBackground", window.ActualThemeVariant, out object? bg) && bg is IBrush brush) window.Background = brush; @@ -97,7 +102,7 @@ private static void UseAdaptiveMaterial(Window window) || objc_msgSend_IntPtr(view, sel_registerName("blendingMode")) != NSVisualEffectBlendingModeBehindWindow) continue; - objc_msgSend_IntPtr_Long(view, sel_registerName("setMaterial:"), NSVisualEffectMaterialUnderWindowBackground); + objc_msgSend_IntPtr_Long(view, sel_registerName("setMaterial:"), NSVisualEffectMaterialSidebar); } } diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml b/src/UniGetUI.Avalonia/Views/MainWindow.axaml index 675c0d1392..372d2f0a5c 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml @@ -22,6 +22,7 @@ @@ -58,6 +59,7 @@ value, which would beat the .open setter). --> + + + + + + + + + + + + + @@ -663,7 +707,6 @@ VerticalAlignment="Center" Margin="0,0,40,0" Height="32" - CornerRadius="5" BorderThickness="1" ClipToBounds="True" automation:AutomationProperties.AccessibilityView="Control"> diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs index 0b38808491..79677d7e64 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs @@ -56,6 +56,9 @@ public partial class MainWindow : Window // AXAML size would overflow past the top of the window (see SetupTitleBar). private const double MAC_TITLE_BAR_CONTROL_SIZE = 24; + // Gap between the window edge and the floating glass sidebar panel on macOS. + private const double MAC_GLASS_INSET = 8; + // Centre line of the macOS traffic lights, measured from the top of the window: they sit centred in // the native 28 px NSTitlebarContainerView. This can't be read back from WindowDecorationMargin, // because ExtendClientAreaTitleBarHeightHint makes that report our requested 44 px instead. @@ -574,8 +577,8 @@ private void SetupTitleBar() // traffic lights' centre line (see ApplyMacTitleBarLayout / MAC_TRAFFIC_LIGHT_*). ExtendClientAreaToDecorationsHint = true; ExtendClientAreaTitleBarHeightHint = 44; - ApplyMacTitleBarControlSizes(); MacGlassWindowHelper.Apply(this); + ApplyMacTitleBarControlSizes(); // Drive height/inset from code rather than the AXAML bindings: in fullscreen the native // title bar is hidden and WindowDecorationMargin collapses to 0, which would clip the @@ -596,7 +599,7 @@ void ApplyMacTitleBarLayout() } TitleBarGrid.Height = _macTitleBarHeight; - MainContentRoot.Margin = new Thickness(0, _macTitleBarHeight, 0, 0); + ApplyMacSidebarLayout(); if (fullScreen) { @@ -620,6 +623,11 @@ void ApplyMacTitleBarLayout() this.GetObservable(WindowStateProperty).SubscribeValue(_ => ApplyMacTitleBarLayout()); this.GetObservable(WindowDecorationMarginProperty).SubscribeValue(_ => ApplyMacTitleBarLayout()); + NavRail.GetObservable(IsVisibleProperty).SubscribeValue(_ => ApplyMacSidebarLayout()); + NavDock.GetObservable(IsVisibleProperty).SubscribeValue(_ => ApplyMacSidebarLayout()); + Classes.CollectionChanged += (_, _) => ApplyMacSidebarLayout(); + SidebarGlass.GetObservable(BoundsProperty).SubscribeValue(_ => UpdateMacWindowSurfaceClip()); + MacWindowSurface.GetObservable(BoundsProperty).SubscribeValue(_ => UpdateMacWindowSurfaceClip()); } else if (OperatingSystem.IsWindows()) { @@ -675,6 +683,61 @@ void ApplyMacTitleBarLayout() /// a button centred on the traffic lights' centre line would overflow past the top of the window. /// The centred search pill is unaffected — it stays centred in the full 44 px bar. /// + // With the glass look the sidebar floats as an inset panel: the labelled dock runs full height + // around the traffic lights, while the narrow rail (and the overlay aligned to it) starts below them. + private void ApplyMacSidebarLayout() + { + double bar = _macTitleBarHeight; + bool glass = Classes.Contains(MacGlassWindowHelper.GLASS_CLASS); + + if (!glass) + { + MacWindowSurface.IsVisible = false; + MainContentRoot.Margin = new Thickness(0, bar, 0, 0); + foreach (Control c in new Control[] { ContentRoot, NavRail, NavDock, NavFlyout }) + c.ClearValue(MarginProperty); + NavDock.ClearValue(PaddingProperty); + SidebarGlass.IsVisible = false; + return; + } + + var railMargin = new Thickness(MAC_GLASS_INSET, bar, MAC_GLASS_INSET, MAC_GLASS_INSET); + var dockMargin = new Thickness(MAC_GLASS_INSET); + + MainContentRoot.Margin = default; + ContentRoot.Margin = new Thickness(0, bar, 0, 0); + NavRail.Margin = railMargin; + NavDock.Margin = dockMargin; + NavDock.Padding = new Thickness(0, bar - MAC_GLASS_INSET, 0, 0); + NavFlyout.Margin = new Thickness(MAC_GLASS_INSET, bar, 0, MAC_GLASS_INSET); + + SidebarGlass.IsVisible = NavRail.IsVisible || NavDock.IsVisible; + SidebarGlass.Margin = NavDock.IsVisible ? dockMargin : railMargin; + MacWindowSurface.IsVisible = true; + UpdateMacWindowSurfaceClip(); + } + + // Punches the sidebar panel's rounded rect out of the opaque window surface, so the native + // vibrancy only shows through the panel (System Settings look) and the content stays solid. + private void UpdateMacWindowSurfaceClip() + { + if (!MacWindowSurface.IsVisible) + return; + + if (!SidebarGlass.IsVisible + || SidebarGlass.TranslatePoint(default, MacWindowSurface) is not { } origin) + { + MacWindowSurface.Clip = null; + return; + } + + double radius = SidebarGlass.CornerRadius.TopLeft; + MacWindowSurface.Clip = new CombinedGeometry( + GeometryCombineMode.Exclude, + new RectangleGeometry(new Rect(MacWindowSurface.Bounds.Size)), + new RectangleGeometry(new Rect(origin, SidebarGlass.Bounds.Size), radius, radius)); + } + private void ApplyMacTitleBarControlSizes() { foreach (Button button in new[] { BackButton, SidebarToggleButton }) From fedbad93e2d40bc94a2a3057303d659ab793ff2e Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 09:40:20 -0400 Subject: [PATCH 03/21] Use an accent selection pill in the macOS glass sidebar --- .../Assets/Styles/Styles.MacGlass.axaml | 1 + src/UniGetUI.Avalonia/Views/MainWindow.axaml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml index d30fd32775..8f145bf6d4 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml @@ -4,6 +4,7 @@ 18 + diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml b/src/UniGetUI.Avalonia/Views/MainWindow.axaml index 372d2f0a5c..a43ee903e3 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml @@ -200,6 +200,25 @@ + + + + + + From 57511ac04eefdca7c62bf7a4a2c0438c2007000f Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 09:46:26 -0400 Subject: [PATCH 04/21] Give macOS menus, the back button and dialogs the glass look --- src/UniGetUI.Avalonia/App.axaml.cs | 9 ++- .../Assets/Styles/Styles.MacGlass.axaml | 12 +++ .../Styles/Styles.MacGlassControls.axaml | 40 ++++++++++ .../Styles/Styles.MacGlassControls.axaml.cs | 12 +++ .../Infrastructure/MacGlassWindowHelper.cs | 78 ++++++++++++++++++- .../Views/MainWindow.axaml.cs | 6 ++ 6 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml create mode 100644 src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml.cs diff --git a/src/UniGetUI.Avalonia/App.axaml.cs b/src/UniGetUI.Avalonia/App.axaml.cs index 4496d9a733..49d77c3187 100644 --- a/src/UniGetUI.Avalonia/App.axaml.cs +++ b/src/UniGetUI.Avalonia/App.axaml.cs @@ -40,12 +40,19 @@ public override void Initialize() if (MicaWindowHelper.IsMicaEnabled()) ApplyWindowsMicaStyling(); else if (MacGlassWindowHelper.IsGlassEnabled()) - Resources.MergedDictionaries.Add(new MacGlassStyles()); + ApplyMacGlassStyling(); #if AVALONIA_DIAGNOSTICS_ENABLED this.AttachDeveloperTools(); #endif } + private void ApplyMacGlassStyling() + { + Resources.MergedDictionaries.Add(new MacGlassStyles()); + Styles.Add(new MacGlassControlStyles()); + MacGlassWindowHelper.EnablePopupGlass(); + } + private void ApplyWindowsMicaStyling() { Resources.MergedDictionaries.Add(new WindowsMicaStyles()); diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml index 8f145bf6d4..937f335670 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml @@ -5,17 +5,27 @@ The blur shows real content behind the window, so surfaces stay more opaque than Mica's. --> 18 + 10 + + + + + + + + + @@ -56,10 +66,12 @@ + + diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml new file mode 100644 index 0000000000..f75e01a953 --- /dev/null +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml.cs b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml.cs new file mode 100644 index 0000000000..4743add760 --- /dev/null +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Markup.Xaml; +using AvaloniaStyles = Avalonia.Styling.Styles; + +namespace UniGetUI.Avalonia.Assets.Styles; + +public partial class MacGlassControlStyles : AvaloniaStyles +{ + public MacGlassControlStyles() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs b/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs index 1e6008ec24..aee9e4f224 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/MacGlassWindowHelper.cs @@ -1,7 +1,10 @@ using System.Runtime.InteropServices; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Primitives; using Avalonia.Media; +using Avalonia.Threading; +using Avalonia.VisualTree; using UniGetUI.Avalonia.Assets.Styles; using UniGetUI.Core.Logging; @@ -18,10 +21,13 @@ internal static partial class MacGlassWindowHelper // AppKit NSVisualEffectMaterial / NSVisualEffectBlendingMode values. private const long NSVisualEffectMaterialSidebar = 7; + private const long NSVisualEffectMaterialMenu = 5; + private const long NSVisualEffectStateActive = 1; private const long NSVisualEffectBlendingModeBehindWindow = 0; private static bool? _reduceTransparency; private static bool _glassConfirmedUnavailable; + private static bool _popupGlassHooked; public static bool IsGlassEnabled() => OperatingSystem.IsMacOS() @@ -40,6 +46,43 @@ public static void Apply(Window window) window.Opened += (_, _) => OnOpened(window); } + /// Gives menus, flyouts and dropdowns the native menu blur with rounded corners. + public static void EnablePopupGlass() + { + if (!IsGlassEnabled() || _popupGlassHooked) + return; + _popupGlassHooked = true; + + Control.LoadedEvent.AddClassHandler((root, _) => ApplyToPopup(root)); + } + + private static void ApplyToPopup(PopupRoot root) + { + // Tooltips keep their plain opaque look, as on macOS. + if (!IsGlassEnabled() || root.GetVisualDescendants().Any(c => c is ToolTip)) + return; + + root.TransparencyLevelHint = new[] { WindowTransparencyLevel.AcrylicBlur }; + root.Background = Brushes.Transparent; + + // Avalonia (re)creates its blur view while applying the hint, so style it once that settled. + Dispatcher.UIThread.Post(() => + { + try + { + double radius = root.TryFindResource("MacGlassMenuCornerRadius", out object? r) && r is CornerRadius cr + ? cr.TopLeft + : 10; + StyleBehindWindowEffectViews(GetNSWindow(root), NSVisualEffectMaterialMenu, radius); + } + catch (Exception ex) + { + Logger.Warn("Could not apply the macOS menu material to a popup"); + Logger.Warn(ex); + } + }); + } + private static void OnOpened(Window window) { // A transparent window without a granted backdrop shows whatever is behind it unblurred, @@ -73,6 +116,11 @@ private static void FallBackToSolid(Window window) if (app.Resources.MergedDictionaries[i] is MacGlassStyles) app.Resources.MergedDictionaries.RemoveAt(i); } + for (int i = app.Styles.Count - 1; i >= 0; i--) + { + if (app.Styles[i] is MacGlassControlStyles) + app.Styles.RemoveAt(i); + } } window.TransparencyLevelHint = new[] { WindowTransparencyLevel.None }; @@ -85,8 +133,12 @@ private static void FallBackToSolid(Window window) // Avalonia creates its behind-window NSVisualEffectView with the deprecated, always-light // NSVisualEffectMaterialLight. A semantic material follows the window's light/dark appearance. private static void UseAdaptiveMaterial(Window window) + => StyleBehindWindowEffectViews(GetNSWindow(window), NSVisualEffectMaterialSidebar, 0); + + // Sets the material of the window's behind-window NSVisualEffectViews; a non-zero radius also + // rounds them (always-active, like native menus) and re-derives the window shadow from that shape. + private static void StyleBehindWindowEffectViews(nint nsWindow, long material, double cornerRadius) { - nint nsWindow = GetNSWindow(window); if (nsWindow == 0) return; @@ -102,11 +154,28 @@ private static void UseAdaptiveMaterial(Window window) || objc_msgSend_IntPtr(view, sel_registerName("blendingMode")) != NSVisualEffectBlendingModeBehindWindow) continue; - objc_msgSend_IntPtr_Long(view, sel_registerName("setMaterial:"), NSVisualEffectMaterialSidebar); + objc_msgSend_IntPtr_Long(view, sel_registerName("setMaterial:"), material); + if (cornerRadius <= 0) + continue; + + objc_msgSend_IntPtr_Long(view, sel_registerName("setState:"), NSVisualEffectStateActive); + objc_msgSend_IntPtr_Long(view, sel_registerName("setWantsLayer:"), 1); + nint layer = objc_msgSend_IntPtr(view, sel_registerName("layer")); + if (layer != 0) + { + objc_msgSend_IntPtr_Double(layer, sel_registerName("setCornerRadius:"), cornerRadius); + objc_msgSend_IntPtr_Long(layer, sel_registerName("setMasksToBounds:"), 1); + } + } + + if (cornerRadius > 0) + { + objc_msgSend_IntPtr_Long(nsWindow, sel_registerName("setHasShadow:"), 1); + objc_msgSend_IntPtr(nsWindow, sel_registerName("invalidateShadow")); } } - private static nint GetNSWindow(Window window) + private static nint GetNSWindow(TopLevel window) { if (window.TryGetPlatformHandle() is not { Handle: not 0 } handle) return 0; @@ -151,6 +220,9 @@ private static bool ShouldReduceTransparency() [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] private static partial nint objc_msgSend_IntPtr_Long(nint receiver, nint selector, long arg); + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] + private static partial nint objc_msgSend_IntPtr_Double(nint receiver, nint selector, double arg); + [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] [return: MarshalAs(UnmanagedType.U1)] private static partial bool objc_msgSend_Bool(nint receiver, nint selector); diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs index 79677d7e64..5f0e9a559a 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs @@ -698,6 +698,8 @@ private void ApplyMacSidebarLayout() c.ClearValue(MarginProperty); NavDock.ClearValue(PaddingProperty); SidebarGlass.IsVisible = false; + BackButton.Background = Brushes.Transparent; + BackButton.CornerRadius = new CornerRadius(4); return; } @@ -715,6 +717,10 @@ private void ApplyMacSidebarLayout() SidebarGlass.Margin = NavDock.IsVisible ? dockMargin : railMargin; MacWindowSurface.IsVisible = true; UpdateMacWindowSurfaceClip(); + + // Back is a round glass button, like the back/forward capsule in System Settings. + BackButton.Bind(BackgroundProperty, this.GetResourceObservable("MacGlassControlBackground")); + BackButton.CornerRadius = new CornerRadius(MAC_TITLE_BAR_CONTROL_SIZE / 2); } // Punches the sidebar panel's rounded rect out of the opaque window surface, so the native From 75cad7c18e99d9314cd6af1fe10a398e372b8fec Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 09:52:15 -0400 Subject: [PATCH 05/21] Match the macOS light glass palette to System Settings --- .../Assets/Styles/Styles.MacGlass.axaml | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml index 937f335670..5f56cd0595 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml @@ -4,7 +4,6 @@ 18 - 10 @@ -16,55 +15,56 @@ - + + - + - - - + + + - - - + + - 0 4 16 0 #1A000000 + 0 2 12 0 #14000000 - - - - - - + + + + + + - - - - + + + + - - - - - + + + + + - + - + - + + From a27813045ae41d9f4ed712ec80e6c2bc76b35b78 Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 10:18:57 -0400 Subject: [PATCH 06/21] Restyle macOS controls for the glass look --- .../Assets/Styles/Styles.MacGlass.axaml | 62 +++++++++++++++ .../Styles/Styles.MacGlassControls.axaml | 76 +++++++++++++++++++ .../Controls/Settings/CheckboxButtonCard.cs | 2 + .../Views/Controls/Settings/CheckboxCard.cs | 2 + .../Controls/Settings/SecureCheckboxCard.cs | 2 + 5 files changed, 144 insertions(+) diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml index 5f56cd0595..be773f83d4 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml @@ -11,6 +11,39 @@ + + 7 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -31,6 +64,21 @@ 0 2 12 0 #14000000 + + + + + + + + + + + + + + + @@ -78,6 +126,20 @@ 0 6 20 0 #4D000000 + + + + + + + + + + + + + + diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml index f75e01a953..e5bb996948 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml @@ -37,4 +37,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs index e67ca7682c..3839bf04bc 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxButtonCard.cs @@ -83,6 +83,7 @@ public CheckboxButtonCard() }; _stateLabel = new TextBlock { + Classes = { "toggle-state-label" }, Text = DisabledLabel, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 8, 0), @@ -102,6 +103,7 @@ public CheckboxButtonCard() Content = new StackPanel { Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center, Children = { _stateLabel, _checkbox }, }; diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs index 9331ed1945..2e6c719ae2 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/CheckboxCard.cs @@ -105,6 +105,7 @@ public CheckboxCard() AutomationProperties.SetControlTypeOverride(_checkbox, AutomationControlType.CheckBox); _stateLabel = new TextBlock { + Classes = { "toggle-state-label" }, Text = DisabledLabel, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 8, 0), @@ -131,6 +132,7 @@ public CheckboxCard() Content = new StackPanel { Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center, Children = { _stateLabel, _checkbox }, }; diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs index dc04284e65..c6d049402b 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/SecureCheckboxCard.cs @@ -89,6 +89,7 @@ public SecureCheckboxCard() AutomationProperties.SetControlTypeOverride(_checkbox, AutomationControlType.CheckBox); _stateLabel = new TextBlock { + Classes = { "toggle-state-label" }, Text = DisabledLabel, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 8, 0), @@ -125,6 +126,7 @@ public SecureCheckboxCard() { Spacing = 4, Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center, Children = { _loading, _stateLabel, _checkbox }, }; From e8f345d56015800bc3faffb631ca0d0cabb7f833 Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 10:26:21 -0400 Subject: [PATCH 07/21] Finish the macOS glass controls: toolbar, package list and toasts --- .../Assets/Styles/Styles.MacGlass.axaml | 2 ++ .../Styles/Styles.MacGlassControls.axaml | 6 +++-- .../Views/Controls/InfoBar.axaml | 16 +++++++++++++- .../SoftwarePages/AbstractPackagesPage.axaml | 22 +++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml index be773f83d4..58bc228e60 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlass.axaml @@ -79,6 +79,7 @@ + @@ -140,6 +141,7 @@ + diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml index e5bb996948..ce4313d393 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml @@ -80,8 +80,10 @@ Button#MainToolbarButtonDropdown:disabled /template/ ContentPresenter#PART_ContentPresenter"> - diff --git a/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml b/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml index 813d09c88a..e1434f3334 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml +++ b/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml @@ -7,6 +7,9 @@ x:DataType="vm:InfoBarViewModel"> + + + + + + + + + + + From bd3835838086512b1d0c754aa28737937bc0af4f Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Tue, 13 Oct 2026 10:30:00 -0400 Subject: [PATCH 08/21] Group the macOS toolbar actions into one shared glass bar --- .../Assets/Styles/Styles.MacGlassControls.axaml | 14 +++++++++++--- .../Views/SoftwarePages/AbstractPackagesPage.axaml | 6 ++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml index ce4313d393..5659bf148f 100644 --- a/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml +++ b/src/UniGetUI.Avalonia/Assets/Styles/Styles.MacGlassControls.axaml @@ -80,11 +80,19 @@ Button#MainToolbarButtonDropdown:disabled /template/ ContentPresenter#PART_ContentPresenter"> - - + - + + @@ -70,28 +77,36 @@ #ToolbarRow Button:disabled /template/ ContentPresenter#PART_ContentPresenter, #ToolbarRow ToggleButton:not(:disabled) /template/ ContentPresenter#PART_ContentPresenter, #ToolbarRow ToggleButton:disabled /template/ ContentPresenter#PART_ContentPresenter"> - + + - - - + + + diff --git a/src/UniGetUI.Avalonia/Views/Controls/ToolbarOverflowPanel.cs b/src/UniGetUI.Avalonia/Views/Controls/ToolbarOverflowPanel.cs index 6ac6df4ec5..52aac01eef 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/ToolbarOverflowPanel.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/ToolbarOverflowPanel.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Layout; +using Avalonia.Media; using Avalonia.VisualTree; namespace UniGetUI.Avalonia.Views.Controls; @@ -10,9 +11,41 @@ public sealed class ToolbarOverflowPanel : Panel public static readonly StyledProperty SpacingProperty = AvaloniaProperty.Register(nameof(Spacing), 4.0); + /// Keeps labels collapsed at every width (icon-only toolbar). + public static readonly StyledProperty AlwaysCollapseLabelsProperty = + AvaloniaProperty.Register(nameof(AlwaysCollapseLabels)); + + /// When set, each run of items between separators is drawn on a capsule of this brush. + public static readonly StyledProperty GroupBackgroundProperty = + AvaloniaProperty.Register(nameof(GroupBackground)); + + public static readonly StyledProperty GroupCornerRadiusProperty = + AvaloniaProperty.Register(nameof(GroupCornerRadius)); + + public static readonly StyledProperty GroupVerticalInsetProperty = + AvaloniaProperty.Register(nameof(GroupVerticalInset)); + static ToolbarOverflowPanel() { - AffectsMeasure(SpacingProperty); + AffectsMeasure(SpacingProperty, AlwaysCollapseLabelsProperty); + } + + // Panel.Render is sealed, so the group capsules come from a visual child kept behind the items. + private readonly GroupBackdrop _backdrop; + + public ToolbarOverflowPanel() + { + _backdrop = new GroupBackdrop(this) { ZIndex = -1, IsHitTestVisible = false }; + VisualChildren.Add(_backdrop); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == GroupBackgroundProperty + || change.Property == GroupCornerRadiusProperty + || change.Property == GroupVerticalInsetProperty) + _backdrop.InvalidateVisual(); } private readonly List _items = new(); @@ -27,6 +60,30 @@ public double Spacing set => SetValue(SpacingProperty, value); } + public bool AlwaysCollapseLabels + { + get => GetValue(AlwaysCollapseLabelsProperty); + set => SetValue(AlwaysCollapseLabelsProperty, value); + } + + public IBrush? GroupBackground + { + get => GetValue(GroupBackgroundProperty); + set => SetValue(GroupBackgroundProperty, value); + } + + public double GroupCornerRadius + { + get => GetValue(GroupCornerRadiusProperty); + set => SetValue(GroupCornerRadiusProperty, value); + } + + public double GroupVerticalInset + { + get => GetValue(GroupVerticalInsetProperty); + set => SetValue(GroupVerticalInsetProperty, value); + } + public Control? OverflowControl { get; set; } public Action? LabelCollapseRequested { get; set; } @@ -73,6 +130,8 @@ protected override Size ArrangeOverride(Size finalSize) overflow.Arrange(new Rect(x, 0, overflowWidth, overflowWidth > 0 ? finalSize.Height : 0)); } + _backdrop.Arrange(new Rect(finalSize)); + _backdrop.InvalidateVisual(); return finalSize; } @@ -148,7 +207,14 @@ private double UsedWidth() private bool UpdateLabelState(double availableWidth) { - if (double.IsInfinity(availableWidth) || availableWidth <= 1 || LabelCollapseRequested is null) return false; + if (LabelCollapseRequested is null) return false; + if (AlwaysCollapseLabels) + { + if (_labelsCollapsed) return false; + ApplyLabelState(true); + return true; + } + if (double.IsInfinity(availableWidth) || availableWidth <= 1) return false; double rowWidth = RowWidth(); @@ -224,4 +290,37 @@ private int CountFittingItems(double availableWidth) while (count > 0 && _items[count - 1] is Separator) count--; return count; } + + private sealed class GroupBackdrop(ToolbarOverflowPanel owner) : Control + { + public override void Render(DrawingContext context) + { + if (owner.GroupBackground is not { } brush) + return; + + // One capsule per run of visible items between separators. + Rect? group = null; + foreach (var child in owner.Children) + { + if (!child.IsVisible || child.Bounds.Width <= 0) + continue; + if (child is Separator) + { + DrawGroup(context, brush, group); + group = null; + continue; + } + group = group is { } current ? current.Union(child.Bounds) : child.Bounds; + } + DrawGroup(context, brush, group); + } + + private void DrawGroup(DrawingContext context, IBrush brush, Rect? group) + { + if (group is not { } rect) + return; + double radius = owner.GroupCornerRadius; + context.DrawRectangle(brush, null, rect.Deflate(new Thickness(0, owner.GroupVerticalInset)), radius, radius); + } + } } diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml index bddc2f05f0..31c1cd00fe 100644 --- a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml +++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml @@ -118,6 +118,9 @@ + + + + + + @@ -712,7 +716,8 @@ automation:AutomationProperties.AccessibilityView="Raw" Data="M0,1 H16 M0,7 H16 M0,13 H16"/> - diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs index 5f0e9a559a..890bd2af41 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs @@ -700,6 +700,7 @@ private void ApplyMacSidebarLayout() SidebarGlass.IsVisible = false; BackButton.Background = Brushes.Transparent; BackButton.CornerRadius = new CornerRadius(4); + TitleBarTextBlock.IsVisible = true; return; } @@ -721,6 +722,9 @@ private void ApplyMacSidebarLayout() // Back is a round glass button, like the back/forward capsule in System Settings. BackButton.Bind(BackgroundProperty, this.GetResourceObservable("MacGlassControlBackground")); BackButton.CornerRadius = new CornerRadius(MAC_TITLE_BAR_CONTROL_SIZE / 2); + + // Apple: don't title windows with the app name; the page header already names the view. + TitleBarTextBlock.IsVisible = Settings.Get(Settings.K.ShowVersionNumberOnTitlebar); } // Punches the sidebar panel's rounded rect out of the opaque window surface, so the native diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml index 31c1cd00fe..686e7203d5 100644 --- a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml +++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml @@ -118,6 +118,9 @@ + @@ -172,6 +175,13 @@ + + + @@ -275,7 +285,7 @@ @@ -304,8 +314,8 @@ - - + +