1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Surface editor - Refactor selection/movement code to match nodes

Device visualizer - Fixed exception when updating devices during render
This commit is contained in:
Robert 2022-04-19 23:11:44 +02:00
parent 52f2338154
commit e5ba48c7f4
13 changed files with 527 additions and 572 deletions

View File

@ -32,7 +32,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The full path to the Artemis data folder /// The full path to the Artemis data folder
/// </summary> /// </summary>
public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis.Avalonia"); public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis");
/// <summary> /// <summary>
/// The full path to the Artemis logs folder /// The full path to the Artemis logs folder

View File

@ -81,8 +81,11 @@ namespace Artemis.UI.Shared
if (!ShowColors) if (!ShowColors)
return; return;
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) lock (_deviceVisualizerLeds)
deviceVisualizerLed.RenderGeometry(drawingContext, false); {
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
deviceVisualizerLed.RenderGeometry(drawingContext, false);
}
} }
finally finally
{ {
@ -244,10 +247,14 @@ namespace Artemis.UI.Shared
{ {
_deviceImage?.Dispose(); _deviceImage?.Dispose();
_deviceImage = null; _deviceImage = null;
_deviceVisualizerLeds.Clear();
_highlightedLeds = new List<DeviceVisualizerLed>(); _highlightedLeds = new List<DeviceVisualizerLed>();
_dimmedLeds = new List<DeviceVisualizerLed>(); _dimmedLeds = new List<DeviceVisualizerLed>();
lock (_deviceVisualizerLeds)
{
_deviceVisualizerLeds.Clear();
}
if (_oldDevice != null) if (_oldDevice != null)
{ {
_oldDevice.RgbDevice.PropertyChanged -= DevicePropertyChanged; _oldDevice.RgbDevice.PropertyChanged -= DevicePropertyChanged;
@ -264,8 +271,11 @@ namespace Artemis.UI.Shared
Device.DeviceUpdated += DeviceUpdated; Device.DeviceUpdated += DeviceUpdated;
// Create all the LEDs // Create all the LEDs
foreach (ArtemisLed artemisLed in Device.Leds) lock (_deviceVisualizerLeds)
_deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed)); {
foreach (ArtemisLed artemisLed in Device.Leds)
_deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed));
}
// Load the device main image on a background thread // Load the device main image on a background thread
ArtemisDevice? device = Device; ArtemisDevice? device = Device;
@ -282,8 +292,11 @@ namespace Artemis.UI.Shared
using IDrawingContextImpl context = renderTargetBitmap.CreateDrawingContext(new ImmediateRenderer(this)); using IDrawingContextImpl context = renderTargetBitmap.CreateDrawingContext(new ImmediateRenderer(this));
using Bitmap bitmap = new(device.Layout.Image.LocalPath); using Bitmap bitmap = new(device.Layout.Image.LocalPath);
context.DrawBitmap(bitmap.PlatformImpl, 1, new Rect(bitmap.Size), new Rect(renderTargetBitmap.Size), BitmapInterpolationMode.HighQuality); context.DrawBitmap(bitmap.PlatformImpl, 1, new Rect(bitmap.Size), new Rect(renderTargetBitmap.Size), BitmapInterpolationMode.HighQuality);
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds) lock (_deviceVisualizerLeds)
deviceVisualizerLed.DrawBitmap(context); {
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
deviceVisualizerLed.DrawBitmap(context);
}
_deviceImage = renderTargetBitmap; _deviceImage = renderTargetBitmap;

View File

@ -10,7 +10,6 @@ using Artemis.UI.Screens.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.ProfileEditor.Properties; using Artemis.UI.Screens.ProfileEditor.Properties;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding; using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline; 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.Properties.Tree;
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers; using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.Settings;
@ -18,103 +17,101 @@ using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.VisualScripting; using Artemis.UI.Screens.VisualScripting;
using Artemis.UI.Screens.VisualScripting.Pins; using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Services;
using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Ninject.Factories namespace Artemis.UI.Ninject.Factories;
public interface IVmFactory
{ {
public interface IVmFactory }
{
} public interface IDeviceVmFactory : IVmFactory
{
public interface IDeviceVmFactory : IVmFactory DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device);
{ DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel);
DevicePropertiesViewModel DevicePropertiesViewModel(ArtemisDevice device); DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device);
DeviceSettingsViewModel DeviceSettingsViewModel(ArtemisDevice device, DevicesTabViewModel devicesTabViewModel); DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device);
DeviceDetectInputViewModel DeviceDetectInputViewModel(ArtemisDevice device); DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device);
DevicePropertiesTabViewModel DevicePropertiesTabViewModel(ArtemisDevice device); DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
DeviceInfoTabViewModel DeviceInfoTabViewModel(ArtemisDevice device); InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
DeviceLedsTabViewModel DeviceLedsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds); }
InputMappingsTabViewModel InputMappingsTabViewModel(ArtemisDevice device, ObservableCollection<ArtemisLed> selectedLeds);
} public interface ISettingsVmFactory : IVmFactory
{
public interface ISettingsVmFactory : IVmFactory PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin);
{
PluginSettingsViewModel CreatePluginSettingsViewModel(Plugin plugin); PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield);
PluginFeatureViewModel CreatePluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo, bool showShield); // DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device);
// DeviceSettingsViewModel CreateDeviceSettingsViewModel(ArtemisDevice device); }
}
public interface ISidebarVmFactory : IVmFactory
public interface ISidebarVmFactory : IVmFactory {
{ SidebarViewModel? SidebarViewModel(IScreen hostScreen);
SidebarViewModel? SidebarViewModel(IScreen hostScreen); SidebarCategoryViewModel SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory);
SidebarCategoryViewModel SidebarCategoryViewModel(SidebarViewModel sidebarViewModel, ProfileCategory profileCategory); SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration);
SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(SidebarViewModel sidebarViewModel, ProfileConfiguration profileConfiguration); }
}
public interface ISurfaceVmFactory : IVmFactory
public interface ISurfaceVmFactory : IVmFactory {
{ SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
SurfaceDeviceViewModel SurfaceDeviceViewModel(ArtemisDevice device); ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device, SurfaceEditorViewModel surfaceEditorViewModel);
ListDeviceViewModel ListDeviceViewModel(ArtemisDevice device); }
}
public interface IPrerequisitesVmFactory : IVmFactory
public interface IPrerequisitesVmFactory : IVmFactory {
{ PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall);
PluginPrerequisiteViewModel PluginPrerequisiteViewModel(PluginPrerequisite pluginPrerequisite, bool uninstall); }
}
public interface IProfileEditorVmFactory : IVmFactory
public interface IProfileEditorVmFactory : IVmFactory {
{ ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen);
ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen); FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder);
FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder); LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer);
LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer); LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer);
LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer); LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer);
LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer); }
}
public interface ILayerPropertyVmFactory : IVmFactory
public interface ILayerPropertyVmFactory : IVmFactory {
{ PropertyViewModel PropertyViewModel(ILayerProperty layerProperty);
PropertyViewModel PropertyViewModel(ILayerProperty layerProperty); PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup); PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerBrush layerBrush); PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect);
PropertyGroupViewModel PropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, BaseLayerEffect layerEffect);
TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
TreeGroupViewModel TreeGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
TimelineViewModel TimelineViewModel(ObservableCollection<PropertyGroupViewModel> propertyGroupViewModels);
TimelineViewModel TimelineViewModel(ObservableCollection<PropertyGroupViewModel> propertyGroupViewModels); TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel);
TimelineGroupViewModel TimelineGroupViewModel(PropertyGroupViewModel propertyGroupViewModel); }
}
public interface IDataBindingVmFactory : IVmFactory
public interface IDataBindingVmFactory : IVmFactory {
{ DataBindingViewModel DataBindingViewModel();
DataBindingViewModel DataBindingViewModel(); }
}
public interface IPropertyVmFactory
public interface IPropertyVmFactory {
{ ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel); ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel);
ITimelinePropertyViewModel TimelinePropertyViewModel(ILayerProperty layerProperty, PropertyViewModel propertyViewModel); }
}
public interface INodeVmFactory : IVmFactory
public interface INodeVmFactory : IVmFactory {
{ NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview);
NodeScriptViewModel NodeScriptViewModel(NodeScript nodeScript, bool isPreview); NodePickerViewModel NodePickerViewModel(NodeScript nodeScript);
NodePickerViewModel NodePickerViewModel(NodeScript nodeScript); NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node);
NodeViewModel NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node); CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to);
CableViewModel CableViewModel(NodeScriptViewModel nodeScriptViewModel, IPin from, IPin to); DragCableViewModel DragCableViewModel(PinViewModel pinViewModel);
DragCableViewModel DragCableViewModel(PinViewModel pinViewModel); InputPinViewModel InputPinViewModel(IPin inputPin);
InputPinViewModel InputPinViewModel(IPin inputPin); OutputPinViewModel OutputPinViewModel(IPin outputPin);
OutputPinViewModel OutputPinViewModel(IPin outputPin); InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel);
InputPinCollectionViewModel InputPinCollectionViewModel(IPinCollection inputPinCollection, NodeScriptViewModel nodeScriptViewModel); OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel);
OutputPinCollectionViewModel OutputPinCollectionViewModel(IPinCollection outputPinCollection, NodeScriptViewModel nodeScriptViewModel); }
}
public interface IConditionVmFactory : IVmFactory
public interface IConditionVmFactory : IVmFactory {
{ AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition);
AlwaysOnConditionViewModel AlwaysOnConditionViewModel(AlwaysOnCondition alwaysOnCondition); PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition);
PlayOnceConditionViewModel PlayOnceConditionViewModel(PlayOnceCondition playOnceCondition); StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition);
StaticConditionViewModel StaticConditionViewModel(StaticCondition staticCondition); EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
EventConditionViewModel EventConditionViewModel(EventCondition eventCondition);
}
} }

View File

@ -4,5 +4,5 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.SurfaceEditor.ListDeviceView"> x:Class="Artemis.UI.Screens.SurfaceEditor.ListDeviceView">
Welcome to Avalonia! Welcome to Avalonia!
</UserControl> </UserControl>

View File

@ -1,18 +1,17 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.SurfaceEditor namespace Artemis.UI.Screens.SurfaceEditor;
{
public partial class ListDeviceView : UserControl
{
public ListDeviceView()
{
InitializeComponent();
}
private void InitializeComponent() public class ListDeviceView : UserControl
{ {
AvaloniaXamlLoader.Load(this); public ListDeviceView()
} {
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
} }
} }

View File

@ -1,32 +1,32 @@
using Artemis.Core; using Artemis.Core;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using ReactiveUI;
using SkiaSharp; 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; Device = device;
private bool _isSelected; SurfaceEditorViewModel = surfaceEditorViewModel;
}
public ListDeviceViewModel(ArtemisDevice device) public ArtemisDevice Device { get; }
{ public SurfaceEditorViewModel SurfaceEditorViewModel { get; }
Device = device;
}
public ArtemisDevice Device { get; } public bool IsSelected
{
get => _isSelected;
set => RaiseAndSetIfChanged(ref _isSelected, value);
}
public bool IsSelected public SKColor Color
{ {
get => _isSelected; get => _color;
set => RaiseAndSetIfChanged(ref _isSelected, value); set => RaiseAndSetIfChanged(ref _color, value);
}
public SKColor Color
{
get => _color;
set => RaiseAndSetIfChanged(ref _color, value);
}
} }
} }

View File

@ -3,9 +3,13 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" 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" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceDeviceView"> x:Class="Artemis.UI.Screens.SurfaceEditor.SurfaceDeviceView"
<Grid> x:DataType="surfaceEditor:SurfaceDeviceViewModel">
<Grid PointerMoved="InputElement_OnPointerMoved"
PointerReleased="InputElement_OnPointerReleased"
Cursor="Hand">
<Grid.Styles> <Grid.Styles>
<Style Selector="Border.selection-border"> <Style Selector="Border.selection-border">
<Setter Property="Opacity" Value="0" /> <Setter Property="Opacity" Value="0" />
@ -15,22 +19,26 @@
</Transitions> </Transitions>
</Setter> </Setter>
</Style> </Style>
<Style Selector="Border.selection-border-selected"> <Style Selector="Border.deselected:pointerover">
<Setter Property="Opacity" Value="0.5" />
</Style>
<Style Selector="Border.selected">
<Setter Property="Opacity" Value="1" /> <Setter Property="Opacity" Value="1" />
</Style> </Style>
</Grid.Styles> </Grid.Styles>
<shared:DeviceVisualizer Device="{Binding Device}" ShowColors="True"/> <shared:DeviceVisualizer Device="{CompiledBinding Device}" ShowColors="True" />
<Border x:Name="SurfaceDeviceBorder" <Border x:Name="SurfaceDeviceBorder"
Classes="selection-border" Classes="selection-border"
Classes.selection-border-selected="{Binding Highlighted}" Classes.selected="{CompiledBinding IsSelected}"
Classes.deselected="{CompiledBinding !IsSelected}"
BorderThickness="1"> BorderThickness="1">
<Border.BorderBrush> <Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource SystemAccentColorLight2}"></SolidColorBrush> <SolidColorBrush Color="{DynamicResource SystemAccentColorLight2}" />
</Border.BorderBrush> </Border.BorderBrush>
<Border.Background> <Border.Background>
<SolidColorBrush Color="{DynamicResource SystemAccentColorLight2}" Opacity="0.2"></SolidColorBrush> <SolidColorBrush Color="{DynamicResource SystemAccentColorLight2}" Opacity="0.2" />
</Border.Background> </Border.Background>
</Border> </Border>
</Grid> </Grid>

View File

@ -1,43 +1,68 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.VisualTree;
namespace Artemis.UI.Screens.SurfaceEditor namespace Artemis.UI.Screens.SurfaceEditor;
public class SurfaceDeviceView : ReactiveUserControl<SurfaceDeviceViewModel>
{ {
public class SurfaceDeviceView : ReactiveUserControl<SurfaceDeviceViewModel> private bool _dragging;
public SurfaceDeviceView()
{ {
public SurfaceDeviceView() InitializeComponent();
{ }
InitializeComponent();
}
/// <inheritdoc /> private void InitializeComponent()
protected override void OnPointerEnter(PointerEventArgs e) {
AvaloniaXamlLoader.Load(this);
}
private void InputElement_OnPointerMoved(object? sender, PointerEventArgs e)
{
PointerPoint point = e.GetCurrentPoint(this.FindAncestorOfType<Canvas>());
if (ViewModel == null || !point.Properties.IsLeftButtonPressed)
return;
if (!_dragging)
{ {
if (ViewModel?.SelectionStatus == SelectionStatus.None) _dragging = true;
if (!ViewModel.IsSelected)
{ {
ViewModel.SelectionStatus = SelectionStatus.Hover; ViewModel.SurfaceEditorViewModel.UpdateSelection(new List<SurfaceDeviceViewModel> {ViewModel}, false, false);
Cursor = new Cursor(StandardCursorType.Hand); ViewModel.SurfaceEditorViewModel.FinishSelection();
} }
base.OnPointerEnter(e); ViewModel.SurfaceEditorViewModel.StartMouseDrag(point.Position);
e.Pointer.Capture((IInputElement?) sender);
} }
/// <inheritdoc /> ViewModel.SurfaceEditorViewModel.UpdateMouseDrag(point.Position, e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Alt));
protected override void OnPointerLeave(PointerEventArgs e)
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) _dragging = false;
{ ViewModel.SurfaceEditorViewModel.FinishSelection();
ViewModel.SelectionStatus = SelectionStatus.None; e.Pointer.Capture(null);
Cursor = new Cursor(StandardCursorType.Arrow);
}
base.OnPointerLeave(e);
} }
else
private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); ViewModel.SurfaceEditorViewModel.UpdateSelection(new List<SurfaceDeviceViewModel> {ViewModel}, e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Control));
ViewModel.SurfaceEditorViewModel.FinishSelection();
} }
e.Handled = true;
} }
} }

View File

@ -8,143 +8,123 @@ using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Avalonia.Input;
using ReactiveUI; using ReactiveUI;
using RGB.NET.Core; using RGB.NET.Core;
using SkiaSharp; using SkiaSharp;
using Point = Avalonia.Point; 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; _rgbService = rgbService;
private readonly IDeviceService _deviceService; _deviceService = deviceService;
private readonly ISettingsService _settingsService; _settingsService = settingsService;
private readonly IDeviceVmFactory _deviceVmFactory; _deviceVmFactory = deviceVmFactory;
private readonly IWindowService _windowService; _windowService = windowService;
private Cursor _cursor;
private double _dragOffsetX;
private double _dragOffsetY;
private SelectionStatus _selectionStatus;
public SurfaceDeviceViewModel(ArtemisDevice device, IRgbService rgbService, IDeviceService deviceService, ISettingsService settingsService, IDeviceVmFactory deviceVmFactory, Device = device;
IWindowService windowService) SurfaceEditorViewModel = surfaceEditorViewModel;
IdentifyDevice = ReactiveCommand.Create<ArtemisDevice>(ExecuteIdentifyDevice);
ViewProperties = ReactiveCommand.CreateFromTask<ArtemisDevice>(ExecuteViewProperties);
}
public ReactiveCommand<ArtemisDevice, Unit> IdentifyDevice { get; }
public ReactiveCommand<ArtemisDevice, Unit> 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; x = (float) Math.Round(x / 10d, 0, MidpointRounding.AwayFromZero) * 10f;
_deviceService = deviceService; y = (float) Math.Round(y / 10d, 0, MidpointRounding.AwayFromZero) * 10f;
_settingsService = settingsService;
_deviceVmFactory = deviceVmFactory;
_windowService = windowService;
_cursor = Cursor.Default;
Device = device;
IdentifyDevice = ReactiveCommand.Create<ArtemisDevice>(ExecuteIdentifyDevice);
ViewProperties = ReactiveCommand.CreateFromTask<ArtemisDevice>(ExecuteViewProperties);
} }
public ReactiveCommand<ArtemisDevice, Unit> IdentifyDevice { get; }
public ReactiveCommand<ArtemisDevice, Unit> ViewProperties { get; }
public ArtemisDevice Device { get; } if (Fits(x, y, ignoreOverlap))
public SelectionStatus SelectionStatus
{ {
get => _selectionStatus; Device.X = x;
set Device.Y = y;
{
RaiseAndSetIfChanged(ref _selectionStatus, value);
this.RaisePropertyChanged(nameof(Highlighted));
}
} }
else if (Fits(x, Device.Y, ignoreOverlap))
public bool Highlighted => SelectionStatus != SelectionStatus.None;
public bool CanDetectInput => Device.DeviceType == RGBDeviceType.Keyboard || Device.DeviceType == RGBDeviceType.Mouse;
public Cursor Cursor
{ {
get => _cursor; Device.X = x;
set => RaiseAndSetIfChanged(ref _cursor, value);
} }
else if (Fits(Device.X, y, ignoreOverlap))
public void StartMouseDrag(Point mouseStartPosition)
{ {
if (SelectionStatus != SelectionStatus.Selected) Device.Y = y;
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<SKRect> own = Device.Leds
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height));
IEnumerable<SKRect> 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)));
} }
} }
public enum SelectionStatus private void ExecuteIdentifyDevice(ArtemisDevice device)
{ {
None, _deviceService.IdentifyDevice(device);
Hover, }
Selected
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<SKRect> own = Device.Leds
.Select(l => SKRect.Create(l.Rectangle.Left + x, l.Rectangle.Top + y, l.Rectangle.Width, l.Rectangle.Height));
IEnumerable<SKRect> 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)));
} }
} }

View File

@ -28,16 +28,14 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="{StaticResource LargeCheckerboardBrush}" Background="{StaticResource LargeCheckerboardBrush}"
ZoomChanged="ZoomBorder_OnZoomChanged" ZoomChanged="ZoomBorder_OnZoomChanged"
PointerPressed="ZoomBorder_OnPointerPressed"
PointerMoved="ZoomBorder_OnPointerMoved"
PointerReleased="ZoomBorder_OnPointerReleased"> PointerReleased="ZoomBorder_OnPointerReleased">
<Grid Name="ContainerGrid" Background="Transparent"> <Grid Name="ContainerGrid" Background="Transparent">
<Grid.Transitions> <Grid.Transitions>
<Transitions> <Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut"/> <TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut" />
</Transitions> </Transitions>
</Grid.Transitions> </Grid.Transitions>
<ItemsControl Items="{Binding SurfaceDeviceViewModels}" ClipToBounds="False"> <ItemsControl Name="DeviceContainer" Items="{Binding SurfaceDeviceViewModels}" ClipToBounds="False">
<ItemsControl.Styles> <ItemsControl.Styles>
<Style Selector="ContentPresenter"> <Style Selector="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Device.X}" /> <Setter Property="Canvas.Left" Value="{Binding Device.X}" />
@ -102,12 +100,12 @@
</ItemsControl> </ItemsControl>
<shared:SelectionRectangle Name="SelectionRectangle" <shared:SelectionRectangle Name="SelectionRectangle"
InputElement="{Binding #ZoomBorder}" InputElement="{Binding #ZoomBorder}"
SelectionUpdated="SelectionRectangle_OnSelectionUpdated" SelectionUpdated="SelectionRectangle_OnSelectionUpdated"
BorderBrush="{DynamicResource SystemAccentColor}" BorderBrush="{DynamicResource SystemAccentColor}"
BorderRadius="8"> BorderRadius="8">
<shared:SelectionRectangle.Background> <shared:SelectionRectangle.Background>
<SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2"></SolidColorBrush> <SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2" />
</shared:SelectionRectangle.Background> </shared:SelectionRectangle.Background>
</shared:SelectionRectangle> </shared:SelectionRectangle>

View File

@ -1,112 +1,72 @@
using System.Reactive; using System.Collections.Generic;
using System.Linq;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Events; using Artemis.UI.Shared.Events;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Generators;
using Avalonia.Controls.PanAndZoom; using Avalonia.Controls.PanAndZoom;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.SurfaceEditor namespace Artemis.UI.Screens.SurfaceEditor;
public class SurfaceEditorView : ReactiveUserControl<SurfaceEditorViewModel>
{ {
public class SurfaceEditorView : ReactiveUserControl<SurfaceEditorViewModel> private readonly ItemsControl _deviceContainer;
private readonly SelectionRectangle _selectionRectangle;
private readonly Border _surfaceBounds;
private readonly ZoomBorder _zoomBorder;
public SurfaceEditorView()
{ {
private readonly Grid _containerGrid; InitializeComponent();
private readonly SelectionRectangle _selectionRectangle;
private readonly Border _surfaceBounds;
private readonly ZoomBorder _zoomBorder;
private bool _dragging;
public SurfaceEditorView() _zoomBorder = this.Find<ZoomBorder>("ZoomBorder");
{ _deviceContainer = this.Find<ItemsControl>("DeviceContainer");
InitializeComponent(); _selectionRectangle = this.Find<SelectionRectangle>("SelectionRectangle");
_surfaceBounds = this.Find<Border>("SurfaceBounds");
_zoomBorder = this.Find<ZoomBorder>("ZoomBorder"); _zoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged;
_containerGrid = this.Find<Grid>("ContainerGrid"); UpdateZoomBorderBackground();
_selectionRectangle = this.Find<SelectionRectangle>("SelectionRectangle"); }
_surfaceBounds = this.Find<Border>("SurfaceBounds");
_zoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged; private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property.Name == nameof(_zoomBorder.Background))
UpdateZoomBorderBackground(); UpdateZoomBorderBackground();
} }
private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) private void InitializeComponent()
{ {
if (e.Property.Name == nameof(_zoomBorder.Background)) AvaloniaXamlLoader.Load(this);
UpdateZoomBorderBackground(); }
}
private void InitializeComponent() private void ZoomBorder_OnZoomChanged(object sender, ZoomChangedEventArgs e)
{ {
AvaloniaXamlLoader.Load(this); UpdateZoomBorderBackground();
} _selectionRectangle.BorderThickness = 1 / _zoomBorder.ZoomX;
_surfaceBounds.BorderThickness = new Thickness(2 / _zoomBorder.ZoomX);
}
private void ZoomBorder_OnZoomChanged(object sender, ZoomChangedEventArgs e) private void SelectionRectangle_OnSelectionUpdated(object? sender, SelectionRectangleEventArgs e)
{ {
UpdateZoomBorderBackground(); List<ItemContainerInfo> itemContainerInfos = _deviceContainer.ItemContainerGenerator.Containers.Where(c => c.ContainerControl.Bounds.Intersects(e.Rectangle)).ToList();
_selectionRectangle.BorderThickness = 1 / _zoomBorder.ZoomX; List<SurfaceDeviceViewModel> viewModels = itemContainerInfos.Where(c => c.Item is SurfaceDeviceViewModel).Select(c => (SurfaceDeviceViewModel) c.Item).ToList();
_surfaceBounds.BorderThickness = new Thickness(2 / _zoomBorder.ZoomX); ViewModel?.UpdateSelection(viewModels, e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Control));
} }
private void ZoomBorder_OnPointerPressed(object? sender, PointerPressedEventArgs e) private void ZoomBorder_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{ {
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) if (!_selectionRectangle.IsSelecting && e.InitialPressMouseButton == MouseButton.Left)
return; ViewModel?.ClearSelection();
}
_dragging = false; private void UpdateZoomBorderBackground()
{
if (e.Source is Border {Name: "SurfaceDeviceBorder"}) if (_zoomBorder.Background is VisualBrush visualBrush)
{ visualBrush.DestinationRect = new RelativeRect(_zoomBorder.OffsetX * -1, _zoomBorder.OffsetY * -1, 20, 20, RelativeUnit.Absolute);
e.Pointer.Capture(_zoomBorder);
e.Handled = true;
}
}
private void ZoomBorder_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
return;
if (ReferenceEquals(e.Pointer.Captured, sender))
{
if (!_dragging)
ViewModel?.StartMouseDrag(e.GetPosition(_containerGrid));
ViewModel?.UpdateMouseDrag(e.GetPosition(_containerGrid), e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Alt));
}
_dragging = true;
}
private void ZoomBorder_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (e.InitialPressMouseButton != MouseButton.Left)
return;
// If the mouse didn't move, apply selection
if (!_dragging)
{
ViewModel?.SelectFirstDeviceAtPoint(e.GetPosition(_containerGrid), e.KeyModifiers.HasFlag(KeyModifiers.Shift));
return;
}
if (ReferenceEquals(e.Pointer.Captured, sender))
{
ViewModel?.StopMouseDrag(e.GetPosition(_containerGrid), e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Alt));
e.Pointer.Capture(null);
}
}
private void SelectionRectangle_OnSelectionUpdated(object? sender, SelectionRectangleEventArgs e)
{
ViewModel?.UpdateSelection(e.Rectangle, e.KeyModifiers.HasFlag(KeyModifiers.Shift));
}
private void UpdateZoomBorderBackground()
{
if (_zoomBorder.Background is VisualBrush visualBrush)
visualBrush.DestinationRect = new RelativeRect(_zoomBorder.OffsetX * -1, _zoomBorder.OffsetY * -1, 20, 20, RelativeUnit.Absolute);
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reactive; using System.Reactive;
@ -8,201 +9,178 @@ using Artemis.Core.Services;
using Artemis.UI.Extensions; using Artemis.UI.Extensions;
using Artemis.UI.Ninject.Factories; using Artemis.UI.Ninject.Factories;
using Avalonia; using Avalonia;
using Avalonia.Skia;
using ReactiveUI; using ReactiveUI;
using SkiaSharp;
namespace Artemis.UI.Screens.SurfaceEditor namespace Artemis.UI.Screens.SurfaceEditor;
public class SurfaceEditorViewModel : MainScreenViewModel
{ {
public class SurfaceEditorViewModel : MainScreenViewModel private readonly IRgbService _rgbService;
private readonly ISettingsService _settingsService;
private List<SurfaceDeviceViewModel>? _initialSelection;
private bool _saving;
public SurfaceEditorViewModel(IScreen hostScreen,
IRgbService rgbService,
ISurfaceVmFactory surfaceVmFactory,
ISettingsService settingsService) : base(hostScreen, "surface-editor")
{ {
private readonly IRgbService _rgbService; _rgbService = rgbService;
private readonly ISettingsService _settingsService; _settingsService = settingsService;
private bool _saving; DisplayName = "Surface Editor";
SurfaceDeviceViewModels = new ObservableCollection<SurfaceDeviceViewModel>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.SurfaceDeviceViewModel(d, this)));
ListDeviceViewModels = new ObservableCollection<ListDeviceViewModel>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex).Select(d => surfaceVmFactory.ListDeviceViewModel(d, this)));
public SurfaceEditorViewModel(IScreen hostScreen, BringToFront = ReactiveCommand.Create<ArtemisDevice>(ExecuteBringToFront);
IRgbService rgbService, BringForward = ReactiveCommand.Create<ArtemisDevice>(ExecuteBringForward);
ISurfaceVmFactory surfaceVmFactory, SendToBack = ReactiveCommand.Create<ArtemisDevice>(ExecuteSendToBack);
ISettingsService settingsService) : base(hostScreen, "surface-editor") SendBackward = ReactiveCommand.Create<ArtemisDevice>(ExecuteSendBackward);
{
_rgbService = rgbService;
_settingsService = settingsService;
DisplayName = "Surface Editor";
SurfaceDeviceViewModels = new ObservableCollection<SurfaceDeviceViewModel>(rgbService.Devices.OrderBy(d => d.ZIndex).Select(surfaceVmFactory.SurfaceDeviceViewModel));
ListDeviceViewModels = new ObservableCollection<ListDeviceViewModel>(rgbService.Devices.OrderBy(d => d.ZIndex).Select(surfaceVmFactory.ListDeviceViewModel));
BringToFront = ReactiveCommand.Create<ArtemisDevice>(ExecuteBringToFront);
BringForward = ReactiveCommand.Create<ArtemisDevice>(ExecuteBringForward);
SendToBack = ReactiveCommand.Create<ArtemisDevice>(ExecuteSendToBack);
SendBackward = ReactiveCommand.Create<ArtemisDevice>(ExecuteSendBackward);
}
public ObservableCollection<SurfaceDeviceViewModel> SurfaceDeviceViewModels { get; }
public ObservableCollection<ListDeviceViewModel> ListDeviceViewModels { get; }
public ReactiveCommand<ArtemisDevice, Unit> BringToFront { get; }
public ReactiveCommand<ArtemisDevice, Unit> BringForward { get; }
public ReactiveCommand<ArtemisDevice, Unit> SendToBack { get; }
public ReactiveCommand<ArtemisDevice, Unit> SendBackward { get; }
public double MaxTextureSize => 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value;
public void ClearSelection()
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
surfaceDeviceViewModel.SelectionStatus = SelectionStatus.None;
}
public void StartMouseDrag(Point mousePosition)
{
SurfaceDeviceViewModel? startedOn = GetViewModelAtPoint(mousePosition);
if (startedOn != null && startedOn.SelectionStatus != SelectionStatus.Selected)
{
startedOn.SelectionStatus = SelectionStatus.Selected;
foreach (SurfaceDeviceViewModel device in SurfaceDeviceViewModels.Where(vm => vm != startedOn))
device.SelectionStatus = SelectionStatus.None;
}
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
surfaceDeviceViewModel.StartMouseDrag(mousePosition);
}
public void UpdateMouseDrag(Point mousePosition, bool round, bool ignoreOverlap)
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition, round, ignoreOverlap);
}
public void StopMouseDrag(Point mousePosition, bool round, bool ignoreOverlap)
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition, round, ignoreOverlap);
if (_saving)
return;
Task.Run(() =>
{
try
{
_saving = true;
_rgbService.SaveDevices();
}
finally
{
_saving = false;
}
});
}
public void SelectFirstDeviceAtPoint(Point point, bool expand)
{
SurfaceDeviceViewModel? toSelect = GetViewModelAtPoint(point);
if (toSelect != null)
toSelect.SelectionStatus = SelectionStatus.Selected;
if (!expand)
{
foreach (SurfaceDeviceViewModel device in SurfaceDeviceViewModels.Where(vm => vm != toSelect))
device.SelectionStatus = SelectionStatus.None;
}
ApplySurfaceSelection();
}
private SurfaceDeviceViewModel? GetViewModelAtPoint(Point point)
{
SKPoint hitTestPoint = point.ToSKPoint();
return SurfaceDeviceViewModels.OrderByDescending(vm => vm.Device.ZIndex).FirstOrDefault(d => d.Device.Rectangle.Contains(hitTestPoint));
}
public void UpdateSelection(Rect rect, bool expand)
{
SKRect hitTestRect = rect.ToSKRect();
foreach (SurfaceDeviceViewModel device in SurfaceDeviceViewModels)
{
if (device.Device.Rectangle.IntersectsWith(hitTestRect))
device.SelectionStatus = SelectionStatus.Selected;
else if (!expand)
device.SelectionStatus = SelectionStatus.None;
}
ApplySurfaceSelection();
}
private void ApplySurfaceSelection()
{
foreach (ListDeviceViewModel viewModel in ListDeviceViewModels)
viewModel.IsSelected = SurfaceDeviceViewModels.Any(s => s.Device == viewModel.Device && s.SelectionStatus == SelectionStatus.Selected);
}
#region Context menu commands
private void ExecuteBringToFront(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
SurfaceDeviceViewModels.Move(SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel), SurfaceDeviceViewModels.Count - 1);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
private void ExecuteBringForward(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
int currentIndex = SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel);
int newIndex = Math.Min(currentIndex + 1, SurfaceDeviceViewModels.Count - 1);
SurfaceDeviceViewModels.Move(currentIndex, newIndex);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
private void ExecuteSendToBack(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
SurfaceDeviceViewModels.Move(SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel), 0);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
private void ExecuteSendBackward(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
int currentIndex = SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel);
int newIndex = Math.Max(currentIndex - 1, 0);
SurfaceDeviceViewModels.Move(currentIndex, newIndex);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
#endregion
} }
public ObservableCollection<SurfaceDeviceViewModel> SurfaceDeviceViewModels { get; }
public ObservableCollection<ListDeviceViewModel> ListDeviceViewModels { get; }
public ReactiveCommand<ArtemisDevice, Unit> BringToFront { get; }
public ReactiveCommand<ArtemisDevice, Unit> BringForward { get; }
public ReactiveCommand<ArtemisDevice, Unit> SendToBack { get; }
public ReactiveCommand<ArtemisDevice, Unit> SendBackward { get; }
public double MaxTextureSize => 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value;
public void UpdateSelection(List<SurfaceDeviceViewModel> devices, bool expand, bool invert)
{
_initialSelection ??= SurfaceDeviceViewModels.Where(d => d.IsSelected).ToList();
if (expand)
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in devices)
surfaceDeviceViewModel.IsSelected = true;
}
else if (invert)
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in devices)
surfaceDeviceViewModel.IsSelected = !_initialSelection.Contains(surfaceDeviceViewModel);
}
else
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in devices)
surfaceDeviceViewModel.IsSelected = true;
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels.Except(devices))
surfaceDeviceViewModel.IsSelected = false;
}
}
public void FinishSelection()
{
_initialSelection = null;
if (_saving)
return;
Task.Run(() =>
{
try
{
_saving = true;
_rgbService.SaveDevices();
}
finally
{
_saving = false;
}
});
}
public void ClearSelection()
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
surfaceDeviceViewModel.IsSelected = false;
}
public void StartMouseDrag(Point mousePosition)
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
surfaceDeviceViewModel.StartMouseDrag(mousePosition);
}
public void UpdateMouseDrag(Point mousePosition, bool round, bool ignoreOverlap)
{
foreach (SurfaceDeviceViewModel surfaceDeviceViewModel in SurfaceDeviceViewModels)
surfaceDeviceViewModel.UpdateMouseDrag(mousePosition, round, ignoreOverlap);
}
private void ApplySurfaceSelection()
{
foreach (ListDeviceViewModel viewModel in ListDeviceViewModels)
viewModel.IsSelected = SurfaceDeviceViewModels.Any(s => s.Device == viewModel.Device && s.IsSelected);
}
#region Context menu commands
private void ExecuteBringToFront(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
SurfaceDeviceViewModels.Move(SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel), SurfaceDeviceViewModels.Count - 1);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
private void ExecuteBringForward(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
int currentIndex = SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel);
int newIndex = Math.Min(currentIndex + 1, SurfaceDeviceViewModels.Count - 1);
SurfaceDeviceViewModels.Move(currentIndex, newIndex);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
private void ExecuteSendToBack(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
SurfaceDeviceViewModels.Move(SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel), 0);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
private void ExecuteSendBackward(ArtemisDevice device)
{
SurfaceDeviceViewModel surfaceDeviceViewModel = SurfaceDeviceViewModels.First(d => d.Device == device);
int currentIndex = SurfaceDeviceViewModels.IndexOf(surfaceDeviceViewModel);
int newIndex = Math.Max(currentIndex - 1, 0);
SurfaceDeviceViewModels.Move(currentIndex, newIndex);
for (int i = 0; i < SurfaceDeviceViewModels.Count; i++)
{
SurfaceDeviceViewModel deviceViewModel = SurfaceDeviceViewModels[i];
deviceViewModel.Device.ZIndex = i + 1;
}
ListDeviceViewModels.Sort(l => l.Device.ZIndex * -1);
_rgbService.SaveDevices();
}
#endregion
} }

View File

@ -2,10 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Presenters;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.VisualTree; using Avalonia.VisualTree;