1
0
Эх сурвалжийг харах

Implemented managed menu bar presenter for native menus

Nikita Tsukanov 6 жил өмнө
parent
commit
465273c883

+ 2 - 0
samples/ControlCatalog/MainWindow.xaml

@@ -15,11 +15,13 @@
         <NativeMenuItem.Menu>
           <NativeMenu>
             <NativeMenuItem Header="Open" Clicked="OnOpenClicked"/>
+            <NativeMenuItemSeperator/>
             <NativeMenuItem Header="Recent">
               <NativeMenuItem.Menu>
                 <NativeMenu/>
               </NativeMenuItem.Menu>
             </NativeMenuItem>
+            <NativeMenuItemSeperator/>
             <NativeMenuItem Header="Quit Avalonia" Clicked="OnCloseClicked" Gesture="CMD+Q"/>
           </NativeMenu>
         </NativeMenuItem.Menu>

+ 1 - 1
samples/ControlCatalog/MainWindow.xaml.cs

@@ -30,7 +30,7 @@ namespace ControlCatalog
             };
 
             DataContext = new MainWindowViewModel(_notificationArea);
-            _recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[1] as NativeMenuItem).Menu;
+            _recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu;
         }
 
         public void OnOpenClicked(object sender, EventArgs args)

+ 4 - 1
samples/ControlCatalog/Pages/MenuPage.xaml

@@ -3,8 +3,11 @@
              x:Class="ControlCatalog.Pages.MenuPage">
   <StackPanel Orientation="Vertical" Spacing="4">
     <TextBlock Classes="h1">Menu</TextBlock>
+    <TextBlock Classes="h2">Exported menu fallback</TextBlock>
+    <TextBlock>(Should be only visible on platforms without desktop-global menu bar)</TextBlock>
+    <NativeMenuBar/>
     <TextBlock Classes="h2">A window menu</TextBlock>
-
+            
         <StackPanel Orientation="Horizontal"
               Margin="0,16,0,0"
               HorizontalAlignment="Center"

+ 2 - 3
src/Avalonia.Controls/NativeMenu.Export.cs

@@ -8,8 +8,7 @@ namespace Avalonia.Controls
     public partial class NativeMenu
     {
         public static readonly AttachedProperty<bool> IsNativeMenuExportedProperty =
-            AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, bool>("IsNativeMenuExported",
-                defaultBindingMode: BindingMode.OneWayToSource);
+            AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, bool>("IsNativeMenuExported");
 
         public static bool GetIsNativeMenuExported(TopLevel tl) => tl.GetValue(IsNativeMenuExportedProperty);
         
@@ -53,7 +52,7 @@ namespace Avalonia.Controls
         }
 
         public static readonly AttachedProperty<NativeMenu> MenuProperty
-            = AvaloniaProperty.RegisterAttached<NativeMenu, AvaloniaObject, NativeMenu>("NativeMenuItems", validate:
+            = AvaloniaProperty.RegisterAttached<NativeMenu, AvaloniaObject, NativeMenu>("Menu", validate:
                 (o, v) =>
                 {
                     if(!(o is Application || o is TopLevel))

+ 1 - 1
src/Avalonia.Controls/NativeMenu.cs

@@ -11,7 +11,7 @@ namespace Avalonia.Controls
 {
     public partial class NativeMenu : AvaloniaObject, IEnumerable<NativeMenuItemBase>
     {
-        private AvaloniaList<NativeMenuItemBase> _items =
+        private readonly AvaloniaList<NativeMenuItemBase> _items =
             new AvaloniaList<NativeMenuItemBase> { ResetBehavior = ResetBehavior.Remove };
         private NativeMenuItem _parent;
         [Content]

+ 36 - 0
src/Avalonia.Controls/NativeMenuBar.cs

@@ -0,0 +1,36 @@
+using System;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+using Avalonia.Styling;
+
+namespace Avalonia.Controls
+{
+    public class NativeMenuBar : TemplatedControl
+    {
+        public static readonly AttachedProperty<bool> EnableMenuItemClickForwardingProperty =
+            AvaloniaProperty.RegisterAttached<NativeMenuBar, MenuItem, Boolean>(
+                "EnableMenuItemClickForwarding");
+
+        static NativeMenuBar()
+        {
+            EnableMenuItemClickForwardingProperty.Changed.Subscribe(args =>
+            {
+                var item = (MenuItem)args.Sender;
+                if (args.NewValue.Equals(true))
+                    item.Click += OnMenuItemClick;
+                else
+                    item.Click -= OnMenuItemClick;
+            });
+        }
+        
+        public static void SetEnableMenuItemClickForwarding(MenuItem menuItem, bool enable)
+        {
+            menuItem.SetValue(EnableMenuItemClickForwardingProperty, enable);
+        }
+
+        private static void OnMenuItemClick(object sender, RoutedEventArgs e)
+        {
+            (((MenuItem)sender).DataContext as NativeMenuItem)?.RaiseClick();
+        }
+    }
+}

+ 5 - 2
src/Avalonia.Controls/NativeMenuItemSeperator.cs

@@ -1,7 +1,10 @@
-namespace Avalonia.Controls
+using System;
+
+namespace Avalonia.Controls
 {
     public class NativeMenuItemSeperator : NativeMenuItemBase
     {
-
+        [Obsolete("This is a temporary hack to make our MenuItem recognize this as a separator, don't use", true)]
+        public string Header => "-";
     }
 }

+ 1 - 0
src/Avalonia.Themes.Default/DefaultTheme.xaml

@@ -51,4 +51,5 @@
   <StyleInclude Source="resm:Avalonia.Themes.Default.AutoCompleteBox.xaml?assembly=Avalonia.Themes.Default"/>
   <StyleInclude Source="resm:Avalonia.Themes.Default.WindowNotificationManager.xaml?assembly=Avalonia.Themes.Default"/>
   <StyleInclude Source="resm:Avalonia.Themes.Default.NotificationCard.xaml?assembly=Avalonia.Themes.Default"/>
+  <StyleInclude Source="resm:Avalonia.Themes.Default.NativeMenuBar.xaml?assembly=Avalonia.Themes.Default"/>
 </Styles>

+ 20 - 0
src/Avalonia.Themes.Default/InverseBooleanValueConverter.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace Avalonia.Themes.Default
+{
+    class InverseBooleanValueConverter : IValueConverter
+    {
+        public bool Default { get; set; }
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value is bool b ? !b : Default;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value is bool b ? !b : !Default;
+        }
+    }
+}

+ 25 - 0
src/Avalonia.Themes.Default/NativeMenuBar.xaml

@@ -0,0 +1,25 @@
+<Style xmlns="https://github.com/avaloniaui"
+       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+       xmlns:local="clr-namespace:Avalonia.Themes.Default"
+       Selector="NativeMenuBar">
+  <Style.Resources>
+    <local:InverseBooleanValueConverter x:Key="AvaloniaThemesDefaultNativeMenuBarInverseBooleanValueConverter" Default="True"/>
+  </Style.Resources>
+  <Setter Property="Template">
+    <ControlTemplate>
+      <Menu
+        IsVisible="{Binding $parent[TopLevel].(NativeMenu.IsNativeMenuExported), Converter={StaticResource AvaloniaThemesDefaultNativeMenuBarInverseBooleanValueConverter}}"
+        Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
+        <Menu.Styles>
+          <Style Selector="MenuItem">
+            <Setter Property="Header" Value="{Binding Header}"/>
+            <Setter Property="Items" Value="{Binding Menu.Items}"/>
+            <Setter Property="Command" Value="{Binding Command}"/>
+            <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
+            <Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True"/>
+          </Style>
+        </Menu.Styles>
+      </Menu>
+    </ControlTemplate>
+  </Setter>
+</Style>