diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index dd6c12229..1c802d465 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -98,7 +98,7 @@ namespace Artemis.Core.Models.Profile { _layerShape = value; if (Path != null) - _layerShape.CalculateRenderProperties(); + CalculateRenderProperties(); } } @@ -221,7 +221,13 @@ namespace Artemis.Core.Models.Profile internal void CalculateRenderProperties() { if (!Leds.Any()) + { + Rectangle = SKRect.Empty; + AbsoluteRectangle = SKRect.Empty; + Path = new SKPath(); + OnRenderPropertiesUpdated(); return; + } // Determine to top-left and bottom-right var minX = Leds.Min(l => l.AbsoluteRenderRectangle.Left); @@ -237,6 +243,7 @@ namespace Artemis.Core.Models.Profile path.AddRect(artemisLed.AbsoluteRenderRectangle); Path = path; + LayerShape.CalculateRenderProperties(); OnRenderPropertiesUpdated(); } @@ -248,12 +255,18 @@ namespace Artemis.Core.Models.Profile #region Events public event EventHandler RenderPropertiesUpdated; + public event EventHandler ShapePropertiesUpdated; private void OnRenderPropertiesUpdated() { RenderPropertiesUpdated?.Invoke(this, EventArgs.Empty); } + private void OnShapePropertiesUpdated() + { + ShapePropertiesUpdated?.Invoke(this, EventArgs.Empty); + } + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs index 32ccdd6e0..c65552f68 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs @@ -9,8 +9,6 @@ namespace Artemis.Core.Models.Profile.LayerShapes protected LayerShape(Layer layer) { Layer = layer; - - Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated; } protected LayerShape(Layer layer, ShapeEntity shapeEntity) @@ -19,14 +17,8 @@ namespace Artemis.Core.Models.Profile.LayerShapes Anchor = new SKPoint(shapeEntity.Anchor?.X ?? 0, shapeEntity.Anchor?.Y ?? 0); Position = new SKPoint(shapeEntity.Position?.X ?? 0, shapeEntity.Position?.Y ?? 0); Size = new SKSize(shapeEntity.Width, shapeEntity.Height); - - Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated; } - private void LayerOnRenderPropertiesUpdated(object sender, EventArgs e) - { - CalculateRenderProperties(); - } /// /// The layer this shape is attached to diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index c396206d2..bc661c5c7 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -95,9 +95,6 @@ namespace Artemis.Core.Services.Storage folder.ApplyToEntity(); foreach (var layer in profile.GetAllLayers()) layer.ApplyToEntity(); - - if (_surfaceService.ActiveSurface != null) - profile.PopulateLeds(_surfaceService.ActiveSurface); } _profileRepository.Save(profile.ProfileEntity); diff --git a/src/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs b/src/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs index 01ac495f0..a92dddf86 100644 --- a/src/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs +++ b/src/Artemis.Plugins.Devices.Wooting/WootingDeviceProvider.cs @@ -19,10 +19,11 @@ namespace Artemis.Plugins.Devices.Wooting public override void EnablePlugin() { - PathHelper.ResolvingAbsolutePath += (sender, args) => ResolveAbsolutePath(typeof(WootingRGBDevice<>), sender, args); - RGB.NET.Devices.Wooting.WootingDeviceProvider.PossibleX64NativePaths.Add(Path.Combine(PluginInfo.Directory.FullName, "x64", "wooting-rgb-sdk64.dll")); - RGB.NET.Devices.Wooting.WootingDeviceProvider.PossibleX86NativePaths.Add(Path.Combine(PluginInfo.Directory.FullName, "x86", "wooting-rgb-sdk.dll")); - _rgbService.AddDeviceProvider(RgbDeviceProvider); + // Disabled for now because the DLLs aren't on the repo + // PathHelper.ResolvingAbsolutePath += (sender, args) => ResolveAbsolutePath(typeof(WootingRGBDevice<>), sender, args); + // RGB.NET.Devices.Wooting.WootingDeviceProvider.PossibleX64NativePaths.Add(Path.Combine(PluginInfo.Directory.FullName, "x64", "wooting-rgb-sdk64.dll")); + // RGB.NET.Devices.Wooting.WootingDeviceProvider.PossibleX86NativePaths.Add(Path.Combine(PluginInfo.Directory.FullName, "x86", "wooting-rgb-sdk.dll")); + // _rgbService.AddDeviceProvider(RgbDeviceProvider); } public override void DisablePlugin() diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs index cffac4f69..da7eb3b58 100644 --- a/src/Artemis.UI/Bootstrapper.cs +++ b/src/Artemis.UI/Bootstrapper.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using System.Windows; +using System.Windows.Media.Animation; using System.Windows.Threading; using Artemis.Core.Ninject; using Artemis.Core.Services.Interfaces; diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs index 543666734..08a045114 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs @@ -2,21 +2,20 @@ using System.Linq; using System.Windows; using Artemis.Core.Models.Profile; -using Artemis.UI.Behaviors; using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem; using Artemis.UI.Services.Interfaces; using GongSolutions.Wpf.DragDrop; -using Stylet; namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree { public class ProfileTreeViewModel : ProfileEditorPanelViewModel, IDropTarget { - private readonly IProfileEditorService _profileEditorService; private readonly IFolderViewModelFactory _folderViewModelFactory; - private readonly ILayerViewModelFactory _layerViewModelFactory; + private readonly IProfileEditorService _profileEditorService; private TreeItemViewModel _selectedTreeItem; + private bool _updatingTree; + public ProfileTreeViewModel(IProfileEditorService profileEditorService, IFolderViewModelFactory folderViewModelFactory, @@ -24,7 +23,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree { _profileEditorService = profileEditorService; _folderViewModelFactory = folderViewModelFactory; - _layerViewModelFactory = layerViewModelFactory; CreateRootFolderViewModel(); _profileEditorService.SelectedProfileChanged += OnSelectedProfileChanged; @@ -38,6 +36,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree get => _selectedTreeItem; set { + if (_updatingTree) return; _selectedTreeItem = value; _profileEditorService.ChangeSelectedProfileElement(value?.ProfileElement); } @@ -98,6 +97,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree private void CreateRootFolderViewModel() { + _updatingTree = true; var firstChild = _profileEditorService.SelectedProfile?.Children?.FirstOrDefault(); if (!(firstChild is Folder folder)) { @@ -106,6 +106,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree } RootFolder = _folderViewModelFactory.Create(folder); + _updatingTree = false; } private static DragDropType GetDragDropType(IDropInfo dropInfo) @@ -141,32 +142,26 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree private void OnSelectedElementChanged(object sender, EventArgs e) { - var vms = RootFolder?.GetAllChildren(); + if (_profileEditorService.SelectedProfileElement == SelectedTreeItem?.ProfileElement) + return; - // Don't set it using the setter or that will trigger the event again - _selectedTreeItem = vms?.FirstOrDefault(vm => vm.ProfileElement == _profileEditorService.SelectedProfileElement); - - if (_selectedTreeItem == null && _profileEditorService.SelectedProfileElement != null) + if (RootFolder == null) { - var parent = vms?.FirstOrDefault(vm => vm.ProfileElement == _profileEditorService.SelectedProfileElement.Parent); - if (parent == null) - { - // Eh.. we did our best.. start over - CreateRootFolderViewModel(); - return; - } - - // Create a new TreeItemViewModel for the new ProfileElement - TreeItemViewModel treeItemViewModel; - if (_profileEditorService.SelectedProfileElement is Folder folder) - treeItemViewModel = _folderViewModelFactory.Create(parent, folder); - else - treeItemViewModel = _layerViewModelFactory.Create(parent, (Layer) _profileEditorService.SelectedProfileElement); - parent.AddExistingElement(treeItemViewModel); - _selectedTreeItem = treeItemViewModel; + CreateRootFolderViewModel(); + return; } - NotifyOfPropertyChange(() => SelectedTreeItem); + _updatingTree = true; + RootFolder.UpdateProfileElements(); + _updatingTree = false; + if (_profileEditorService.SelectedProfileElement == null) + SelectedTreeItem = null; + else + { + var vms = RootFolder.GetAllChildren(); + vms.Add(RootFolder); + SelectedTreeItem = vms.FirstOrDefault(vm => vm.ProfileElement == _profileEditorService.SelectedProfileElement); + } } private void OnSelectedProfileChanged(object sender, EventArgs e) diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs index a6799d99a..37bc1aa93 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs @@ -21,7 +21,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem ProfileElement profileElement, IProfileEditorService profileEditorService, IDialogService dialogService, - IFolderViewModelFactory folderViewModelFactory, + IFolderViewModelFactory folderViewModelFactory, ILayerViewModelFactory layerViewModelFactory) { _profileEditorService = profileEditorService; @@ -155,7 +155,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem _profileEditorService.UpdateSelectedProfile(); } - private void UpdateProfileElements() + public void UpdateProfileElements() { // Order the children var vmsList = Children.OrderBy(v => v.ProfileElement.Order).ToList(); @@ -175,13 +175,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem { existing = Children.FirstOrDefault(p => p is FolderViewModel vm && vm.ProfileElement == folder); if (existing == null) - Children.Add(_folderViewModelFactory.Create((FolderViewModel) this, folder)); + Children.Add(_folderViewModelFactory.Create((FolderViewModel)this, folder)); } else if (profileElement is Layer layer) { existing = Children.FirstOrDefault(p => p is LayerViewModel vm && vm.ProfileElement == layer); if (existing == null) - Children.Add(_layerViewModelFactory.Create((FolderViewModel) this, layer)); + Children.Add(_layerViewModelFactory.Create((FolderViewModel)this, layer)); } existing?.UpdateProfileElements(); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerView.xaml index 951ee9dcf..ef712b146 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerView.xaml @@ -1,14 +1,43 @@  - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs index 78430d528..93d0609fd 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs @@ -3,9 +3,11 @@ using System.Linq; using System.Windows; using System.Windows.Media; using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Profile.LayerShapes; using Artemis.Core.Models.Surface; using Artemis.UI.Extensions; using RGB.NET.Core; +using Rectangle = Artemis.Core.Models.Profile.LayerShapes.Rectangle; namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization { @@ -15,18 +17,35 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization { Layer = layer; - CreateLayerGeometry(); - Layer.RenderPropertiesUpdated += (sender, args) => CreateLayerGeometry(); + Update(); + Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated; } public Layer Layer { get; } public Geometry LayerGeometry { get; set; } + public Geometry OpacityGeometry { get; set; } + public Geometry ShapeGeometry { get; set; } + public Rect ViewportRectangle { get; set; } + + private void Update() + { + CreateLayerGeometry(); + CreateShapeGeometry(); + CreateViewportRectangle(); + } private void CreateLayerGeometry() { + if (!Layer.Leds.Any()) + { + LayerGeometry = Geometry.Empty; + OpacityGeometry = Geometry.Empty; + ViewportRectangle = Rect.Empty; + return; + } + var layerGeometry = Geometry.Empty; - foreach (var led in Layer.Leds) { Geometry geometry; @@ -34,15 +53,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization { case Shape.Custom: if (led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad) - geometry = CreateCustomGeometry(led, 2.0); + geometry = CreateCustomGeometry(led, 2); else - geometry = CreateCustomGeometry(led, 1.0); + geometry = CreateCustomGeometry(led, 1); break; case Shape.Rectangle: - if (led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad) - geometry = CreateKeyCapGeometry(led); - else - geometry = CreateRectangleGeometry(led); + geometry = CreateRectangleGeometry(led); break; case Shape.Circle: geometry = CreateCircleGeometry(led); @@ -54,27 +70,88 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization layerGeometry = Geometry.Combine(layerGeometry, geometry, GeometryCombineMode.Union, null, 5, ToleranceType.Absolute); } + var opacityGeometry = Geometry.Combine(Geometry.Empty, layerGeometry, GeometryCombineMode.Exclude, new TranslateTransform()); + layerGeometry.Freeze(); + opacityGeometry.Freeze(); LayerGeometry = layerGeometry; - LayerGeometry.Freeze(); + OpacityGeometry = opacityGeometry; + } + + private void CreateShapeGeometry() + { + if (Layer.LayerShape == null || !Layer.Leds.Any()) + { + ShapeGeometry = Geometry.Empty; + return; + } + + var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); + var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); + var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; + var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; + + var rect = new Rect( + x + width * Layer.LayerShape.Position.X, + y + height * Layer.LayerShape.Position.Y, + width * Layer.LayerShape.Size.Width, + height * Layer.LayerShape.Size.Height + ); + var shapeGeometry = Geometry.Empty; + switch (Layer.LayerShape) + { + case Ellipse _: + shapeGeometry = new EllipseGeometry(rect); + break; + case Fill _: + shapeGeometry = LayerGeometry; + break; + case Polygon _: + // TODO + shapeGeometry = new RectangleGeometry(rect); + break; + case Rectangle _: + shapeGeometry = new RectangleGeometry(rect); + break; + } + + shapeGeometry.Freeze(); + ShapeGeometry = shapeGeometry; + } + + + private void CreateViewportRectangle() + { + if (!Layer.Leds.Any()) + { + ViewportRectangle = Rect.Empty; + return; + } + + var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); + var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); + var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; + var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; + ViewportRectangle = new Rect(x - x * Layer.LayerShape.Position.X, y - y * Layer.LayerShape.Position.Y, width, height); } private Geometry CreateRectangleGeometry(ArtemisLed led) { - return new RectangleGeometry(led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1)); + var rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1); + rect.Inflate(1, 1); + return new RectangleGeometry(rect); } private Geometry CreateCircleGeometry(ArtemisLed led) { - return new EllipseGeometry(led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1)); - } - - private Geometry CreateKeyCapGeometry(ArtemisLed led) - { - return new RectangleGeometry(led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1), 1.6, 1.6); + var rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1); + rect.Inflate(1, 1); + return new EllipseGeometry(rect); } private Geometry CreateCustomGeometry(ArtemisLed led, double deflateAmount) { + var rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1); + rect.Inflate(1, 1); try { var geometry = Geometry.Combine( @@ -85,11 +162,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization { Children = new TransformCollection { - new ScaleTransform(led.RgbLed.ActualSize.Width - deflateAmount, led.RgbLed.ActualSize.Height - deflateAmount), - new TranslateTransform(deflateAmount / 2, deflateAmount / 2) + new ScaleTransform(rect.Width, rect.Height), + new TranslateTransform(rect.X, rect.Y) } } ); + return geometry; } catch (Exception) @@ -97,5 +175,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization return CreateRectangleGeometry(led); } } + + private void LayerOnRenderPropertiesUpdated(object sender, EventArgs e) + { + Update(); + } + + public void Dispose() + { + Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated; + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLedView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLedView.xaml index 748391ed3..1f25a5673 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLedView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLedView.xaml @@ -49,7 +49,7 @@ - +