From 542934639616f357ebe7570adc4363078f7fcdd6 Mon Sep 17 00:00:00 2001 From: Diogo Trindade Date: Sun, 30 Apr 2023 17:32:51 +0100 Subject: [PATCH] UI - Replaced tuples with custom classes for binding As far as i know, it's not possible to have CompiledBindings with tuples, since tuple values are fields and not properties. This change allows for compiled bindings, and makes the intent behind the tuples clearer --- .../Controls/EnumComboBox.axaml | 5 +- .../Controls/EnumComboBox.axaml.cs | 58 +++++++++++++------ .../Device/Tabs/InputMappingsTabView.axaml | 4 +- .../Device/Tabs/InputMappingsTabViewModel.cs | 41 ++++++++++--- .../Properties/Tree/TreePropertyView.axaml | 2 +- .../Pins/InputPinView.axaml.cs | 2 +- .../Pins/OutputPinView.axaml.cs | 2 +- .../Screens/EnumEqualsNodeCustomView.axaml | 10 ++-- .../Screens/EnumEqualsNodeCustomViewModel.cs | 41 ++++++++++--- 9 files changed, 120 insertions(+), 45 deletions(-) diff --git a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml index 1849c97e5..19c39fd7d 100644 --- a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml +++ b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml @@ -3,11 +3,12 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + xmlns:local="clr-namespace:Artemis.UI.Shared" x:Class="Artemis.UI.Shared.EnumComboBox"> - - + + diff --git a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs index bd702941f..8fcd1eba4 100644 --- a/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs +++ b/src/Artemis.UI.Shared/Controls/EnumComboBox.axaml.cs @@ -20,11 +20,9 @@ public partial class EnumComboBox : UserControl /// public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay); - private readonly ObservableCollection<(Enum, string)> _currentValues = new(); + private readonly ObservableCollection _currentValues = new(); private Type? _currentType; - private ComboBox? _enumComboBox; - /// /// Creates a new instance of the class. /// @@ -54,35 +52,35 @@ public partial class EnumComboBox : UserControl private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e) { - if (_enumComboBox == null || _enumComboBox.SelectedIndex == -1) + if (ChildEnumComboBox == null || ChildEnumComboBox.SelectedIndex == -1) return; - (Enum enumValue, _) = _currentValues[_enumComboBox.SelectedIndex]; - if (!Equals(Value, enumValue)) - Value = enumValue; + EnumComboBoxItem v = _currentValues[ChildEnumComboBox.SelectedIndex]; + if (!Equals(Value, v.Value)) + Value = v.Value; } private void UpdateValues() { Type? newType = Value?.GetType(); - if (_enumComboBox == null || newType == null || _currentType == newType) + if (ChildEnumComboBox == null || newType == null || _currentType == newType) return; _currentValues.Clear(); foreach ((Enum, string) valueDesc in EnumUtilities.GetAllValuesAndDescriptions(newType)) - _currentValues.Add(valueDesc); + _currentValues.Add(new EnumComboBoxItem(value: valueDesc.Item1, description: valueDesc.Item2)); _currentType = newType; } private void UpdateSelection() { - if (_enumComboBox == null || Value is not Enum) + if (ChildEnumComboBox == null || Value is not Enum) return; - (Enum, string) value = _currentValues.FirstOrDefault(v => v.Item1.Equals(Value)); - if (!Equals(value.Item1, _enumComboBox.SelectedItem)) - _enumComboBox.SelectedItem = value; + EnumComboBoxItem? value = _currentValues.FirstOrDefault(v => v.Value.Equals(Value)); + if (!Equals(value?.Value, ChildEnumComboBox.SelectedItem)) + ChildEnumComboBox.SelectedItem = value; } #region Overrides of TemplatedControl @@ -90,12 +88,11 @@ public partial class EnumComboBox : UserControl /// protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { - _enumComboBox = this.Get("ChildEnumComboBox"); - _enumComboBox.ItemsSource = _currentValues; + ChildEnumComboBox.ItemsSource = _currentValues; UpdateValues(); UpdateSelection(); - _enumComboBox.SelectionChanged += OnSelectionChanged; + ChildEnumComboBox.SelectionChanged += OnSelectionChanged; base.OnAttachedToLogicalTree(e); } @@ -103,11 +100,36 @@ public partial class EnumComboBox : UserControl /// protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { - if (_enumComboBox != null) - _enumComboBox.SelectionChanged -= OnSelectionChanged; + if (ChildEnumComboBox != null) + ChildEnumComboBox.SelectionChanged -= OnSelectionChanged; base.OnDetachedFromLogicalTree(e); } #endregion +} + +/// +/// Represents an item in the +/// +public class EnumComboBoxItem +{ + /// + /// Creates a new instance of the class. + /// + public EnumComboBoxItem(Enum value, string description) + { + Value = value; + Description = description; + } + + /// + /// Gets or sets the value of the item + /// + public Enum Value { get; set; } + + /// + /// Gets or sets the description of the item + /// + public string Description { get; set; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml b/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml index ccd896545..e7c3bb42d 100644 --- a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml +++ b/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabView.axaml @@ -48,8 +48,8 @@ AutoGenerateColumns="False" Margin="10"> - - + + diff --git a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs index 5ab20e8fe..4d3acd760 100644 --- a/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs +++ b/src/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs @@ -1,4 +1,4 @@ -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using System.Reactive.Disposables; @@ -31,8 +31,8 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase Device = device; DisplayName = "Input Mappings"; - InputMappings = new ObservableCollection<(ArtemisLed, ArtemisLed)>(); - DeleteMapping = ReactiveCommand.Create<(ArtemisLed, ArtemisLed)>(ExecuteDeleteMapping); + InputMappings = new ObservableCollection(); + DeleteMapping = ReactiveCommand.Create(ExecuteDeleteMapping); this.WhenActivated(d => { @@ -48,7 +48,7 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase }); } - public ReactiveCommand<(ArtemisLed, ArtemisLed), Unit> DeleteMapping { get; } + public ReactiveCommand DeleteMapping { get; } public ArtemisDevice Device { get; } @@ -58,11 +58,11 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase set => RaiseAndSetIfChanged(ref _selectedLed, value); } - public ObservableCollection<(ArtemisLed, ArtemisLed)> InputMappings { get; } + public ObservableCollection InputMappings { get; } - private void ExecuteDeleteMapping((ArtemisLed, ArtemisLed) inputMapping) + private void ExecuteDeleteMapping(ArtemisInputMapping inputMapping) { - Device.InputMappings.Remove(inputMapping.Item1); + Device.InputMappings.Remove(inputMapping.Original); UpdateInputMappings(); } @@ -92,7 +92,7 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase if (InputMappings.Any()) InputMappings.Clear(); - foreach ((ArtemisLed, ArtemisLed) tuple in Device.InputMappings.Select(m => (m.Key, m.Value))) + foreach (ArtemisInputMapping tuple in Device.InputMappings.Select(m => new ArtemisInputMapping(m.Key, m.Value))) InputMappings.Add(tuple); } @@ -100,4 +100,29 @@ public class InputMappingsTabViewModel : ActivatableViewModelBase { SelectedLed = _selectedLeds.FirstOrDefault(); } +} + +/// +/// Represents a pair of LEDs, the original and the replacement +/// +public class ArtemisInputMapping +{ + /// + /// Creates a new instance of the class + /// + public ArtemisInputMapping(ArtemisLed original, ArtemisLed replacement) + { + Original = original; + Replacement = replacement; + } + + /// + /// The original LED + /// + public ArtemisLed Original { get; set; } + + /// + /// The replacement LED + /// + public ArtemisLed Replacement { get; set; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml index 64b32bf15..2d87a8108 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyView.axaml @@ -15,7 +15,7 @@ BorderBrush="{DynamicResource ButtonBorderBrush}" BorderThickness="0,0,0,1" Height="29"> - + ("PinPoint")); + InitializePin(PinPoint); } diff --git a/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml.cs b/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml.cs index 9ba43f5c8..ed186c039 100644 --- a/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml.cs +++ b/src/Artemis.UI/Screens/VisualScripting/Pins/OutputPinView.axaml.cs @@ -10,7 +10,7 @@ public partial class OutputPinView : PinView public OutputPinView() { InitializeComponent(); - InitializePin(this.Get("PinPoint")); + InitializePin(PinPoint); } diff --git a/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml b/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml index ef9b82c8e..3d6357933 100644 --- a/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml +++ b/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomView.axaml @@ -3,10 +3,12 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + xmlns:vm="clr-namespace:Artemis.VisualScripting.Nodes.Operators.Screens" + x:DataType="vm:EnumEqualsNodeCustomViewModel" x:Class="Artemis.VisualScripting.Nodes.Operators.Screens.EnumEqualsNodeCustomView"> - @@ -17,7 +19,7 @@ - + diff --git a/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomViewModel.cs b/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomViewModel.cs index 7fee56fa6..f4a99a08d 100644 --- a/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomViewModel.cs +++ b/src/Artemis.VisualScripting/Nodes/Operators/Screens/EnumEqualsNodeCustomViewModel.cs @@ -42,15 +42,15 @@ public class EnumEqualsNodeCustomViewModel : CustomNodeViewModel }); } - public ObservableCollection<(long, string)> EnumValues { get; } = new(); + public ObservableCollection EnumValues { get; } = new(); - public (long, string) CurrentValue + public EnumValueItem CurrentValue { - get => EnumValues.FirstOrDefault(v => v.Item1 == _node.Storage); + get => EnumValues.FirstOrDefault(v => v.Value == _node.Storage); set { - if (!Equals(_node.Storage, value.Item1)) - _nodeEditorService.ExecuteCommand(Script, new UpdateStorage(_node, value.Item1)); + if (!Equals(_node.Storage, value.Value)) + _nodeEditorService.ExecuteCommand(Script, new UpdateStorage(_node, value.Value)); } } @@ -58,13 +58,38 @@ public class EnumEqualsNodeCustomViewModel : CustomNodeViewModel { Dispatcher.UIThread.Post(() => { - List<(long, string)> values = Enum.GetValues(type).Cast().Select(e => (Convert.ToInt64(e), e.Humanize())).ToList(); + List values = Enum.GetValues(type).Cast().Select(e => new EnumValueItem(value: Convert.ToInt64(e), name: e.Humanize())).ToList(); if (values.Count > 20) - EnumValues.AddRange(values.OrderBy(v => v.Item2)); + EnumValues.AddRange(values.OrderBy(v => v.Name)); else - EnumValues.AddRange(Enum.GetValues(type).Cast().Select(e => (Convert.ToInt64(e), e.Humanize()))); + EnumValues.AddRange(values); this.RaisePropertyChanged(nameof(CurrentValue)); }, DispatcherPriority.Background); } +} + +/// +/// Represents a single enum value +/// +public class EnumValueItem +{ + /// + /// Creates a new instance of the class. + /// + public EnumValueItem(long value, string name) + { + Value = value; + Name = name; + } + + /// + /// The underlying value of the enum + /// + public long Value { get; set; } + + /// + /// The name of the enum value + /// + public string Name { get; set; } } \ No newline at end of file