From 32ffa2f179d6ae169f5872e9950e2e1925b5529f Mon Sep 17 00:00:00 2001 From: RMBGAME Date: Tue, 7 Jun 2022 20:02:26 +0800 Subject: [PATCH] Bump to Avalonia 0.10.15 Bump to FluentAvalonia 1.4.0 --- Directory.Packages.props | 32 +- references/FluentAvalonia | 2 +- .../AvaloniaWin32WindowingPlatformImpl.cs | 409 ------------------ .../Application/UI/App.axaml.cs | 19 +- .../Application/UI/Styles/Controls.xaml | 71 +-- .../Application/UI/Styles/Window.xaml | 1 - .../Controls/UserControl/CarouselBanner.axaml | 17 +- .../UserControl/CarouselBanner.axaml.cs | 57 +++ .../UI/Views/Pages/Settings/Settings_UI.axaml | 1 + .../UI/Views/Pages/StartPage.axaml | 4 +- .../Views/Windows/WindowBase/ICoreWindow.cs | 32 +- .../ST.Client.Avalonia.csproj | 4 +- src/ST.Client/Models/Enums/AppTheme.cs | 7 + .../UI/Resx/AppResources.Designer.cs | 9 + src/ST.Client/UI/Resx/AppResources.resx | 3 + .../UI/ViewModels/MainWindowViewModel.cs | 2 +- 16 files changed, 176 insertions(+), 494 deletions(-) delete mode 100644 src/ST.Client.Desktop.Avalonia/Application/Services/Implementation/AvaloniaWin32WindowingPlatformImpl.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 86878db9fb6..69ce4901a79 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -126,11 +126,11 @@ - - + + - - + + @@ -231,22 +231,22 @@ - - - - - - - - + + + + + + + + - + - - - + + + diff --git a/references/FluentAvalonia b/references/FluentAvalonia index 111fb72062a..f60506bab41 160000 --- a/references/FluentAvalonia +++ b/references/FluentAvalonia @@ -1 +1 @@ -Subproject commit 111fb72062a44bf59c8364a227a2088c72092286 +Subproject commit f60506bab41ac2fd1b975e7850e33af6e43475a1 diff --git a/src/ST.Client.Desktop.Avalonia/Application/Services/Implementation/AvaloniaWin32WindowingPlatformImpl.cs b/src/ST.Client.Desktop.Avalonia/Application/Services/Implementation/AvaloniaWin32WindowingPlatformImpl.cs deleted file mode 100644 index c1bbe2e3289..00000000000 --- a/src/ST.Client.Desktop.Avalonia/Application/Services/Implementation/AvaloniaWin32WindowingPlatformImpl.cs +++ /dev/null @@ -1,409 +0,0 @@ -#pragma warning disable CA1416 // 验证平台兼容性 -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Platform; -using Avalonia.Controls.Primitives; -using Avalonia.Platform; -using Avalonia.ReactiveUI; -using Avalonia.Rendering; -using Avalonia.Styling; -using Avalonia.Win32; -using FluentAvalonia.Interop; -using System; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.Application.Services.Implementation -{ - [SupportedOSPlatform("Windows7.0")] - public sealed class AvaloniaWin32WindowingPlatformImpl : IWindowingPlatform - { - public IWindowImpl CreateWindow() - { - if (!Design.IsDesignMode && OperatingSystem2.IsWindows10AtLeast()) - { - return CreateWin10Window(); - } - - //return PlatformManager.CreateWindow(); - return ((IWindowingPlatform)Win32Platform).CreateWindow(); - } - - public IWindowImpl CreateEmbeddableWindow() - { - return ((IWindowingPlatform)Win32Platform).CreateEmbeddableWindow(); - } - - public ITrayIconImpl? CreateTrayIcon() - { - return ((IWindowingPlatform)Win32Platform).CreateTrayIcon(); - } - - static Win32Platform GetWin32Platform() - { - var type = typeof(Win32Platform); - const string name = "Instance"; - var property = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Static) ?? type.GetProperty(name, BindingFlags.Public | BindingFlags.Static); - if (property == null) - throw new NullReferenceException("Avalonia.Win32.Win32Platform.Instance not found."); - if (property.PropertyType != type) - throw new TypeAccessException("Avalonia.Win32.Win32Platform.Instance type error."); - var value = (Win32Platform?)property.GetValue(null); - if (value == null) - throw new NullReferenceException("Avalonia.Win32.Win32Platform.Instance is null."); - return value; - } - - static readonly Lazy _Win32Platform = new(GetWin32Platform); - - public static Win32Platform Win32Platform => _Win32Platform.Value; - - [SupportedOSPlatform("Windows10.0.10240.0")] - public static WindowImpl CreateWin10Window() => new Window10Impl(); - - [SupportedOSPlatform("Windows10.0.10240.0")] - internal class Window10Impl : WindowImpl - { - public Window10Impl() : base() - { - //Win32Interop.OSVERSIONINFOEX version = new Win32Interop.OSVERSIONINFOEX - //{ - // OSVersionInfoSize = Marshal.SizeOf() - //}; - - //Win32Interop.RtlGetVersion(ref version); - - //if (version.MajorVersion < 10) - if (!OperatingSystem2.IsWindows10AtLeast()) - { - throw new NotSupportedException("Windows versions earlier than 10 are not supported"); - } - - //_isWindows11 = version.BuildNumber >= 22000; - //_version = version; - } - - protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) - { - switch ((WM)msg) - { - case WM.ACTIVATE: - EnsureExtended(); - break; - - case WM.NCCALCSIZE: - // Follows logic from how to extend window frame + WindowsTerminal + Firefox - // Windows Terminal only handles WPARAM = TRUE & only adjusts the top of the - // rgrc[0] RECT & gets the correct result - // Firefox, on the other hand, handles BOTH times WM_NCCALCSIZE is called, - // and modifies the RECT. - // This particularly differs from the "built-in" method in Avalonia in that - // I retain the SystemBorder & ability resize the window in the transparent - // area over the drop shadows, meaning resize handles don't overlap the window - - if (wParam != IntPtr.Zero && - _owner?.Window.CanResize == true) - { - var ncParams = Marshal.PtrToStructure(lParam); - - var originalTop = ncParams.rgrc[0].top; - - var ret = Win32Interop.DefWindowProc(hWnd, (uint)WM.NCCALCSIZE, wParam, lParam); - if (ret != IntPtr.Zero) - return ret; - - var newSize = ncParams.rgrc[0]; - - if (newSize.Width == ncParams.rgrc[1].Width && - newSize.Height == ncParams.rgrc[1].Height) - { - Marshal.StructureToPtr(ncParams, lParam, true); - return IntPtr.Zero; - } - - newSize.top = originalTop; - - if (WindowState == WindowState.Maximized || - WindowState == WindowState.FullScreen) - { - //newSize.top += GetResizeHandleHeight(); - } - else - { - newSize.left += 8; - newSize.right -= 8; - newSize.bottom -= 8; - } - - ncParams.rgrc[0] = newSize; - - Marshal.StructureToPtr(ncParams, lParam, true); - return IntPtr.Zero; - } - break; - - //case WM.NCHITTEST: - // return HandleNCHitTest(lParam); - - case WM.SIZE: - if (_fakingMaximizeButton) - { - // Sometimes the effect can get stuck, so if we resize, clear it - _owner.FakeMaximizePressed(false); - _wasFakeMaximizeDown = false; - } - break; - - case WM.NCMOUSEMOVE: - if (_fakingMaximizeButton) - { - var point = PointToClient(PointFromLParam(lParam)); - _owner.FakeMaximizeHover(_owner.HitTestMaximizeButton(point)); - return IntPtr.Zero; - } - break; - - case WM.NCLBUTTONDOWN: - if (_fakingMaximizeButton) - { - var point = PointToClient(PointFromLParam(lParam)); - _owner.FakeMaximizePressed(_owner.HitTestMaximizeButton(point)); - _wasFakeMaximizeDown = true; - - // This is important. If we don't tell the System we've handled this, we'll get that - // classic Win32 button showing when we mouse press, and that's not good - return IntPtr.Zero; - } - break; - - case WM.NCLBUTTONUP: - if (_fakingMaximizeButton && _wasFakeMaximizeDown) - { - _owner.FakeMaximizePressed(false); - _wasFakeMaximizeDown = false; - _owner.FakeMaximizeClick(); - return IntPtr.Zero; - } - break; - - case WM.RBUTTONUP: - { - // Enables the system menu on right click of titlebar region - // This respects custom titlebars by querying titlebar region - // first - var pt = PointFromLParam(lParam); - if (_owner.HitTestTitleBarRegion(pt.ToPoint(RenderScaling))) - { - var sysMenu = Win32Interop.GetSystemMenu(hWnd, false); - - bool isMaximized = WindowState == WindowState.Maximized; - - var mii = new Win32Interop.MENUITEMINFO(MIIM.STATE); - void SetState(SC item, bool enabled) - { - mii.fState = enabled ? 0u : 3u; - Win32Interop.SetMenuItemInfo(sysMenu, (uint)item, false, ref mii); - } - SetState(SC.RESTORE, isMaximized); - SetState(SC.MOVE, !isMaximized); - SetState(SC.SIZE, !isMaximized); - SetState(SC.MINIMIZE, true); - SetState(SC.MAXIMIZE, !isMaximized); - SetState(SC.CLOSE, true); - Win32Interop.SetMenuDefaultItem(sysMenu, int.MaxValue, 0); - - unsafe - { - var scPt = PointToScreen(pt.ToPoint(1)); - - var ret = Win32Interop.TrackPopupMenu(sysMenu, 0x0100, scPt.X, scPt.Y, 0, hWnd, (RECT*)null); - if (ret != 0) - { - Win32Interop.PostMessage(hWnd, (uint)WM.SYSCOMMAND, new IntPtr(ret), IntPtr.Zero); - } - } - } - } - break; - - case WM.SYSCOMMAND: - // Enables ALT+SPACE to open the system menu - if (((SC)wParam) == SC.KEYMENU) - { - return Win32Interop.DefWindowProc(hWnd, msg, wParam, lParam); - } - break; - } - - return base.WndProc(hWnd, msg, wParam, lParam); - } - - internal void SetOwner(ICoreWindow wnd) - { - _owner = wnd; - - _owner.Window.Opened += (s, e) => - { - //_owner.Resized(new Size(_owner.Window.Width += 32, _owner.Window.Height += 16), PlatformResizeReason.Layout); - - _owner.Window.GetObservable(Window.WindowStateProperty) - .Subscribe(x => - { - if (x == WindowState.Normal) - { - _owner.Resized(new Size(_owner.Window.Width += 32, _owner.Window.Height += 16), PlatformResizeReason.Layout); - } - }); - }; - - if (OperatingSystem2.IsWindows11AtLeast()) - { - ((IPseudoClasses)_owner.Classes).Set(":windows11", true); - } - else - { - ((IPseudoClasses)_owner.Classes).Set(":windows10", true); - } - } - - private int GetResizeHandleHeight() - { - //if (_version.BuildNumber >= 14393) - if (OperatingSystem2.IsWindowsVersionAtLeast(10, 0, 14393)) - { - return Win32Interop.GetSystemMetricsForDpi(92 /*SM_CXPADDEDBORDER*/, (uint)(RenderScaling * 96)) + - Win32Interop.GetSystemMetricsForDpi(33 /* SM_CYSIZEFRAME */, (uint)(RenderScaling * 96)); - } - - return Win32Interop.GetSystemMetrics(92 /* SM_CXPADDEDBORDER */) + - Win32Interop.GetSystemMetrics(33/* SM_CYSIZEFRAME */); - } - - private void EnsureExtended() - { - // We completely ignore anything for extending client area in Avalonia Window impl b/c - // we're doing super specialized stuff to ensure the best experience interacting with - // the window and mimic-ing a "modern app" - var marg = new Win32Interop.MARGINS(); - - // WS_OVERLAPPEDWINDOW - // 0x00C00000L 0x00080000L 不需要标题栏 - // 0x00040000L 创建一个具有粗框的窗口可以用来调整窗口的大小 - var style = 0x00000000L | 0X00800000L | 0x00020000L | 0x00010000L; - - // This is causing the window to appear solid but is completely transparent. Weird... - //Win32Interop.GetWindowLongPtr(Hwnd, -16).ToInt32(); - - RECT frame = new RECT(); - Win32Interop.AdjustWindowRectExForDpi(ref frame, - (int)style, false, 0, (int)(RenderScaling * 96)); - - marg.topHeight = -frame.top + (OperatingSystem2.IsWindows11AtLeast() ? 0 : -1); - Win32Interop.DwmExtendFrameIntoClientArea(Handle.Handle, ref marg); - - Win32Interop.SetWindowPos(Handle.Handle, IntPtr.Zero, 0, 0, 0, 0, - 0x0020 /*SWP_FRAMECHANGED*/ | 0x0002 /*SWP_NOMOVE*/ | 0x0200 /*SWP_NOREPOSITION*/ - | 0x0001 /*SWP_NOSIZE*/ | 0x0004 /*SWP_NOZORDER*/); - } - - protected IntPtr HandleNCHitTest(IntPtr lParam) - { - // Because we still have the System Border (which technically extends beyond the actual window - // into where the Drop shadows are), we can use DefWindowProc here to handle resizing, except - // on the top. We'll handle that below - var originalRet = Win32Interop.DefWindowProc(Hwnd, (uint)WM.NCHITTEST, IntPtr.Zero, lParam); - if (originalRet != new IntPtr(1)) - { - return originalRet; - } - - // At this point, we know that the cursor is inside the client area so it - // has to be either the little border at the top of our custom title bar, - // the drag bar or something else in the XAML island. But the XAML Island - // handles WM_NCHITTEST on its own so actually it cannot be the XAML - // Island. Then it must be the drag bar or the little border at the top - // which the user can use to move or resize the window. - - var point = PointToClient(PointFromLParam(lParam)); - - RECT rcWindow; - Win32Interop.GetWindowRect(Hwnd, out rcWindow); - - // On the Top border, the resize handle overlaps with the Titlebar area, which matches - // a typical Win32 window or modern app window - var resizeBorderHeight = GetResizeHandleHeight(); - bool isOnResizeBorder = point.Y < resizeBorderHeight; - - // Make sure the caption buttons still get precedence - // This is where things get tricky too. On Win11, we still want to suppor the snap - // layout feature when hovering over the Maximize button. Unfortunately no API exists - // yet to call that manually if using a custom titlebar. But, if we return HT_MAXBUTTON - // here, the pointer events no longer enter the window - // See https://github.com/dotnet/wpf/issues/4825 for more on this... - // To hack our way into making this work, we'll return HT_MAXBUTTON here, but manually - // manage the state and handle stuff through the WM_NCLBUTTON... events - // This only applies on Windows 11, Windows 10 will work normally b/c no snap layout thing - - if (_owner!.HitTestCaptionButtons(point)) - { - if (OperatingSystem2.IsWindows11AtLeast()) - { - var result = _owner.HitTestMaximizeButton(point); - - if (result) - { - _fakingMaximizeButton = true; - return new IntPtr(9); - } - } - } - else - { - if (_fakingMaximizeButton) - { - _fakingMaximizeButton = false; - _owner.FakeMaximizeHover(false); - _owner.FakeMaximizePressed(false); - } - - if (WindowState != WindowState.Maximized && isOnResizeBorder) - return new IntPtr(12); // HT_TOP - - if (_owner.HitTestTitleBarRegion(point)) - return new IntPtr(2); //HT_CAPTION - } - - if (_fakingMaximizeButton) - { - _fakingMaximizeButton = false; - _owner.FakeMaximizeHover(false); - _owner.FakeMaximizePressed(false); - } - _fakingMaximizeButton = false; - // return HT_CLIENT, we're in the normal window - return new IntPtr(1); - } - - private PixelPoint PointFromLParam(IntPtr lParam) - { - return new PixelPoint((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)); - } - - private static int ToInt32(IntPtr ptr) - { - if (IntPtr.Size == 4) - return ptr.ToInt32(); - - return (int)(ptr.ToInt64() & 0xffffffff); - } - - private bool _wasFakeMaximizeDown; - private bool _fakingMaximizeButton; - //private bool _isWindows11 = false; - private ICoreWindow? _owner; - //private Win32Interop.OSVERSIONINFOEX _version; - } - } -} -#pragma warning restore CA1416 // 验证平台兼容性 \ No newline at end of file diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs b/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs index 7b10a7efe62..56aca4d6346 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs @@ -100,24 +100,28 @@ public AppTheme Theme public void SetThemeNotChangeValue(AppTheme value) { - string? the; + string? themeName = null; //FluentThemeMode mode; switch (value) { + case AppTheme.HighContrast: + themeName = FluentAvaloniaTheme.HighContrastModeString; + //mode = FluentThemeMode.Light; + break; case AppTheme.Light: - the = "Light"; + themeName = FluentAvaloniaTheme.LightModeString; //mode = FluentThemeMode.Light; break; case AppTheme.Dark: default: - the = "Dark"; + themeName = FluentAvaloniaTheme.DarkModeString; //mode = FluentThemeMode.Dark; break; } - //var uri_0 = new Uri($"avares://Avalonia.Themes.Fluent/Fluent{the}.xaml"); - var uri_1 = new Uri($"avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/Theme{the}.xaml"); + //var uri_0 = new Uri($"avares://Avalonia.Themes.Fluent/Fluent{themeName}.xaml"); + var uri_1 = new Uri($"avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/Theme{themeName}.xaml"); //Styles[0] = new FluentTheme(uri_0) //{ @@ -127,7 +131,10 @@ public void SetThemeNotChangeValue(AppTheme value) { Source = uri_1, }; - AvaloniaLocator.Current.GetService()!.RequestedTheme = the; + + var faTheme = AvaloniaLocator.Current.GetService(); + if (faTheme != null) + faTheme.RequestedTheme = themeName; } public static void SetThemeAccent(string? colorHex) diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls.xaml b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls.xaml index 378162c59b2..e078460fdc7 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls.xaml +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls.xaml @@ -2,43 +2,44 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="using:System"> - - 10 10 10 10 - 10 0 10 0 - 0.4,0,0.6,1 - 0.4,0,0.6,1 - 32 - 0, 32, 0, 0 - 0.85 - 34, 30, 34, 0 - 34, 0, 34, 0 - 16 + + 10 10 10 10 + 10 0 10 0 + 0.4,0,0.6,1 + 0.4,0,0.6,1 + 32 + 0, 32, 0, 0 + 0.85 + 34, 30, 34, 0 + 34, 0, 34, 0 + 16 - - - + + Null + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + - - + + diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Window.xaml b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Window.xaml index 8789028d1ca..e11c874eb77 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Window.xaml +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Window.xaml @@ -6,7 +6,6 @@ xmlns:wnd="using:Avalonia.Controls"> - diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Controls/UserControl/CarouselBanner.axaml b/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Controls/UserControl/CarouselBanner.axaml index 46f8d56bf8e..222f44949da 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Controls/UserControl/CarouselBanner.axaml +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Controls/UserControl/CarouselBanner.axaml @@ -5,17 +5,24 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="200" xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:controls="using:System.Application.UI.Views.Controls" + xmlns:gif="using:AvaloniaGif" x:Class="System.Application.UI.Views.Controls.CarouselBanner"> - + - - - + + + + + + + -