diff --git a/src/Ursa/Controls/TagInput/TagInput.cs b/src/Ursa/Controls/TagInput/TagInput.cs index c51fd8d..687a3ff 100644 --- a/src/Ursa/Controls/TagInput/TagInput.cs +++ b/src/Ursa/Controls/TagInput/TagInput.cs @@ -1,7 +1,6 @@ using System.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; -using System.ComponentModel; using Avalonia; using Avalonia.Collections; using Avalonia.Controls; @@ -9,11 +8,8 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; -using Avalonia.Data; -using Avalonia.Data.Converters; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Layout; using Avalonia.Styling; using Irihi.Avalonia.Shared.Common; using Irihi.Avalonia.Shared.Helpers; @@ -28,16 +24,73 @@ public class TagInput : TemplatedControl public const string PART_ItemsControl = "PART_ItemsControl"; public const string PART_Watermark = "PART_Watermark"; - private readonly TextBox _textBox; - private ItemsControl? _itemsControl; - private Visual? _watermark; - public static readonly StyledProperty> TagsProperty = AvaloniaProperty.Register>( nameof(Tags)); public static readonly StyledProperty WatermarkProperty = TextBox.WatermarkProperty.AddOwner(); + public static readonly StyledProperty MaxCountProperty = AvaloniaProperty.Register( + nameof(MaxCount), int.MaxValue); + + public static readonly DirectProperty ItemsProperty = + AvaloniaProperty.RegisterDirect( + nameof(Items), o => o.Items); + + public static readonly StyledProperty InputThemeProperty = + AvaloniaProperty.Register( + nameof(InputTheme)); + + public static readonly StyledProperty ItemTemplateProperty = + AvaloniaProperty.Register( + nameof(ItemTemplate)); + + public static readonly StyledProperty SeparatorProperty = AvaloniaProperty.Register( + nameof(Separator)); + + public static readonly StyledProperty LostFocusBehaviorProperty = + AvaloniaProperty.Register( + nameof(LostFocusBehavior)); + + + public static readonly StyledProperty AllowDuplicatesProperty = AvaloniaProperty.Register( + nameof(AllowDuplicates), true); + + public static readonly StyledProperty InnerLeftContentProperty = + AvaloniaProperty.Register( + nameof(InnerLeftContent)); + + public static readonly StyledProperty InnerRightContentProperty = + AvaloniaProperty.Register( + nameof(InnerRightContent)); + + private readonly TextBox _textBox; + + private IList _items = null!; + private ItemsControl? _itemsControl; + + private TextPresenter? _presenter; + private Visual? _watermark; + + + static TagInput() + { + InputThemeProperty.Changed.AddClassHandler((o, e) => o.OnInputThemePropertyChanged(e)); + TagsProperty.Changed.AddClassHandler((o, e) => o.OnTagsPropertyChanged(e)); + } + + public TagInput() + { + _textBox = new TextBox(); + _textBox.AddHandler(KeyDownEvent, OnTextBoxKeyDown, RoutingStrategies.Tunnel); + _textBox.AddHandler(LostFocusEvent, OnTextBox_LostFocus, RoutingStrategies.Bubble); + Items = new AvaloniaList + { + _textBox + }; + Tags = new ObservableCollection(); + } + public string? Watermark { get => GetValue(WatermarkProperty); @@ -50,37 +103,58 @@ public class TagInput : TemplatedControl set => SetValue(TagsProperty, value); } - public static readonly StyledProperty MaxCountProperty = AvaloniaProperty.Register( - nameof(MaxCount), int.MaxValue); - public int MaxCount { get => GetValue(MaxCountProperty); set => SetValue(MaxCountProperty, value); } - public static readonly DirectProperty ItemsProperty = - AvaloniaProperty.RegisterDirect( - nameof(Items), o => o.Items); - - private IList _items; - public IList Items { get => _items; private set => SetAndRaise(ItemsProperty, ref _items, value); } - public TagInput() + public ControlTheme InputTheme { - _textBox = new TextBox(); - _textBox.AddHandler(KeyDownEvent, OnTextBoxKeyDown, RoutingStrategies.Tunnel); - _textBox.AddHandler(LostFocusEvent, OnTextBox_LostFocus, RoutingStrategies.Bubble); - Items = new AvaloniaList - { - _textBox - }; - Tags = new ObservableCollection(); + get => GetValue(InputThemeProperty); + set => SetValue(InputThemeProperty, value); + } + + public IDataTemplate? ItemTemplate + { + get => GetValue(ItemTemplateProperty); + set => SetValue(ItemTemplateProperty, value); + } + + public string Separator + { + get => GetValue(SeparatorProperty); + set => SetValue(SeparatorProperty, value); + } + + public LostFocusBehavior LostFocusBehavior + { + get => GetValue(LostFocusBehaviorProperty); + set => SetValue(LostFocusBehaviorProperty, value); + } + + public bool AllowDuplicates + { + get => GetValue(AllowDuplicatesProperty); + set => SetValue(AllowDuplicatesProperty, value); + } + + public object? InnerLeftContent + { + get => GetValue(InnerLeftContentProperty); + set => SetValue(InnerLeftContentProperty, value); + } + + public object? InnerRightContent + { + get => GetValue(InnerRightContentProperty); + set => SetValue(InnerRightContentProperty, value); } private void OnTextBox_LostFocus(object? sender, RoutedEventArgs e) @@ -96,81 +170,6 @@ public class TagInput : TemplatedControl } } - public static readonly StyledProperty InputThemeProperty = - AvaloniaProperty.Register( - nameof(InputTheme)); - - public ControlTheme InputTheme - { - get => GetValue(InputThemeProperty); - set => SetValue(InputThemeProperty, value); - } - - public static readonly StyledProperty ItemTemplateProperty = - AvaloniaProperty.Register( - nameof(ItemTemplate)); - - public IDataTemplate? ItemTemplate - { - get => GetValue(ItemTemplateProperty); - set => SetValue(ItemTemplateProperty, value); - } - - public static readonly StyledProperty SeparatorProperty = AvaloniaProperty.Register( - nameof(Separator)); - - public string Separator - { - get => GetValue(SeparatorProperty); - set => SetValue(SeparatorProperty, value); - } - - public static readonly StyledProperty LostFocusBehaviorProperty = AvaloniaProperty.Register( - nameof(LostFocusBehavior)); - - public LostFocusBehavior LostFocusBehavior - { - get => GetValue(LostFocusBehaviorProperty); - set => SetValue(LostFocusBehaviorProperty, value); - } - - - public static readonly StyledProperty AllowDuplicatesProperty = AvaloniaProperty.Register( - nameof(AllowDuplicates), defaultValue: true); - - public bool AllowDuplicates - { - get => GetValue(AllowDuplicatesProperty); - set => SetValue(AllowDuplicatesProperty, value); - } - - public static readonly StyledProperty InnerLeftContentProperty = - AvaloniaProperty.Register( - nameof(InnerLeftContent)); - - public object? InnerLeftContent - { - get => GetValue(InnerLeftContentProperty); - set => SetValue(InnerLeftContentProperty, value); - } - - public static readonly StyledProperty InnerRightContentProperty = - AvaloniaProperty.Register( - nameof(InnerRightContent)); - - public object? InnerRightContent - { - get => GetValue(InnerRightContentProperty); - set => SetValue(InnerRightContentProperty, value); - } - - - static TagInput() - { - InputThemeProperty.Changed.AddClassHandler((o, e) => o.OnInputThemePropertyChanged(e)); - TagsProperty.Changed.AddClassHandler((o, e) => o.OnTagsPropertyChanged(e)); - } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); @@ -178,46 +177,37 @@ public class TagInput : TemplatedControl _watermark = e.NameScope.Find(PART_Watermark); } - private TextPresenter? _presenter; protected override void OnLoaded(RoutedEventArgs e) { base.OnLoaded(e); if (_watermark is null) return; _presenter = _textBox.GetTemplateChildren().OfType().FirstOrDefault(); - _presenter?.GetObservable(TextPresenter.PreeditTextProperty).Subscribe(a => CheckEmpty()); - _textBox.GetObservable(TextBox.TextProperty).Subscribe(a => CheckEmpty()); + _presenter?.GetObservable(TextPresenter.PreeditTextProperty).Subscribe(_ => CheckEmpty()); + _textBox.GetObservable(TextBox.TextProperty).Subscribe(_ => CheckEmpty()); if (Tags is INotifyCollectionChanged incc) - { - incc.GetWeakCollectionChangedObservable().Subscribe(a => this.CheckEmpty()); - } + incc.GetWeakCollectionChangedObservable().Subscribe(_ => CheckEmpty()); } private void OnInputThemePropertyChanged(AvaloniaPropertyChangedEventArgs args) { - var newTheme = args.GetNewValue(); - if (newTheme?.TargetType == typeof(TextBox)) - { - _textBox.Theme = newTheme; - } + var newTheme = args.GetNewValue(); + if (newTheme?.TargetType == typeof(TextBox)) _textBox.Theme = newTheme; } - + private void CheckEmpty() { - if(string.IsNullOrWhiteSpace(_presenter?.PreeditText) && string.IsNullOrEmpty(_textBox?.Text) && (Tags.Count==0)) - { + if (string.IsNullOrWhiteSpace(_presenter?.PreeditText) && string.IsNullOrEmpty(_textBox.Text) && + Tags.Count == 0) PseudoClasses.Set(PseudoClassName.PC_Empty, true); - } else - { PseudoClasses.Set(PseudoClassName.PC_Empty, false); - } } private void OnTagsPropertyChanged(AvaloniaPropertyChangedEventArgs args) { - var newTags = args.GetNewValue>(); - var oldTags = args.GetOldValue>(); - + var newTags = args.GetNewValue?>(); + var oldTags = args.GetOldValue?>(); + if (Items is AvaloniaList avaloniaList) { avaloniaList.RemoveRange(0, avaloniaList.Count - 1); @@ -227,23 +217,13 @@ public class TagInput : TemplatedControl Items.Clear(); Items.Add(_textBox); } - - if (newTags != null) - { - for (int i = 0; i < newTags.Count; i++) - { - Items.Insert(Items.Count - 1, newTags[i]); - } - } - if (oldTags is INotifyCollectionChanged inccold) - { - inccold.CollectionChanged-= OnCollectionChanged; - } - if (Tags is INotifyCollectionChanged incc) - { - incc.CollectionChanged += OnCollectionChanged; - } + if (newTags != null) + for (var i = 0; i < newTags.Count; i++) + Items.Insert(Items.Count - 1, newTags[i]); + if (oldTags is INotifyCollectionChanged inccold) inccold.CollectionChanged -= OnCollectionChanged; + + if (Tags is INotifyCollectionChanged incc) incc.CollectionChanged += OnCollectionChanged; } private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -251,27 +231,21 @@ public class TagInput : TemplatedControl if (e.Action == NotifyCollectionChangedAction.Add) { var items = e.NewItems; - int index = e.NewStartingIndex; + var index = e.NewStartingIndex; foreach (var item in items) - { if (item is string s) { Items.Insert(index, s); index++; } - } } else if (e.Action == NotifyCollectionChangedAction.Remove) { var items = e.OldItems; - int index = e.OldStartingIndex; + var index = e.OldStartingIndex; foreach (var item in items) - { - if (item is string s) - { + if (item is string) Items.RemoveAt(index); - } - } } else if (e.Action == NotifyCollectionChangedAction.Reset) { @@ -279,50 +253,38 @@ public class TagInput : TemplatedControl Items.Add(_textBox); InvalidateVisual(); } - } private void OnTextBoxKeyDown(object? sender, KeyEventArgs args) { if (args.Key == Key.Enter) - { AddTags(); - } else if (args.Key == Key.Delete || args.Key == Key.Back) - { - if (string.IsNullOrEmpty(_textBox.Text)||_textBox.Text?.Length == 0) + if (string.IsNullOrEmpty(_textBox.Text) || _textBox.Text?.Length == 0) { - if (Tags.Count == 0) - { - return; - } - int index = Items.Count - 2; + if (Tags.Count == 0) return; + var index = Items.Count - 2; // Items.RemoveAt(index); Tags.RemoveAt(index); } - } } - + private void AddTags() { if (!(_textBox.Text?.Length > 0)) return; string[] values; if (!string.IsNullOrEmpty(Separator)) - { - values = _textBox.Text.Split(new string[] { Separator }, + values = _textBox.Text.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries); - } else - { values = new[] { _textBox.Text }; - } - if (!AllowDuplicates && Tags != null) + if (!AllowDuplicates) values = values.Distinct().Except(Tags).ToArray(); - for (int i = 0; i < values.Length; i++) + for (var i = 0; i < values.Length; i++) { - int index = Items.Count - 1; + var index = Items.Count - 1; // Items.Insert(index, values[i]); Tags?.Insert(index, values[i]); } @@ -333,16 +295,12 @@ public class TagInput : TemplatedControl public void Close(object o) { if (o is Control t) - { if (t.Parent is ContentPresenter presenter) { - int? index = _itemsControl?.IndexFromContainer(presenter); + var index = _itemsControl?.IndexFromContainer(presenter); if (index is >= 0 && index < Items.Count - 1) - { // Items.RemoveAt(index.Value); Tags.RemoveAt(index.Value); - } } - } } } \ No newline at end of file