From 1bb6fad799ba644f30a199cb851c539d00219335 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 26 Mar 2021 10:21:40 +0100 Subject: [PATCH 01/16] Fix ShowInTaskbar on Win32. To hide the taskbar icon on win32 we need to parent the window to a hidden window. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 33 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index a42dd5fc07..e242c7b247 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -85,6 +85,7 @@ namespace Avalonia.Win32 private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; private bool _isCloseRequested; private bool _shown; + private WindowImpl _hiddenWindow; public WindowImpl() { @@ -581,7 +582,7 @@ namespace Avalonia.Win32 public void SetParent(IWindowImpl parent) { _parent = (WindowImpl)parent; - SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent._hwnd); + SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent?._hwnd ?? IntPtr.Zero); } public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable); @@ -755,6 +756,11 @@ namespace Avalonia.Win32 } } + private WindowImpl GetOrCreateHiddenWindow() + { + return _hiddenWindow ??= new WindowImpl(); + } + private void CreateDropTarget() { var odt = new OleDropTarget(this, _owner); @@ -1094,16 +1100,35 @@ namespace Avalonia.Win32 if (newProperties.ShowInTaskbar) { exStyle |= WindowStyles.WS_EX_APPWINDOW; + + if (_hiddenWindow is object && _parent == _hiddenWindow) + { + // Can't enable the taskbar icon by clearing the parent window unless the window + // is hidden. Hide the window and show it again with the same activation state + // when we've finished. Interestingly it seems to work fine the other way. + var shown = IsWindowVisible(_hwnd); + var activated = GetActiveWindow() == _hwnd; + + if (shown) + Hide(); + + SetParent(null); + + if (shown) + Show(activated); + } + } else { + // To hide a non-owned window's taskbar icon we need to parent it to a hidden window. + if (_parent is null) + SetParent(GetOrCreateHiddenWindow()); + exStyle &= ~WindowStyles.WS_EX_APPWINDOW; } SetExtendedStyle(exStyle); - - // TODO: To hide non-owned window from taskbar we need to parent it to a hidden window. - // Otherwise it will still show in the taskbar. } WindowStyles style; From 3184ac7a0ff59d0af6e40c66d7b2f1ad06a889c8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 26 Mar 2021 10:39:22 +0100 Subject: [PATCH 02/16] Dispose the hidden window. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index e242c7b247..2f4183b8fc 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -527,6 +527,7 @@ namespace Avalonia.Win32 } _framebuffer.Dispose(); + _hiddenWindow?.Dispose(); } public void Invalidate(Rect rect) From 1a8f2f7f354084f130bcad5d7e393b4434e34fc4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 18 Dec 2020 20:56:58 -0500 Subject: [PATCH 03/16] Add tapped event args --- src/Avalonia.Input/Gestures.cs | 20 +++++---- src/Avalonia.Input/InputElement.cs | 8 ++-- src/Avalonia.Input/TappedGestureEventArgs.cs | 44 ++++++++++++++++++++ 3 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 src/Avalonia.Input/TappedGestureEventArgs.cs diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index 1be2595ebe..8d7a02f12e 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -6,17 +6,17 @@ namespace Avalonia.Input { public static class Gestures { - public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( "Tapped", RoutingStrategies.Bubble, typeof(Gestures)); - public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( "DoubleTapped", RoutingStrategies.Bubble, typeof(Gestures)); - public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( "RightTapped", RoutingStrategies.Bubble, typeof(Gestures)); @@ -24,7 +24,7 @@ namespace Avalonia.Input public static readonly RoutedEvent ScrollGestureEvent = RoutedEvent.Register( "ScrollGesture", RoutingStrategies.Bubble, typeof(Gestures)); - + public static readonly RoutedEvent ScrollGestureEndedEvent = RoutedEvent.Register( "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); @@ -89,7 +89,7 @@ namespace Avalonia.Input { if (s_lastPress.TryGetTarget(out var target) && target == e.Source) { - e.Source.RaiseEvent(new RoutedEventArgs(DoubleTappedEvent)); + e.Source.RaiseEvent(new DoubleTappedEventArgs(e)); } } } @@ -105,8 +105,14 @@ namespace Avalonia.Input { if (e.InitialPressMouseButton == MouseButton.Left || e.InitialPressMouseButton == MouseButton.Right) { - var et = e.InitialPressMouseButton != MouseButton.Right ? TappedEvent : RightTappedEvent; - e.Source.RaiseEvent(new RoutedEventArgs(et)); + if (e.InitialPressMouseButton != MouseButton.Right) + { + e.Source.RaiseEvent(new RightTappedEventArgs(e)); + } + else + { + e.Source.RaiseEvent(new TappedEventArgs(e)); + } } } } diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index f3996cea76..e0594b5ce6 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -176,12 +176,12 @@ namespace Avalonia.Input /// /// Defines the event. /// - public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; + public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; /// /// Defines the event. /// - public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; + public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; private bool _isEffectivelyEnabled = true; private bool _isFocused; @@ -346,7 +346,7 @@ namespace Avalonia.Input /// /// Occurs when a tap gesture occurs on the control. /// - public event EventHandler Tapped + public event EventHandler Tapped { add { AddHandler(TappedEvent, value); } remove { RemoveHandler(TappedEvent, value); } @@ -355,7 +355,7 @@ namespace Avalonia.Input /// /// Occurs when a double-tap gesture occurs on the control. /// - public event EventHandler DoubleTapped + public event EventHandler DoubleTapped { add { AddHandler(DoubleTappedEvent, value); } remove { RemoveHandler(DoubleTappedEvent, value); } diff --git a/src/Avalonia.Input/TappedGestureEventArgs.cs b/src/Avalonia.Input/TappedGestureEventArgs.cs new file mode 100644 index 0000000000..bed76f5abd --- /dev/null +++ b/src/Avalonia.Input/TappedGestureEventArgs.cs @@ -0,0 +1,44 @@ +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace Avalonia.Input +{ + public class DoubleTappedEventArgs : RoutedEventArgs + { + private readonly PointerEventArgs lastPointerEventArgs; + + public DoubleTappedEventArgs(PointerEventArgs lastPointerEventArgs) + : base(Gestures.DoubleTappedEvent) + { + this.lastPointerEventArgs = lastPointerEventArgs; + } + + public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); + } + + public class TappedEventArgs : RoutedEventArgs + { + private readonly PointerEventArgs lastPointerEventArgs; + + public TappedEventArgs(PointerEventArgs lastPointerEventArgs) + : base(Gestures.DoubleTappedEvent) + { + this.lastPointerEventArgs = lastPointerEventArgs; + } + + public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); + } + + public class RightTappedEventArgs : RoutedEventArgs + { + private readonly PointerEventArgs lastPointerEventArgs; + + public RightTappedEventArgs(PointerEventArgs lastPointerEventArgs) + : base(Gestures.DoubleTappedEvent) + { + this.lastPointerEventArgs = lastPointerEventArgs; + } + + public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); + } +} From f7a41c079333afdbb37e24ad8572b23873751e04 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 23 Jan 2021 19:18:36 -0500 Subject: [PATCH 04/16] Use single TappedEventArgs for all tapped events --- src/Avalonia.Input/Gestures.cs | 10 ++--- src/Avalonia.Input/InputElement.cs | 4 +- src/Avalonia.Input/TappedEventArgs.cs | 18 ++++++++ src/Avalonia.Input/TappedGestureEventArgs.cs | 44 -------------------- 4 files changed, 25 insertions(+), 51 deletions(-) create mode 100644 src/Avalonia.Input/TappedEventArgs.cs delete mode 100644 src/Avalonia.Input/TappedGestureEventArgs.cs diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index 8d7a02f12e..f5499c12f3 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -11,12 +11,12 @@ namespace Avalonia.Input RoutingStrategies.Bubble, typeof(Gestures)); - public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( "DoubleTapped", RoutingStrategies.Bubble, typeof(Gestures)); - public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( "RightTapped", RoutingStrategies.Bubble, typeof(Gestures)); @@ -89,7 +89,7 @@ namespace Avalonia.Input { if (s_lastPress.TryGetTarget(out var target) && target == e.Source) { - e.Source.RaiseEvent(new DoubleTappedEventArgs(e)); + e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e)); } } } @@ -107,11 +107,11 @@ namespace Avalonia.Input { if (e.InitialPressMouseButton != MouseButton.Right) { - e.Source.RaiseEvent(new RightTappedEventArgs(e)); + e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); } else { - e.Source.RaiseEvent(new TappedEventArgs(e)); + e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e)); } } } diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index e0594b5ce6..8f99770b3b 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -181,7 +181,7 @@ namespace Avalonia.Input /// /// Defines the event. /// - public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; + public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; private bool _isEffectivelyEnabled = true; private bool _isFocused; @@ -355,7 +355,7 @@ namespace Avalonia.Input /// /// Occurs when a double-tap gesture occurs on the control. /// - public event EventHandler DoubleTapped + public event EventHandler DoubleTapped { add { AddHandler(DoubleTappedEvent, value); } remove { RemoveHandler(DoubleTappedEvent, value); } diff --git a/src/Avalonia.Input/TappedEventArgs.cs b/src/Avalonia.Input/TappedEventArgs.cs new file mode 100644 index 0000000000..02add509cd --- /dev/null +++ b/src/Avalonia.Input/TappedEventArgs.cs @@ -0,0 +1,18 @@ +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace Avalonia.Input +{ + public class TappedEventArgs : RoutedEventArgs + { + private readonly PointerEventArgs lastPointerEventArgs; + + public TappedEventArgs(RoutedEvent routedEvent, PointerEventArgs lastPointerEventArgs) + : base(routedEvent) + { + this.lastPointerEventArgs = lastPointerEventArgs; + } + + public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); + } +} diff --git a/src/Avalonia.Input/TappedGestureEventArgs.cs b/src/Avalonia.Input/TappedGestureEventArgs.cs deleted file mode 100644 index bed76f5abd..0000000000 --- a/src/Avalonia.Input/TappedGestureEventArgs.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Avalonia.Interactivity; -using Avalonia.VisualTree; - -namespace Avalonia.Input -{ - public class DoubleTappedEventArgs : RoutedEventArgs - { - private readonly PointerEventArgs lastPointerEventArgs; - - public DoubleTappedEventArgs(PointerEventArgs lastPointerEventArgs) - : base(Gestures.DoubleTappedEvent) - { - this.lastPointerEventArgs = lastPointerEventArgs; - } - - public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); - } - - public class TappedEventArgs : RoutedEventArgs - { - private readonly PointerEventArgs lastPointerEventArgs; - - public TappedEventArgs(PointerEventArgs lastPointerEventArgs) - : base(Gestures.DoubleTappedEvent) - { - this.lastPointerEventArgs = lastPointerEventArgs; - } - - public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); - } - - public class RightTappedEventArgs : RoutedEventArgs - { - private readonly PointerEventArgs lastPointerEventArgs; - - public RightTappedEventArgs(PointerEventArgs lastPointerEventArgs) - : base(Gestures.DoubleTappedEvent) - { - this.lastPointerEventArgs = lastPointerEventArgs; - } - - public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo); - } -} From e0d7841b58483b9e43fc175ce9b39ce13a77135e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 26 Jan 2021 00:36:22 -0500 Subject: [PATCH 05/16] Fix right/left tapped raise event --- src/Avalonia.Input/Gestures.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index f5499c12f3..f2cc9e9072 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -105,7 +105,7 @@ namespace Avalonia.Input { if (e.InitialPressMouseButton == MouseButton.Left || e.InitialPressMouseButton == MouseButton.Right) { - if (e.InitialPressMouseButton != MouseButton.Right) + if (e.InitialPressMouseButton == MouseButton.Right) { e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); } From e750512a4ba9dc07b5e020747dbd3fab163e6c3f Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 28 Mar 2021 19:21:02 -0400 Subject: [PATCH 06/16] Update API contract --- src/Avalonia.Input/ApiCompatBaseline.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index fff2fb2806..98eb8598d8 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -1,4 +1,13 @@ Compat issues with assembly Avalonia.Input: MembersMustExist : Member 'public Avalonia.Platform.IPlatformHandle Avalonia.Input.Cursor.PlatformCursor.get()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.RightTappedEvent' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.TappedEvent' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TappedEvent' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract. -Total Issues: 2 +Total Issues: 11 From 6c608c6d0d67a8da67b030c7b2fe25992811e940 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 29 Mar 2021 22:33:56 -0400 Subject: [PATCH 07/16] Avoid breaking changes for next release --- src/Avalonia.Input/ApiCompatBaseline.txt | 11 +---------- src/Avalonia.Input/Gestures.cs | 6 +++--- src/Avalonia.Input/InputElement.cs | 8 ++++---- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index 98eb8598d8..fff2fb2806 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -1,13 +1,4 @@ Compat issues with assembly Avalonia.Input: MembersMustExist : Member 'public Avalonia.Platform.IPlatformHandle Avalonia.Input.Cursor.PlatformCursor.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.RightTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.TappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract. -Total Issues: 11 +Total Issues: 2 diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index f2cc9e9072..60b6b9e947 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -6,17 +6,17 @@ namespace Avalonia.Input { public static class Gestures { - public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( "Tapped", RoutingStrategies.Bubble, typeof(Gestures)); - public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( "DoubleTapped", RoutingStrategies.Bubble, typeof(Gestures)); - public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( + public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( "RightTapped", RoutingStrategies.Bubble, typeof(Gestures)); diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index 8f99770b3b..f3996cea76 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -176,12 +176,12 @@ namespace Avalonia.Input /// /// Defines the event. /// - public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; + public static readonly RoutedEvent TappedEvent = Gestures.TappedEvent; /// /// Defines the event. /// - public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; + public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; private bool _isEffectivelyEnabled = true; private bool _isFocused; @@ -346,7 +346,7 @@ namespace Avalonia.Input /// /// Occurs when a tap gesture occurs on the control. /// - public event EventHandler Tapped + public event EventHandler Tapped { add { AddHandler(TappedEvent, value); } remove { RemoveHandler(TappedEvent, value); } @@ -355,7 +355,7 @@ namespace Avalonia.Input /// /// Occurs when a double-tap gesture occurs on the control. /// - public event EventHandler DoubleTapped + public event EventHandler DoubleTapped { add { AddHandler(DoubleTappedEvent, value); } remove { RemoveHandler(DoubleTappedEvent, value); } From 79665bfc742a85a76c9ba3db6ff3242e3c67393b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 5 Apr 2021 20:42:23 +0100 Subject: [PATCH 08/16] maintain the client size when switching between extended and non extended client areas. Previously the client area would grow to fill the window. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index a42dd5fc07..feffa03d15 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -883,20 +883,19 @@ namespace Avalonia.Win32 _isClientAreaExtended = false; return; } - - GetWindowRect(_hwnd, out var rcClient); + GetClientRect(_hwnd, out var rcClient); + GetWindowRect(_hwnd, out var rcWindow); // Inform the application of the frame change. SetWindowPos(_hwnd, - IntPtr.Zero, - rcClient.left, rcClient.top, - rcClient.Width, rcClient.Height, - SetWindowPosFlags.SWP_FRAMECHANGED); - + IntPtr.Zero, + rcWindow.left, rcWindow.top, + rcClient.Width, rcClient.Height, + SetWindowPosFlags.SWP_FRAMECHANGED); + if (_isClientAreaExtended && WindowState != WindowState.FullScreen) { var margins = UpdateExtendMargins(); - DwmExtendFrameIntoClientArea(_hwnd, ref margins); } else @@ -906,6 +905,8 @@ namespace Avalonia.Win32 _offScreenMargin = new Thickness(); _extendedMargins = new Thickness(); + + Resize(new Size(rcWindow.Width, rcWindow.Height)); } if(!_isClientAreaExtended || (_extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.SystemChrome) && From 9c192f0e3784c3d1262ea98a7622d4564292a56d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 5 Apr 2021 20:46:37 +0100 Subject: [PATCH 09/16] whitespace. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index feffa03d15..48baf00177 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -892,7 +892,7 @@ namespace Avalonia.Win32 rcWindow.left, rcWindow.top, rcClient.Width, rcClient.Height, SetWindowPosFlags.SWP_FRAMECHANGED); - + if (_isClientAreaExtended && WindowState != WindowState.FullScreen) { var margins = UpdateExtendMargins(); From 2de40fafd2dc235a55c093a70fec46e0aa912940 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 5 Apr 2021 20:47:28 +0100 Subject: [PATCH 10/16] whitespace. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 48baf00177..b87abdd5ed 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -892,7 +892,7 @@ namespace Avalonia.Win32 rcWindow.left, rcWindow.top, rcClient.Width, rcClient.Height, SetWindowPosFlags.SWP_FRAMECHANGED); - + if (_isClientAreaExtended && WindowState != WindowState.FullScreen) { var margins = UpdateExtendMargins(); From a1d653188120e22a461f1b0d6d4fb0dbe52418f5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 6 Apr 2021 14:09:25 +0200 Subject: [PATCH 11/16] Use existing OffscreenParentWindow to hide taskbar icon. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 2f4183b8fc..40874abe33 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -85,7 +85,7 @@ namespace Avalonia.Win32 private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; private bool _isCloseRequested; private bool _shown; - private WindowImpl _hiddenWindow; + private bool _hiddenWindowIsParent; public WindowImpl() { @@ -527,7 +527,6 @@ namespace Avalonia.Win32 } _framebuffer.Dispose(); - _hiddenWindow?.Dispose(); } public void Invalidate(Rect rect) @@ -757,11 +756,6 @@ namespace Avalonia.Win32 } } - private WindowImpl GetOrCreateHiddenWindow() - { - return _hiddenWindow ??= new WindowImpl(); - } - private void CreateDropTarget() { var odt = new OleDropTarget(this, _owner); @@ -1102,7 +1096,7 @@ namespace Avalonia.Win32 { exStyle |= WindowStyles.WS_EX_APPWINDOW; - if (_hiddenWindow is object && _parent == _hiddenWindow) + if (_hiddenWindowIsParent) { // Can't enable the taskbar icon by clearing the parent window unless the window // is hidden. Hide the window and show it again with the same activation state @@ -1118,13 +1112,15 @@ namespace Avalonia.Win32 if (shown) Show(activated); } - } else { // To hide a non-owned window's taskbar icon we need to parent it to a hidden window. if (_parent is null) - SetParent(GetOrCreateHiddenWindow()); + { + SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, OffscreenParentWindow.Handle); + _hiddenWindowIsParent = true; + } exStyle &= ~WindowStyles.WS_EX_APPWINDOW; } From 1b681688f78a9280378f4968671bedd56b7c09d5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 6 Apr 2021 14:30:50 +0100 Subject: [PATCH 12/16] take into account render scaling. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index b87abdd5ed..3e1c8238b3 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -906,7 +906,7 @@ namespace Avalonia.Win32 _offScreenMargin = new Thickness(); _extendedMargins = new Thickness(); - Resize(new Size(rcWindow.Width, rcWindow.Height)); + Resize(new Size(rcWindow.Width/ RenderScaling, rcWindow.Height / RenderScaling)); } if(!_isClientAreaExtended || (_extendChromeHints.HasFlagCustom(ExtendClientAreaChromeHints.SystemChrome) && From 60a0ec9a3a3a7247e8ac1ad33cede8a5d2cda757 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 6 Apr 2021 15:17:22 +0100 Subject: [PATCH 13/16] take into account the extended mode when Resize is called. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3e1c8238b3..b68203cb5a 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -483,8 +483,8 @@ namespace Avalonia.Win32 IntPtr.Zero, 0, 0, - requestedClientWidth + (windowRect.Width - clientRect.Width), - requestedClientHeight + (windowRect.Height - clientRect.Height), + requestedClientWidth + (_isClientAreaExtended ? 0 : windowRect.Width - clientRect.Width), + requestedClientHeight + (_isClientAreaExtended ? 0 : windowRect.Height - clientRect.Height), SetWindowPosFlags.SWP_RESIZE); } } From 81fc978d642884141f0f48a2d860d9f15769bea0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 8 Apr 2021 12:36:32 +0200 Subject: [PATCH 14/16] Use hidden window when clearing parent... ...if `ShowInTaskbar == false`. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 0fbd58eeb9..d1dfb4a675 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -581,8 +581,15 @@ namespace Avalonia.Win32 public void SetParent(IWindowImpl parent) { - _parent = (WindowImpl)parent; - SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent?._hwnd ?? IntPtr.Zero); + var parentHwnd = ((WindowImpl)parent)?._hwnd ?? IntPtr.Zero; + + if (parentHwnd == IntPtr.Zero && !_windowProperties.ShowInTaskbar) + { + parentHwnd = OffscreenParentWindow.Handle; + _hiddenWindowIsParent = true; + } + + SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, parentHwnd); } public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable); @@ -1108,6 +1115,7 @@ namespace Avalonia.Win32 if (shown) Hide(); + _hiddenWindowIsParent = false; SetParent(null); if (shown) From c71b0e0f292113e841457044a50e1d2c430159e6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 8 Apr 2021 12:43:53 +0200 Subject: [PATCH 15/16] Make showing the window use correct offscreen parent. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index d1dfb4a675..f3f8618cc9 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -572,8 +572,7 @@ namespace Avalonia.Win32 public virtual void Show(bool activate) { - SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent != null ? _parent._hwnd : IntPtr.Zero); - + SetParent(_parent); ShowWindow(_showWindowState, activate); } From 32e6ca7e2aafa76671725e3cf7c51158db7e99a9 Mon Sep 17 00:00:00 2001 From: SutandoTsukai181 <52977072+SutandoTsukai181@users.noreply.github.com> Date: Thu, 8 Apr 2021 17:11:23 +0300 Subject: [PATCH 16/16] Add missing XButtons 1 and 2 to MouseButton --- src/Avalonia.Input/PointerEventArgs.cs | 4 +++- src/Avalonia.Input/PointerPoint.cs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs index 451f80b1df..ba39f7ca8e 100644 --- a/src/Avalonia.Input/PointerEventArgs.cs +++ b/src/Avalonia.Input/PointerEventArgs.cs @@ -107,7 +107,9 @@ namespace Avalonia.Input None, Left, Right, - Middle + Middle, + XButton1, + XButton2 } public class PointerPressedEventArgs : PointerEventArgs diff --git a/src/Avalonia.Input/PointerPoint.cs b/src/Avalonia.Input/PointerPoint.cs index a316e0d964..ebc64fa2f9 100644 --- a/src/Avalonia.Input/PointerPoint.cs +++ b/src/Avalonia.Input/PointerPoint.cs @@ -90,6 +90,10 @@ namespace Avalonia.Input return MouseButton.Middle; if (kind == PointerUpdateKind.RightButtonPressed || kind == PointerUpdateKind.RightButtonReleased) return MouseButton.Right; + if (kind == PointerUpdateKind.XButton1Pressed || kind == PointerUpdateKind.XButton1Released) + return MouseButton.XButton1; + if (kind == PointerUpdateKind.XButton2Pressed || kind == PointerUpdateKind.XButton2Released) + return MouseButton.XButton2; return MouseButton.None; } }