From 628a62e250062204ca1954429a2af8858520075c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 20 Feb 2024 02:53:28 -0800 Subject: [PATCH] Implement MacOSProperties.IsTemplateIcon attached property on TrayIcon (#14348) * Implement MacOS.IsTemplateIcon attached property on TrayIcon * Use MacOS.IsTemplateIcon in the ControlCatalog * Rename MacOS to MacOSProperties * Extract IsTemplateIcon to ITrayIconWithIsTemplateImpl --- native/Avalonia.Native/src/OSX/trayicon.h | 7 ++-- native/Avalonia.Native/src/OSX/trayicon.mm | 22 ++++++++++++ samples/ControlCatalog/App.xaml | 2 +- .../Platform/ITrayIconImpl.cs | 9 +++++ .../Platform/MacOSProperties.cs | 35 +++++++++++++++++++ src/Avalonia.Controls/TrayIcon.cs | 2 ++ src/Avalonia.Native/TrayIconImpl.cs | 7 +++- src/Avalonia.Native/avn.idl | 1 + src/Windows/Avalonia.Win32/TrayIconImpl.cs | 2 +- 9 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 src/Avalonia.Controls/Platform/MacOSProperties.cs diff --git a/native/Avalonia.Native/src/OSX/trayicon.h b/native/Avalonia.Native/src/OSX/trayicon.h index da30477005..86b61608c4 100644 --- a/native/Avalonia.Native/src/OSX/trayicon.h +++ b/native/Avalonia.Native/src/OSX/trayicon.h @@ -15,7 +15,8 @@ class AvnTrayIcon : public ComSingleObject { private: NSStatusItem* _native; - + bool _isTemplateIcon; + public: FORWARD_IUNKNOWN() @@ -28,8 +29,10 @@ public: virtual HRESULT SetMenu (IAvnMenu* menu) override; virtual HRESULT SetIsVisible (bool isVisible) override; - + virtual HRESULT SetToolTipText (char* text) override; + + virtual HRESULT SetIsTemplateIcon (bool isTemplateIcon) override; }; #endif /* trayicon_h */ diff --git a/native/Avalonia.Native/src/OSX/trayicon.mm b/native/Avalonia.Native/src/OSX/trayicon.mm index 5b75b9cc19..917ff87694 100644 --- a/native/Avalonia.Native/src/OSX/trayicon.mm +++ b/native/Avalonia.Native/src/OSX/trayicon.mm @@ -45,6 +45,7 @@ HRESULT AvnTrayIcon::SetIcon (void* data, size_t length) size.width = originalSize.width * scaleFactor; [image setSize: size]; + [image setTemplate: _isTemplateIcon]; [_native setImage:image]; } else @@ -98,3 +99,24 @@ HRESULT AvnTrayIcon::SetToolTipText(char* text) return S_OK; } + +HRESULT AvnTrayIcon::SetIsTemplateIcon(bool isTemplateIcon) +{ + START_COM_CALL; + + @autoreleasepool + { + if (_isTemplateIcon != isTemplateIcon) + { + _isTemplateIcon = isTemplateIcon; + + NSImage *image = [_native image]; + if (image) + { + [image setTemplate: _isTemplateIcon]; + } + } + } + + return S_OK; +} \ No newline at end of file diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index 3999191696..157009a5e3 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -64,7 +64,7 @@ - + diff --git a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs index 4ef9397d04..e6a4c7210d 100644 --- a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs +++ b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs @@ -32,4 +32,13 @@ namespace Avalonia.Platform /// Action? OnClicked { get; set; } } + + [Unstable] + public interface ITrayIconWithIsTemplateImpl : ITrayIconImpl + { + /// + /// Sets if the tray icon has a template/monochrome icon or not. + /// + void SetIsTemplateIcon(bool isTemplateIcon); + } } diff --git a/src/Avalonia.Controls/Platform/MacOSProperties.cs b/src/Avalonia.Controls/Platform/MacOSProperties.cs new file mode 100644 index 0000000000..00ecb374e2 --- /dev/null +++ b/src/Avalonia.Controls/Platform/MacOSProperties.cs @@ -0,0 +1,35 @@ +using Avalonia.Platform; + +namespace Avalonia.Controls; + +/// +/// Set of MacOS specific attached properties that allow deeper customization of the application per platform. +/// +public class MacOSProperties +{ + static MacOSProperties() + { + IsTemplateIconProperty.Changed.AddClassHandler(TrayIconIsTemplateIconChanged); + } + + /// + /// Defines the IsTemplateIcon attached property. + /// + public static readonly AttachedProperty IsTemplateIconProperty = + AvaloniaProperty.RegisterAttached("IsTemplateIcon"); + + /// + /// A Boolean value that determines whether the TrayIcon image represents a template image. + /// + public static void SetIsTemplateIcon(TrayIcon obj, bool value) => obj.SetValue(IsTemplateIconProperty, value); + + /// + /// Returns a Boolean value that indicates whether the TrayIcon image is a template image. + /// + public static bool GetIsTemplateIcon(TrayIcon obj) => obj.GetValue(IsTemplateIconProperty); + + private static void TrayIconIsTemplateIconChanged(TrayIcon trayIcon, AvaloniaPropertyChangedEventArgs args) + { + (trayIcon.Impl as ITrayIconWithIsTemplateImpl)?.SetIsTemplateIcon(args.GetNewValue()); + } +} diff --git a/src/Avalonia.Controls/TrayIcon.cs b/src/Avalonia.Controls/TrayIcon.cs index a675a549bc..1b91a08e34 100644 --- a/src/Avalonia.Controls/TrayIcon.cs +++ b/src/Avalonia.Controls/TrayIcon.cs @@ -182,6 +182,8 @@ namespace Avalonia.Controls public INativeMenuExporter? NativeMenuExporter => _impl?.MenuExporter; + internal ITrayIconImpl? Impl => _impl; + private static void Lifetime_Exit(object? sender, ControlledApplicationLifetimeExitEventArgs e) { var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized."); diff --git a/src/Avalonia.Native/TrayIconImpl.cs b/src/Avalonia.Native/TrayIconImpl.cs index bac21d5811..4c9bed0e7c 100644 --- a/src/Avalonia.Native/TrayIconImpl.cs +++ b/src/Avalonia.Native/TrayIconImpl.cs @@ -8,7 +8,7 @@ using Avalonia.Platform; namespace Avalonia.Native { - internal class TrayIconImpl : ITrayIconImpl + internal class TrayIconImpl : ITrayIconWithIsTemplateImpl { private readonly IAvnTrayIcon _native; @@ -58,6 +58,11 @@ namespace Avalonia.Native _native.SetIsVisible(visible.AsComBool()); } + public void SetIsTemplateIcon(bool isTemplateIcon) + { + _native.SetIsTemplateIcon(isTemplateIcon.AsComBool()); + } + public INativeMenuExporter? MenuExporter { get; } } } diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 3a715c33ee..87b95953bf 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -1000,6 +1000,7 @@ interface IAvnTrayIcon : IUnknown HRESULT SetMenu(IAvnMenu* menu); HRESULT SetIsVisible(bool isVisible); HRESULT SetToolTipText(char* text); + HRESULT SetIsTemplateIcon(bool text); } [uuid(a7724dc1-cf6b-4fa8-9d23-228bf2593edc)] diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index c7548ec336..df70515f99 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -104,7 +104,7 @@ namespace Avalonia.Win32 { UpdateIcon(!visible); } - + /// public void SetToolTipText(string? text) {