Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: MenuFlyout support for keyboard accelerators #17228

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e43e53d
chore: MenuFlyout adjustments prep
MartinZikmund Jun 13, 2024
93dddd4
chore: Start MenuFlyout update for KA
MartinZikmund Jun 14, 2024
2ffa7c3
chore: Properties and docs
MartinZikmund Jun 19, 2024
3399595
feat: ToggleMenuFlyoutItemAutomationPeer
MartinZikmund Jun 19, 2024
64a5c2d
feat: Update ToggleMenuFlyoutItem to winui3/release/1.5.4
MartinZikmund Jun 19, 2024
accab48
chore: Add helper MenuFlyout classes
MartinZikmund Jun 19, 2024
6acd57b
feat: Update MenuFlyoutSubItem to winui3/release/1.5.4
MartinZikmund Jun 19, 2024
4551ec5
feat: MenuFlyoutSubItemAutomationPeer
MartinZikmund Jun 19, 2024
130e423
chore: In progress adjustments of MenuFlyout
MartinZikmund Jun 19, 2024
b12bb9d
feat: MenuFlyoutItemAutomationPeer
MartinZikmund Jun 20, 2024
9b21bee
feat: MenuFlyoutPresenterAutomationPeer
MartinZikmund Jun 20, 2024
7c481e7
feat: Update MenuFlyoutItem to winui3/release/1.5.4
MartinZikmund Jun 20, 2024
45cba9a
chore: Updating MenuFlyout
MartinZikmund Jun 20, 2024
dad45d8
feat: Update MenuFlyout to winui3/release/1.5.4
MartinZikmund Jun 20, 2024
93041b0
chore: Adjustments to resolve errors
MartinZikmund Jun 20, 2024
f92f97b
test: Re-enable MenuFlyout Keyboard Accelerator tests
MartinZikmund Jun 20, 2024
f109756
chore: Ensure KeyboardAccelerators are registered in OnPropertyChanged
MartinZikmund Jun 21, 2024
a5c575f
chore: Use Invoke instead of InvokeClick
MartinZikmund Jun 21, 2024
f9c308d
feat: CascadingMenuHelper update to winui3/release/1.5.4
MartinZikmund Jun 24, 2024
1be2d2e
chore: Mark additional files updated
MartinZikmund Jun 25, 2024
842defd
feat: ElevationHelper
MartinZikmund Jun 25, 2024
0017c4e
feat: Update MenuFlyoutPresenter to winui3/release/1.5.4
MartinZikmund Jun 25, 2024
3d94bf0
chore: Cleanup unused code
MartinZikmund Jun 25, 2024
279dfec
test: Enable additional KA tests
MartinZikmund Jun 25, 2024
5424ebb
chore: Leave KACollection on change
MartinZikmund Jun 25, 2024
db788fa
chore: Unused members
MartinZikmund Jun 25, 2024
2182678
chore: Adjust build
MartinZikmund Jun 25, 2024
83f6063
chore: New hiding
MartinZikmund Jun 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Tests.Common;
using MUXControlsTestApp.Utilities;
using Private.Infrastructure;
using Windows.System;
using MenuBarItem = Microsoft/* UWP don't rename */.UI.Xaml.Controls.MenuBarItem;
using FocusHelper = Uno.UI.RuntimeTests.MUX.Input.Focus.FocusHelper;
using MenuBarItem = Microsoft/* UWP don't rename */.UI.Xaml.Controls.MenuBarItem;

#if !HAS_UNO_WINUI
using Microsoft/* UWP don't rename */.UI.Xaml.Tests.Common;
Expand Down Expand Up @@ -1437,7 +1436,6 @@ await TestServices.RunOnUIThread(() =>

[TestMethod]
[TestProperty("Description", "Validates that Button control fires the accelerators on its attached MenuFlyout.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyButtonFlyoutWithMenuFlyoutCanInvokeAcceleratorDefinedOnMenuItem()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1501,7 +1499,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Verify accelerators submenuitem in menuflyout on button control.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyAcceleratorsDefinedOnSubMenuItemInMenuFlyoutOnButton()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1547,8 +1544,10 @@ await TestServices.RunOnUIThread(async () =>

ButtonWithFlyout = (Button)rootPanel.FindName("ButtonWithFlyout");
MenuItem_KA = ((MenuFlyoutItem)rootPanel.FindName("MenuItem")).KeyboardAccelerators[0];
Sub_MenuItem_KA = ((MenuFlyoutItem)rootPanel.FindName("Sub_MenuItem_KA")).KeyboardAccelerators[0];
Sub_Sub_MenuItem_KA = ((MenuFlyoutItem)rootPanel.FindName("Sub_Sub_MenuItem_KA")).KeyboardAccelerators[0];
var menuFlyout = (ButtonWithFlyout.Flyout as MenuFlyout);
var rootSubItem = (MenuFlyoutSubItem)menuFlyout.Items[1];
Sub_MenuItem_KA = ((MenuFlyoutItem)rootSubItem.Items[1]).KeyboardAccelerators[0];
Sub_Sub_MenuItem_KA = ((MenuFlyoutItem)((MenuFlyoutSubItem)rootSubItem.Items[0]).Items[0]).KeyboardAccelerators[0];
});
await TestServices.WindowHelper.WaitForIdle();

Expand Down Expand Up @@ -1578,7 +1577,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Verify accelerators submenuitem in menuflyout on menubar control.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyAcceleratorsDefinedOnSubMenuItemInMenuFlyoutOnMenuBar()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1624,8 +1622,11 @@ await TestServices.RunOnUIThread(async () =>

ButtonWithoutFlyout = (Button)rootPanel.FindName("ButtonWithoutFlyout");
MenuItem_KA = ((MenuFlyoutItem)rootPanel.FindName("MenuItem")).KeyboardAccelerators[0];
Sub_MenuItem_KA = ((MenuFlyoutItem)rootPanel.FindName("Sub_MenuItem_KA")).KeyboardAccelerators[0];
Sub_Sub_MenuItem_KA = ((MenuFlyoutItem)rootPanel.FindName("Sub_Sub_MenuItem_KA")).KeyboardAccelerators[0];
var menuBar = (MenuBar)rootPanel.Children[0];
var mainItem = menuBar.Items[0];
var rootSubItem = (MenuFlyoutSubItem)mainItem.Items[1];
Sub_MenuItem_KA = ((MenuFlyoutItem)rootSubItem.Items[1]).KeyboardAccelerators[0];
Sub_Sub_MenuItem_KA = ((MenuFlyoutItem)((MenuFlyoutSubItem)rootSubItem.Items[0]).Items[0]).KeyboardAccelerators[0];
});
await TestServices.WindowHelper.WaitForIdle();

Expand Down Expand Up @@ -1655,7 +1656,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that accelerators on MenuBar works when menu item is collapsed.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyAcceleratorDefinedOnMenuBarMenuItems()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1715,7 +1715,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that accelerators on MenuBar works when menu item is opened up.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyAcceleratorDefinedOnMenuBarMenuItemsWhenItsOpened()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1781,7 +1780,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that accelerators on MenuBar works after menu item is opened up and closed again.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyAcceleratorDefinedOnMenuBarMenuItemsWhenItsOpenedAndClosed()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1850,7 +1848,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that StandardUICommands defined on MenuBar after setting window content, works when menu item is collapsed.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyStandarUICommandsDefinedOnMenuBarMenuItemsAfterSettingWindowContent()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1901,7 +1898,6 @@ await TestServices.RunOnUIThread(() =>

[TestMethod]
[TestProperty("Description", "Validates that StandardUICommands on MenuBar works when menu item closed.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyStandarUICommandsDefinedOnMenuBarMenuItems()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -1949,7 +1945,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that StandarUICommands on MenuBar works when menu item is opened up.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyStandarUICommandsDefinedOnMenuBarMenuItemsWhenItsOpened()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -2003,7 +1998,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that StandarUICommands on MenuBar works after menu item opened up and closed again.")]
[Ignore("Requires MenuFlyout support for Keyboard Accelerators #17133")]
public async Task VerifyStandarUICommandsDefinedOnMenuBarMenuItemsWhenItsOpenedAndClosed()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -2202,9 +2196,11 @@ await TestServices.RunOnUIThread(async () =>
TestServices.WindowHelper.WindowContent = rootPanel;
await TestServices.WindowHelper.WaitForLoaded(rootPanel);

ButtonFlyout = (Flyout)rootPanel.FindName("ButtonFlyout");
flyoutButton1 = (Button)rootPanel.FindName("flyoutButton1");
flyoutButton2 = (Button)rootPanel.FindName("flyoutButton2");
var button1 = (Button)rootPanel.FindName("focusButton1");
ButtonFlyout = (Flyout)button1.ContextFlyout;
var stackPanel = (StackPanel)ButtonFlyout.Content;
flyoutButton1 = (Button)stackPanel.Children[0];
flyoutButton2 = (Button)stackPanel.Children[1];
focusButton = (Button)rootPanel.FindName("focusButton");
ctrl1Accelerator = flyoutButton1.KeyboardAccelerators[0];
ctrl1Accelerator.ScopeOwner = ButtonFlyout;
Expand Down Expand Up @@ -2289,7 +2285,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that Button control fires the global accelerators on its attached MenuFlyout.")]
[Ignore("Requires MenuFlyout and ContextFlyout support for Keyboard Accelerators #17133 and #17134")]
public async Task VerifyButtonContextFlyoutWithMenuFlyoutCanInvokeAcceleratorDefinedOnMenuFlyout()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -2831,7 +2826,6 @@ await TestServices.RunOnUIThread(async () =>

[TestMethod]
[TestProperty("Description", "Validates that ListView fires the accelerators on its attached ContextFlyout.")]
[Ignore("Requires ContextFlyout support for Keyboard Accelerators #17134")]
public async Task VerifyListViewContextFlyoutCanInvokeHiddenAccelerator()
{
const string rootPanelXaml =
Expand Down Expand Up @@ -3084,7 +3078,6 @@ await TestServices.RunOnUIThread(() =>

[TestMethod]
[TestProperty("Description", "Validates that ListView does not fire the accelerators on its attached ContextFlyout, as it is scoped to be local.")]
[Ignore("Requires ContextFlyout support for Keyboard Accelerators #17134")]
public async Task VerifyListViewContextFlyoutCanNotInvokeHiddenLocallyScopedAccelerator()
{
const string rootPanelXaml =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ public async Task When_MenuFlyoutItem_CommandChanging()

await WindowHelper.WaitForLoaded(flyoutItem);

flyoutItem.InvokeClick();
flyoutItem.Invoke();
MartinZikmund marked this conversation as resolved.
Show resolved Hide resolved

// Force close the flyout as InvokeClick does not do so.
item.CloseMenuFlyout();
Expand All @@ -330,7 +330,7 @@ public async Task When_MenuFlyoutItem_CommandChanging()

Assert.IsFalse(flyoutItem.IsEnabled);

flyoutItem.InvokeClick();
flyoutItem.Invoke();
item.CloseMenuFlyout();
}

Expand Down
14 changes: 11 additions & 3 deletions src/Uno.UI/DirectUI/DXamlCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@

using System;
using System.Collections.Generic;
using Microsoft.UI.Xaml.Controls;
using Uno.UI.Helpers.WinUI;
using Uno.UI.Xaml.Controls;
using Uno.UI.Xaml.Core;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
using Microsoft.UI.Xaml.Controls;
using Uno.UI.Xaml.Core.Scaling;
using Windows.Foundation;

namespace DirectUI
{
Expand Down Expand Up @@ -98,5 +97,14 @@ internal void OnCompositionContentStateChangedForUWP()
// TODO Uno: Not needed now.
// OnUWPWindowSizeChanged();
}

internal bool IsKeyboardPresent
{
get
{
// TODO:MZ: Detect HW keyboard where possible.
return true;
}
}
}
}
42 changes: 42 additions & 0 deletions src/Uno.UI/DirectUI/ElevationHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
// MUX Reference dxaml\phone\lib\ElevationHelper.cpp, tag winui3/release/1.5.4, commit 98a60c8

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;

namespace DirectUI;

internal static class ElevationHelper
{
// The initial Z offset applied to all elevated controls
private const float s_elevationBaseDepth = 32.0f;
// This additional Z offset will be applied for each tier of logically parented controls
private const float s_elevationIterativeDepth = 8.0f;

internal static void ApplyThemeShadow(UIElement target)
{
var themeShadow = new ThemeShadow();
target.Shadow = themeShadow;
}

internal static void ApplyElevationEffect(UIElement target, int depth)
{
// Calculate the Z offset based on the depth of the shadow
var calculatedZDepth = s_elevationBaseDepth + (depth * s_elevationIterativeDepth);

var endTranslation = new Vector3(0.0f, 0.0f, calculatedZDepth);

// Apply a translation facade value
target.Translation = endTranslation;

// Apply a shadow to the element
ApplyThemeShadow(target);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
#pragma warning disable 114 // new keyword hiding
namespace Microsoft.UI.Xaml.Automation.Peers
{
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false
[global::Uno.NotImplemented]
#endif
public partial class MenuFlyoutItemAutomationPeer : global::Microsoft.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer, global::Microsoft.UI.Xaml.Automation.Provider.IInvokeProvider
{
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public MenuFlyoutItemAutomationPeer(global::Microsoft.UI.Xaml.Controls.MenuFlyoutItem owner) : base(owner)
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.Automation.Peers.MenuFlyoutItemAutomationPeer", "MenuFlyoutItemAutomationPeer.MenuFlyoutItemAutomationPeer(MenuFlyoutItem owner)");
}
#endif
// Forced skipping of method Microsoft.UI.Xaml.Automation.Peers.MenuFlyoutItemAutomationPeer.MenuFlyoutItemAutomationPeer(Microsoft.UI.Xaml.Controls.MenuFlyoutItem)
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public void Invoke()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#pragma warning disable 114 // new keyword hiding
namespace Microsoft.UI.Xaml.Automation.Peers
{
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false
[global::Uno.NotImplemented]
#endif
public partial class MenuFlyoutPresenterAutomationPeer : global::Microsoft.UI.Xaml.Automation.Peers.ItemsControlAutomationPeer
{
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public MenuFlyoutPresenterAutomationPeer(global::Microsoft.UI.Xaml.Controls.MenuFlyoutPresenter owner) : base(owner)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private void ProcessMenuItems(NSMenuItem platformItem, IList<MenuFlyoutItemBase>

case MenuFlyoutItem flyoutItem:
var subPlatformItem2 = new NSMenuItem(flyoutItem.Text, (s, e) => OnItemActivated(flyoutItem)) { Enabled = true };
flyoutItem.InvokeClick();
flyoutItem.Invoke();
platformItem.Submenu.AddItem(subPlatformItem2);
break;

Expand All @@ -89,7 +89,7 @@ private void ProcessMenuItems(NSMenuItem platformItem, IList<MenuFlyoutItemBase>

private void OnItemActivated(MenuFlyoutItem flyoutItem)
{
flyoutItem.InvokeClick();
flyoutItem.Invoke();
}

protected override Size MeasureOverride(Size availableSize)
Expand Down
21 changes: 10 additions & 11 deletions src/Uno.UI/UI/Xaml/Automation/ElementNotEnabledException.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
using System;

namespace Microsoft.UI.Xaml.Automation
namespace Microsoft.UI.Xaml.Automation;

public class ElementNotEnabledException : Exception
{
public class ElementNotEnabledException : Exception
public ElementNotEnabledException() : base()
{
public ElementNotEnabledException() : base()
{
}
}

public ElementNotEnabledException(string message) : base(message)
{
}
public ElementNotEnabledException(string message) : base(message)
{
}

public ElementNotEnabledException(string message, Exception innerException) : base(message, innerException)
{
}
public ElementNotEnabledException(string message, Exception innerException) : base(message, innerException)
{
}
}
27 changes: 27 additions & 0 deletions src/Uno.UI/UI/Xaml/Automation/Peers/AutomationPeer.mux.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,32 @@ public partial class AutomationPeer
{
[NotImplemented]
internal static bool ListenerExistsHelper(AutomationEvents eventId) => false;

/// <summary>
/// Removes the leading and trailing spaces in the provided string and returns the trimmed version
/// or an empty string when no characters are left.
/// Because it is recommended to set an AppBarButton, AppBarToggleButton, MenuFlyoutItem or ToggleMenuFlyoutItem's
/// KeyboardAcceleratorTextOverride to a single space to hide their keyboard accelerator UI, this trimming method
/// prevents automation tools like Narrator from emitting a space when navigating to such an element.
/// </summary>
private protected static string GetTrimmedKeyboardAcceleratorTextOverride(string keyboardAcceleratorTextOverride)
{
// Return an empty string when the provided keyboardAcceleratorTextOverride is already empty.
if (!string.IsNullOrEmpty(keyboardAcceleratorTextOverride))
{
var trimmedKeyboardAcceleratorTextOverride = keyboardAcceleratorTextOverride.TrimStart(' ');

// Return an empty string when the remaining string is empty.
if (!string.IsNullOrEmpty(trimmedKeyboardAcceleratorTextOverride))
{
// Trim the trailing spaces as well.
trimmedKeyboardAcceleratorTextOverride = trimmedKeyboardAcceleratorTextOverride.TrimEnd(' ');
return trimmedKeyboardAcceleratorTextOverride;
}
}

// Return an empty string
return "";
}
}
}
Loading
Loading