From 2d2d8fa5a76a0fde45aaf5b93f3b0ae3da93b730 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 8 Sep 2021 13:02:29 +0100 Subject: [PATCH] add example win32 tray icon. --- samples/ControlCatalog/App.xaml.cs | 8 +- samples/ControlCatalog/MainWindow.xaml.cs | 2 + src/Windows/Avalonia.Win32/TrayIconImpl.cs | 182 +++++++++++---------- 3 files changed, 103 insertions(+), 89 deletions(-) diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 008ef6570b..663451e9c1 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -103,9 +103,13 @@ namespace ControlCatalog { desktopLifetime.MainWindow = new MainWindow(); - var trayIcon = new TrayIcon(); + var trayIcon1 = new TrayIcon(); - trayIcon.Icon = desktopLifetime.MainWindow.Icon; + trayIcon1.Icon = desktopLifetime.MainWindow.Icon; + + var trayIcon2 = new TrayIcon(); + + trayIcon2.Icon = desktopLifetime.MainWindow.Icon; } else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) singleViewLifetime.MainView = new MainView(); diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 2446c0e1c9..a9900471c5 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -35,6 +35,8 @@ namespace ControlCatalog var mainMenu = this.FindControl("MainMenu"); mainMenu.AttachedToVisualTree += MenuAttached; + + ExtendClientAreaChromeHints = Avalonia.Platform.ExtendClientAreaChromeHints.OSXThickTitleBar; } public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit"; diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 1219e5eba5..a27a913974 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -6,93 +6,12 @@ using System.Runtime.InteropServices; using Avalonia.Controls; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Platform; +using Avalonia.Threading; using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { - /// - /// Custom Win32 window messages for the NotifyIcon - /// - public enum CustomWindowsMessage : uint - { - WM_TRAYICON = WindowsMessage.WM_APP + 1024, - WM_TRAYMOUSE = WindowsMessage.WM_USER + 1024 - - } - - public class TrayIconManagedPopupPositionerPopupImplHelper : IManagedPopupPositionerPopup - { - public delegate void MoveResizeDelegate(PixelPoint position, Size size, double scaling); - private readonly MoveResizeDelegate _moveResize; - private Window _hiddenWindow; - - public TrayIconManagedPopupPositionerPopupImplHelper(MoveResizeDelegate moveResize) - { - _moveResize = moveResize; - _hiddenWindow = new Window(); - } - - public IReadOnlyList Screens => - _hiddenWindow.Screens.All.Select(s => new ManagedPopupPositionerScreenInfo( - s.Bounds.ToRect(1), s.Bounds.ToRect(1))).ToList(); - - public Rect ParentClientAreaScreenGeometry - { - get - { - var point = _hiddenWindow.Screens.Primary.Bounds.TopLeft; - var size = _hiddenWindow.Screens.Primary.Bounds.Size; - return new Rect(point.X, point.Y, size.Width * _hiddenWindow.Screens.Primary.PixelDensity, size.Height * _hiddenWindow.Screens.Primary.PixelDensity); - } - } - - public void MoveAndResize(Point devicePoint, Size virtualSize) - { - _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _hiddenWindow.Screens.Primary.PixelDensity); - } - - public virtual double Scaling => _hiddenWindow.Screens.Primary.PixelDensity; - } - - public class TrayPopupRoot : Window - { - private ManagedPopupPositioner _positioner; - - public TrayPopupRoot() - { - _positioner = new ManagedPopupPositioner(new TrayIconManagedPopupPositionerPopupImplHelper(MoveResize)); - Topmost = true; - - LostFocus += TrayPopupRoot_LostFocus; - } - - private void MoveResize(PixelPoint position, Size size, double scaling) - { - PlatformImpl.Move(position); - PlatformImpl.Resize(size, PlatformResizeReason.Layout); - } - - private void TrayPopupRoot_LostFocus(object sender, Interactivity.RoutedEventArgs e) - { - Close(); - } - - protected override void ArrangeCore(Rect finalRect) - { - base.ArrangeCore(finalRect); - - _positioner.Update(new PopupPositionerParameters - { - Anchor = PopupAnchor.TopLeft, - Gravity = PopupGravity.BottomRight, - AnchorRectangle = new Rect(Position.ToPoint(1) / Screens.Primary.PixelDensity, new Size(1, 1)), - Size = finalRect.Size, - ConstraintAdjustment = PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY, - }); - } - } - public class TrayIconImpl : ITrayIconImpl { private readonly int _uniqueId = 0; @@ -163,7 +82,14 @@ namespace Avalonia.Win32 { throw new NotImplementedException(); } - + /// + /// Custom Win32 window messages for the NotifyIcon + /// + public enum CustomWindowsMessage : uint + { + WM_TRAYICON = WindowsMessage.WM_APP + 1024, + WM_TRAYMOUSE = WindowsMessage.WM_USER + 1024 + } private void UpdateIcon(bool remove = false) { var iconData = new NOTIFYICONDATA() @@ -210,8 +136,6 @@ namespace Avalonia.Win32 // Click?.Invoke(this, new EventArgs()); //} //_doubleClick = false; - - Debug.WriteLine($"Clicked {lParam:X}"); break; case (int)WindowsMessage.WM_LBUTTONDBLCLK: @@ -226,9 +150,13 @@ namespace Avalonia.Win32 default: break; } - } - return DefWindowProc(hWnd, msg, wParam, lParam); + return IntPtr.Zero; + } + else + { + return DefWindowProc(hWnd, msg, wParam, lParam); + } } private static void OnRightClicked() @@ -258,5 +186,85 @@ namespace Avalonia.Win32 _trayMenu.Show(); } + + class TrayPopupRoot : Window + { + private ManagedPopupPositioner _positioner; + + public TrayPopupRoot() + { + _positioner = new ManagedPopupPositioner(new TrayIconManagedPopupPositionerPopupImplHelper(MoveResize)); + Topmost = true; + + Deactivated += TrayPopupRoot_Deactivated; + } + + private void TrayPopupRoot_Deactivated(object sender, EventArgs e) + { + Dispatcher.UIThread.Post(() => + { + Close(); + }); + } + + private void MoveResize(PixelPoint position, Size size, double scaling) + { + PlatformImpl.Move(position); + PlatformImpl.Resize(size, PlatformResizeReason.Layout); + } + + private void TrayPopupRoot_LostFocus(object sender, Interactivity.RoutedEventArgs e) + { + Close(); + } + + protected override void ArrangeCore(Rect finalRect) + { + base.ArrangeCore(finalRect); + + _positioner.Update(new PopupPositionerParameters + { + Anchor = PopupAnchor.TopLeft, + Gravity = PopupGravity.BottomRight, + AnchorRectangle = new Rect(Position.ToPoint(1) / Screens.Primary.PixelDensity, new Size(1, 1)), + Size = finalRect.Size, + ConstraintAdjustment = PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY, + }); + } + + class TrayIconManagedPopupPositionerPopupImplHelper : IManagedPopupPositionerPopup + { + public delegate void MoveResizeDelegate(PixelPoint position, Size size, double scaling); + private readonly MoveResizeDelegate _moveResize; + private Window _hiddenWindow; + + public TrayIconManagedPopupPositionerPopupImplHelper(MoveResizeDelegate moveResize) + { + _moveResize = moveResize; + _hiddenWindow = new Window(); + } + + public IReadOnlyList Screens => + _hiddenWindow.Screens.All.Select(s => new ManagedPopupPositionerScreenInfo( + s.Bounds.ToRect(1), s.Bounds.ToRect(1))).ToList(); + + public Rect ParentClientAreaScreenGeometry + { + get + { + var point = _hiddenWindow.Screens.Primary.Bounds.TopLeft; + var size = _hiddenWindow.Screens.Primary.Bounds.Size; + return new Rect(point.X, point.Y, size.Width * _hiddenWindow.Screens.Primary.PixelDensity, size.Height * _hiddenWindow.Screens.Primary.PixelDensity); + } + } + + public void MoveAndResize(Point devicePoint, Size virtualSize) + { + _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _hiddenWindow.Screens.Primary.PixelDensity); + } + + public virtual double Scaling => _hiddenWindow.Screens.Primary.PixelDensity; + } + } } }