Browse Source

Add tooltip support to NativeMenuItem. (#13350)

* Add tooltip support to NativeMenuItem.

* Add support for NativeMenuItem tooltips on macOS.
pull/13357/head
Steveice10 2 years ago
committed by GitHub
parent
commit
e0c6e11a42
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      native/Avalonia.Native/src/OSX/menu.h
  2. 14
      native/Avalonia.Native/src/OSX/menu.mm
  3. 1
      samples/IntegrationTestApp/MainWindow.axaml.cs
  4. 17
      src/Avalonia.Controls/NativeMenuItem.cs
  5. 7
      src/Avalonia.Native/IAvnMenuItem.cs
  6. 1
      src/Avalonia.Native/avn.idl
  7. 1
      src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
  8. 36
      tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs

4
native/Avalonia.Native/src/OSX/menu.h

@ -43,7 +43,9 @@ public:
virtual HRESULT SetSubMenu (IAvnMenu* menu) override;
virtual HRESULT SetTitle (char* utf8String) override;
virtual HRESULT SetToolTip (char* utf8String) override;
virtual HRESULT SetGesture (AvnKey key, AvnInputModifiers modifiers) override;
virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override;

14
native/Avalonia.Native/src/OSX/menu.mm

@ -127,6 +127,20 @@ HRESULT AvnAppMenuItem::SetTitle (char* utf8String)
}
}
HRESULT AvnAppMenuItem::SetToolTip (char* utf8String)
{
START_COM_CALL;
@autoreleasepool
{
if (utf8String != nullptr)
{
[_native setToolTip:[NSString stringWithUTF8String:(const char*)utf8String]];
}
return S_OK;
}
}
HRESULT AvnAppMenuItem::SetGesture (AvnKey key, AvnInputModifiers modifiers)
{

1
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -51,6 +51,7 @@ namespace IntegrationTestApp
var menuItem = new NativeMenuItem
{
Header = (string)tabItem.Header!,
ToolTip = (string)tabItem.Header!,
IsChecked = tabItem.IsSelected,
ToggleType = NativeMenuItemToggleType.Radio,
};

17
src/Avalonia.Controls/NativeMenuItem.cs

@ -72,6 +72,23 @@ namespace Avalonia.Controls
set => SetValue(HeaderProperty, value);
}
/// <summary>
/// Defines the <see cref="ToolTip"/> property.
/// </summary>
public static readonly StyledProperty<string?> ToolTipProperty =
AvaloniaProperty.Register<NativeMenuItem, string?>(nameof(ToolTip));
/// <summary>
/// Gets or sets the tooltip associated with the menu item.
/// This may not be supported by the native menu provider, but
/// will be passed on to the non-native fallback menu item if used.
/// </summary>
public string? ToolTip
{
get => GetValue(ToolTipProperty);
set => SetValue(ToolTipProperty, value);
}
public static readonly StyledProperty<KeyGesture?> GestureProperty =
AvaloniaProperty.Register<NativeMenuItem, KeyGesture?>(nameof(Gesture));

7
src/Avalonia.Native/IAvnMenuItem.cs

@ -38,6 +38,8 @@ namespace Avalonia.Native.Interop.Impl
SetTitle(title);
}
private void UpdateToolTip(string toolTip) => SetToolTip(toolTip ?? "");
private void UpdateIsChecked(bool isChecked) => SetIsChecked(isChecked.AsComBool());
private void UpdateToggleType(NativeMenuItemToggleType toggleType)
@ -107,6 +109,8 @@ namespace Avalonia.Native.Interop.Impl
{
UpdateTitle(item.Header);
UpdateToolTip(item.ToolTip);
UpdateGesture(item.Gesture);
UpdateAction(ManagedMenuItem as NativeMenuItem);
@ -120,6 +124,9 @@ namespace Avalonia.Native.Interop.Impl
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.HeaderProperty)
.Subscribe(x => UpdateTitle(x)));
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.ToolTipProperty)
.Subscribe(x => UpdateToolTip(x)));
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.GestureProperty)
.Subscribe(x => UpdateGesture(x)));

1
src/Avalonia.Native/avn.idl

@ -1022,6 +1022,7 @@ interface IAvnMenuItem : IUnknown
{
HRESULT SetSubMenu(IAvnMenu* menu);
HRESULT SetTitle(char* utf8String);
HRESULT SetToolTip(char* utf8String);
HRESULT SetGesture(AvnKey key, AvnInputModifiers modifiers);
HRESULT SetAction(IAvnPredicateCallback* predicate, IAvnActionCallback* callback);
HRESULT SetIsChecked(bool isChecked);

1
src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml

@ -12,6 +12,7 @@
<Menu.Styles>
<Style Selector="MenuItem" x:DataType="NativeMenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="ToolTip.Tip" Value="{Binding ToolTip}"/>
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="InputGesture" Value="{Binding Gesture}"/>
<Setter Property="ItemsSource" Value="{Binding Menu.Items}"/>

36
tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs

@ -1,4 +1,5 @@
using OpenQA.Selenium.Appium;
using System.Threading;
using OpenQA.Selenium.Appium;
using Xunit;
namespace Avalonia.IntegrationTests.Appium
@ -58,5 +59,38 @@ namespace Avalonia.IntegrationTests.Appium
Assert.True(menuBar.FindElementsByName("_Options").Count == 0);
Assert.True(menuBar.FindElementsByName("Options").Count == 1);
}
[PlatformFact(TestPlatforms.Windows)]
public void Win32_Avalonia_Menu_Has_ToolTip_If_Defined()
{
var viewMenu = _session.FindElementByXPath("//MenuItem[@Name='View']");
viewMenu.Click();
var buttonMenuItem = viewMenu.FindElementByName("Button");
buttonMenuItem.MovePointerOver();
// Wait for tooltip to open.
Thread.Sleep(1000);
var toolTipCandidates = _session.FindElementsByClassName("TextBlock");
Assert.Contains(toolTipCandidates, x => x.Text == "Button");
}
[PlatformFact(TestPlatforms.MacOS)]
public void MacOS_Native_Menu_Has_ToolTip_If_Defined()
{
var menuBar = _session.FindElementByXPath("/XCUIElementTypeApplication/XCUIElementTypeMenuBar");
var viewMenu = menuBar.FindElementByName("View");
viewMenu.Click();
var buttonMenuItem = viewMenu.FindElementByName("Button");
buttonMenuItem.MovePointerOver();
// Wait for tooltip to open.
Thread.Sleep(2000);
var toolTipCandidates = _session.FindElementsByClassName("XCUIElementTypeStaticText");
Assert.Contains(toolTipCandidates, x => x.Text == "Button");
}
}
}

Loading…
Cancel
Save