mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
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:
1 parent
368aebe1d0
commit
21600a946c
17 files changed
+337
-99
No files matched your search
+1
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+129
-9
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user