From 2bf2e60ae04604ded6bd0dbce0807ca1b8762711 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 22 Feb 2020 10:00:45 +0100 Subject: [PATCH 01/13] Don't add extra pixel to AccessText measurement. It's not needed; the underscore can be drawn in the descender space. --- src/Avalonia.Controls/Primitives/AccessText.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/AccessText.cs b/src/Avalonia.Controls/Primitives/AccessText.cs index 5adc8d2448..f6fea89ec9 100644 --- a/src/Avalonia.Controls/Primitives/AccessText.cs +++ b/src/Avalonia.Controls/Primitives/AccessText.cs @@ -84,17 +84,6 @@ namespace Avalonia.Controls.Primitives return base.CreateFormattedText(constraint, StripAccessKey(text)); } - /// - /// Measures the control. - /// - /// The available size for the control. - /// The desired size. - protected override Size MeasureOverride(Size availableSize) - { - var result = base.MeasureOverride(availableSize); - return result.WithHeight(result.Height + 1); - } - /// protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { From 8a0ccea2731e2df48d105f9e5fea25e6f1361fb9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 22 Feb 2020 10:02:19 +0100 Subject: [PATCH 02/13] Add MenuItem.InputGestureText. --- src/Avalonia.Controls/MenuItem.cs | 51 +++++++++++++++++++++++ src/Avalonia.Themes.Default/MenuItem.xaml | 26 ++++++++---- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index e0baa5e679..ae36b5d830 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -13,6 +13,7 @@ using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -48,6 +49,12 @@ namespace Avalonia.Controls public static readonly StyledProperty IconProperty = AvaloniaProperty.Register(nameof(Icon)); + /// + /// Defines the property. + /// + public static readonly StyledProperty InputGestureTextProperty = + AvaloniaProperty.Register(nameof(InputGestureText)); + /// /// Defines the property. /// @@ -93,6 +100,7 @@ namespace Avalonia.Controls private ICommand _command; private bool _commandCanExecute = true; private Popup _popup; + private IDisposable _gridHack; /// /// Initializes static members of the class. @@ -194,6 +202,19 @@ namespace Avalonia.Controls set { SetValue(IconProperty, value); } } + /// + /// Gets or sets the input gesture that will be displayed in the menu item. + /// + /// + /// Setting this property does not cause the input gesture to be handled by the menu item, + /// it simply displays the gesture text in the menu. + /// + public object InputGestureText + { + get { return GetValue(InputGestureTextProperty); } + set { SetValue(InputGestureTextProperty, value); } + } + /// /// Gets or sets a value indicating whether the is currently selected. /// @@ -306,6 +327,36 @@ namespace Avalonia.Controls } } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + if (this.GetVisualParent() is IControl parent) + { + // HACK: This nasty but it's all WPF's fault. Grid uses an inherited attached + // property to store SharedSizeGroup state, except property inheritance is done + // down the logical tree. In this case, the control which is setting + // Grid.IsSharedSizeScope="True" is not in the logical tree. Instead of fixing + // the way Grid stores shared size state, the developers of WPF just created a + // binding of the internal state of the visual parent to the menu item. We don't + // have much choice but to do the same for now unless we want to refactor Grid, + // which I honestly am not brave enough to do right now. Here's the same hack in + // the WPF codebase: + // + // https://github.com/dotnet/wpf/blob/89537909bdf36bc918e88b37751add46a8980bb0/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L2126-L2141 + _gridHack = Bind( + DefinitionBase.PrivateSharedSizeScopeProperty, + parent.GetBindingObservable(DefinitionBase.PrivateSharedSizeScopeProperty)); + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + _gridHack.Dispose(); + _gridHack = null; + } + /// /// Called when the is clicked. /// diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml index 93989d3782..431adacb47 100644 --- a/src/Avalonia.Themes.Default/MenuItem.xaml +++ b/src/Avalonia.Themes.Default/MenuItem.xaml @@ -11,7 +11,14 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> - + + + + + + + + + + Grid.Column="4"/> + Margin="4 2" + Grid.IsSharedSizeScope="True"/> @@ -102,10 +113,11 @@ BorderThickness="{TemplateBinding BorderThickness}"> + Items="{TemplateBinding Items}" + ItemsPanel="{TemplateBinding ItemsPanel}" + ItemTemplate="{TemplateBinding ItemTemplate}" + Margin="2" + Grid.IsSharedSizeScope="True"/> From deebe6090ffddfff6e5da007d9c56a68d2ffe5a5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 22 Feb 2020 10:02:33 +0100 Subject: [PATCH 03/13] Show input gesture text in control catalog. --- samples/ControlCatalog/Pages/MenuPage.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml index 868f0df6ad..cae5ab54b1 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml +++ b/samples/ControlCatalog/Pages/MenuPage.xaml @@ -16,13 +16,13 @@ Defined in XAML - + - + From f5c9539c7a0365adaee6bd597c5df894fb26912a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Feb 2020 09:31:58 +0100 Subject: [PATCH 04/13] Use correct property type. --- src/Avalonia.Controls/MenuItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index ae36b5d830..1479d737a6 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -209,7 +209,7 @@ namespace Avalonia.Controls /// Setting this property does not cause the input gesture to be handled by the menu item, /// it simply displays the gesture text in the menu. /// - public object InputGestureText + public string InputGestureText { get { return GetValue(InputGestureTextProperty); } set { SetValue(InputGestureTextProperty, value); } From 2944099428f56240f3f35c55add86abf894b8064 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 27 Feb 2020 10:50:39 +0100 Subject: [PATCH 05/13] Display gestures in NativeMenuBar. To do this we needed to change the `KeyGesture.ToString()` method to produce input gesture text suitable for menus. Also demonstrated in `MainWindow` how to produce different gestures/headers depending on platform. --- samples/ControlCatalog/MainWindow.xaml | 6 ++- samples/ControlCatalog/MainWindow.xaml.cs | 14 ++++--- src/Avalonia.Input/KeyGesture.cs | 42 +++++++++++++++---- .../NativeMenuBar.xaml | 1 + .../KeyGestureTests.cs | 31 +++++++++++--- 5 files changed, 73 insertions(+), 21 deletions(-) diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index 248f94082d..d25de9c1f5 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -14,7 +14,7 @@ - + @@ -22,7 +22,9 @@ - + diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index 38cbde9d92..b40fdb4a17 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -1,13 +1,11 @@ +using System; +using System.Runtime.InteropServices; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Notifications; -using Avalonia.Controls.Primitives; +using Avalonia.Input; using Avalonia.Markup.Xaml; -using Avalonia.Threading; using ControlCatalog.ViewModels; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace ControlCatalog { @@ -35,6 +33,12 @@ namespace ControlCatalog mainMenu.AttachedToVisualTree += MenuAttached; } + public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit"; + + public static KeyGesture MenuQuitGesture => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? + new KeyGesture(Key.Q, KeyModifiers.Meta) : + new KeyGesture(Key.F4, KeyModifiers.Alt); + public void MenuAttached(object sender, VisualTreeAttachmentEventArgs e) { if (NativeMenu.GetIsNativeMenuExported(this) && sender is Menu mainMenu) diff --git a/src/Avalonia.Input/KeyGesture.cs b/src/Avalonia.Input/KeyGesture.cs index 490c31bef9..36920fdaad 100644 --- a/src/Avalonia.Input/KeyGesture.cs +++ b/src/Avalonia.Input/KeyGesture.cs @@ -1,9 +1,10 @@ -// Copyright (c) The Avalonia Project. All rights reserved. +// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; +using System.Text; namespace Avalonia.Input { @@ -108,19 +109,43 @@ namespace Avalonia.Input public override string ToString() { - var parts = new List(); + var s = new StringBuilder(); - foreach (var flag in Enum.GetValues(typeof(KeyModifiers)).Cast()) + static void Plus(StringBuilder s) { - if (KeyModifiers.HasFlag(flag) && flag != KeyModifiers.None) + if (s.Length > 0) { - parts.Add(flag.ToString()); + s.Append("+"); } } - parts.Add(Key.ToString()); + if (KeyModifiers.HasFlagCustom(KeyModifiers.Control)) + { + s.Append("Ctrl"); + } + + if (KeyModifiers.HasFlagCustom(KeyModifiers.Shift)) + { + Plus(s); + s.Append("Shift"); + } + + if (KeyModifiers.HasFlagCustom(KeyModifiers.Alt)) + { + Plus(s); + s.Append("Alt"); + } + + if (KeyModifiers.HasFlagCustom(KeyModifiers.Meta)) + { + Plus(s); + s.Append("⌘"); + } + + Plus(s); + s.Append(Key); - return string.Join(" + ", parts); + return s.ToString(); } public bool Matches(KeyEventArgs keyEvent) => ResolveNumPadOperationKey(keyEvent.Key) == Key && keyEvent.KeyModifiers == KeyModifiers; @@ -141,7 +166,8 @@ namespace Avalonia.Input return KeyModifiers.Control; } - if (modifier.Equals("cmd".AsSpan(), StringComparison.OrdinalIgnoreCase)) + if (modifier.Equals("cmd".AsSpan(), StringComparison.OrdinalIgnoreCase) || + modifier.Equals("⌘".AsSpan(), StringComparison.OrdinalIgnoreCase)) { return KeyModifiers.Meta; } diff --git a/src/Avalonia.Themes.Default/NativeMenuBar.xaml b/src/Avalonia.Themes.Default/NativeMenuBar.xaml index 2832bab226..5d21378a64 100644 --- a/src/Avalonia.Themes.Default/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Default/NativeMenuBar.xaml @@ -13,6 +13,7 @@ + +