From 76da0b36160d2f6dc1bb51b29ff0ab5e0af028fd Mon Sep 17 00:00:00 2001 From: rabbitism Date: Wed, 21 Feb 2024 02:15:30 +0800 Subject: [PATCH 01/12] feat: initialize, setup demo. make sure MVVM works. --- .../MenuDataTemplateSelector.cs | 0 .../ToolBarItemTemplateSelector.cs | 48 +++++++++++++ .../ViewLocator.cs | 0 demo/Ursa.Demo/Models/MenuKeys.cs | 1 + demo/Ursa.Demo/Pages/ToolBarDemo.axaml | 24 +++++++ demo/Ursa.Demo/Pages/ToolBarDemo.axaml.cs | 13 ++++ .../Ursa.Demo/ViewModels/MainViewViewModel.cs | 1 + demo/Ursa.Demo/ViewModels/MenuViewModel.cs | 1 + .../ViewModels/ToolBarDemoViewModel.cs | 72 +++++++++++++++++++ src/Ursa.Themes.Semi/Controls/ToolBar.axaml | 37 ++++++++++ src/Ursa.Themes.Semi/Controls/_index.axaml | 1 + src/Ursa/Controls/ToolBar/OverflowMode.cs | 8 +++ src/Ursa/Controls/ToolBar/ToolBar.cs | 61 ++++++++++++++++ .../Controls/ToolBar/ToolBarOverflowPanel.cs | 8 +++ src/Ursa/Controls/ToolBar/ToolBarPanel.cs | 34 +++++++++ src/Ursa/Ursa.csproj | 4 ++ 16 files changed, 313 insertions(+) rename demo/Ursa.Demo/{Converters => DataTemplates}/MenuDataTemplateSelector.cs (100%) create mode 100644 demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs rename demo/Ursa.Demo/{Converters => DataTemplates}/ViewLocator.cs (100%) create mode 100644 demo/Ursa.Demo/Pages/ToolBarDemo.axaml create mode 100644 demo/Ursa.Demo/Pages/ToolBarDemo.axaml.cs create mode 100644 demo/Ursa.Demo/ViewModels/ToolBarDemoViewModel.cs create mode 100644 src/Ursa.Themes.Semi/Controls/ToolBar.axaml create mode 100644 src/Ursa/Controls/ToolBar/OverflowMode.cs create mode 100644 src/Ursa/Controls/ToolBar/ToolBar.cs create mode 100644 src/Ursa/Controls/ToolBar/ToolBarOverflowPanel.cs create mode 100644 src/Ursa/Controls/ToolBar/ToolBarPanel.cs diff --git a/demo/Ursa.Demo/Converters/MenuDataTemplateSelector.cs b/demo/Ursa.Demo/DataTemplates/MenuDataTemplateSelector.cs similarity index 100% rename from demo/Ursa.Demo/Converters/MenuDataTemplateSelector.cs rename to demo/Ursa.Demo/DataTemplates/MenuDataTemplateSelector.cs diff --git a/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs b/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs new file mode 100644 index 0000000..9caceef --- /dev/null +++ b/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs @@ -0,0 +1,48 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Data; +using Ursa.Demo.ViewModels; + +namespace Ursa.Demo.Converters; + +public class ToolBarItemTemplateSelector: IDataTemplate +{ + public static ToolBarItemTemplateSelector Instance { get; } = new(); + public Control? Build(object? param) + { + if (param is null) return null; + if (param is ToolBarButtonItemViewModel vm) + { + return new Button() + { + [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, + [!Button.CommandProperty] = new Binding() { Path = "Command" }, + }; + } + if (param is ToolBarCheckBoxItemViweModel cb) + { + return new CheckBox() + { + [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, + [!ToggleButton.IsCheckedProperty] = new Binding() { Path = "IsChecked" }, + [!Button.CommandProperty] = new Binding() { Path = "Command" }, + }; + } + if (param is ToolBarComboBoxItemViewModel combo) + { + return new ComboBox() + { + [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, + [!SelectingItemsControl.SelectedItemProperty] = new Binding() { Path = "SelectedItem" }, + [!ItemsControl.ItemsSourceProperty] = new Binding() { Path = "Items" }, + }; + } + return new Button() { Content = "Undefined Item" }; + } + + public bool Match(object? data) + { + return data is ToolBarItemViewModel; + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/Converters/ViewLocator.cs b/demo/Ursa.Demo/DataTemplates/ViewLocator.cs similarity index 100% rename from demo/Ursa.Demo/Converters/ViewLocator.cs rename to demo/Ursa.Demo/DataTemplates/ViewLocator.cs diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs index 4c707a6..1351630 100644 --- a/demo/Ursa.Demo/Models/MenuKeys.cs +++ b/demo/Ursa.Demo/Models/MenuKeys.cs @@ -31,5 +31,6 @@ public static class MenuKeys public const string MenuKeyTimeline = "Timeline"; public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon"; public const string MenuKeyThemeToggler = "ThemeToggler"; + public const string MenuKeyToolBar = "ToolBar"; } \ No newline at end of file diff --git a/demo/Ursa.Demo/Pages/ToolBarDemo.axaml b/demo/Ursa.Demo/Pages/ToolBarDemo.axaml new file mode 100644 index 0000000..61af50f --- /dev/null +++ b/demo/Ursa.Demo/Pages/ToolBarDemo.axaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml index 848b1b9..fdbbda4 100644 --- a/src/Ursa.Themes.Semi/Controls/_index.axaml +++ b/src/Ursa.Themes.Semi/Controls/_index.axaml @@ -30,5 +30,6 @@ + diff --git a/src/Ursa/Controls/ToolBar/OverflowMode.cs b/src/Ursa/Controls/ToolBar/OverflowMode.cs new file mode 100644 index 0000000..975c7e5 --- /dev/null +++ b/src/Ursa/Controls/ToolBar/OverflowMode.cs @@ -0,0 +1,8 @@ +namespace Ursa.Controls; + +public enum OverflowMode +{ + AsNeeded, + Always, + Never +} \ No newline at end of file diff --git a/src/Ursa/Controls/ToolBar/ToolBar.cs b/src/Ursa/Controls/ToolBar/ToolBar.cs new file mode 100644 index 0000000..0582c65 --- /dev/null +++ b/src/Ursa/Controls/ToolBar/ToolBar.cs @@ -0,0 +1,61 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Layout; + +namespace Ursa.Controls; + +[TemplatePart(PART_OverflowPanel, typeof(Panel))] +public class ToolBar: HeaderedItemsControl +{ + public const string PART_OverflowPanel = "PART_OverflowPanel"; + + internal Panel? OverflowPanel { get; private set; } + + private static readonly ITemplate DefaultTemplate = + new FuncTemplate(() => new ToolBarPanel() { Orientation = Orientation.Horizontal }); + + public static readonly StyledProperty OrientationProperty = + StackPanel.OrientationProperty.AddOwner(); + + public Orientation Orientation + { + get => GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + + public static readonly StyledProperty BandProperty = AvaloniaProperty.Register( + nameof(Band)); + + public int Band + { + get => GetValue(BandProperty); + set => SetValue(BandProperty, value); + } + + static ToolBar() + { + IsTabStopProperty.OverrideDefaultValue(false); + ItemsPanelProperty.OverrideDefaultValue(DefaultTemplate); + OrientationProperty.OverrideDefaultValue(Orientation.Horizontal); + } + + protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey) + { + return NeedsContainer(item, out recycleKey); + } + + protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey) + { + return new ContentPresenter(); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + OverflowPanel = e.NameScope.Find(PART_OverflowPanel); + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/ToolBar/ToolBarOverflowPanel.cs b/src/Ursa/Controls/ToolBar/ToolBarOverflowPanel.cs new file mode 100644 index 0000000..be52346 --- /dev/null +++ b/src/Ursa/Controls/ToolBar/ToolBarOverflowPanel.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace Ursa.Controls; + +public class ToolBarOverflowPanel: StackPanel +{ + +} \ No newline at end of file diff --git a/src/Ursa/Controls/ToolBar/ToolBarPanel.cs b/src/Ursa/Controls/ToolBar/ToolBarPanel.cs new file mode 100644 index 0000000..288d0e4 --- /dev/null +++ b/src/Ursa/Controls/ToolBar/ToolBarPanel.cs @@ -0,0 +1,34 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.LogicalTree; + +namespace Ursa.Controls; + +public class ToolBarPanel: StackPanel +{ + private ToolBar? _parent; + private Panel? _overflowPanel; + + internal Panel? OverflowPanel => _overflowPanel ??= _parent?.OverflowPanel; + internal ToolBar? ParentToolBar => _parent ??= this.TemplatedParent as ToolBar; + + static ToolBarPanel() + { + OrientationProperty.OverrideDefaultValue(Orientation.Horizontal); + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + _parent = this.TemplatedParent as ToolBar; + if (_parent is null) return; + this[!OrientationProperty] = _parent[!OrientationProperty]; + } + + protected override Size MeasureOverride(Size availableSize) + { + return base.MeasureOverride(availableSize); + + } +} \ No newline at end of file diff --git a/src/Ursa/Ursa.csproj b/src/Ursa/Ursa.csproj index ec98fe6..2bd6329 100644 --- a/src/Ursa/Ursa.csproj +++ b/src/Ursa/Ursa.csproj @@ -17,4 +17,8 @@ + + + + From ab4b74d055444a1ddb9242ae458a08e484d68a14 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Wed, 21 Feb 2024 14:30:51 +0800 Subject: [PATCH 02/12] feat: wip. --- demo/Ursa.Demo/App.axaml | 2 +- .../ToolBarItemTemplateSelector.cs | 4 ++++ .../ViewModels/ToolBarDemoViewModel.cs | 6 +++--- src/Ursa.Themes.Semi/Controls/ToolBar.axaml | 4 ++-- src/Ursa/Controls/ToolBar/ToolBar.cs | 6 ++++++ src/Ursa/Controls/ToolBar/ToolBarPanel.cs | 21 +++++++++++++++++-- 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/demo/Ursa.Demo/App.axaml b/demo/Ursa.Demo/App.axaml index 342fcd6..abf9048 100644 --- a/demo/Ursa.Demo/App.axaml +++ b/demo/Ursa.Demo/App.axaml @@ -5,6 +5,6 @@ xmlns:u-semi="https://irihi.tech/ursa/themes/semi"> - + \ No newline at end of file diff --git a/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs b/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs index 9caceef..f48b834 100644 --- a/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs +++ b/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs @@ -2,6 +2,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; +using Ursa.Controls; using Ursa.Demo.ViewModels; namespace Ursa.Demo.Converters; @@ -18,6 +19,7 @@ public class ToolBarItemTemplateSelector: IDataTemplate { [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, [!Button.CommandProperty] = new Binding() { Path = "Command" }, + [!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" } }; } if (param is ToolBarCheckBoxItemViweModel cb) @@ -27,6 +29,7 @@ public class ToolBarItemTemplateSelector: IDataTemplate [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, [!ToggleButton.IsCheckedProperty] = new Binding() { Path = "IsChecked" }, [!Button.CommandProperty] = new Binding() { Path = "Command" }, + [!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" } }; } if (param is ToolBarComboBoxItemViewModel combo) @@ -36,6 +39,7 @@ public class ToolBarItemTemplateSelector: IDataTemplate [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, [!SelectingItemsControl.SelectedItemProperty] = new Binding() { Path = "SelectedItem" }, [!ItemsControl.ItemsSourceProperty] = new Binding() { Path = "Items" }, + [!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" } }; } return new Button() { Content = "Undefined Item" }; diff --git a/demo/Ursa.Demo/ViewModels/ToolBarDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/ToolBarDemoViewModel.cs index ad510d9..d154f0e 100644 --- a/demo/Ursa.Demo/ViewModels/ToolBarDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/ToolBarDemoViewModel.cs @@ -13,11 +13,11 @@ public partial class ToolBarDemoViewModel: ObservableObject { Items = new() { - new ToolBarButtonItemViewModel() { Content = "New" }, + new ToolBarButtonItemViewModel() { Content = "New", OverflowMode = OverflowMode.AsNeeded}, new ToolBarButtonItemViewModel() { Content = "Open" }, new ToolBarButtonItemViewModel() { Content = "Save" }, new ToolBarCheckBoxItemViweModel() { Content = "Bold" }, - new ToolBarCheckBoxItemViweModel() { Content = "Italic" }, + new ToolBarCheckBoxItemViweModel() { Content = "Italic", OverflowMode = OverflowMode.Never}, new ToolBarComboBoxItemViewModel() { Content = "Font Size", Items = new (){ "10", "12", "14" } } }; } @@ -25,7 +25,7 @@ public partial class ToolBarDemoViewModel: ObservableObject public abstract class ToolBarItemViewModel: ObservableObject { - + public OverflowMode OverflowMode { get; set; } } public class ToolBarButtonItemViewModel: ToolBarItemViewModel diff --git a/src/Ursa.Themes.Semi/Controls/ToolBar.axaml b/src/Ursa.Themes.Semi/Controls/ToolBar.axaml index 7f1d026..ab349f5 100644 --- a/src/Ursa.Themes.Semi/Controls/ToolBar.axaml +++ b/src/Ursa.Themes.Semi/Controls/ToolBar.axaml @@ -23,8 +23,8 @@ Fill="Gray" /> - - + + diff --git a/src/Ursa/Controls/ToolBar/ToolBar.cs b/src/Ursa/Controls/ToolBar/ToolBar.cs index 0582c65..eefc2bb 100644 --- a/src/Ursa/Controls/ToolBar/ToolBar.cs +++ b/src/Ursa/Controls/ToolBar/ToolBar.cs @@ -36,6 +36,12 @@ public class ToolBar: HeaderedItemsControl set => SetValue(BandProperty, value); } + public static readonly AttachedProperty OverflowModeProperty = + AvaloniaProperty.RegisterAttached("OverflowMode"); + + public static void SetOverflowMode(Control? obj, OverflowMode value) => obj.SetValue(OverflowModeProperty, value); + public static OverflowMode GetOverflowMode(Control? obj) => obj.GetValue(OverflowModeProperty); + static ToolBar() { IsTabStopProperty.OverrideDefaultValue(false); diff --git a/src/Ursa/Controls/ToolBar/ToolBarPanel.cs b/src/Ursa/Controls/ToolBar/ToolBarPanel.cs index 288d0e4..c71558e 100644 --- a/src/Ursa/Controls/ToolBar/ToolBarPanel.cs +++ b/src/Ursa/Controls/ToolBar/ToolBarPanel.cs @@ -28,7 +28,24 @@ public class ToolBarPanel: StackPanel protected override Size MeasureOverride(Size availableSize) { - return base.MeasureOverride(availableSize); - + var size = base.MeasureOverride(availableSize); + var children = this.Children; + var children2 = this.OverflowPanel?.Children; + var all = children.ToList(); + if (children2 != null) + { + all.AddRange(children2); + } + this.Children.Clear(); + OverflowPanel?.Children.Clear(); + for (int i = 0; i < all.Count - 1; i++) + { + this.Children.Add(all[i]); + } + if (all.Count > 0) + { + OverflowPanel?.Children.Add(all.Last()); + } + return size; } } \ No newline at end of file From 6f4e11c7f55d39fc44a955a17d6913ea2ddd52c7 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Wed, 21 Feb 2024 22:48:11 +0800 Subject: [PATCH 03/12] feat: layout calculation. --- .../ToolBarItemTemplateSelector.cs | 3 +- demo/Ursa.Demo/Pages/ToolBarDemo.axaml | 7 +- demo/Ursa.Demo/Views/MainWindow.axaml.cs | 1 + src/Ursa.Themes.Semi/Controls/ToolBar.axaml | 41 ++++-- src/Ursa/Controls/ToolBar/ToolBar.cs | 22 +++ src/Ursa/Controls/ToolBar/ToolBarPanel.cs | 126 +++++++++++++++--- 6 files changed, 166 insertions(+), 34 deletions(-) diff --git a/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs b/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs index f48b834..76e669b 100644 --- a/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs +++ b/demo/Ursa.Demo/DataTemplates/ToolBarItemTemplateSelector.cs @@ -28,7 +28,6 @@ public class ToolBarItemTemplateSelector: IDataTemplate { [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, [!ToggleButton.IsCheckedProperty] = new Binding() { Path = "IsChecked" }, - [!Button.CommandProperty] = new Binding() { Path = "Command" }, [!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" } }; } @@ -39,7 +38,7 @@ public class ToolBarItemTemplateSelector: IDataTemplate [!ContentControl.ContentProperty] = new Binding() { Path = "Content" }, [!SelectingItemsControl.SelectedItemProperty] = new Binding() { Path = "SelectedItem" }, [!ItemsControl.ItemsSourceProperty] = new Binding() { Path = "Items" }, - [!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" } + [ToolBar.OverflowModeProperty] = OverflowMode.Always, }; } return new Button() { Content = "Undefined Item" }; diff --git a/demo/Ursa.Demo/Pages/ToolBarDemo.axaml b/demo/Ursa.Demo/Pages/ToolBarDemo.axaml index 61af50f..743ae82 100644 --- a/demo/Ursa.Demo/Pages/ToolBarDemo.axaml +++ b/demo/Ursa.Demo/Pages/ToolBarDemo.axaml @@ -10,15 +10,16 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Ursa.Demo.Pages.ToolBarDemo"> - +