From e5ba48c7f4a478d33d67411afde5c96daeb22e83 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 19 Apr 2022 23:11:44 +0200 Subject: [PATCH] Surface editor - Refactor selection/movement code to match nodes Device visualizer - Fixed exception when updating devices during render --- src/Artemis.Core/Constants.cs | 2 +- .../Controls/DeviceVisualizer.cs | 27 +- .../Ninject/Factories/IVMFactory.cs | 167 ++++---- .../SurfaceEditor/ListDeviceView.axaml | 4 +- .../SurfaceEditor/ListDeviceView.axaml.cs | 25 +- .../SurfaceEditor/ListDeviceViewModel.cs | 44 +-- .../SurfaceEditor/SurfaceDeviceView.axaml | 26 +- .../SurfaceEditor/SurfaceDeviceView.axaml.cs | 73 ++-- .../SurfaceEditor/SurfaceDeviceViewModel.cs | 214 +++++------ .../SurfaceEditor/SurfaceEditorView.axaml | 16 +- .../SurfaceEditor/SurfaceEditorView.axaml.cs | 138 +++---- .../SurfaceEditor/SurfaceEditorViewModel.cs | 360 ++++++++---------- .../Screens/VisualScripting/NodeView.axaml.cs | 3 - 13 files changed, 527 insertions(+), 572 deletions(-) diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs index 2422ef063..b63d493c2 100644 --- a/src/Artemis.Core/Constants.cs +++ b/src/Artemis.Core/Constants.cs @@ -32,7 +32,7 @@ namespace Artemis.Core /// /// The full path to the Artemis data folder /// - public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis.Avalonia"); + public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis"); /// /// The full path to the Artemis logs folder diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs index a90ca2550..206cfd5f2 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs @@ -81,8 +81,11 @@ namespace Artemis.UI.Shared if (!ShowColors) return; - foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) - deviceVisualizerLed.RenderGeometry(drawingContext, false); + lock (_deviceVisualizerLeds) + { + foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) + deviceVisualizerLed.RenderGeometry(drawingContext, false); + } } finally { @@ -244,10 +247,14 @@ namespace Artemis.UI.Shared { _deviceImage?.Dispose(); _deviceImage = null; - _deviceVisualizerLeds.Clear(); _highlightedLeds = new List(); _dimmedLeds = new List(); + lock (_deviceVisualizerLeds) + { + _deviceVisualizerLeds.Clear(); + } + if (_oldDevice != null) { _oldDevice.RgbDevice.PropertyChanged -= DevicePropertyChanged; @@ -264,8 +271,11 @@ namespace Artemis.UI.Shared Device.DeviceUpdated += DeviceUpdated; // Create all the LEDs - foreach (ArtemisLed artemisLed in Device.Leds) - _deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed)); + lock (_deviceVisualizerLeds) + { + foreach (ArtemisLed artemisLed in Device.Leds) + _deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed)); + } // Load the device main image on a background thread ArtemisDevice? device = Device; @@ -282,8 +292,11 @@ namespace Artemis.UI.Shared using IDrawingContextImpl context = renderTargetBitmap.CreateDrawingContext(new ImmediateRenderer(this)); using Bitmap bitmap = new(device.Layout.Image.LocalPath); context.DrawBitmap(bitmap.PlatformImpl, 1, new Rect(bitmap.Size), new Rect(renderTargetBitmap.Size), BitmapInterpolationMode.HighQuality); - foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) - deviceVisualizerLed.DrawBitmap(context); + lock (_deviceVisualizerLeds) + { + foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) + deviceVisualizerLed.DrawBitmap(context); + } _deviceImage = renderTargetBitmap; diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index 9759807b8..d96befb87 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -10,7 +10,6 @@ using Artemis.UI.Screens.ProfileEditor.ProfileTree; using Artemis.UI.Screens.ProfileEditor.Properties; using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding; using Artemis.UI.Screens.ProfileEditor.Properties.Timeline; -using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments; using Artemis.UI.Screens.ProfileEditor.Properties.Tree; using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers; using Artemis.UI.Screens.Settings; @@ -18,103 +17,101 @@ using Artemis.UI.Screens.Sidebar; using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.VisualScripting; using Artemis.UI.Screens.VisualScripting.Pins; -using Artemis.UI.Services; -using DynamicData.Binding; using ReactiveUI; -namespace Artemis.UI.Ninject.Factories +namespace Artemis.UI.Ninject.Factories; + +public interface IVmFactory { - public interface IVmFactory - { - } +} - public interface IDeviceVmFactory : IVmFactory - { - DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device); - DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel); - DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device); - DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device); - DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device); - DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds); - InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds); - } +public interface IDeviceVmFactory : IVmFactory +{ + DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device); + DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel); + DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device); + DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device); + DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device); + DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds); + InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection selectedLeds); +} - public interface ISettingsVmFactory : IVmFactory - { - PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin); - PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield); - // DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device); - } +public interface ISettingsVmFactory : IVmFactory +{ + PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin); - public interface ISidebarVmFactory : IVmFactory - { - SidebarViewModel? SidebarViewModel(IScreen hostScreen); - SidebarCategoryViewModel SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory); - SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration); - } + PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield); + // DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device); +} - public interface ISurfaceVmFactory : IVmFactory - { - SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device); - ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device); - } +public interface ISidebarVmFactory : IVmFactory +{ + SidebarViewModel? SidebarViewModel(IScreen hostScreen); + SidebarCategoryViewModel SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory); + SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration); +} - public interface IPrerequisitesVmFactory : IVmFactory - { - PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall); - } +public interface ISurfaceVmFactory : IVmFactory +{ + SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel); + ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel); +} - public interface IProfileEditorVmFactory : IVmFactory - { - ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen); - FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder); - LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer); - LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer); - LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer); - } +public interface IPrerequisitesVmFactory : IVmFactory +{ + PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall); +} - public interface ILayerPropertyVmFactory : IVmFactory - { - PropertyViewModel PropertyViewModel(ILayerProperty layerProperty); - PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup); - PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush); - PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect); +public interface IProfileEditorVmFactory : IVmFactory +{ + ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen); + FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder); + LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer); + LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer); + LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer); +} - TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel); - - TimelineViewModel TimelineViewModel(ObservableCollection propertyGroupViewModels); - TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel); - } +public interface ILayerPropertyVmFactory : IVmFactory +{ + PropertyViewModel PropertyViewModel(ILayerProperty layerProperty); + PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup); + PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush); + PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect); - public interface IDataBindingVmFactory : IVmFactory - { - DataBindingViewModel DataBindingViewModel(); - } + TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel); - public interface IPropertyVmFactory - { - ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel); - ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel); - } + TimelineViewModel TimelineViewModel(ObservableCollection propertyGroupViewModels); + TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel); +} - public interface INodeVmFactory : IVmFactory - { - NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview); - NodePickerViewModel NodePickerViewModel(NodeScript nodeScript); - NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node); - CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to); - DragCableViewModel DragCableViewModel(PinViewModel pinViewModel); - InputPinViewModel InputPinViewModel(IPin inputPin); - OutputPinViewModel OutputPinViewModel(IPin outputPin); - InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel); - OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel); - } +public interface IDataBindingVmFactory : IVmFactory +{ + DataBindingViewModel DataBindingViewModel(); +} - public interface IConditionVmFactory : IVmFactory - { - AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition); - PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition); - StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition); - EventConditionViewModel EventConditionViewModel(EventCondition eventCondition); - } +public interface IPropertyVmFactory +{ + ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel); + ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel); +} + +public interface INodeVmFactory : IVmFactory +{ + NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview); + NodePickerViewModel NodePickerViewModel(NodeScript nodeScript); + NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node); + CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to); + DragCableViewModel DragCableViewModel(PinViewModel pinViewModel); + InputPinViewModel InputPinViewModel(IPin inputPin); + OutputPinViewModel OutputPinViewModel(IPin outputPin); + InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel); + OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel); +} + +public interface IConditionVmFactory : IVmFactory +{ + AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition); + PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition); + StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition); + EventConditionViewModel EventConditionViewModel(EventCondition eventCondition); } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml b/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml index 6b84a9517..fda598852 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml @@ -4,5 +4,5 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.SurfaceEditor.ListDeviceView"> - Welcome to Avalonia! - + Welcome to Avalonia! + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml.cs b/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml.cs index 168b337a0..8d06e454c 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceView.axaml.cs @@ -1,18 +1,17 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Artemis.UI.Screens.SurfaceEditor -{ - public partial class ListDeviceView : UserControl - { - public ListDeviceView() - { - InitializeComponent(); - } +namespace Artemis.UI.Screens.SurfaceEditor; - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } +public class ListDeviceView : UserControl +{ + public ListDeviceView() + { + InitializeComponent(); } -} + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs index c9f5db87a..ee9ded844 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs @@ -1,32 +1,32 @@ using Artemis.Core; using Artemis.UI.Shared; -using ReactiveUI; using SkiaSharp; -namespace Artemis.UI.Screens.SurfaceEditor +namespace Artemis.UI.Screens.SurfaceEditor; + +public class ListDeviceViewModel : ViewModelBase { - public class ListDeviceViewModel : ViewModelBase + private SKColor _color; + private bool _isSelected; + + public ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel) { - private SKColor _color; - private bool _isSelected; - - public ListDeviceViewModel(ArtemisDevice device) - { - Device = device; - } + Device = device; + SurfaceEditorViewModel = surfaceEditorViewModel; + } - public ArtemisDevice Device { get; } - - public bool IsSelected - { - get => _isSelected; - set => RaiseAndSetIfChanged(ref _isSelected, value); - } + public ArtemisDevice Device { get; } + public SurfaceEditorViewModel SurfaceEditorViewModel { get; } - public SKColor Color - { - get => _color; - set => RaiseAndSetIfChanged(ref _color, value); - } + public bool IsSelected + { + get => _isSelected; + set => RaiseAndSetIfChanged(ref _isSelected, value); + } + + public SKColor Color + { + get => _color; + set => RaiseAndSetIfChanged(ref _color, value); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml index 048c4c887..7c4cd14b1 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml @@ -3,9 +3,13 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" + xmlns:surfaceEditor="clr-namespace:Artemis.UI.Screens.SurfaceEditor" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceDeviceView"> - + x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceDeviceView" + x:DataType="surfaceEditor:SurfaceDeviceViewModel"> + - + - + - - + + - - + + diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml.cs b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml.cs index 27674f033..1de325892 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceView.axaml.cs @@ -1,43 +1,68 @@ +using System.Collections.Generic; +using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; +using Avalonia.VisualTree; -namespace Artemis.UI.Screens.SurfaceEditor +namespace Artemis.UI.Screens.SurfaceEditor; + +public class SurfaceDeviceView : ReactiveUserControl { - public class SurfaceDeviceView : ReactiveUserControl + private bool _dragging; + + public SurfaceDeviceView() { - public SurfaceDeviceView() - { - InitializeComponent(); - } + InitializeComponent(); + } - /// - protected override void OnPointerEnter(PointerEventArgs e) + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void InputElement_OnPointerMoved(object? sender, PointerEventArgs e) + { + PointerPoint point = e.GetCurrentPoint(this.FindAncestorOfType()); + if (ViewModel == null || !point.Properties.IsLeftButtonPressed) + return; + + if (!_dragging) { - if (ViewModel?.SelectionStatus == SelectionStatus.None) + _dragging = true; + + if (!ViewModel.IsSelected) { - ViewModel.SelectionStatus = SelectionStatus.Hover; - Cursor = new Cursor(StandardCursorType.Hand); + ViewModel.SurfaceEditorViewModel.UpdateSelection(new List {ViewModel}, false, false); + ViewModel.SurfaceEditorViewModel.FinishSelection(); } - base.OnPointerEnter(e); + ViewModel.SurfaceEditorViewModel.StartMouseDrag(point.Position); + e.Pointer.Capture((IInputElement?) sender); } - /// - protected override void OnPointerLeave(PointerEventArgs e) + ViewModel.SurfaceEditorViewModel.UpdateMouseDrag(point.Position, e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Alt)); + + e.Handled = true; + } + + private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + if (ViewModel == null || e.InitialPressMouseButton != MouseButton.Left) + return; + + if (_dragging) { - if (ViewModel?.SelectionStatus == SelectionStatus.Hover) - { - ViewModel.SelectionStatus = SelectionStatus.None; - Cursor = new Cursor(StandardCursorType.Arrow); - } - - base.OnPointerLeave(e); + _dragging = false; + ViewModel.SurfaceEditorViewModel.FinishSelection(); + e.Pointer.Capture(null); } - - private void InitializeComponent() + else { - AvaloniaXamlLoader.Load(this); + ViewModel.SurfaceEditorViewModel.UpdateSelection(new List {ViewModel}, e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Control)); + ViewModel.SurfaceEditorViewModel.FinishSelection(); } + + e.Handled = true; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs index bd122fb35..f1cc051c5 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs @@ -8,143 +8,123 @@ using Artemis.Core.Services; using Artemis.UI.Ninject.Factories; using Artemis.UI.Shared; using Artemis.UI.Shared.Services; -using Avalonia.Input; using ReactiveUI; using RGB.NET.Core; using SkiaSharp; using Point = Avalonia.Point; -namespace Artemis.UI.Screens.SurfaceEditor +namespace Artemis.UI.Screens.SurfaceEditor; + +public class SurfaceDeviceViewModel : ActivatableViewModelBase { - public class SurfaceDeviceViewModel : ActivatableViewModelBase + private readonly IDeviceService _deviceService; + private readonly IDeviceVmFactory _deviceVmFactory; + private readonly IRgbService _rgbService; + private readonly ISettingsService _settingsService; + private readonly IWindowService _windowService; + private double _dragOffsetX; + private double _dragOffsetY; + private bool _isSelected; + + public SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel, IRgbService rgbService, IDeviceService deviceService, ISettingsService settingsService, + IDeviceVmFactory deviceVmFactory, + IWindowService windowService) { - private readonly IRgbService _rgbService; - private readonly IDeviceService _deviceService; - private readonly ISettingsService _settingsService; - private readonly IDeviceVmFactory _deviceVmFactory; - private readonly IWindowService _windowService; - private Cursor _cursor; - private double _dragOffsetX; - private double _dragOffsetY; - private SelectionStatus _selectionStatus; + _rgbService = rgbService; + _deviceService = deviceService; + _settingsService = settingsService; + _deviceVmFactory = deviceVmFactory; + _windowService = windowService; - public SurfaceDeviceViewModel(ArtemisDevice device, IRgbService rgbService, IDeviceService deviceService, ISettingsService settingsService, IDeviceVmFactory deviceVmFactory, - IWindowService windowService) + Device = device; + SurfaceEditorViewModel = surfaceEditorViewModel; + + IdentifyDevice = ReactiveCommand.Create(ExecuteIdentifyDevice); + ViewProperties = ReactiveCommand.CreateFromTask(ExecuteViewProperties); + } + + public ReactiveCommand IdentifyDevice { get; } + public ReactiveCommand ViewProperties { get; } + + public ArtemisDevice Device { get; } + public SurfaceEditorViewModel SurfaceEditorViewModel { get; } + + public bool IsSelected + { + get => _isSelected; + set => RaiseAndSetIfChanged(ref _isSelected, value); + } + + public bool CanDetectInput => Device.DeviceType == RGBDeviceType.Keyboard || Device.DeviceType == RGBDeviceType.Mouse; + + public void StartMouseDrag(Point mouseStartPosition) + { + if (!IsSelected) + return; + + _dragOffsetX = Device.X - mouseStartPosition.X; + _dragOffsetY = Device.Y - mouseStartPosition.Y; + } + + public void UpdateMouseDrag(Point mousePosition, bool round, bool ignoreOverlap) + { + if (!IsSelected) + return; + + float x = (float) (mousePosition.X + _dragOffsetX); + float y = (float) (mousePosition.Y + _dragOffsetY); + + if (round) { - _rgbService = rgbService; - _deviceService = deviceService; - _settingsService = settingsService; - _deviceVmFactory = deviceVmFactory; - _windowService = windowService; - _cursor = Cursor.Default; - - Device = device; - - IdentifyDevice = ReactiveCommand.Create(ExecuteIdentifyDevice); - ViewProperties = ReactiveCommand.CreateFromTask(ExecuteViewProperties); + x = (float) Math.Round(x / 10d, 0, MidpointRounding.AwayFromZero) * 10f; + y = (float) Math.Round(y / 10d, 0, MidpointRounding.AwayFromZero) * 10f; } - public ReactiveCommand IdentifyDevice { get; } - public ReactiveCommand ViewProperties { get; } - public ArtemisDevice Device { get; } - - public SelectionStatus SelectionStatus + if (Fits(x, y, ignoreOverlap)) { - get => _selectionStatus; - set - { - RaiseAndSetIfChanged(ref _selectionStatus, value); - this.RaisePropertyChanged(nameof(Highlighted)); - } + Device.X = x; + Device.Y = y; } - - public bool Highlighted => SelectionStatus != SelectionStatus.None; - - public bool CanDetectInput => Device.DeviceType == RGBDeviceType.Keyboard || Device.DeviceType == RGBDeviceType.Mouse; - - public Cursor Cursor + else if (Fits(x, Device.Y, ignoreOverlap)) { - get => _cursor; - set => RaiseAndSetIfChanged(ref _cursor, value); + Device.X = x; } - - public void StartMouseDrag(Point mouseStartPosition) + else if (Fits(Device.X, y, ignoreOverlap)) { - if (SelectionStatus != SelectionStatus.Selected) - return; - - _dragOffsetX = Device.X - mouseStartPosition.X; - _dragOffsetY = Device.Y - mouseStartPosition.Y; - } - - public void UpdateMouseDrag(Point mousePosition, bool round, bool ignoreOverlap) - { - if (SelectionStatus != SelectionStatus.Selected) - return; - - float x = (float) (mousePosition.X + _dragOffsetX); - float y = (float) (mousePosition.Y + _dragOffsetY); - - if (round) - { - x = (float) Math.Round(x / 10d, 0, MidpointRounding.AwayFromZero) * 10f; - y = (float) Math.Round(y / 10d, 0, MidpointRounding.AwayFromZero) * 10f; - } - - - if (Fits(x, y, ignoreOverlap)) - { - Device.X = x; - Device.Y = y; - } - else if (Fits(x, Device.Y, ignoreOverlap)) - { - Device.X = x; - } - else if (Fits(Device.X, y, ignoreOverlap)) - { - Device.Y = y; - } - } - - private void ExecuteIdentifyDevice(ArtemisDevice device) - { - _deviceService.IdentifyDevice(device); - } - - private async Task ExecuteViewProperties(ArtemisDevice device) - { - await _windowService.ShowDialogAsync(_deviceVmFactory.DevicePropertiesViewModel(device)); - } - - private bool Fits(float x, float y, bool ignoreOverlap) - { - if (x < 0 || y < 0) - return false; - - double maxTextureSize = 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value; - if (x + Device.Rectangle.Width > maxTextureSize || y + Device.Rectangle.Height > maxTextureSize) - return false; - - if (ignoreOverlap) - return true; - - IEnumerable own = Device.Leds - .Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height)); - IEnumerable others = _rgbService.EnabledDevices - .Where(d => d != Device && d.IsEnabled) - .SelectMany(d => d.Leds) - .Select(l => SKRect.Create(l.Rectangle.Left + l.Device.X, l.Rectangle.Top + l.Device.Y, l.Rectangle.Width, l.Rectangle.Height)); - - return !own.Any(o => others.Any(l => l.IntersectsWith(o))); + Device.Y = y; } } - public enum SelectionStatus + private void ExecuteIdentifyDevice(ArtemisDevice device) { - None, - Hover, - Selected + _deviceService.IdentifyDevice(device); + } + + private async Task ExecuteViewProperties(ArtemisDevice device) + { + await _windowService.ShowDialogAsync(_deviceVmFactory.DevicePropertiesViewModel(device)); + } + + private bool Fits(float x, float y, bool ignoreOverlap) + { + if (x < 0 || y < 0) + return false; + + double maxTextureSize = 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value; + if (x + Device.Rectangle.Width > maxTextureSize || y + Device.Rectangle.Height > maxTextureSize) + return false; + + if (ignoreOverlap) + return true; + + IEnumerable own = Device.Leds + .Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height)); + IEnumerable others = _rgbService.EnabledDevices + .Where(d => d != Device && d.IsEnabled) + .SelectMany(d => d.Leds) + .Select(l => SKRect.Create(l.Rectangle.Left + l.Device.X, l.Rectangle.Top + l.Device.Y, l.Rectangle.Width, l.Rectangle.Height)); + + return !own.Any(o => others.Any(l => l.IntersectsWith(o))); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml index 21c590fa1..ce05a97ff 100644 --- a/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml +++ b/src/Artemis.UI/Screens/SurfaceEditor/SurfaceEditorView.axaml @@ -28,16 +28,14 @@ HorizontalAlignment="Stretch" Background="{StaticResource LargeCheckerboardBrush}" ZoomChanged="ZoomBorder_OnZoomChanged" - PointerPressed="ZoomBorder_OnPointerPressed" - PointerMoved="ZoomBorder_OnPointerMoved" PointerReleased="ZoomBorder_OnPointerReleased"> - + - +