From f0c90c2ae8dab51b1642e3dc2372e692193f6f49 Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Thu, 31 Oct 2024 12:12:54 +0100 Subject: [PATCH] Fix caption buttons on multi monitor setups --- .../Controls/MetroWindow.cs | 255 +++++++- .../Utilities/WindowOption.NativeMethods.cs | 18 - .../EleCho.WpfSuite/Utilities/WindowOption.cs | 588 ------------------ dnSpy/dnSpy/MainApp/App.xaml.cs | 3 +- dnSpy/dnSpy/Themes/wpf.styles.templates.xaml | 17 +- 5 files changed, 260 insertions(+), 621 deletions(-) delete mode 100644 dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.NativeMethods.cs delete mode 100644 dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.cs diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Controls/MetroWindow.cs b/dnSpy/dnSpy.Contracts.DnSpy/Controls/MetroWindow.cs index df40e5c0de..998b6ea05f 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Controls/MetroWindow.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Controls/MetroWindow.cs @@ -19,10 +19,12 @@ You should have received a copy of the GNU General Public License using System; using System.Diagnostics; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; +using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; @@ -104,21 +106,33 @@ static Size GetDpi_Win81(IntPtr hWnd) { return new Size(dpiX, dpiY); } - struct RECT { - public int left, top, right, bottom; - RECT(bool dummy) => left = top = right = bottom = 0;// disable compiler warning - } - [DllImport("user32", SetLastError = true)] static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); [DllImport("shcore", SetLastError = true)] static extern int GetDpiForMonitor(IntPtr hmonitor, int dpiType, out int dpiX, out int dpiY); + static readonly DependencyPropertyKey uiElementIsMouseOverPropertyKey = + (DependencyPropertyKey)typeof(UIElement).GetField("IsMouseOverPropertyKey", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null)!; + static readonly DependencyPropertyKey buttonIsPressedPropertyKey = + (DependencyPropertyKey)typeof(ButtonBase).GetField("IsPressedPropertyKey", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null)!; + + private Visual? minimizeButton; + private Visual? maximizeButton; + private Visual? closeButton; + int WM_DPICHANGED_counter = 0; IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { const int WM_DPICHANGED = 0x02E0; const int WM_MOUSEHWHEEL = 0x020E; const int WM_NCHITTEST = 0x0084; + const int WM_NCMOUSELEAVE = 0x02A2; + const int WM_NCLBUTTONDOWN = 0x00A1; + const int WM_NCLBUTTONUP = 0x00A2; + const int WM_MOUSELEAVE = 0x02A3; + + const nint HTCLOSE = 20; + const nint HTMAXBUTTON = 9; + const nint HTMINBUTTON = 8; if (msg == WM_DPICHANGED) { if (WM_DPICHANGED_counter != 0) @@ -162,6 +176,139 @@ IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool hand } } + var hitPoint = new Point((short)lParam, (short)((uint)lParam >> 16)); + + var result = IntPtr.Zero; + if (minimizeButton is not null) { + var hitResult = VisualTreeHelper.HitTest(minimizeButton, minimizeButton.PointFromScreen(hitPoint)); + if (hitResult is not null) { + minimizeButton.SetValue(uiElementIsMouseOverPropertyKey, true); + handled = true; + result = HTMINBUTTON; + } + else { + minimizeButton.SetValue(uiElementIsMouseOverPropertyKey, false); + if (minimizeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, false); + } + } + if (maximizeButton is not null) { + var hitResult = VisualTreeHelper.HitTest(maximizeButton, maximizeButton.PointFromScreen(hitPoint)); + if (hitResult is not null) { + maximizeButton.SetValue(uiElementIsMouseOverPropertyKey, true); + handled = true; + result = HTMAXBUTTON; + } + else { + maximizeButton.SetValue(uiElementIsMouseOverPropertyKey, false); + if (maximizeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, false); + } + } + if (closeButton is not null) { + var hitResult = VisualTreeHelper.HitTest(closeButton, closeButton.PointFromScreen(hitPoint)); + if (hitResult is not null) { + closeButton.SetValue(uiElementIsMouseOverPropertyKey, true); + handled = true; + result = HTCLOSE; + } + else { + closeButton.SetValue(uiElementIsMouseOverPropertyKey, false); + if (closeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, false); + } + + } + return result; + } + + if (msg == WM_NCLBUTTONDOWN) { + var hitPoint = new Point((short)lParam, (short)((uint)lParam >> 16)); + + if (minimizeButton is not null) { + var hitResult = VisualTreeHelper.HitTest(minimizeButton, minimizeButton.PointFromScreen(hitPoint)); + if (hitResult is not null) { + if (minimizeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, true); + handled = true; + return IntPtr.Zero; + } + } + if (maximizeButton is not null) { + var hitResult = VisualTreeHelper.HitTest(maximizeButton, maximizeButton.PointFromScreen(hitPoint)); + if (hitResult is not null) { + if (maximizeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, true); + handled = true; + return IntPtr.Zero; + } + } + if (closeButton is not null) { + var hitResult = VisualTreeHelper.HitTest(closeButton, closeButton.PointFromScreen(hitPoint)); + if (hitResult is not null) { + if (closeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, true); + handled = true; + return IntPtr.Zero; + } + } + return IntPtr.Zero; + } + + if (msg == WM_NCLBUTTONUP) { + if (minimizeButton is ButtonBase minBtn) { + bool shouldClick = (bool)minBtn.GetValue(buttonIsPressedPropertyKey.DependencyProperty); + minBtn.SetValue(buttonIsPressedPropertyKey, false); + if (shouldClick) { + minBtn.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, minBtn)); + ExecuteCommand(minBtn); + } + + handled = true; + } + if (maximizeButton is ButtonBase maxBtn) { + bool shouldClick = (bool)maxBtn.GetValue(buttonIsPressedPropertyKey.DependencyProperty); + maxBtn.SetValue(buttonIsPressedPropertyKey, false); + if (shouldClick) { + maxBtn.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, maxBtn)); + ExecuteCommand(maxBtn); + } + + handled = true; + } + if (closeButton is ButtonBase closeBtn) { + bool shouldClick = (bool)closeBtn.GetValue(buttonIsPressedPropertyKey.DependencyProperty); + closeBtn.SetValue(buttonIsPressedPropertyKey, false); + if (shouldClick) { + closeBtn.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, closeBtn)); + ExecuteCommand(closeBtn); + } + + handled = true; + } + + return IntPtr.Zero; + } + + if (msg == WM_NCMOUSELEAVE || msg == WM_MOUSELEAVE) { + if (minimizeButton is not null) { + minimizeButton.SetValue(uiElementIsMouseOverPropertyKey, false); + if (minimizeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, false); + } + + if (maximizeButton is not null) { + maximizeButton.SetValue(uiElementIsMouseOverPropertyKey, false); + if (maximizeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, false); + } + + if (closeButton is not null) { + closeButton.SetValue(uiElementIsMouseOverPropertyKey, false); + if (closeButton is ButtonBase button) + button.SetValue(buttonIsPressedPropertyKey, false); + } + return IntPtr.Zero; } @@ -193,6 +340,20 @@ private protected virtual bool HandleHorizontalScroll(IInputElement element, sho return false; } + private static void ExecuteCommand(ICommandSource commandSource) { + var command = commandSource.Command; + if (command is null) + return; + object commandParameter = commandSource.CommandParameter; + if (command is RoutedCommand routedCommand) { + var target = commandSource.CommandTarget ?? commandSource as IInputElement; + if (routedCommand.CanExecute(commandParameter, target)) + routedCommand.Execute(commandParameter, target); + } + else if (command.CanExecute(commandParameter)) + command.Execute(commandParameter); + } + /// /// Gets the DPI /// @@ -266,6 +427,90 @@ public ImageReference SystemMenuImage { set => SetValue(SystemMenuImageProperty, value); } + /// + /// The DependencyProperty of IsMinimumButton property + /// + public static readonly DependencyProperty IsMinimizeButtonProperty = + DependencyProperty.RegisterAttached("IsMinimizeButton", typeof(bool), typeof(MetroWindow), new FrameworkPropertyMetadata(false, OnIsMinimizeButtonChanged)); + + /// + /// Get value of IsMinimumButton property + /// + /// + /// + public static bool GetIsMinimizeButton(DependencyObject obj) => (bool)obj.GetValue(IsMinimizeButtonProperty); + + /// + /// Set value of IsMinimumButton property + /// + /// + /// + public static void SetIsMinimizeButton(DependencyObject obj, bool value) => obj.SetValue(IsMinimizeButtonProperty, value); + + static void OnIsMinimizeButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + if (d is not Visual v) + return; + if (GetWindow(d) is not MetroWindow win) + return; + win.minimizeButton = v; + } + + /// + /// The DependencyProperty of IsMaximumButton property + /// + public static readonly DependencyProperty IsMaximizeButtonProperty = + DependencyProperty.RegisterAttached("IsMaximizeButton", typeof(bool), typeof(MetroWindow), new FrameworkPropertyMetadata(false, OnIsMaximizeButtonChanged)); + + /// + /// Get value of IsMaximumButton property + /// + /// + /// + public static bool GetIsMaximizeButton(DependencyObject obj) => (bool)obj.GetValue(IsMaximizeButtonProperty); + + /// + /// Set value of IsMaximumButton property + /// + /// + /// + public static void SetIsMaximizeButton(DependencyObject obj, bool value) => obj.SetValue(IsMaximizeButtonProperty, value); + + static void OnIsMaximizeButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + if (d is not Visual v) + return; + if (GetWindow(d) is not MetroWindow win) + return; + win.maximizeButton = v; + } + + /// + /// The DependencyProperty of IsCloseButton property + /// + public static readonly DependencyProperty IsCloseButtonProperty = + DependencyProperty.RegisterAttached("IsCloseButton", typeof(bool), typeof(MetroWindow), new FrameworkPropertyMetadata(false, OnIsCloseButtonChanged)); + + /// + /// Get value of IsCloseButton property + /// + /// + /// + public static bool GetIsCloseButton(DependencyObject obj) => (bool)obj.GetValue(IsCloseButtonProperty); + + /// + /// Set value of IsCloseButton property + /// + /// + /// + public static void SetIsCloseButton(DependencyObject obj, bool value) => obj.SetValue(IsCloseButtonProperty, value); + + static void OnIsCloseButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + if (d is not Visual v) + return; + if (GetWindow(d) is not MetroWindow win) + return; + win.closeButton = v; + } + /// /// Maximized element property /// diff --git a/dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.NativeMethods.cs b/dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.NativeMethods.cs deleted file mode 100644 index 38df140a63..0000000000 --- a/dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.NativeMethods.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace EleCho.WpfSuite { - public partial class WindowOption { - internal static class NativeDefinition { - public const nint WM_NCHITTEST = 0x0084; - public const nint WM_NCMOUSELEAVE = 0x02A2; - public const nint WM_NCLBUTTONDOWN = 0x00A1; - public const nint WM_NCLBUTTONUP = 0x00A2; - public const nint WM_MOUSEMOVE = 0x0200; - - public const nint HTCLOSE = 20; - public const nint HTMAXBUTTON = 9; - public const nint HTMINBUTTON = 8; - } - } -} diff --git a/dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.cs b/dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.cs deleted file mode 100644 index 7ff483a145..0000000000 --- a/dnSpy/dnSpy/Lib/EleCho.WpfSuite/Utilities/WindowOption.cs +++ /dev/null @@ -1,588 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Interop; -using System.Windows.Media; -using System.Windows.Shell; -using System.Xml.Linq; -using static EleCho.WpfSuite.WindowOption.NativeDefinition; - -namespace EleCho.WpfSuite { - /// - /// Window options - /// - public partial class WindowOption { - static readonly Version s_versionWindows10_1809 = new Version(10, 0, 17763); - static readonly Version s_versionWindows10 = new Version(10, 0); - - /// - /// DWM Supports Corner, BorderColor, CaptionColor, TextColor - /// - static readonly Version s_versionWindows11_22000 = new Version(10, 0, 22000); - - /// - /// DWM Supports Dark mode and Backdrop property - /// - static readonly Version s_versionWindows11_22621 = new Version(10, 0, 22621); - - static readonly Version s_versionCurrentWindows = Environment.OSVersion.Version; - - static Dictionary? s_maximumButtons; - static Dictionary? s_minimumButtons; - static Dictionary? s_closeButtons; - - static DependencyPropertyKey s_uiElementIsMouseOverPropertyKey = - (DependencyPropertyKey)typeof(UIElement).GetField("IsMouseOverPropertyKey", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null)!; - - static DependencyPropertyKey s_buttonIsPressedPropertyKey = - (DependencyPropertyKey)typeof(ButtonBase).GetField("IsPressedPropertyKey", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null)!; - - /// - /// Get value of IsMaximumButton property - /// - /// - /// - public static bool GetIsMaximumButton(DependencyObject obj) { - return (bool)obj.GetValue(IsMaximumButtonProperty); - } - - /// - /// Set value of IsMaximumButton property - /// - /// - /// - public static void SetIsMaximumButton(DependencyObject obj, bool value) { - obj.SetValue(IsMaximumButtonProperty, value); - } - - /// - /// Get value of IsMinimumButton property - /// - /// - /// - public static bool GetIsMinimumButton(DependencyObject obj) { - return (bool)obj.GetValue(IsMinimumButtonProperty); - } - - /// - /// Set value of IsMinimumButton property - /// - /// - /// - public static void SetIsMinimumButton(DependencyObject obj, bool value) { - obj.SetValue(IsMinimumButtonProperty, value); - } - - /// - /// Get value of IsCloseButton property - /// - /// - /// - public static bool GetIsCloseButton(DependencyObject obj) { - return (bool)obj.GetValue(IsCloseButtonProperty); - } - - /// - /// Set value of IsCloseButton property - /// - /// - /// - public static void SetIsCloseButton(DependencyObject obj, bool value) { - obj.SetValue(IsCloseButtonProperty, value); - } - - /// - /// The DependencyProperty of IsMaximumButton property - /// - public static readonly DependencyProperty IsMaximumButtonProperty = - DependencyProperty.RegisterAttached("IsMaximumButton", typeof(bool), typeof(WindowOption), new FrameworkPropertyMetadata(false, OnIsMaximumButtonChanged)); - - /// - /// The DependencyProperty of IsMinimumButton property - /// - public static readonly DependencyProperty IsMinimumButtonProperty = - DependencyProperty.RegisterAttached("IsMinimumButton", typeof(bool), typeof(WindowOption), new FrameworkPropertyMetadata(false, OnIsMinimumButtonChanged)); - - /// - /// The DependencyProperty of IsCloseButton property - /// - public static readonly DependencyProperty IsCloseButtonProperty = - DependencyProperty.RegisterAttached("IsCloseButton", typeof(bool), typeof(WindowOption), new FrameworkPropertyMetadata(false, OnIsCloseButtonChanged)); - - #region DependencyProperty Callbacks - - private static void OnIsMaximumButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (d is not FrameworkElement frameworkElement) { - throw new InvalidOperationException("Target DependencyObject is not FrameworkElement"); - } - - if (DesignerProperties.GetIsInDesignMode(d)) { - return; - } - - if (Window.GetWindow(d) is Window window) { - DoAfterWindowSourceInitialized(window, () => { - ApplyIsMaximumButton(window, frameworkElement, (bool)e.NewValue); - }); - } - else { - DoAfterElementLoaded(frameworkElement, () => { - if (Window.GetWindow(frameworkElement) is Window loadedWindow) { - DoAfterWindowSourceInitialized(loadedWindow, () => { - ApplyIsMaximumButton(loadedWindow, frameworkElement, (bool)e.NewValue); - }); - } - else { - throw new InvalidOperationException("Cannot find Window of Visual"); - } - }); - } - } - - private static void OnIsMinimumButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (d is not FrameworkElement frameworkElement) { - throw new InvalidOperationException("Target DependencyObject is not FrameworkElement"); - } - - if (DesignerProperties.GetIsInDesignMode(d)) { - return; - } - - if (Window.GetWindow(d) is Window window) { - DoAfterWindowSourceInitialized(window, () => { - ApplyIsMinimumButton(window, frameworkElement, (bool)e.NewValue); - }); - } - else { - DoAfterElementLoaded(frameworkElement, () => { - if (Window.GetWindow(frameworkElement) is Window loadedWindow) { - DoAfterWindowSourceInitialized(loadedWindow, () => { - ApplyIsMinimumButton(loadedWindow, frameworkElement, (bool)e.NewValue); - }); - } - else { - throw new InvalidOperationException("Cannot find Window of Visual"); - } - }); - } - } - - private static void OnIsCloseButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (d is not FrameworkElement frameworkElement) { - throw new InvalidOperationException("Target DependencyObject is not FrameworkElement"); - } - - if (DesignerProperties.GetIsInDesignMode(d)) { - return; - } - - if (Window.GetWindow(d) is Window window) { - DoAfterWindowSourceInitialized(window, () => { - ApplyIsCloseButton(window, frameworkElement, (bool)e.NewValue); - }); - } - else { - DoAfterElementLoaded(frameworkElement, () => { - if (Window.GetWindow(frameworkElement) is Window loadedWindow) { - DoAfterWindowSourceInitialized(loadedWindow, () => { - ApplyIsCloseButton(loadedWindow, frameworkElement, (bool)e.NewValue); - }); - } - else { - throw new InvalidOperationException("Cannot find Window of Visual"); - } - }); - } - } - - private static IntPtr WindowCaptionButtonsInteropHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { - if (handled) { - return IntPtr.Zero; - } - - switch ((nint)msg) { - case NativeDefinition.WM_NCHITTEST: { - var x = (int)((ulong)lParam & 0x0000FFFF); - var y = (int)((ulong)lParam & 0xFFFF0000) >> 16; - var result = default(IntPtr); - - if (s_maximumButtons is not null && - s_maximumButtons.TryGetValue(hwnd, out var maximumButtonVisual)) { - var relativePoint = maximumButtonVisual.PointFromScreen(new Point(x, y)); - var hitResult = VisualTreeHelper.HitTest(maximumButtonVisual, relativePoint); - - if (hitResult is not null) { - maximumButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, true); - - handled = true; - result = NativeDefinition.HTMAXBUTTON; - } - else { - maximumButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, false); - - if (maximumButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, false); - } - } - } - - if (s_minimumButtons is not null && - s_minimumButtons.TryGetValue(hwnd, out var minimumButtonVisual)) { - var relativePoint = minimumButtonVisual.PointFromScreen(new Point(x, y)); - var hitResult = VisualTreeHelper.HitTest(minimumButtonVisual, relativePoint); - - if (hitResult is not null) { - minimumButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, true); - - handled = true; - result = NativeDefinition.HTMINBUTTON; - } - else { - minimumButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, false); - - if (minimumButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, false); - } - } - } - - if (s_closeButtons is not null && - s_closeButtons.TryGetValue(hwnd, out var closeButtonVisual)) { - var relativePoint = closeButtonVisual.PointFromScreen(new Point(x, y)); - var hitResult = VisualTreeHelper.HitTest(closeButtonVisual, relativePoint); - - if (hitResult is not null) { - closeButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, true); - - handled = true; - result = NativeDefinition.HTCLOSE; - } - else { - closeButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, false); - - if (closeButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, false); - } - } - } - - return result; - } - - case NativeDefinition.WM_NCLBUTTONDOWN: { - var x = (int)((ulong)lParam & 0x0000FFFF); - var y = (int)((ulong)lParam & 0xFFFF0000) >> 16; - - if (s_maximumButtons is not null && - s_maximumButtons.TryGetValue(hwnd, out var maximumButtonVisual)) { - var relativePoint = maximumButtonVisual.PointFromScreen(new Point(x, y)); - var hitResult = VisualTreeHelper.HitTest(maximumButtonVisual, relativePoint); - - if (hitResult is not null) { - if (maximumButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, true); - } - - handled = true; - } - } - - if (s_minimumButtons is not null && - s_minimumButtons.TryGetValue(hwnd, out var minimumButtonVisual)) { - var relativePoint = minimumButtonVisual.PointFromScreen(new Point(x, y)); - var hitResult = VisualTreeHelper.HitTest(minimumButtonVisual, relativePoint); - - if (hitResult is not null) { - if (minimumButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, true); - } - - handled = true; - } - } - - if (s_closeButtons is not null && - s_closeButtons.TryGetValue(hwnd, out var closeButtonVisual)) { - var relativePoint = closeButtonVisual.PointFromScreen(new Point(x, y)); - var hitResult = VisualTreeHelper.HitTest(closeButtonVisual, relativePoint); - - if (hitResult is not null) { - if (closeButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, true); - } - - handled = true; - } - } - - break; - } - - case NativeDefinition.WM_NCLBUTTONUP: { - var x = (int)((ulong)lParam & 0x0000FFFF); - var y = (int)((ulong)lParam & 0xFFFF0000) >> 16; - - if (s_maximumButtons is not null && - s_maximumButtons.TryGetValue(hwnd, out var maximumButtonVisual)) { - if (maximumButtonVisual is ButtonBase button) { - bool shouldClick = false; - if ((bool)button.GetValue(s_buttonIsPressedPropertyKey.DependencyProperty)) { - shouldClick = true; - } - - button.SetValue(s_buttonIsPressedPropertyKey, false); - - if (shouldClick) { - button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button)); - button.Command?.Execute(button.CommandParameter); - } - - handled = true; - } - } - - if (s_minimumButtons is not null && - s_minimumButtons.TryGetValue(hwnd, out var minimumButtonVisual)) { - if (minimumButtonVisual is ButtonBase button) { - bool shouldClick = false; - if ((bool)button.GetValue(s_buttonIsPressedPropertyKey.DependencyProperty)) { - shouldClick = true; - } - - button.SetValue(s_buttonIsPressedPropertyKey, false); - - if (shouldClick) { - button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button)); - button.Command?.Execute(button.CommandParameter); - } - - handled = true; - } - } - - if (s_closeButtons is not null && - s_closeButtons.TryGetValue(hwnd, out var closeButtonVisual)) { - if (closeButtonVisual is ButtonBase button) { - bool shouldClick = false; - if ((bool)button.GetValue(s_buttonIsPressedPropertyKey.DependencyProperty)) { - shouldClick = true; - } - - button.SetValue(s_buttonIsPressedPropertyKey, false); - - if (shouldClick) { - button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button)); - button.Command?.Execute(button.CommandParameter); - } - - handled = true; - } - } - - break; - } - - case NativeDefinition.WM_NCMOUSELEAVE: { - var x = (int)((ulong)lParam & 0x0000FFFF); - var y = (int)((ulong)lParam & 0xFFFF0000) >> 16; - - if (s_maximumButtons is not null && - s_maximumButtons.TryGetValue(hwnd, out var maximumButtonVisual)) { - maximumButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, false); - - if (maximumButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, false); - } - } - - if (s_minimumButtons is not null && - s_minimumButtons.TryGetValue(hwnd, out var minimumButtonVisual)) { - minimumButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, false); - - if (minimumButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, false); - } - } - - if (s_closeButtons is not null && - s_closeButtons.TryGetValue(hwnd, out var closeButtonVisual)) { - closeButtonVisual.SetValue(s_uiElementIsMouseOverPropertyKey, false); - - if (closeButtonVisual is ButtonBase button) { - button.SetValue(s_buttonIsPressedPropertyKey, false); - } - } - - break; - } - } - - return IntPtr.Zero; - } - - #endregion - - #region Utilities - - private static void DoAfterWindowSourceInitialized(Window window, Action action) { - var eventHandler = default(EventHandler); - - eventHandler = (s, e) => { - action?.Invoke(); - window.SourceInitialized -= eventHandler; - }; - - window.SourceInitialized += eventHandler; - } - - private static void DoAfterElementLoaded(FrameworkElement element, Action action) { - var eventHandler = default(RoutedEventHandler); - - eventHandler = (s, e) => { - action?.Invoke(); - element.Loaded -= eventHandler; - }; - - element.Loaded += eventHandler; - } - - private static bool HasWindowCaptionButton(nint hwnd) { - if (s_minimumButtons is not null && s_minimumButtons.ContainsKey(hwnd)) - return true; - if (s_maximumButtons is not null && s_maximumButtons.ContainsKey(hwnd)) - return true; - if (s_closeButtons is not null && s_closeButtons.ContainsKey(hwnd)) - return true; - - return false; - } - - #endregion - - #region Final Logic - - private static unsafe void ApplyIsMaximumButton(Window window, Visual visual, bool isMaximumButton) { - var windowInteropHelper = new WindowInteropHelper(window); - var windowHandle = windowInteropHelper.EnsureHandle(); - - var hwndSource = HwndSource.FromHwnd(windowHandle); - - if (isMaximumButton) { - if (s_maximumButtons is null) { - s_maximumButtons = new(); - } - - if (HasWindowCaptionButton(windowHandle)) { - hwndSource.AddHook(WindowCaptionButtonsInteropHook); - } - - if (s_maximumButtons.ContainsKey(windowHandle)) { - throw new InvalidOperationException("MaximumButton is already set to another Visual"); - } - - s_maximumButtons[windowHandle] = visual; - } - else { - if (s_maximumButtons is null) { - return; - } - - s_maximumButtons.Remove(windowHandle); - - if (s_maximumButtons.Count == 0) { - s_maximumButtons = null; - } - - if (!HasWindowCaptionButton(windowHandle)) { - hwndSource.RemoveHook(WindowCaptionButtonsInteropHook); - } - } - } - - private static unsafe void ApplyIsMinimumButton(Window window, Visual visual, bool isMinimumButton) { - var windowInteropHelper = new WindowInteropHelper(window); - var windowHandle = windowInteropHelper.EnsureHandle(); - - var hwndSource = HwndSource.FromHwnd(windowHandle); - - if (isMinimumButton) { - if (s_minimumButtons is null) { - s_minimumButtons = new(); - } - - if (HasWindowCaptionButton(windowHandle)) { - hwndSource.AddHook(WindowCaptionButtonsInteropHook); - } - - if (s_minimumButtons.ContainsKey(windowHandle)) { - throw new InvalidOperationException("MinimumButton is already set to another Visual"); - } - - s_minimumButtons[windowHandle] = visual; - } - else { - if (s_minimumButtons is null) { - return; - } - - s_minimumButtons.Remove(windowHandle); - - if (s_minimumButtons.Count == 0) { - s_minimumButtons = null; - } - - if (!HasWindowCaptionButton(windowHandle)) { - hwndSource.RemoveHook(WindowCaptionButtonsInteropHook); - } - } - } - - private static unsafe void ApplyIsCloseButton(Window window, Visual visual, bool isCloseButton) { - var windowInteropHelper = new WindowInteropHelper(window); - var windowHandle = windowInteropHelper.EnsureHandle(); - - var hwndSource = HwndSource.FromHwnd(windowHandle); - - if (isCloseButton) { - if (s_closeButtons is null) { - s_closeButtons = new(); - } - - if (HasWindowCaptionButton(windowHandle)) { - hwndSource.AddHook(WindowCaptionButtonsInteropHook); - } - - if (s_closeButtons.ContainsKey(windowHandle)) { - throw new InvalidOperationException("MinimumButton is already set to another Visual"); - } - - s_closeButtons[windowHandle] = visual; - } - else { - if (s_closeButtons is null) { - return; - } - - s_closeButtons.Remove(windowHandle); - - if (s_closeButtons.Count == 0) { - s_closeButtons = null; - } - - if (!HasWindowCaptionButton(windowHandle)) { - hwndSource.RemoveHook(WindowCaptionButtonsInteropHook); - } - } - } - - #endregion - } -} diff --git a/dnSpy/dnSpy/MainApp/App.xaml.cs b/dnSpy/dnSpy/MainApp/App.xaml.cs index 35d2fff675..3cb60206ee 100644 --- a/dnSpy/dnSpy/MainApp/App.xaml.cs +++ b/dnSpy/dnSpy/MainApp/App.xaml.cs @@ -95,6 +95,8 @@ static void ShowException(Exception? ex) { Task initializeMEFTask; Stopwatch? startupStopwatch; public App(bool readSettings, Stopwatch startupStopwatch) { + WineFixes.Initialize(); + resourceManagerTokenCacheImpl = new ResourceManagerTokenCacheImpl(); args = new AppCommandLineArgs(); @@ -106,7 +108,6 @@ public App(bool readSettings, Stopwatch startupStopwatch) { ResourceHelper.SetResourceManagerTokenCache(resourceManagerTokenCacheImpl); AppDirectories.SetSettingsFilename(args.SettingsFilename); - WineFixes.Initialize(); AddAppContextFixes(); InstallExceptionHandlers(); InitializeComponent(); diff --git a/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml b/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml index 22538ebd82..d9e2882c6f 100644 --- a/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml +++ b/dnSpy/dnSpy/Themes/wpf.styles.templates.xaml @@ -22,8 +22,7 @@ xmlns:disasmsettingsx86="clr-namespace:dnSpy.Disassembly.X86" xmlns:disasmviewersettings="clr-namespace:dnSpy.Disassembly.Viewer" xmlns:bm="clr-namespace:dnSpy.Bookmarks.Settings" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:ws="clr-namespace:EleCho.WpfSuite"> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">