Profile editor - Clarified which LEDs are part of the selected layer

Profile editor - Re-implemented LED dimming
Profile editor - Hide layer bounds of unselected layers
Profile editor - Dim layer shapes of unselected layers (option to hide them NYI)
Profile editor - Disable edit tool when no layer is selected or the selected layer has no LEDs
Gradient picker - Fixed the preview being at a slight angle
Color brush - Fixed the gradient being at a 45 degree angle
Settings - Added color scheme option to manually switch between light and dark
This commit is contained in:
SpoinkyNL committed 2020-04-22 19:55:23 +02:00
1 parent 341eb99489
commit 8bc33de401
17 files changed
+337 -99

No files matched your search

@@ -121,7 +121,7 @@
</DockPanel>
<!-- Properties tree -->
<materialDesign:DialogHost Identifier="PropertyTreeDialogHost" CloseOnClickAway="True" Grid.Row="1">
<materialDesign:DialogHost Identifier="PropertyTreeDialogHost" DialogTheme="Inherit" CloseOnClickAway="True" Grid.Row="1">
<ScrollViewer x:Name="PropertyTreeScrollViewer"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
@@ -10,22 +10,42 @@
d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:ProfileLayerViewModel}}">
<UserControl.Resources>
<Style TargetType="{x:Type Canvas}" x:Key="SelectedStyle">
<Style TargetType="{x:Type Path}" x:Key="SelectedShapeStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Opacity)" To="0.8"
Duration="0:0:0.25" />
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="Gray" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="(Path.StrokeThickness)" To="0.5" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Opacity)" To="1"
Duration="0:0:0.25" />
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="{StaticResource Accent700}" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="(Path.StrokeThickness)" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Path}" x:Key="SelectedLayerStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Path.Opacity)" To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Path.Opacity)" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
@@ -33,29 +53,23 @@
</Style.Triggers>
</Style>
</UserControl.Resources>
<Canvas Style="{StaticResource SelectedStyle}">
<!-- The part of the layer's shape that is inside the layer -->
<Path Data="{Binding ShapeGeometry, Mode=OneWay}">
<Path.Fill>
<SolidColorBrush Color="{StaticResource Accent700}" Opacity="0.15" />
</Path.Fill>
<Canvas>
<!-- The part of the layer's shape that falls outside the layer -->
<Path Data="{Binding ShapeGeometry, Mode=OneWay}"
StrokeThickness="0.5"
Style="{StaticResource SelectedShapeStyle}">
<Path.Stroke>
<SolidColorBrush Color="{StaticResource Accent700}" />
<SolidColorBrush Color="{StaticResource Primary700}" />
</Path.Stroke>
</Path>
<Rectangle Width="{Binding LayerRect.Width}"
Height="{Binding LayerRect.Height}"
Margin="{Binding LayerRectMargin}"
StrokeThickness="1"
StrokeDashArray="4 2"
StrokeLineJoin="Round"
x:Name="LayerPath">
<Rectangle.Stroke>
<SolidColorBrush Color="{StaticResource Accent400}" />
</Rectangle.Stroke>
</Rectangle>
<Path Data="{Binding LayerGeometry, Mode=OneWay}"
ClipToBounds="False"
Stroke="#A7A7A7"
StrokeThickness="1"
StrokeLineJoin="Round"
x:Name="LayerPath"
Style="{StaticResource SelectedLayerStyle}">
</Path>
</Canvas>
</UserControl>
@@ -1,15 +0,0 @@
using System.Windows.Controls;
namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
/// <summary>
/// Interaction logic for ProfileLayerView.xaml
/// </summary>
public partial class ProfileLayerView : UserControl
{
public ProfileLayerView()
{
InitializeComponent();
}
}
}
@@ -1,11 +1,17 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.LayerShapes;
using Artemis.Core.Models.Surface;
using Artemis.UI.Extensions;
using Artemis.UI.Services.Interfaces;
using RGB.NET.Core;
using Stylet;
using Rectangle = Artemis.Core.Models.Profile.LayerShapes.Rectangle;
namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
@@ -28,10 +34,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
}
public Layer Layer { get; }
public Rect LayerRect { get; set; }
public Thickness LayerRectMargin => LayerRect == Rect.Empty ? new Thickness() : new Thickness(LayerRect.Left, LayerRect.Top, 0, 0);
public bool IsSelected { get; set; }
public Geometry LayerGeometry { get; set; }
public Geometry OpacityGeometry { get; set; }
public Geometry ShapeGeometry { get; set; }
public RenderTargetBitmap LayerGeometryBitmap { get; set; }
public Rect ViewportRectangle { get; set; }
public Thickness LayerPosition => new Thickness(ViewportRectangle.Left, ViewportRectangle.Top, 0, 0);
public bool IsSelected { get; set; }
public void Dispose()
{
@@ -44,12 +54,68 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private void Update()
{
IsSelected = _profileEditorService.SelectedProfileElement == Layer;
if (!Layer.Leds.Any() || Layer.LayerShape == null)
LayerRect = Rect.Empty;
else
LayerRect = _layerEditorService.GetLayerBounds(Layer);
CreateLayerGeometry();
CreateShapeGeometry();
CreateViewportRectangle();
}
private void CreateLayerGeometry()
{
if (!Layer.Leds.Any())
{
LayerGeometry = Geometry.Empty;
OpacityGeometry = Geometry.Empty;
ViewportRectangle = Rect.Empty;
return;
}
var group = new GeometryGroup();
group.FillRule = FillRule.Nonzero;
foreach (var led in Layer.Leds)
{
Geometry geometry;
switch (led.RgbLed.Shape)
{
case Shape.Custom:
if (led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
geometry = CreateCustomGeometry(led, 2);
else
geometry = CreateCustomGeometry(led, 1);
break;
case Shape.Rectangle:
geometry = CreateRectangleGeometry(led);
break;
case Shape.Circle:
geometry = CreateCircleGeometry(led);
break;
default:
throw new ArgumentOutOfRangeException();
}
group.Children.Add(geometry);
}
var layerGeometry = group.GetOutlinedPathGeometry();
var opacityGeometry = Geometry.Combine(Geometry.Empty, layerGeometry, GeometryCombineMode.Exclude, new TranslateTransform());
LayerGeometry = layerGeometry;
OpacityGeometry = opacityGeometry;
// Render the store as a bitmap
var drawingImage = new DrawingImage(new GeometryDrawing(new SolidColorBrush(Colors.Black), null, LayerGeometry));
var image = new Image {Source = drawingImage};
var bitmap = new RenderTargetBitmap(
(int) (LayerGeometry.Bounds.Width * 2.5),
(int) (LayerGeometry.Bounds.Height * 2.5),
96,
96,
PixelFormats.Pbgra32
);
image.Arrange(new Rect(0, 0, bitmap.Width, bitmap.Height));
bitmap.Render(image);
bitmap.Freeze();
LayerGeometryBitmap = bitmap;
}
private void CreateShapeGeometry()
@@ -79,6 +145,59 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
});
}
private void CreateViewportRectangle()
{
if (!Layer.Leds.Any() || Layer.LayerShape == null)
{
ViewportRectangle = Rect.Empty;
return;
}
ViewportRectangle = _layerEditorService.GetLayerBounds(Layer);
}
private Geometry CreateRectangleGeometry(ArtemisLed led)
{
var rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1);
rect.Inflate(1, 1);
return new RectangleGeometry(rect);
}
private Geometry CreateCircleGeometry(ArtemisLed led)
{
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(
Geometry.Empty,
Geometry.Parse(led.RgbLed.ShapeData),
GeometryCombineMode.Union,
new TransformGroup
{
Children = new TransformCollection
{
new ScaleTransform(rect.Width, rect.Height),
new TranslateTransform(rect.X, rect.Y)
}
}
);
return geometry;
}
catch (Exception)
{
return CreateRectangleGeometry(led);
}
}
#region Event handlers
private void LayerOnRenderPropertiesUpdated(object sender, EventArgs e)
@@ -98,7 +217,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private void ProfileEditorServiceOnProfilePreviewUpdated(object sender, EventArgs e)
{
Update();
CreateShapeGeometry();
CreateViewportRectangle();
}
#endregion
@@ -57,7 +57,7 @@
<ListBoxItem ToolTip="Pan over different parts of the surface">
<materialDesign:PackIcon Kind="HandLeft" />
</ListBoxItem>
<ListBoxItem ToolTip="Edit shape in layer">
<ListBoxItem ToolTip="Edit shape in layer" IsEnabled="{Binding CanSelectEditTool}">
<materialDesign:PackIcon Kind="Edit" />
</ListBoxItem>
<ListBoxItem ToolTip="Change layer selection (hold SHIFT to add to existing selection)">
@@ -122,7 +122,9 @@
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:DeviceVisualizer Device="{Binding}" ShowColors="True"/>
<controls:DeviceVisualizer Device="{Binding}"
ShowColors="True"
HighlightedLeds="{Binding DataContext.HighlightedLeds, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=OneWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
@@ -158,11 +160,11 @@
<StackPanel ZIndex="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10">
<materialDesign:Card Padding="8">
<StackPanel Orientation="Horizontal">
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding HighlightSelectedLayer.Value}">
Dim LEDs outside selected layer
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding OnlyShowSelectedShape.Value}" IsEnabled="False">
Only show shape of selected layer (NYI)
</CheckBox>
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" Margin="10 0 0 0" IsChecked="{Binding PauseRenderingOnFocusLoss.Value}">
Pause visualization on focus loss
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" Margin="10 0 0 0" IsChecked="{Binding HighlightSelectedLayer.Value}">
Highlight LEDs of selected layer
</CheckBox>
</StackPanel>
</materialDesign:Card>
@@ -51,7 +51,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
CanvasViewModels = new BindableCollection<CanvasViewModel>();
Devices = new BindableCollection<ArtemisDevice>();
DimmedLeds = new BindableCollection<ArtemisLed>();
HighlightedLeds = new BindableCollection<ArtemisLed>();
SelectedLeds = new BindableCollection<ArtemisLed>();
});
@@ -64,21 +64,22 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
public bool IsInitializing { get; private set; }
public bool CanSelectEditTool { get; set; }
public PanZoomViewModel PanZoomViewModel { get; set; }
public BindableCollection<CanvasViewModel> CanvasViewModels { get; set; }
public BindableCollection<ArtemisDevice> Devices { get; set; }
public BindableCollection<ArtemisLed> DimmedLeds { get; set; }
public BindableCollection<ArtemisLed> HighlightedLeds { get; set; }
public BindableCollection<ArtemisLed> SelectedLeds { get; set; }
public PluginSetting<bool> OnlyShowSelectedShape { get; set; }
public PluginSetting<bool> HighlightSelectedLayer { get; set; }
public PluginSetting<bool> PauseRenderingOnFocusLoss { get; set; }
public VisualizationToolViewModel ActiveToolViewModel
{
get => _activeToolViewModel;
set
private set
{
// Remove the tool from the canvas
if (_activeToolViewModel != null)
@@ -120,8 +121,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
protected override void OnInitialActivate()
{
OnlyShowSelectedShape = _settingsService.GetSetting("ProfileEditor.OnlyShowSelectedShape", true);
HighlightSelectedLayer = _settingsService.GetSetting("ProfileEditor.HighlightSelectedLayer", true);
PauseRenderingOnFocusLoss = _settingsService.GetSetting("ProfileEditor.PauseRenderingOnFocusLoss", true);
HighlightSelectedLayer.SettingChanged += HighlightSelectedLayerOnSettingChanged;
_surfaceService.ActiveSurfaceConfigurationSelected += OnActiveSurfaceConfigurationSelected;
@@ -131,7 +132,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
base.OnInitialActivate();
}
protected override void OnClose()
{
HighlightSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged;
@@ -140,8 +141,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
OnlyShowSelectedShape.Save();
HighlightSelectedLayer.Save();
PauseRenderingOnFocusLoss.Save();
base.OnClose();
}
@@ -183,9 +184,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private void UpdateLedsDimStatus()
{
DimmedLeds.Clear();
HighlightedLeds.Clear();
if (HighlightSelectedLayer.Value && _profileEditorService.SelectedProfileElement is Layer layer)
DimmedLeds.AddRange(layer.Leds);
HighlightedLeds.AddRange(layer.Leds);
}
#region Buttons
@@ -300,14 +301,38 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private void OnProfileElementSelected(object sender, EventArgs e)
{
UpdateLedsDimStatus();
CanApplyToLayer = _profileEditorService.SelectedProfileElement is Layer;
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 OnSelectedProfileElementUpdated(object sender, EventArgs e)
{
ApplyActiveProfile();
UpdateLedsDimStatus();
CanApplyToLayer = _profileEditorService.SelectedProfileElement is Layer;
if (_profileEditorService.SelectedProfileElement is Layer layer)
{
CanApplyToLayer = true;
CanSelectEditTool = layer.Leds.Any();
}
else
{
CanApplyToLayer = false;
CanSelectEditTool = false;
}
if (CanSelectEditTool == false && ActiveToolIndex == 1)
ActivateToolByIndex(2);
}
public void Handle(MainWindowFocusChangedEvent message)