From 11c9a8a82207ab14c92736a9aa73093b60898e8d Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 12 Nov 2019 20:20:05 +0100 Subject: [PATCH] Implemented devices and LEDs in profile editor --- src/Artemis.UI/Artemis.UI.csproj | 3 +- ...lFactory.cs => IModuleViewModelFactory.cs} | 0 .../IProfileEditorViewModelFactory.cs | 10 ++ src/Artemis.UI/Ninject/UiModule.cs | 3 +- .../ProfileEditor/ProfileDeviceViewModel.cs | 37 +++- .../ProfileEditor/ProfileEditorViewModel.cs | 168 +++++++++++++++++- .../ProfileEditor/ProfileLedViewModel.cs | 18 +- .../SurfaceEditor/SurfaceDeviceViewModel.cs | 4 +- .../ViewModels/Screens/ModuleRootViewModel.cs | 5 +- .../Screens/SurfaceEditorViewModel.cs | 9 +- .../ProfileEditor/ProfileDeviceView.xaml | 29 ++- .../ProfileEditor/ProfileEditorView.xaml | 162 ++++++++++++++++- .../SurfaceEditor/SurfaceDeviceView.xaml | 3 + 13 files changed, 402 insertions(+), 49 deletions(-) rename src/Artemis.UI/Ninject/Factories/{ModuleViewModelFactory.cs => IModuleViewModelFactory.cs} (100%) create mode 100644 src/Artemis.UI/Ninject/Factories/IProfileEditorViewModelFactory.cs diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 1f336f351..50e19fad7 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -166,7 +166,8 @@ - + + True diff --git a/src/Artemis.UI/Ninject/Factories/ModuleViewModelFactory.cs b/src/Artemis.UI/Ninject/Factories/IModuleViewModelFactory.cs similarity index 100% rename from src/Artemis.UI/Ninject/Factories/ModuleViewModelFactory.cs rename to src/Artemis.UI/Ninject/Factories/IModuleViewModelFactory.cs diff --git a/src/Artemis.UI/Ninject/Factories/IProfileEditorViewModelFactory.cs b/src/Artemis.UI/Ninject/Factories/IProfileEditorViewModelFactory.cs new file mode 100644 index 000000000..8708d0236 --- /dev/null +++ b/src/Artemis.UI/Ninject/Factories/IProfileEditorViewModelFactory.cs @@ -0,0 +1,10 @@ +using Artemis.Core.Plugins.Abstract; +using Artemis.UI.ViewModels.Controls.ProfileEditor; + +namespace Artemis.UI.Ninject.Factories +{ + public interface IProfileEditorViewModelFactory + { + ProfileEditorViewModel CreateModuleViewModel(Module module); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Ninject/UiModule.cs b/src/Artemis.UI/Ninject/UiModule.cs index 38bf34559..a4c5d340e 100644 --- a/src/Artemis.UI/Ninject/UiModule.cs +++ b/src/Artemis.UI/Ninject/UiModule.cs @@ -36,7 +36,8 @@ namespace Artemis.UI.Ninject // Bind the module VM Bind().ToFactory(); - + Bind().ToFactory(); + // Bind all UI services as singletons Kernel.Bind(x => { diff --git a/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileDeviceViewModel.cs b/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileDeviceViewModel.cs index 4f81e64af..cfcd70bc2 100644 --- a/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileDeviceViewModel.cs +++ b/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileDeviceViewModel.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; -using RGB.NET.Core; +using System.Windows; +using Artemis.Core.Models.Surface; using Stylet; namespace Artemis.UI.ViewModels.Controls.ProfileEditor @@ -8,18 +9,44 @@ namespace Artemis.UI.ViewModels.Controls.ProfileEditor { private readonly List _leds; - public ProfileDeviceViewModel(IRGBDevice device) + public ProfileDeviceViewModel(Device device) { Device = device; _leds = new List(); - foreach (var led in Device) - _leds.Add(new ProfileLedViewModel(led)); + if (Device.RgbDevice != null) + { + foreach (var led in Device.RgbDevice) + _leds.Add(new ProfileLedViewModel(led)); + } + } + + public Device Device { get; set; } + + public double X + { + get => Device.X; + set => Device.X = value; + } + + public double Y + { + get => Device.Y; + set => Device.Y = value; + } + + public int ZIndex + { + get => Device.ZIndex; + set => Device.ZIndex = value; } - public IRGBDevice Device { get; } public IReadOnlyCollection Leds => _leds.AsReadOnly(); + public Rect DeviceRectangle => Device.RgbDevice == null + ? new Rect() + : new Rect(X, Y, Device.RgbDevice.Size.Width, Device.RgbDevice.Size.Height); + public void Update() { foreach (var ledViewModel in _leds) diff --git a/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileEditorViewModel.cs b/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileEditorViewModel.cs index 1cda2e580..e25a147cf 100644 --- a/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileEditorViewModel.cs +++ b/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileEditorViewModel.cs @@ -1,16 +1,170 @@ -using System; -using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; +using Artemis.Core.Events; +using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.Abstract; +using Artemis.Core.Services.Interfaces; +using Artemis.Core.Services.Storage; +using Artemis.UI.ViewModels.Screens; +using Artemis.UI.ViewModels.Utilities; +using Stylet; namespace Artemis.UI.ViewModels.Controls.ProfileEditor { - public class ProfileEditorViewModel : ModuleViewModel + public class ProfileEditorViewModel : ModuleViewModel { - public ProfileEditorViewModel(Module module) : base(module, "Profile Editor") + public ProfileEditorViewModel(Module module, ISurfaceService surfaceService, ICoreService coreService) : base(module, "Profile Editor") { + surfaceService.ActiveSurfaceConfigurationChanged += OnActiveSurfaceConfigurationChanged; + coreService.FrameRendered += CoreServiceOnFrameRendered; + Devices = new ObservableCollection(); + Execute.OnUIThread(() => + { + SelectionRectangle = new RectangleGeometry(); + PanZoomViewModel = new PanZoomViewModel(); + }); + + ApplySurfaceConfiguration(surfaceService.ActiveSurface); } + + public ObservableCollection Devices { get; set; } + public RectangleGeometry SelectionRectangle { get; set; } + public PanZoomViewModel PanZoomViewModel { get; set; } + + private void OnActiveSurfaceConfigurationChanged(object sender, SurfaceConfigurationEventArgs e) + { + ApplySurfaceConfiguration(e.Surface); + } + + private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e) + { + foreach (var profileDeviceViewModel in Devices) + profileDeviceViewModel.Update(); + } + + private void ApplySurfaceConfiguration(Surface surface) + { + // Make sure all devices have an up-to-date VM + foreach (var surfaceDeviceConfiguration in surface.Devices) + { + // Create VMs for missing devices + var viewModel = Devices.FirstOrDefault(vm => vm.Device.RgbDevice == surfaceDeviceConfiguration.RgbDevice); + if (viewModel == null) + Execute.OnUIThread(() => Devices.Add(new ProfileDeviceViewModel(surfaceDeviceConfiguration))); + // Update existing devices + else + viewModel.Device = surfaceDeviceConfiguration; + } + } + + #region Selection + + private MouseDragStatus _mouseDragStatus; + private Point _mouseDragStartPoint; + + // ReSharper disable once UnusedMember.Global - Called from view + public void EditorGridMouseClick(object sender, MouseEventArgs e) + { + if (IsPanKeyDown()) + return; + + var position = e.GetPosition((IInputElement) sender); + var relative = PanZoomViewModel.GetRelativeMousePosition(sender, e); + if (e.LeftButton == MouseButtonState.Pressed) + StartMouseDrag(position, relative); + else + StopMouseDrag(position); + } + + // ReSharper disable once UnusedMember.Global - Called from view + public void EditorGridMouseMove(object sender, MouseEventArgs e) + { + // If holding down Ctrl, pan instead of move/select + if (IsPanKeyDown()) + { + Pan(sender, e); + return; + } + + var position = e.GetPosition((IInputElement) sender); + if (_mouseDragStatus == MouseDragStatus.Selecting) + UpdateSelection(position); + } + + private void StartMouseDrag(Point position, Point relative) + { + _mouseDragStatus = MouseDragStatus.Selecting; + _mouseDragStartPoint = position; + + // Any time dragging starts, start with a new rect + SelectionRectangle.Rect = new Rect(); + } + + private void StopMouseDrag(Point position) + { + var selectedRect = new Rect(_mouseDragStartPoint, position); + // TODO: Select LEDs + + Mouse.OverrideCursor = null; + _mouseDragStatus = MouseDragStatus.None; + } + + private void UpdateSelection(Point position) + { + if (IsPanKeyDown()) + return; + + lock (Devices) + { + var selectedRect = new Rect(_mouseDragStartPoint, position); + SelectionRectangle.Rect = selectedRect; + + // TODO: Highlight LEDs + } + } + + #endregion + + #region Panning and zooming + + public void EditorGridMouseWheel(object sender, MouseWheelEventArgs e) + { + PanZoomViewModel.ProcessMouseScroll(sender, e); + } + + public void EditorGridKeyDown(object sender, KeyEventArgs e) + { + if ((e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl) && e.IsDown) + Mouse.OverrideCursor = Cursors.ScrollAll; + } + + public void EditorGridKeyUp(object sender, KeyEventArgs e) + { + if ((e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl) && e.IsUp) + Mouse.OverrideCursor = null; + } + + public void Pan(object sender, MouseEventArgs e) + { + PanZoomViewModel.ProcessMouseMove(sender, e); + + // Empty the selection rect since it's shown while mouse is down + SelectionRectangle.Rect = Rect.Empty; + } + + public void ResetZoomAndPan() + { + PanZoomViewModel.Reset(); + } + + private bool IsPanKeyDown() + { + return Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl); + } + + #endregion } -} +} \ No newline at end of file diff --git a/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileLedViewModel.cs b/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileLedViewModel.cs index 7e776b7e5..b3fa52017 100644 --- a/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileLedViewModel.cs +++ b/src/Artemis.UI/ViewModels/Controls/ProfileEditor/ProfileLedViewModel.cs @@ -13,7 +13,7 @@ namespace Artemis.UI.ViewModels.Controls.ProfileEditor public ProfileLedViewModel(Led led) { Led = led; - + Execute.OnUIThread(CreateLedGeometry); Update(); } @@ -92,13 +92,9 @@ namespace Artemis.UI.ViewModels.Controls.ProfileEditor public void Update() { - if (ColorsEnabled) - { - if (Led.Id == LedId.Keyboard_Y) - Console.WriteLine(); - var newColor = Led.Color.ToMediaColor(); - SetColor(newColor); - } + var newColor = Led.Color.ToMediaColor(); + if (!DisplayColor.Equals(newColor)) + DisplayColor = newColor; if (Math.Abs(Led.LedRectangle.X - X) > 0.1) X = Led.LedRectangle.X; @@ -112,11 +108,5 @@ namespace Artemis.UI.ViewModels.Controls.ProfileEditor if (Math.Abs(Led.LedRectangle.Height - Height) > 0.1) Height = Led.LedRectangle.Height; } - - public void SetColor(Color color) - { - if (!DisplayColor.Equals(color)) - DisplayColor = color; - } } } \ No newline at end of file diff --git a/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs b/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs index 6978b91b4..0e9d4baaf 100644 --- a/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs +++ b/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs @@ -3,17 +3,15 @@ using System.Collections.Generic; using System.Windows; using System.Windows.Input; using Artemis.Core.Models.Surface; -using RGB.NET.Core; using Stylet; -using Point = System.Windows.Point; namespace Artemis.UI.ViewModels.Controls.SurfaceEditor { public class SurfaceDeviceViewModel : PropertyChangedBase { + private readonly List _leds; private double _dragOffsetX; private double _dragOffsetY; - private readonly List _leds; public SurfaceDeviceViewModel(Device device) { diff --git a/src/Artemis.UI/ViewModels/Screens/ModuleRootViewModel.cs b/src/Artemis.UI/ViewModels/Screens/ModuleRootViewModel.cs index 00eb1e8bf..302f8664f 100644 --- a/src/Artemis.UI/ViewModels/Screens/ModuleRootViewModel.cs +++ b/src/Artemis.UI/ViewModels/Screens/ModuleRootViewModel.cs @@ -1,4 +1,5 @@ using Artemis.Core.Plugins.Abstract; +using Artemis.UI.Ninject.Factories; using Artemis.UI.ViewModels.Controls.ProfileEditor; using Stylet; @@ -6,10 +7,10 @@ namespace Artemis.UI.ViewModels.Screens { public class ModuleRootViewModel : Screen { - public ModuleRootViewModel(Module module) + public ModuleRootViewModel(Module module, IProfileEditorViewModelFactory profileEditorViewModelFactory) { Module = module; - ModuleViewModels = new BindableCollection {new ProfileEditorViewModel(Module)}; + ModuleViewModels = new BindableCollection {profileEditorViewModelFactory.CreateModuleViewModel(Module)}; ModuleViewModels.AddRange(Module.GetViewModels()); } diff --git a/src/Artemis.UI/ViewModels/Screens/SurfaceEditorViewModel.cs b/src/Artemis.UI/ViewModels/Screens/SurfaceEditorViewModel.cs index fd5310a93..ba4543113 100644 --- a/src/Artemis.UI/ViewModels/Screens/SurfaceEditorViewModel.cs +++ b/src/Artemis.UI/ViewModels/Screens/SurfaceEditorViewModel.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; @@ -33,10 +32,11 @@ namespace Artemis.UI.ViewModels.Screens _dialogService = dialogService; } - public RectangleGeometry SelectionRectangle { get; set; } public ObservableCollection Devices { get; set; } public ObservableCollection SurfaceConfigurations { get; set; } - + public RectangleGeometry SelectionRectangle { get; set; } + public PanZoomViewModel PanZoomViewModel { get; set; } + public Surface SelectedSurface { get => _selectedSurface; @@ -47,7 +47,6 @@ namespace Artemis.UI.ViewModels.Screens } } - public PanZoomViewModel PanZoomViewModel { get; set; } public string Title => "Surface Editor"; public Surface CreateSurfaceConfiguration(string name) @@ -189,7 +188,7 @@ namespace Artemis.UI.ViewModels.Screens #endregion - #region RgbDevice selection + #region Selection private MouseDragStatus _mouseDragStatus; private Point _mouseDragStartPoint; diff --git a/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileDeviceView.xaml b/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileDeviceView.xaml index b905ca0c7..e557a3777 100644 --- a/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileDeviceView.xaml +++ b/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileDeviceView.xaml @@ -1,23 +1,34 @@ - + d:DesignHeight="450" d:DesignWidth="800"> - + + - + + + + + + + diff --git a/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileEditorView.xaml b/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileEditorView.xaml index 97243184e..52372dbe8 100644 --- a/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileEditorView.xaml +++ b/src/Artemis.UI/Views/Controls/ProfileEditor/ProfileEditorView.xaml @@ -5,10 +5,168 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Artemis.UI.Views.Controls.ProfileEditor" xmlns:profileEditor="clr-namespace:Artemis.UI.ViewModels.Controls.ProfileEditor" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:s="https://github.com/canton7/Stylet" + xmlns:models="clr-namespace:Artemis.Core.Models.Surface;assembly=Artemis.Core" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance {x:Type profileEditor:ProfileEditorViewModel}}"> - - + + + + + + + + + + + + + + + + + + + + + Profile editor + + The profile defines what colors the LEDs will have. Multiple groups of LEDs are defined into layers. On these layers you can apply effects. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Artemis.UI/Views/Controls/SurfaceEditor/SurfaceDeviceView.xaml b/src/Artemis.UI/Views/Controls/SurfaceEditor/SurfaceDeviceView.xaml index f22c4dc3b..2aa1881cf 100644 --- a/src/Artemis.UI/Views/Controls/SurfaceEditor/SurfaceDeviceView.xaml +++ b/src/Artemis.UI/Views/Controls/SurfaceEditor/SurfaceDeviceView.xaml @@ -19,6 +19,7 @@ + + @@ -51,6 +53,7 @@ +