فهرست منبع

avalonia: Added ButtonStrip

Kenric Nugteren 3 ماه پیش
والد
کامیت
042a7bb314

+ 177 - 0
InABox.Avalonia/Components/ButtonStrip/ButtonStrip.cs

@@ -0,0 +1,177 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.LogicalTree;
+using Avalonia.Media;
+using Avalonia.Metadata;
+using Avalonia.Threading;
+using CommunityToolkit.Mvvm.Input;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace InABox.Avalonia.Components;
+
+public partial class ButtonStrip : TemplatedControl
+{
+    public static StyledProperty<IBrush?> SelectedBackgroundProperty = AvaloniaProperty.Register<ButtonStripItem, IBrush?>(
+        nameof(SelectedBackground));
+    public static StyledProperty<IBrush?> SelectedForegroundProperty = AvaloniaProperty.Register<ButtonStripItem, IBrush?>(
+        nameof(SelectedForeground));
+    public static StyledProperty<double> ItemSpacingProperty = AvaloniaProperty.Register<ButtonStripItem, double>(
+        nameof(ItemSpacing));
+    public static StyledProperty<ObservableCollection<ButtonStripItem>> ItemsProperty =
+        AvaloniaProperty.Register<ButtonStripItem, ObservableCollection<ButtonStripItem>>(nameof(Items));
+
+    public IBrush? SelectedBackground
+    {
+        get => GetValue(SelectedBackgroundProperty);
+        set => SetValue(SelectedBackgroundProperty, value);
+    }
+    public IBrush? SelectedForeground
+    {
+        get => GetValue(SelectedForegroundProperty);
+        set => SetValue(SelectedForegroundProperty, value);
+    }
+    public double ItemSpacing
+    {
+        get => GetValue(ItemSpacingProperty);
+        set => SetValue(ItemSpacingProperty, value);
+    }
+
+    [Content]
+    public ObservableCollection<ButtonStripItem> Items
+    {
+        get => GetValue(ItemsProperty);
+        private set => SetValue(ItemsProperty, value);
+    }
+
+    public ButtonStripItem? SelectedItem
+    {
+        get => Items.FirstOrDefault(x => x.Selected);
+        set
+        {
+            foreach(var item in Items)
+            {
+                item.Selected = item == value;
+            }
+        }
+    }
+
+    public event EventHandler SelectionChanged;
+
+    public ButtonStrip()
+    {
+        Items = new();
+        Items.CollectionChanged += Items_CollectionChanged;
+    }
+
+    private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+    {
+        SelectedItem ??= Items.FirstOrDefault();
+        switch (e.Action)
+        {
+            case NotifyCollectionChangedAction.Add:
+                AddControlItemsToLogicalChildren(e.NewItems);
+                break;
+            case NotifyCollectionChangedAction.Remove:
+                RemoveControlItemsFromLogicalChildren(e.OldItems);
+                break;
+        }
+        foreach(var item in Items)
+        {
+            item.Command = ItemSelectedCommand;
+        }
+    }
+
+    private void AddControlItemsToLogicalChildren(IEnumerable? items)
+    {
+        if (items is null) return;
+
+        List<ILogical>? toAdd = null;
+        foreach(var i in items)
+        {
+            if(i is Control control && !LogicalChildren.Contains(control))
+            {
+                toAdd ??= new();
+                toAdd.Add(control);
+            }
+        }
+        if(toAdd is not null)
+        {
+            LogicalChildren.AddRange(toAdd);
+        }
+    }
+    private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
+    {
+        if (items is null) return;
+
+        List<ILogical>? toRemove = null;
+        foreach(var i in items)
+        {
+            if(i is Control control)
+            {
+                toRemove ??= new();
+                toRemove.Add(control);
+            }
+        }
+        if(toRemove is not null)
+        {
+            LogicalChildren.RemoveAll(toRemove);
+        }
+    }
+
+    [RelayCommand]
+    private void ItemSelected(ButtonStripItem item)
+    {
+        var children = this.GetLogicalChildren().ToArray();
+        SelectedItem = item;
+    }
+}
+
+[PseudoClasses(":selected")]
+public class ButtonStripItem : TemplatedControl
+{
+    public static StyledProperty<string> TextProperty = AvaloniaProperty.Register<ButtonStripItem, string>(nameof(Text));
+    public static StyledProperty<bool> SelectedProperty = AvaloniaProperty.Register<ButtonStripItem, bool>(nameof(Selected));
+    public static StyledProperty<int> IndexProperty = AvaloniaProperty.Register<ButtonStripItem, int>(nameof(Index));
+    public static StyledProperty<ICommand> CommandProperty = AvaloniaProperty.Register<ButtonStripItem, ICommand>(nameof(Command));
+
+    public string Text
+    {
+        get => GetValue(TextProperty);
+        set => SetValue(TextProperty, value);
+    }
+    public bool Selected
+    {
+        get => GetValue(SelectedProperty);
+        set => SetValue(SelectedProperty, value);
+    }
+    public int Index
+    {
+        get => GetValue(IndexProperty);
+        set => SetValue(IndexProperty, value);
+    }
+    public ICommand Command
+    {
+        get => GetValue(CommandProperty);
+        set => SetValue(CommandProperty, value);
+    }
+
+    static ButtonStripItem()
+    {
+        SelectedProperty.Changed.AddClassHandler<ButtonStripItem>(Selected_Changed);
+    }
+
+    private static void Selected_Changed(ButtonStripItem item, AvaloniaPropertyChangedEventArgs args)
+    {
+        item.PseudoClasses.Set(":selected", item.Selected);
+    }
+}

+ 6 - 0
InABox.Avalonia/Theme/Classes/Button.axaml

@@ -26,6 +26,12 @@
         <Setter Property="Background" Value="{Binding $parent[Button].Background}" />
         <Setter Property="Foreground" Value="{Binding $parent[Button].Foreground}" />
     </Style>
+    <Style Selector="Button.NoHover:pointerover /template/ ContentPresenter#PART_ContentPresenter">
+        <Setter Property="BorderBrush" Value="{Binding $parent[Button].BorderBrush}" />
+        <Setter Property="BorderThickness" Value="{Binding $parent[Button].BorderThickness}" />
+        <Setter Property="Background" Value="{Binding $parent[Button].Background}" />
+        <Setter Property="Foreground" Value="{Binding $parent[Button].Foreground}" />
+    </Style>
 
     <Style Selector="Button.Transparent">
         <Setter Property="HorizontalAlignment" Value="Stretch" />

+ 47 - 0
InABox.Avalonia/Theme/Classes/ButtonStrip.axaml

@@ -0,0 +1,47 @@
+<Styles xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+		xmlns:components="using:InABox.Avalonia.Components">
+	<Style Selector="components|ButtonStrip">
+		<Setter Property="Template">
+			<ControlTemplate>
+				<ScrollViewer VerticalScrollBarVisibility="Disabled"
+							  HorizontalScrollBarVisibility="Auto"
+							  BorderBrush="{TemplateBinding BorderBrush}"
+							  CornerRadius="{TemplateBinding CornerRadius}">
+					<ItemsControl ItemsSource="{TemplateBinding Items}">
+						<ItemsControl.ItemsPanel>
+							<ItemsPanelTemplate>
+								<StackPanel Orientation="Horizontal" Spacing="{Binding $parent[components:ButtonStrip].ItemSpacing}"/>
+							</ItemsPanelTemplate>
+						</ItemsControl.ItemsPanel>
+					</ItemsControl>
+				</ScrollViewer>
+			</ControlTemplate>
+		</Setter>
+		<Setter Property="Background" Value="{DynamicResource PrsButtonBackground}"/>
+		<Setter Property="Foreground" Value="{DynamicResource PrsButtonForeground}"/>
+		<Setter Property="SelectedBackground" Value="{DynamicResource PrsTileBackground}"/>
+		<Setter Property="SelectedForeground" Value="{DynamicResource PrsTileForeground}"/>
+	</Style>
+	<Style Selector="components|ButtonStrip components|ButtonStripItem">
+		<Setter Property="Template">
+			<ControlTemplate>
+				<Button Classes="NoHover"
+						CommandParameter="{TemplateBinding}"
+						Command="{TemplateBinding Command}"
+						Background="{TemplateBinding Background}"
+						Foreground="{TemplateBinding Foreground}"
+						Content="{TemplateBinding Text}">
+				</Button>
+			</ControlTemplate>
+		</Setter>
+	</Style>
+	<Style Selector="components|ButtonStrip > components|ButtonStripItem">
+		<Setter Property="Background" Value="{Binding $parent[components:ButtonStrip].Background}"/>
+		<Setter Property="Foreground" Value="{Binding $parent[components:ButtonStrip].Foreground}"/>
+	</Style>
+	<Style Selector="components|ButtonStrip > components|ButtonStripItem:selected">
+		<Setter Property="Background" Value="{Binding $parent[components:ButtonStrip].SelectedBackground}"/>
+		<Setter Property="Foreground" Value="{Binding $parent[components:ButtonStrip].SelectedForeground}"/>
+	</Style>
+</Styles>

+ 5 - 4
InABox.Avalonia/Theme/Styles.axaml

@@ -4,13 +4,14 @@
 
 	<StyleInclude Source="/Theme/Classes/Border.axaml" />
 	<StyleInclude Source="/Theme/Classes/Button.axaml" />
+	<StyleInclude Source="/Theme/Classes/ButtonStrip.axaml" />
 	<StyleInclude Source="/Theme/Classes/CheckBox.axaml" />
+	<StyleInclude Source="/Theme/Classes/DateSelectorButton.axaml" />
 	<StyleInclude Source="/Theme/Classes/Image.axaml" />
 	<StyleInclude Source="/Theme/Classes/Label.axaml" />
-	<StyleInclude Source="/Theme/Classes/TextBox.axaml" />
-	<StyleInclude Source="/Theme/Classes/Separator.axaml" />
 	<StyleInclude Source="/Theme/Classes/ListBox.axaml" />
-	<StyleInclude Source="/Theme/Classes/TabStrip.axaml" />
+	<StyleInclude Source="/Theme/Classes/Separator.axaml" />
 	<StyleInclude Source="/Theme/Classes/TabItem.axaml" />
-	<StyleInclude Source="/Theme/Classes/DateSelectorButton.axaml" />
+	<StyleInclude Source="/Theme/Classes/TabStrip.axaml" />
+	<StyleInclude Source="/Theme/Classes/TextBox.axaml" />
 </Styles>