From e2a67e42c7b417a2cdd9879da04d8fcf00858be4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 24 Jan 2015 18:07:46 +0100 Subject: [PATCH] Prevent popup from deactivating parent window. --- Perspex.Controls/Platform/ITopLevelImpl.cs | 2 + Perspex.Controls/TopLevel.cs | 11 ++ .../Perspex.Win32/Interop/UnmanagedMethods.cs | 8 ++ Windows/Perspex.Win32/PopupImpl.cs | 14 ++- Windows/Perspex.Win32/WindowImpl.cs | 109 ++++++++++-------- 5 files changed, 96 insertions(+), 48 deletions(-) diff --git a/Perspex.Controls/Platform/ITopLevelImpl.cs b/Perspex.Controls/Platform/ITopLevelImpl.cs index c0737c03c3..4bd03cdc2f 100644 --- a/Perspex.Controls/Platform/ITopLevelImpl.cs +++ b/Perspex.Controls/Platform/ITopLevelImpl.cs @@ -21,6 +21,8 @@ namespace Perspex.Platform Action Closed { get; set; } + Action Deactivated { get; set; } + Action Input { get; set; } Action Paint { get; set; } diff --git a/Perspex.Controls/TopLevel.cs b/Perspex.Controls/TopLevel.cs index 5772109a50..45a5217bde 100644 --- a/Perspex.Controls/TopLevel.cs +++ b/Perspex.Controls/TopLevel.cs @@ -80,6 +80,7 @@ namespace Perspex.Controls this.PlatformImpl.SetOwner(this); this.PlatformImpl.Activated = this.HandleActivated; + this.PlatformImpl.Deactivated = this.HandleDeactivated; this.PlatformImpl.Closed = this.HandleClosed; this.PlatformImpl.Input = this.HandleInput; this.PlatformImpl.Paint = this.HandlePaint; @@ -104,6 +105,8 @@ namespace Perspex.Controls public event EventHandler Closed; + public event EventHandler Deactivated; + public Size ClientSize { get { return this.GetValue(ClientSizeProperty); } @@ -175,6 +178,14 @@ namespace Perspex.Controls } } + private void HandleDeactivated() + { + if (this.Deactivated != null) + { + this.Deactivated(this, EventArgs.Empty); + } + } + private void HandleInput(RawInputEventArgs e) { this.inputManager.Process(e); diff --git a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs index f02c1b871b..f4262d867d 100644 --- a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs +++ b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs @@ -42,6 +42,14 @@ namespace Perspex.Win32.Interop IDC_HELP = 32651 } + public enum MouseActivate : int + { + MA_ACTIVATE = 1, + MA_ACTIVATEANDEAT = 2, + MA_NOACTIVATE = 3, + MA_NOACTIVATEANDEAT = 4 + } + [Flags] public enum SetWindowPosFlags : uint { diff --git a/Windows/Perspex.Win32/PopupImpl.cs b/Windows/Perspex.Win32/PopupImpl.cs index 60305530a5..13ccf24a8c 100644 --- a/Windows/Perspex.Win32/PopupImpl.cs +++ b/Windows/Perspex.Win32/PopupImpl.cs @@ -38,8 +38,7 @@ namespace Perspex.Win32 UnmanagedMethods.WindowStyles exStyle = UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW | - UnmanagedMethods.WindowStyles.WS_EX_TOPMOST | - UnmanagedMethods.WindowStyles.WS_EX_NOACTIVATE; + UnmanagedMethods.WindowStyles.WS_EX_TOPMOST; return UnmanagedMethods.CreateWindowEx( (int)exStyle, @@ -55,5 +54,16 @@ namespace Perspex.Win32 IntPtr.Zero, IntPtr.Zero); } + + protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) + { + switch ((UnmanagedMethods.WindowsMessage)msg) + { + case UnmanagedMethods.WindowsMessage.WM_MOUSEACTIVATE: + return (IntPtr)UnmanagedMethods.MouseActivate.MA_NOACTIVATE; + default: + return base.WndProc(hWnd, msg, wParam, lParam); + } + } } } diff --git a/Windows/Perspex.Win32/WindowImpl.cs b/Windows/Perspex.Win32/WindowImpl.cs index ce80c0e3c9..4d7dd8588a 100644 --- a/Windows/Perspex.Win32/WindowImpl.cs +++ b/Windows/Perspex.Win32/WindowImpl.cs @@ -37,6 +37,8 @@ namespace Perspex.Win32 public Action Closed { get; set; } + public Action Deactivated { get; set; } + public Action Input { get; set; } public Action Paint { get; set; } @@ -148,50 +150,8 @@ namespace Perspex.Win32 IntPtr.Zero); } - private void CreateWindow() - { - // Ensure that the delegate doesn't get garbage collected by storing it as a field. - this.wndProcDelegate = new UnmanagedMethods.WndProc(this.WndProc); - - this.className = Guid.NewGuid().ToString(); - - UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX - { - cbSize = Marshal.SizeOf(typeof(UnmanagedMethods.WNDCLASSEX)), - style = 0, - lpfnWndProc = this.wndProcDelegate, - hInstance = Marshal.GetHINSTANCE(this.GetType().Module), - hCursor = UnmanagedMethods.LoadCursor(IntPtr.Zero, (int)UnmanagedMethods.Cursor.IDC_ARROW), - hbrBackground = (IntPtr)5, - lpszClassName = this.className, - }; - - ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx); - - if (atom == 0) - { - throw new Win32Exception(); - } - - this.hwnd = this.CreateWindowOverride(atom); - - if (this.hwnd == IntPtr.Zero) - { - throw new Win32Exception(); - } - - this.Handle = new PlatformHandle(this.hwnd, "HWND"); - } - - private Point ScreenToClient(uint x, uint y) - { - var p = new UnmanagedMethods.POINT { X = (int)x, Y = (int)y }; - UnmanagedMethods.ScreenToClient(this.hwnd, ref p); - return new Point(p.X, p.Y); - } - [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] - private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) + protected virtual IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { const double WheelDelta = 120.0; uint timestamp = unchecked((uint)UnmanagedMethods.GetMessageTime()); @@ -203,9 +163,23 @@ namespace Perspex.Win32 switch ((UnmanagedMethods.WindowsMessage)msg) { case UnmanagedMethods.WindowsMessage.WM_ACTIVATE: - if (this.Activated != null) + switch ((int)lParam & 0xffff) { - this.Activated(); + case 1: + case 2: + if (this.Activated != null) + { + this.Activated(); + } + + break; + case 0: + if (this.Deactivated != null) + { + this.Deactivated(); + } + + break; } return IntPtr.Zero; @@ -304,7 +278,7 @@ namespace Perspex.Win32 case UnmanagedMethods.WindowsMessage.WM_SIZE: if (this.Resized != null) - { + { var clientSize = new Size((int)lParam & 0xffff, (int)lParam >> 16); this.Resized(clientSize); } @@ -320,5 +294,48 @@ namespace Perspex.Win32 return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); } + + + private void CreateWindow() + { + // Ensure that the delegate doesn't get garbage collected by storing it as a field. + this.wndProcDelegate = new UnmanagedMethods.WndProc(this.WndProc); + + this.className = Guid.NewGuid().ToString(); + + UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX + { + cbSize = Marshal.SizeOf(typeof(UnmanagedMethods.WNDCLASSEX)), + style = 0, + lpfnWndProc = this.wndProcDelegate, + hInstance = Marshal.GetHINSTANCE(this.GetType().Module), + hCursor = UnmanagedMethods.LoadCursor(IntPtr.Zero, (int)UnmanagedMethods.Cursor.IDC_ARROW), + hbrBackground = (IntPtr)5, + lpszClassName = this.className, + }; + + ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx); + + if (atom == 0) + { + throw new Win32Exception(); + } + + this.hwnd = this.CreateWindowOverride(atom); + + if (this.hwnd == IntPtr.Zero) + { + throw new Win32Exception(); + } + + this.Handle = new PlatformHandle(this.hwnd, "HWND"); + } + + private Point ScreenToClient(uint x, uint y) + { + var p = new UnmanagedMethods.POINT { X = (int)x, Y = (int)y }; + UnmanagedMethods.ScreenToClient(this.hwnd, ref p); + return new Point(p.X, p.Y); + } } }