diff --git a/src/Artemis.UI.Shared/Controls/Flyouts/MaterialIconPickerFlyout.cs b/src/Artemis.UI.Shared/Controls/Flyouts/MaterialIconPickerFlyout.cs index cf220336d..99321b5e0 100644 --- a/src/Artemis.UI.Shared/Controls/Flyouts/MaterialIconPickerFlyout.cs +++ b/src/Artemis.UI.Shared/Controls/Flyouts/MaterialIconPickerFlyout.cs @@ -19,7 +19,20 @@ public sealed class MaterialIconPickerFlyout : Flyout protected override Control CreatePresenter() { _picker ??= new MaterialIconPicker.MaterialIconPicker(); - PickerFlyoutPresenter presenter = new() {Content = MaterialIconPicker}; + _picker.Flyout = this; + FlyoutPresenter presenter = new() {Content = MaterialIconPicker}; return presenter; } + + #region Overrides of FlyoutBase + + /// + protected override void OnClosed() + { + if (_picker != null) + _picker.Flyout = null; + base.OnClosed(); + } + + #endregion } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPicker.cs b/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPicker.cs index e1e65c9fa..5777b21de 100644 --- a/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPicker.cs +++ b/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPicker.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reactive.Linq; using System.Text.RegularExpressions; using System.Windows.Input; +using Artemis.UI.Shared.Flyouts; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; @@ -28,16 +29,35 @@ public partial class MaterialIconPicker : TemplatedControl public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay); + /// + /// Gets the command to execute when deleting stops. + /// + public static readonly DirectProperty SelectIconProperty = + AvaloniaProperty.RegisterDirect(nameof(SelectIcon), g => g.SelectIcon); + + private readonly ICommand _selectIcon; + private ItemsRepeater? _iconsContainer; + private SourceList? _iconsSource; private TextBox? _searchBox; private IDisposable? _sub; - private ItemsRepeater? _iconsContainer; - private readonly ICommand _selectIcon; + private readonly Dictionary _enumNames; /// public MaterialIconPicker() { - _selectIcon = ReactiveCommand.Create(i => Value = i); + _selectIcon = ReactiveCommand.Create(i => + { + Value = i; + Flyout?.Hide(); + }); + + // Build a list of enum names and values, this is required because a value may have more than one name + _enumNames = new Dictionary(); + MaterialIconKind[] values = Enum.GetValues(); + string[] names = Enum.GetNames(); + for (int index = 0; index < names.Length; index++) + _enumNames[names[index]] = values[index]; } /// @@ -49,12 +69,6 @@ public partial class MaterialIconPicker : TemplatedControl set => SetValue(ValueProperty, value); } - /// - /// Gets the command to execute when deleting stops. - /// - public static readonly DirectProperty SelectIconProperty = - AvaloniaProperty.RegisterDirect(nameof(SelectIcon), g => g.SelectIcon); - /// /// Gets the command to execute when deleting stops. /// @@ -64,16 +78,14 @@ public partial class MaterialIconPicker : TemplatedControl private init => SetAndRaise(SelectIconProperty, ref _selectIcon, value); } - #region Overrides of TemplatedControl + internal MaterialIconPickerFlyout? Flyout { get; set; } - /// - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + private void Setup() { - _searchBox = e.NameScope.Find("SearchBox"); - _iconsContainer = e.NameScope.Find("IconsContainer"); - if (_iconsContainer == null) + if (_searchBox == null || _iconsContainer == null) return; + // Build a list of values, they are not unique because a value with multiple names occurs once per name _iconsSource = new SourceList(); _iconsSource.AddRange(Enum.GetValues().Distinct()); _sub = _iconsSource.Connect() @@ -84,6 +96,25 @@ public partial class MaterialIconPicker : TemplatedControl _iconsContainer.ItemsSource = icons; } + #region Overrides of TemplatedControl + + /// + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + _searchBox = e.NameScope.Find("SearchBox"); + _iconsContainer = e.NameScope.Find("IconsContainer"); + + Setup(); + base.OnApplyTemplate(e); + } + + /// + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + Setup(); + base.OnAttachedToLogicalTree(e); + } + /// protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { @@ -91,6 +122,9 @@ public partial class MaterialIconPicker : TemplatedControl _iconsSource = null; _sub?.Dispose(); _sub = null; + + if (_searchBox != null) + _searchBox.Text = ""; base.OnDetachedFromLogicalTree(e); } @@ -99,8 +133,11 @@ public partial class MaterialIconPicker : TemplatedControl if (string.IsNullOrWhiteSpace(text)) return _ => true; + // Strip out whitespace and find all matching enum values text = StripWhiteSpaceRegex().Replace(text, ""); - return data => data.ToString().Contains(text, StringComparison.InvariantCultureIgnoreCase); + HashSet values = _enumNames.Where(n => n.Key.Contains(text, StringComparison.OrdinalIgnoreCase)).Select(n => n.Value).ToHashSet(); + // Only show those that matched + return data => values.Contains(data); } [GeneratedRegex("\\s+")] diff --git a/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPickerButton.cs b/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPickerButton.cs index c05b7d74d..53c751ac3 100644 --- a/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPickerButton.cs +++ b/src/Artemis.UI.Shared/Controls/MaterialIconPicker/MaterialIconPickerButton.cs @@ -31,7 +31,7 @@ public class MaterialIconPickerButton : TemplatedControl /// Gets or sets the desired flyout placement. /// public static readonly StyledProperty PlacementProperty = - AvaloniaProperty.Register(nameof(Placement)); + AvaloniaProperty.Register(nameof(Placement), PlacementMode.BottomEdgeAlignedLeft); private Button? _button; private MaterialIconPickerFlyout? _flyout; @@ -79,14 +79,21 @@ public class MaterialIconPickerButton : TemplatedControl if (_flyout == null) return; - // Logic here is taken from Fluent Avalonia's ColorPicker which also reuses the same control since it's large - _flyout.MaterialIconPicker.Value = Value; - - _flyout.Placement = Placement; - _flyout.ShowAt(_button != null ? _button : this); - _flyoutActive = true; - + MaterialIconPickerFlyout flyout = new(); + flyout.FlyoutPresenterClasses.Add("material-icon-picker-presenter"); + flyout.MaterialIconPicker.Value = Value; + flyout.Placement = Placement; + + flyout.Closed += FlyoutOnClosed; + flyout.ShowAt(this); FlyoutOpened?.Invoke(this, EventArgs.Empty); + + void FlyoutOnClosed(object? closedSender, EventArgs closedEventArgs) + { + Value = flyout.MaterialIconPicker.Value; + flyout.Closed -= FlyoutOnClosed; + FlyoutClosed?.Invoke(this, EventArgs.Empty); + } } #region Overrides of TemplatedControl diff --git a/src/Artemis.UI.Shared/Styles/Controls/GradientPickerButton.axaml b/src/Artemis.UI.Shared/Styles/Controls/GradientPickerButton.axaml index 13f5ae0a5..75aafc057 100644 --- a/src/Artemis.UI.Shared/Styles/Controls/GradientPickerButton.axaml +++ b/src/Artemis.UI.Shared/Styles/Controls/GradientPickerButton.axaml @@ -28,7 +28,6 @@ -