|
@@ -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);
|
|
|
+ }
|
|
|
+}
|