1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
2021-07-11 19:45:34 +02:00

441 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Events;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Visualization.Tools;
using Artemis.UI.Screens.Shared;
using Artemis.UI.Shared.Services;
using SkiaSharp;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Visualization
{
public class ProfileViewModel : Conductor<CanvasViewModel>.Collection.AllActive, IProfileEditorPanelViewModel, IHandle<MainWindowKeyEvent>
{
private readonly ICoreService _coreService;
private readonly IProfileEditorService _profileEditorService;
private readonly IProfileLayerVmFactory _profileLayerVmFactory;
private readonly IRgbService _rgbService;
private readonly ISettingsService _settingsService;
private readonly IVisualizationToolVmFactory _visualizationToolVmFactory;
private int _activeToolIndex;
private VisualizationToolViewModel _activeToolViewModel;
private bool _canApplyToLayer;
private bool _canSelectEditTool;
private BindableCollection<ArtemisDevice> _devices;
private BindableCollection<ArtemisLed> _highlightedLeds;
private DateTime _lastUpdate;
private PanZoomViewModel _panZoomViewModel;
private Layer _previousSelectedLayer;
private int _previousTool;
public ProfileViewModel(IProfileEditorService profileEditorService,
IRgbService rgbService,
ICoreService coreService,
ISettingsService settingsService,
IEventAggregator eventAggregator,
IVisualizationToolVmFactory visualizationToolVmFactory,
IProfileLayerVmFactory profileLayerVmFactory)
{
_profileEditorService = profileEditorService;
_rgbService = rgbService;
_coreService = coreService;
_settingsService = settingsService;
_visualizationToolVmFactory = visualizationToolVmFactory;
_profileLayerVmFactory = profileLayerVmFactory;
eventAggregator.Subscribe(this);
}
public bool CanSelectEditTool
{
get => _canSelectEditTool;
set => SetAndNotify(ref _canSelectEditTool, value);
}
public PanZoomViewModel PanZoomViewModel
{
get => _panZoomViewModel;
set => SetAndNotify(ref _panZoomViewModel, value);
}
public BindableCollection<ArtemisDevice> Devices
{
get => _devices;
set => SetAndNotify(ref _devices, value);
}
public BindableCollection<ArtemisLed> HighlightedLeds
{
get => _highlightedLeds;
set => SetAndNotify(ref _highlightedLeds, value);
}
public VisualizationToolViewModel ActiveToolViewModel
{
get => _activeToolViewModel;
private set
{
// Remove the tool from the canvas
if (_activeToolViewModel != null)
lock (Items)
{
Items.Remove(_activeToolViewModel);
NotifyOfPropertyChange(() => Items);
}
// Set the new tool
SetAndNotify(ref _activeToolViewModel, value);
// Add the new tool to the canvas
if (_activeToolViewModel != null)
lock (Items)
{
Items.Add(_activeToolViewModel);
NotifyOfPropertyChange(() => Items);
}
}
}
public int ActiveToolIndex
{
get => _activeToolIndex;
set
{
if (!SetAndNotify(ref _activeToolIndex, value)) return;
ActivateToolByIndex(value);
}
}
public bool CanApplyToLayer
{
get => _canApplyToLayer;
set => SetAndNotify(ref _canApplyToLayer, value);
}
public bool SuspendedEditing => _profileEditorService.SuspendEditing;
protected override void OnInitialActivate()
{
PanZoomViewModel = new PanZoomViewModel
{
LimitToZero = false,
PanX = _settingsService.GetSetting("ProfileEditor.PanX", 0d).Value,
PanY = _settingsService.GetSetting("ProfileEditor.PanY", 0d).Value,
Zoom = _settingsService.GetSetting("ProfileEditor.Zoom", 0d).Value
};
Devices = new BindableCollection<ArtemisDevice>();
HighlightedLeds = new BindableCollection<ArtemisLed>();
ApplyDevices();
ActivateToolByIndex(0);
ApplyActiveProfile();
_lastUpdate = DateTime.Now;
_coreService.FrameRendered += OnFrameRendered;
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
_rgbService.DeviceRemoved += RgbServiceOnDevicesModified;
_profileEditorService.SuspendEditingChanged += ProfileEditorServiceOnSuspendEditingChanged;
_profileEditorService.SelectedProfileChanged += OnSelectedProfileChanged;
_profileEditorService.SelectedProfileElementChanged += OnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementSaved += OnSelectedProfileElementSaved;
base.OnInitialActivate();
}
protected override void OnClose()
{
_coreService.FrameRendered -= OnFrameRendered;
_rgbService.DeviceAdded -= RgbServiceOnDevicesModified;
_rgbService.DeviceRemoved -= RgbServiceOnDevicesModified;
_profileEditorService.SuspendEditingChanged -= ProfileEditorServiceOnSuspendEditingChanged;
_profileEditorService.SelectedProfileChanged -= OnSelectedProfileChanged;
_profileEditorService.SelectedProfileElementChanged -= OnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementSaved -= OnSelectedProfileElementSaved;
if (_previousSelectedLayer != null)
_previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
_settingsService.GetSetting("ProfileEditor.PanX", 0d).Value = PanZoomViewModel.PanX;
_settingsService.GetSetting("ProfileEditor.PanX", 0d).Save();
_settingsService.GetSetting("ProfileEditor.PanY", 0d).Value = PanZoomViewModel.PanY;
_settingsService.GetSetting("ProfileEditor.PanY", 0d).Save();
_settingsService.GetSetting("ProfileEditor.Zoom", 0d).Value = PanZoomViewModel.Zoom;
_settingsService.GetSetting("ProfileEditor.Zoom", 0d).Save();
base.OnClose();
}
private void RgbServiceOnDevicesModified(object sender, DeviceEventArgs e)
{
ApplyDevices();
}
private void ApplyActiveProfile()
{
List<ProfileLayerViewModel> layerViewModels = Items.Where(vm => vm is ProfileLayerViewModel).Cast<ProfileLayerViewModel>().ToList();
List<Layer> layers = _profileEditorService.SelectedProfile?.GetAllLayers() ?? new List<Layer>();
// Add new layers missing a VM
foreach (Layer layer in layers)
{
if (layerViewModels.All(vm => vm.Layer != layer))
Items.Add(_profileLayerVmFactory.Create(layer, PanZoomViewModel));
}
// Remove layers that no longer exist
IEnumerable<ProfileLayerViewModel> toRemove = layerViewModels.Where(vm => !layers.Contains(vm.Layer));
foreach (ProfileLayerViewModel profileLayerViewModel in toRemove)
Items.Remove(profileLayerViewModel);
}
private void ApplyDevices()
{
Devices.Clear();
Devices.AddRange(_rgbService.EnabledDevices.Where(d => d.IsEnabled).OrderBy(d => d.ZIndex));
}
private void UpdateLedsDimStatus()
{
HighlightedLeds.Clear();
if (_profileEditorService.SelectedProfileElement is Layer layer)
HighlightedLeds.AddRange(layer.Leds);
}
private void UpdateCanSelectEditTool()
{
if (SuspendedEditing)
{
CanApplyToLayer = false;
CanSelectEditTool = false;
return;
}
if (_profileEditorService.SelectedProfileElement is Layer layer)
{
CanApplyToLayer = true;
CanSelectEditTool = (layer.LayerBrush == null || layer.LayerBrush.SupportsTransformation) && layer.Leds.Any();
}
else
{
CanApplyToLayer = false;
CanSelectEditTool = false;
}
if (CanSelectEditTool == false && ActiveToolIndex == 1)
ActivateToolByIndex(2);
}
#region Buttons
private void ActivateToolByIndex(int value)
{
if (value == 1 && !CanSelectEditTool)
return;
switch (value)
{
case 0:
ActiveToolViewModel = _visualizationToolVmFactory.ViewpointMoveToolViewModel(PanZoomViewModel);
break;
case 1:
ActiveToolViewModel = _visualizationToolVmFactory.EditToolViewModel(PanZoomViewModel);
break;
case 2:
ActiveToolViewModel = _visualizationToolVmFactory.SelectionToolViewModel(PanZoomViewModel);
break;
case 3:
ActiveToolViewModel = _visualizationToolVmFactory.SelectionRemoveToolViewModel(PanZoomViewModel);
break;
}
ActiveToolIndex = value;
}
public void ResetZoomAndPan()
{
if (!Devices.Any())
{
PanZoomViewModel.Reset();
return;
}
// Create a rect surrounding all devices
SKRect rect = new(
Devices.Min(d => d.Rectangle.Left),
Devices.Min(d => d.Rectangle.Top),
Devices.Max(d => d.Rectangle.Right),
Devices.Max(d => d.Rectangle.Bottom)
);
PanZoomViewModel.Reset(rect);
}
#endregion
#region Mouse
public void CanvasMouseDown(object sender, MouseButtonEventArgs e)
{
((IInputElement) sender).CaptureMouse();
ActiveToolViewModel?.MouseDown(sender, e);
e.Handled = false;
}
public void CanvasMouseUp(object sender, MouseButtonEventArgs e)
{
((IInputElement) sender).ReleaseMouseCapture();
ActiveToolViewModel?.MouseUp(sender, e);
e.Handled = false;
}
public void CanvasMouseMove(object sender, MouseEventArgs e)
{
ActiveToolViewModel?.MouseMove(sender, e);
}
public void CanvasMouseWheel(object sender, MouseWheelEventArgs e)
{
PanZoomViewModel.ProcessMouseScroll(sender, e);
ActiveToolViewModel?.MouseWheel(sender, e);
}
#endregion
#region Event handlers
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
{
TimeSpan delta = DateTime.Now - _lastUpdate;
_lastUpdate = DateTime.Now;
if (SuspendedEditing || !_settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true).Value || _profileEditorService.SelectedProfile == null)
return;
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllFolders()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllLayers()
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
.SelectMany(p => p.GetAllDataBindingRegistrations()))
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
// TODO: Only update when there are data bindings
if (!_profileEditorService.Playing)
_profileEditorService.UpdateProfilePreview();
}
private void HighlightSelectedLayerOnSettingChanged(object sender, EventArgs e)
{
UpdateLedsDimStatus();
}
private void OnSelectedProfileChanged(object sender, EventArgs e)
{
ApplyActiveProfile();
}
private void OnSelectedProfileElementChanged(object sender, EventArgs e)
{
if (_previousSelectedLayer != null)
{
_previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
_previousSelectedLayer.Transform.LayerPropertyOnCurrentValueSet -= TransformValueChanged;
}
if (_profileEditorService.SelectedProfileElement is Layer layer)
{
_previousSelectedLayer = layer;
_previousSelectedLayer.LayerBrushUpdated += SelectedLayerOnLayerBrushUpdated;
_previousSelectedLayer.Transform.LayerPropertyOnCurrentValueSet += TransformValueChanged;
}
else
{
_previousSelectedLayer = null;
}
ApplyActiveProfile();
UpdateLedsDimStatus();
UpdateCanSelectEditTool();
}
private void SelectedLayerOnLayerBrushUpdated(object sender, EventArgs e)
{
UpdateCanSelectEditTool();
}
private void TransformValueChanged(object sender, LayerPropertyEventArgs e)
{
if (ActiveToolIndex != 1)
ActivateToolByIndex(1);
}
private void OnSelectedProfileElementSaved(object sender, EventArgs e)
{
ApplyActiveProfile();
UpdateLedsDimStatus();
if (_profileEditorService.SelectedProfileElement is Layer layer)
{
CanApplyToLayer = true;
CanSelectEditTool = layer.Leds.Any();
}
else
{
CanApplyToLayer = false;
CanSelectEditTool = false;
}
if (CanSelectEditTool == false && ActiveToolIndex == 1)
ActivateToolByIndex(2);
}
private void ProfileEditorServiceOnSuspendEditingChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(SuspendedEditing));
UpdateCanSelectEditTool();
}
public void Handle(MainWindowKeyEvent message)
{
if (message.KeyDown)
{
if (ActiveToolIndex != 0)
{
_previousTool = ActiveToolIndex;
if ((message.EventArgs.Key == Key.LeftCtrl || message.EventArgs.Key == Key.RightCtrl) && message.EventArgs.IsDown)
ActivateToolByIndex(0);
}
ActiveToolViewModel?.KeyDown(message.EventArgs);
// If T is pressed while Ctrl is down, that makes it Ctrl+T > swap to transformation tool on Ctrl release
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.LeftCtrl))
{
if (message.EventArgs.Key == Key.T)
_previousTool = 1;
else if (message.EventArgs.Key == Key.Q)
_previousTool = 2;
else if (message.EventArgs.Key == Key.W)
_previousTool = 3;
}
}
else
{
if ((message.EventArgs.Key == Key.LeftCtrl || message.EventArgs.Key == Key.RightCtrl) && message.EventArgs.IsUp)
ActivateToolByIndex(_previousTool);
ActiveToolViewModel?.KeyUp(message.EventArgs);
}
}
#endregion
}
}