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 SelectedBackgroundProperty = AvaloniaProperty.Register( nameof(SelectedBackground)); public static StyledProperty SelectedForegroundProperty = AvaloniaProperty.Register( nameof(SelectedForeground)); public static StyledProperty ItemSpacingProperty = AvaloniaProperty.Register( nameof(ItemSpacing)); public static StyledProperty> ItemsProperty = AvaloniaProperty.Register>(nameof(Items)); public static StyledProperty ItemsSourceProperty = AvaloniaProperty.Register(nameof(ItemsSource)); public static StyledProperty SelectedCommandProperty = AvaloniaProperty.Register(nameof(SelectedCommand)); public static StyledProperty SelectedItemProperty = AvaloniaProperty.Register(nameof(SelectedItem)); 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); } public ICommand SelectedCommand { get => GetValue(SelectedCommandProperty); set => SetValue(SelectedCommandProperty, value); } public IEnumerable? ItemsSource { get => GetValue(ItemsSourceProperty); set => SetValue(ItemsSourceProperty, value); } [Content] public ObservableCollection Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); } private ButtonStripItem? SelectedButton { get => Items.FirstOrDefault(x => x.Selected); set { foreach(var item in Items) { item.Selected = item == value; } SelectedItem = value?.Tag; } } public object? SelectedItem { get => GetValue(SelectedItemProperty); set => SetValue(SelectedItemProperty, value); } public event EventHandler? SelectionChanged; static ButtonStrip() { ItemsProperty.Changed.AddClassHandler(Items_Changed); ItemsSourceProperty.Changed.AddClassHandler(ItemsSource_Changed); } private static void ItemsSource_Changed(ButtonStrip strip, AvaloniaPropertyChangedEventArgs args) { if(strip.ItemsSource is INotifyCollectionChanged notify) { notify.CollectionChanged += (sender, e) => strip.RebuildItems(); } strip.RebuildItems(); } private void RebuildItems() { Items.Clear(); if(ItemsSource is not null) { foreach(var item in ItemsSource) { if(item is ButtonStripItem btn) { Items.Add(btn); } else { Items.Add(new(item?.ToString() ?? "") { Tag = item }); } } } } private static void Items_Changed(ButtonStrip strip, AvaloniaPropertyChangedEventArgs args) { strip.LogicalChildren.Clear(); if (strip.Items is not null) { strip.SelectedButton = strip.Items.FirstOrDefault(); strip.LogicalChildren.AddRange(strip.Items); foreach(var item in strip.Items) { item.Command = strip.ItemSelectedCommand; } } } public ButtonStrip() { Items = new(); Items.CollectionChanged += Items_CollectionChanged; } private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { SelectedButton ??= 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? 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? 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(); SelectedButton = item; SelectionChanged?.Invoke(this, new()); SelectedCommand?.Execute(item.Tag); } public void AddRange(IEnumerable items) { foreach(var item in items) { Items.Add(new(item)); } } } [PseudoClasses(":selected")] public class ButtonStripItem : TemplatedControl { public static StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text)); public static StyledProperty SelectedProperty = AvaloniaProperty.Register(nameof(Selected)); public static StyledProperty IndexProperty = AvaloniaProperty.Register(nameof(Index)); public static StyledProperty CommandProperty = AvaloniaProperty.Register(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); } public ButtonStripItem() { } public ButtonStripItem(string text) { Text = text; } static ButtonStripItem() { SelectedProperty.Changed.AddClassHandler(Selected_Changed); } private static void Selected_Changed(ButtonStripItem item, AvaloniaPropertyChangedEventArgs args) { item.PseudoClasses.Set(":selected", item.Selected); } }