Implemented layer LED assignment

Added a centralised ProfileEditorSurface for communication between VMs
Prefixed Surface, Device and Led with Artemis to differentiate them better
This commit is contained in:
Robert committed 2019-11-29 17:37:22 +01:00
1 parent c7a5532c34
commit 16decaae42
50 files changed
+556 -662

No files matched your search

@@ -9,17 +9,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
public class ProfileDeviceViewModel : PropertyChangedBase
{
public ProfileDeviceViewModel(Device device)
public ProfileDeviceViewModel(ArtemisDevice device)
{
Device = device;
Leds = new ObservableCollection<ProfileLedViewModel>();
if (Device.RgbDevice != null)
Task.Run(AddLedsAsync);
Task.Run(AddLedsAsync);
}
public ObservableCollection<ProfileLedViewModel> Leds { get; set; }
public Device Device { get; set; }
public ArtemisDevice Device { get; set; }
public bool AddedLeds { get; private set; }
public double X
@@ -52,7 +51,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private async Task AddLedsAsync()
{
var index = 0;
foreach (var led in Device.RgbDevice.ToList())
foreach (var led in Device.Leds.ToList())
{
Execute.OnUIThreadSync(() => Leds.Add(new ProfileLedViewModel(led)));
if (index % 5 == 0)
@@ -1,57 +1,60 @@
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:visualization="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.Visualization"
xmlns:surfaceEditor="clr-namespace:Artemis.UI.Screens.SurfaceEditor.Visualization"
x:Class="Artemis.UI.Screens.Module.ProfileEditor.Visualization.ProfileLedView"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance {x:Type visualization:ProfileLedViewModel}}"
d:DesignHeight="25" d:DesignWidth="25">
<UserControl x:Class="Artemis.UI.Screens.Module.ProfileEditor.Visualization.ProfileLedView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:visualization="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.Visualization"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance {x:Type visualization:ProfileLedViewModel}}"
d:DesignHeight="25" d:DesignWidth="25">
<UserControl.Resources>
<converters:NullToImageConverter x:Key="NullToImageConverter" />
<Style TargetType="{x:Type Path}" x:Key="DimStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDimmed}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Shape.Opacity)" To="0.2" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Shape.Opacity)" To="1" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Canvas Width="{Binding Led.ActualSize.Width, Mode=OneWay}" Height="{Binding Led.ActualSize.Height, Mode=OneWay}">
<Canvas Width="{Binding Led.RgbLed.ActualSize.Width, Mode=OneWay}" Height="{Binding Led.RgbLed.ActualSize.Height, Mode=OneWay}">
<Canvas.Background>
<ImageBrush AlignmentX="Center" AlignmentY="Center" Stretch="Fill"
ImageSource="{Binding Led.Image, Converter={StaticResource NullToImageConverter}, Mode=OneWay}" />
ImageSource="{Binding Led.RgbLed.Image, Converter={StaticResource NullToImageConverter}, Mode=OneWay}" />
</Canvas.Background>
<Path Data="{Binding DisplayGeometry, Mode=OneWay}" ClipToBounds="False">
<Path Data="{Binding DisplayGeometry, Mode=OneWay}" ClipToBounds="False" Style="{StaticResource DimStyle}">
<Path.Fill>
<SolidColorBrush Color="{Binding DisplayColor, Mode=OneWay}" Opacity="0.333" />
</Path.Fill>
</Path>
<Path Data="{Binding StrokeGeometry, Mode=OneWay}" ClipToBounds="False">
<Path Data="{Binding StrokeGeometry, Mode=OneWay}" ClipToBounds="False" Style="{StaticResource DimStyle}">
<Path.Fill>
<SolidColorBrush Color="{Binding DisplayColor, Mode=OneWay}" />
</Path.Fill>
</Path>
<!-- Selection -->
<Path Data="{Binding DisplayGeometry, Mode=OneWay}" ClipToBounds="False" StrokeThickness="2">
<Path.Style>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectionStatus}" Value="{x:Static surfaceEditor:SelectionStatus.Hover}">
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="ToSelected" />
<BeginStoryboard x:Name="ToHover">
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="{StaticResource IdealForegroundColor}" Duration="0:0:0.25" />
<ColorAnimation
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)"
To="{StaticResource IdealForegroundColor}" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding SelectionStatus}" Value="{x:Static surfaceEditor:SelectionStatus.Selected}">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="ToHover" />
<BeginStoryboard x:Name="ToSelected">
<Storyboard>
<ColorAnimation
@@ -63,27 +66,15 @@
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding SelectionStatus}" Value="{x:Static surfaceEditor:SelectionStatus.None}">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.25" >
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ColorAnimation
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="Transparent" Duration="0:0:0.25" />
<ColorAnimation
Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)"
To="Transparent" Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
@@ -1,8 +1,8 @@
using System;
using System.Windows;
using System.Windows.Media;
using Artemis.Core.Models.Surface;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.SurfaceEditor.Visualization;
using RGB.NET.Core;
using Stylet;
using Color = System.Windows.Media.Color;
@@ -11,21 +11,23 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
public class ProfileLedViewModel : PropertyChangedBase
{
public ProfileLedViewModel(Led led)
public ProfileLedViewModel(ArtemisLed led)
{
Led = led;
// Don't want ActualLocation here since rotation is done in XAML
X = Led.Location.X * Led.Device.Scale.Horizontal;
Y = Led.Location.Y * Led.Device.Scale.Vertical;
Width = Led.ActualSize.Width;
Height = Led.ActualSize.Height;
X = led.RgbLed.Location.X * led.RgbLed.Device.Scale.Horizontal;
Y = led.RgbLed.Location.Y * led.RgbLed.Device.Scale.Vertical;
Width = led.RgbLed.ActualSize.Width;
Height = led.RgbLed.ActualSize.Height;
Execute.PostToUIThread(CreateLedGeometry);
}
public Led Led { get; }
public SelectionStatus SelectionStatus { get; set; }
public ArtemisLed Led { get; }
public bool IsSelected { get; set; }
public bool IsDimmed { get; set; }
public double X { get; }
public double Y { get; }
@@ -36,18 +38,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
public Geometry StrokeGeometry { get; private set; }
public Color DisplayColor { get; private set; }
private void CreateLedGeometry()
{
switch (Led.Shape)
switch (Led.RgbLed.Shape)
{
case Shape.Custom:
if (Led.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
CreateCustomGeometry(2.0);
else
CreateCustomGeometry(1.0);
break;
case Shape.Rectangle:
if (Led.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
CreateKeyCapGeometry();
else
CreateRectangleGeometry();
@@ -86,7 +89,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
DisplayGeometry = Geometry.Combine(
Geometry.Empty,
Geometry.Parse(Led.ShapeData),
Geometry.Parse(Led.RgbLed.ShapeData),
GeometryCombineMode.Union,
new TransformGroup
{
@@ -106,7 +109,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
public void Update()
{
var newColor = Led.Color.ToMediaColor();
var newColor = Led.RgbLed.Color.ToMediaColor();
Execute.PostToUIThread(() =>
{
if (!DisplayColor.Equals(newColor))
@@ -5,6 +5,7 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Artemis.Core.Events;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services;
@@ -13,21 +14,26 @@ using Artemis.UI.Events;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.Shared;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.SurfaceEditor.Visualization;
using Artemis.UI.Services.Interfaces;
using RGB.NET.Core;
using Stylet;
using Point = System.Windows.Point;
namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
public class ProfileViewModel : ProfileEditorPanelViewModel, IHandle<ProfileEditorSelectedElementChanged>, IHandle<MainWindowFocusChanged>
public class ProfileViewModel : ProfileEditorPanelViewModel, IHandle<MainWindowFocusChangedEvent>
{
private readonly IProfileEditorService _profileEditorService;
private readonly ISettingsService _settingsService;
private readonly ISurfaceService _surfaceService;
private TimerUpdateTrigger _updateTrigger;
public ProfileViewModel(ISurfaceService surfaceService, ISettingsService settingsService, IEventAggregator eventAggregator)
public ProfileViewModel(IProfileEditorService profileEditorService,
ISurfaceService surfaceService,
ISettingsService settingsService,
IEventAggregator eventAggregator)
{
_profileEditorService = profileEditorService;
_surfaceService = surfaceService;
_settingsService = settingsService;
Devices = new ObservableCollection<ProfileDeviceViewModel>();
@@ -42,6 +48,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
ApplySurfaceConfiguration(surfaceService.ActiveSurface);
CreateUpdateTrigger();
_profileEditorService.SelectedProfileElementChanged += OnSelectedProfileElementChanged;
_profileEditorService.SelectedProfileElementUpdated += OnSelectedProfileElementChanged;
eventAggregator.Subscribe(this);
}
@@ -71,7 +79,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
ApplySurfaceConfiguration(e.Surface);
}
private void ApplySurfaceConfiguration(Surface surface)
private void ApplySurfaceConfiguration(ArtemisSurface surface)
{
// Make sure all devices have an up-to-date VM
foreach (var surfaceDeviceConfiguration in surface.Devices)
@@ -120,11 +128,27 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
}
}
private void UpdateLedsDimStatus()
{
if (HighlightSelectedLayer.Value && _profileEditorService.SelectedProfileElement is Layer layer)
{
foreach (var led in Devices.SelectMany(d => d.Leds))
led.IsDimmed = !layer.Leds.Contains(led.Led);
}
else
{
foreach (var led in Devices.SelectMany(d => d.Leds))
led.IsDimmed = false;
}
}
protected override void OnActivate()
{
HighlightSelectedLayer = _settingsService.GetSetting("ProfileEditor.HighlightSelectedLayer", true);
PauseRenderingOnFocusLoss = _settingsService.GetSetting("ProfileEditor.PauseRenderingOnFocusLoss", true);
HighlightSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged;
_updateTrigger.Start();
base.OnActivate();
}
@@ -138,6 +162,45 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
base.OnDeactivate();
}
#region Context menu actions
public bool CanApplyToLayer { get; set; }
public void CreateLayer()
{
}
public void ApplyToLayer()
{
if (!(_profileEditorService.SelectedProfileElement is Layer layer))
return;
layer.ClearLeds();
layer.AddLeds(Devices.SelectMany(d => d.Leds).Where(vm => vm.IsSelected).Select(vm => vm.Led));
_profileEditorService.UpdateSelectedProfileElement();
}
public void SelectAll()
{
foreach (var ledVm in Devices.SelectMany(d => d.Leds))
ledVm.IsSelected = true;
}
public void InverseSelection()
{
foreach (var ledVm in Devices.SelectMany(d => d.Leds))
ledVm.IsSelected = !ledVm.IsSelected;
}
public void ClearSelection()
{
foreach (var ledVm in Devices.SelectMany(d => d.Leds))
ledVm.IsSelected = false;
}
#endregion
#region Selection
private MouseDragStatus _mouseDragStatus;
@@ -188,10 +251,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
foreach (var profileLedViewModel in device.Leds)
{
if (PanZoomViewModel.TransformContainingRect(profileLedViewModel.Led.AbsoluteLedRectangle.ToWindowsRect(1)).IntersectsWith(selectedRect))
profileLedViewModel.SelectionStatus = SelectionStatus.Selected;
if (PanZoomViewModel.TransformContainingRect(profileLedViewModel.Led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1)).IntersectsWith(selectedRect))
profileLedViewModel.IsSelected = true;
else if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
profileLedViewModel.SelectionStatus = SelectionStatus.None;
profileLedViewModel.IsSelected = false;
}
}
@@ -210,10 +273,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
foreach (var profileLedViewModel in device.Leds)
{
if (PanZoomViewModel.TransformContainingRect(profileLedViewModel.Led.AbsoluteLedRectangle.ToWindowsRect(1)).IntersectsWith(selectedRect))
profileLedViewModel.SelectionStatus = SelectionStatus.Selected;
if (PanZoomViewModel.TransformContainingRect(profileLedViewModel.Led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1)).IntersectsWith(selectedRect))
profileLedViewModel.IsSelected = true;
else if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
profileLedViewModel.SelectionStatus = SelectionStatus.None;
profileLedViewModel.IsSelected = false;
}
}
}
@@ -259,17 +322,24 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
#endregion
#region EventAggregator handlers
#region Event handlers
public void Handle(ProfileEditorSelectedElementChanged message)
private void HighlightSelectedLayerOnSettingChanged(object sender, EventArgs e)
{
if (HighlightSelectedLayer.Value)
{
}
UpdateLedsDimStatus();
}
public void Handle(MainWindowFocusChanged message)
private void OnSelectedProfileElementChanged(object sender, EventArgs e)
{
UpdateLedsDimStatus();
CanApplyToLayer = _profileEditorService.SelectedProfileElement is Layer;
}
public void Handle(MainWindowFocusChangedEvent message)
{
if (PauseRenderingOnFocusLoss == null || ScreenState != ScreenState.Active)
return;
if (PauseRenderingOnFocusLoss.Value && !message.IsFocused)
_updateTrigger.Stop();
else if (PauseRenderingOnFocusLoss.Value && message.IsFocused)