Profile editor - Moved files

This commit is contained in:
SpoinkyNL committed 2020-08-15 17:53:12 +02:00
1 parent f359256ede
commit 75e2fb1689
86 files changed
+199 -215

No files matched your search

@@ -0,0 +1,30 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Visualization.Tools.EditToolView"
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:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.Visualization.Tools"
xmlns:userControls="clr-namespace:Artemis.UI.Screens.ProfileEditor.Visualization.UserControls"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:tools="clr-namespace:Artemis.UI.Screens.ProfileEditor.Visualization.Tools"
xmlns:userControls1="clr-namespace:Artemis.UI.Screens.ProfileEditor.Visualization.UserControls"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type tools:EditToolViewModel}}">
<Canvas UseLayoutRounding="False">
<userControls1:LayerShapeControl Zoom="{Binding ProfileViewModel.PanZoomViewModel.Zoom}"
ShapePath="{Binding ShapePath}"
ShapeAnchor="{Binding ShapeAnchor}"
ShapeGeometry="{Binding ShapeGeometry}"
RotateMouseDown="{s:Action RotateMouseDown}"
RotateMouseUp="{s:Action RotateMouseUp}"
RotateMouseMove="{s:Action RotateMouseMove}"
ResizeMouseDown="{s:Action ResizeMouseDown}"
ResizeMouseUp="{s:Action ResizeMouseUp}"
ResizeMouseMove="{s:Action ResizeMouseMove}"
MoveMouseDown="{s:Action MoveMouseDown}"
MoveMouseUp="{s:Action MoveMouseUp}"
MoveMouseMove="{s:Action MoveMouseMove}" />
</Canvas>
</UserControl>
@@ -0,0 +1,420 @@
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Artemis.Core.Models.Profile;
using Artemis.UI.Events;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services.Interfaces;
using SkiaSharp;
using SkiaSharp.Views.WPF;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
public class EditToolViewModel : VisualizationToolViewModel
{
private readonly ILayerEditorService _layerEditorService;
private SKPoint _dragOffset;
private SKPoint _dragStart;
private SKPoint _topLeft;
private SKPath _shapePath;
private SKPoint _shapeAnchor;
private RectangleGeometry _shapeGeometry;
public EditToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService)
: base(profileViewModel, profileEditorService)
{
_layerEditorService = layerEditorService;
Cursor = Cursors.Arrow;
Update();
profileEditorService.ProfileSelected += (sender, args) => Update();
profileEditorService.ProfileElementSelected += (sender, args) => Update();
profileEditorService.SelectedProfileElementUpdated += (sender, args) => Update();
profileEditorService.ProfilePreviewUpdated += (sender, args) => Update();
}
public SKPath ShapePath
{
get => _shapePath;
set => SetAndNotify(ref _shapePath, value);
}
public SKPoint ShapeAnchor
{
get => _shapeAnchor;
set => SetAndNotify(ref _shapeAnchor, value);
}
public RectangleGeometry ShapeGeometry
{
get => _shapeGeometry;
set => SetAndNotify(ref _shapeGeometry, value);
}
private void Update()
{
if (!(ProfileEditorService.SelectedProfileElement is Layer layer))
return;
ShapePath = _layerEditorService.GetLayerPath(layer, true, true, true);
ShapeAnchor = _layerEditorService.GetLayerAnchorPosition(layer).ToSKPoint();
Execute.PostToUIThread(() =>
{
var shapeGeometry = new RectangleGeometry(_layerEditorService.GetLayerBounds(layer))
{
Transform = _layerEditorService.GetLayerTransformGroup(layer)
};
shapeGeometry.Freeze();
ShapeGeometry = shapeGeometry;
});
// Store the last top-left for easy later on
_topLeft = _layerEditorService.GetLayerPath(layer, true, true, true).Points[0];
}
#region Rotation
private bool _rotating;
private float _previousDragAngle;
public void RotateMouseDown(object sender, ShapeControlEventArgs e)
{
_rotating = true;
if (ProfileEditorService.SelectedProfileElement is Layer layer)
_previousDragAngle = CalculateAngle(layer, sender, e.MouseEventArgs);
else
_previousDragAngle = 0;
}
public void RotateMouseUp(object sender, ShapeControlEventArgs e)
{
ProfileEditorService.UpdateSelectedProfileElement();
_rotating = false;
}
public void RotateMouseMove(object sender, ShapeControlEventArgs e)
{
if (!_rotating || !(ProfileEditorService.SelectedProfileElement is Layer layer))
return;
var previousDragAngle = _previousDragAngle;
var newRotation = CalculateAngle(layer, sender, e.MouseEventArgs);
_previousDragAngle = newRotation;
// Allow the user to rotate the shape in increments of 5
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
newRotation = (float) Math.Round(newRotation / 5f) * 5f;
var difference = newRotation - previousDragAngle;
if (difference < -350)
difference += 360;
else if (difference > 350)
difference -= 360;
newRotation = layer.Transform.Rotation.CurrentValue + difference;
// Round the end-result to increments of 5 as well, to avoid staying on an offset
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
newRotation = (float) Math.Round(newRotation / 5f) * 5f;
else
newRotation = (float) Math.Round(newRotation, 2, MidpointRounding.AwayFromZero);
layer.Transform.Rotation.SetCurrentValue(newRotation, ProfileEditorService.CurrentTime);
ProfileEditorService.UpdateProfilePreview();
}
#endregion
#region Size
private bool _isResizing;
private SKSize _dragStartScale;
public void ResizeMouseDown(object sender, ShapeControlEventArgs e)
{
if (_isResizing || !(ProfileEditorService.SelectedProfileElement is Layer layer))
return;
var dragStart = GetRelativePosition(sender, e.MouseEventArgs).ToSKPoint();
_dragOffset = _layerEditorService.GetDragOffset(layer, dragStart);
_dragStart = dragStart + _dragOffset;
_dragStartScale = layer.Transform.Scale.CurrentValue;
_isResizing = true;
}
public void ResizeMouseUp(object sender, ShapeControlEventArgs e)
{
ProfileEditorService.UpdateSelectedProfileElement();
_isResizing = false;
}
public void ResizeMouseMove(object sender, ShapeControlEventArgs e)
{
if (!_isResizing || !(ProfileEditorService.SelectedProfileElement is Layer layer))
return;
float width, height;
var position = GetRelativePosition(sender, e.MouseEventArgs).ToSKPoint() + _dragOffset;
switch (e.ShapeControlPoint)
{
case ShapeControlPoint.TopLeft:
height = VerticalResize(layer, position, ResizeOrigin.Top);
width = HorizontalResize(layer, position, ResizeOrigin.Left);
break;
case ShapeControlPoint.TopRight:
height = VerticalResize(layer, position, ResizeOrigin.Top);
width = HorizontalResize(layer, position, ResizeOrigin.Right);
break;
case ShapeControlPoint.BottomRight:
height = VerticalResize(layer, position, ResizeOrigin.Bottom);
width = HorizontalResize(layer, position, ResizeOrigin.Right);
break;
case ShapeControlPoint.BottomLeft:
height = VerticalResize(layer, position, ResizeOrigin.Bottom);
width = HorizontalResize(layer, position, ResizeOrigin.Left);
break;
case ShapeControlPoint.TopCenter:
height = VerticalResize(layer, position, ResizeOrigin.Top);
width = layer.Transform.Scale.CurrentValue.Width;
break;
case ShapeControlPoint.RightCenter:
width = HorizontalResize(layer, position, ResizeOrigin.Right);
height = layer.Transform.Scale.CurrentValue.Height;
break;
case ShapeControlPoint.BottomCenter:
width = layer.Transform.Scale.CurrentValue.Width;
height = VerticalResize(layer, position, ResizeOrigin.Bottom);
break;
case ShapeControlPoint.LeftCenter:
width = HorizontalResize(layer, position, ResizeOrigin.Left);
height = layer.Transform.Scale.CurrentValue.Height;
break;
default:
throw new ArgumentOutOfRangeException();
}
// Make the sides even if shift is held down
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && e.ShapeControlPoint < ShapeControlPoint.TopCenter)
{
var bounds = _layerEditorService.GetLayerBounds(layer);
var smallestSide = Math.Min(bounds.Width * width, bounds.Height * height);
width = (float) Math.Round(1.0 / bounds.Width * smallestSide, 2, MidpointRounding.AwayFromZero);
height = (float) Math.Round(1.0 / bounds.Height * smallestSide, 2, MidpointRounding.AwayFromZero);
}
layer.Transform.Scale.SetCurrentValue(new SKSize(width, height), ProfileEditorService.CurrentTime);
ProfileEditorService.UpdateProfilePreview();
}
private float HorizontalResize(Layer layer, SKPoint position, ResizeOrigin origin)
{
// Apply rotation to the mouse
var points = UnTransformPoints(new[] {position, _dragStart}, layer, ShapeAnchor, false);
position = points[0];
var dragStart = points[1];
var shapePath = _layerEditorService.GetLayerPath(layer, true, false, false);
var scalePerPixel = 1f / shapePath.Bounds.Width;
var anchorDistance = origin == ResizeOrigin.Left
? shapePath.Bounds.Left - ShapeAnchor.X
: shapePath.Bounds.Right - ShapeAnchor.X;
var anchorOffset = anchorDistance / shapePath.Bounds.Width;
var pixelsToAdd = (position - dragStart).X / anchorOffset;
var scaleToAdd = scalePerPixel * pixelsToAdd * 100f;
return (float) Math.Round(Math.Max(0, _dragStartScale.Width + scaleToAdd), 2, MidpointRounding.AwayFromZero);
}
private float VerticalResize(Layer layer, SKPoint position, ResizeOrigin origin)
{
// Apply rotation to the mouse
var points = UnTransformPoints(new[] {position, _dragStart}, layer, ShapeAnchor, false);
position = points[0];
var dragStart = points[1];
var shapePath = _layerEditorService.GetLayerPath(layer, true, false, false);
var scalePerPixel = 1f / shapePath.Bounds.Height;
var anchorDistance = origin == ResizeOrigin.Top
? shapePath.Bounds.Top - ShapeAnchor.Y
: shapePath.Bounds.Bottom - ShapeAnchor.Y;
var anchorOffset = anchorDistance / shapePath.Bounds.Height;
var pixelsToAdd = (position - dragStart).Y / anchorOffset;
var scaleToAdd = scalePerPixel * pixelsToAdd * 100f;
return (float) Math.Round(Math.Max(0, _dragStartScale.Height + scaleToAdd), 2, MidpointRounding.AwayFromZero);
}
#endregion
#region Position
private bool _movingShape;
private bool _movingAnchor;
private bool _draggingHorizontally;
private bool _draggingVertically;
private SKPoint _dragStartAnchor;
public void MoveMouseDown(object sender, ShapeControlEventArgs e)
{
if (!(ProfileEditorService.SelectedProfileElement is Layer layer))
return;
if (e.ShapeControlPoint == ShapeControlPoint.LayerShape)
{
var dragStart = GetRelativePosition(sender, e.MouseEventArgs).ToSKPoint();
_dragOffset = _layerEditorService.GetDragOffset(layer, dragStart);
_dragStart = dragStart + _dragOffset;
_movingShape = true;
}
else if (e.ShapeControlPoint == ShapeControlPoint.Anchor)
{
var dragStartPosition = GetRelativePosition(sender, e.MouseEventArgs).ToSKPoint();
// Mouse doesn't care about rotation so get the layer path without rotation
var path = _layerEditorService.GetLayerPath(layer, true, true, false);
var topLeft = path.Points[0];
// Measure from the top-left of the shape (without rotation)
_dragOffset = topLeft + (dragStartPosition - topLeft);
// Get the absolute layer anchor and make it relative to the unrotated shape
_dragStartAnchor = _layerEditorService.GetLayerAnchorPosition(layer).ToSKPoint() - topLeft;
// Ensure the anchor starts in the center of the shape it is now relative to
_dragStartAnchor.X -= path.Bounds.Width / 2f;
_dragStartAnchor.Y -= path.Bounds.Height / 2f;
_movingAnchor = true;
}
}
public void MoveMouseUp(object sender, ShapeControlEventArgs e)
{
ProfileEditorService.UpdateSelectedProfileElement();
_movingShape = false;
_movingAnchor = false;
}
public void MoveMouseMove(object sender, ShapeControlEventArgs e)
{
if (_movingShape)
MoveShape(sender, e.MouseEventArgs);
else if (_movingAnchor)
MoveAnchor(sender, e.MouseEventArgs);
}
public void MoveShape(object sender, MouseEventArgs e)
{
if (!(ProfileEditorService.SelectedProfileElement is Layer layer))
return;
var position = GetRelativePosition(sender, e).ToSKPoint() + _dragOffset;
// Allow the user to move the shape only horizontally or vertically when holding down shift
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
// Keep the X position static if dragging vertically
if (_draggingVertically)
position.X = _dragStart.X;
// Keep the Y position static if dragging horizontally
else if (_draggingHorizontally)
position.Y = _dragStart.Y;
// Snap into place only if the mouse moved atleast a full pixel
else if (Math.Abs(position.X - _dragStart.X) > 1 || Math.Abs(position.Y - _dragStart.Y) > 1)
{
// Pick between X and Y by comparing which moved the furthers from the starting point
_draggingHorizontally = Math.Abs(position.X - _dragStart.X) > Math.Abs(position.Y - _dragStart.Y);
_draggingVertically = Math.Abs(position.X - _dragStart.X) < Math.Abs(position.Y - _dragStart.Y);
return;
}
}
// Reset both states when shift is not held down
else
{
_draggingVertically = false;
_draggingHorizontally = false;
}
// Scale down the resulting position and make it relative
var scaled = _layerEditorService.GetScaledPoint(layer, position, true);
// Round and update the position property
layer.Transform.Position.SetCurrentValue(RoundPoint(scaled, 3), ProfileEditorService.CurrentTime);
ProfileEditorService.UpdateProfilePreview();
}
public void MoveAnchor(object sender, MouseEventArgs e)
{
if (!_movingAnchor || !(ProfileEditorService.SelectedProfileElement is Layer layer))
return;
// The start anchor is relative to an unrotated version of the shape
var start = _dragStartAnchor;
// Add the current position to the start anchor to determine the new position
var current = start + (GetRelativePosition(sender, e).ToSKPoint() - _dragOffset);
// In order to keep the mouse movement unrotated, counter-act the active rotation
var countered = UnTransformPoints(new[] {start, current}, layer, start, true);
var scaled = _layerEditorService.GetScaledPoint(layer, countered[1], false);
// Update the anchor point, this causes the shape to move
layer.Transform.AnchorPoint.SetCurrentValue(RoundPoint(scaled, 3), ProfileEditorService.CurrentTime);
// TopLeft is not updated yet and acts as a snapshot of the top-left before changing the anchor
var path = _layerEditorService.GetLayerPath(layer, true, true, true);
// Calculate the (scaled) difference between the old and now position
var difference = _layerEditorService.GetScaledPoint(layer, _topLeft - path.Points[0], false);
// Apply the difference so that the shape effectively stays in place
layer.Transform.Position.SetCurrentValue(RoundPoint(layer.Transform.Position.CurrentValue + difference, 3), ProfileEditorService.CurrentTime);
ProfileEditorService.UpdateProfilePreview();
}
#endregion
#region Private methods
private SKPoint RoundPoint(SKPoint point, int decimals)
{
return new SKPoint((float) Math.Round(point.X, decimals, MidpointRounding.AwayFromZero), (float) Math.Round(point.Y, decimals, MidpointRounding.AwayFromZero));
}
private SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot, bool includeScale)
{
using var counterRotatePath = new SKPath();
counterRotatePath.AddPoly(skPoints, false);
counterRotatePath.Transform(SKMatrix.MakeRotationDegrees(layer.Transform.Rotation.CurrentValue * -1, pivot.X, pivot.Y));
if (includeScale)
counterRotatePath.Transform(SKMatrix.MakeScale(1f / (layer.Transform.Scale.CurrentValue.Width / 100f), 1f / (layer.Transform.Scale.CurrentValue.Height / 100f)));
return counterRotatePath.Points;
}
private Point GetRelativePosition(object sender, MouseEventArgs mouseEventArgs)
{
var parent = VisualTreeHelper.GetParent((DependencyObject) sender);
return mouseEventArgs.GetPosition((IInputElement) parent);
}
private float CalculateAngle(Layer layer, object mouseEventSender, MouseEventArgs mouseEvent)
{
var layerBounds = _layerEditorService.GetLayerBounds(layer);
var start = _layerEditorService.GetLayerAnchorPosition(layer);
var arrival = GetRelativePosition(mouseEventSender, mouseEvent);
var radian = (float) Math.Atan2(start.Y - arrival.Y, start.X - arrival.X);
var angle = radian * (180f / (float) Math.PI);
if (angle < 0f)
angle += 360f;
return angle;
}
#endregion
}
internal enum ResizeOrigin
{
Left,
Right,
Top,
Bottom
}
}
@@ -0,0 +1,45 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Visualization.Tools.SelectionRemoveToolView"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="Shape" x:Key="ShowIfMouseDown">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseDown}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Canvas Background="Transparent" Width="50" Height="50">
<Rectangle Style="{StaticResource ShowIfMouseDown}"
Width="{Binding DragRectangle.Width}"
Height="{Binding DragRectangle.Height}"
Canvas.Left="{Binding DragRectangle.X}"
Canvas.Top="{Binding DragRectangle.Y}"
Stroke="{DynamicResource PrimaryHueLightBrush}"
StrokeDashArray="4 4"
StrokeThickness="1"
Opacity="0">
<Rectangle.Fill>
<SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" />
</Rectangle.Fill>
</Rectangle>
</Canvas>
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
/// <summary>
/// Interaction logic for SelectionRemoveToolView.xaml
/// </summary>
public partial class SelectionRemoveToolView : UserControl
{
public SelectionRemoveToolView()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,73 @@
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Artemis.Core.Models.Profile;
using Artemis.UI.Properties;
using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
public class SelectionRemoveToolViewModel : VisualizationToolViewModel
{
private Rect _dragRectangle;
public SelectionRemoveToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService)
{
using (var stream = new MemoryStream(Resources.aero_pen_min))
{
Cursor = new Cursor(stream);
}
}
public Rect DragRectangle
{
get => _dragRectangle;
set => SetAndNotify(ref _dragRectangle, value);
}
public override void MouseUp(object sender, MouseButtonEventArgs e)
{
base.MouseUp(sender, e);
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
var selectedRect = new Rect(MouseDownStartPosition, position);
// Get selected LEDs
var selectedLeds = ProfileViewModel.GetLedsInRectangle(selectedRect);
ProfileViewModel.SelectedLeds.Clear();
ProfileViewModel.SelectedLeds.AddRange(selectedLeds);
// Apply the selection to the selected layer layer
if (ProfileEditorService.SelectedProfileElement is Layer layer)
{
var remainingLeds = layer.Leds.Except(selectedLeds).ToList();
layer.ClearLeds();
layer.AddLeds(remainingLeds);
ProfileEditorService.UpdateSelectedProfileElement();
}
}
public override void MouseMove(object sender, MouseEventArgs e)
{
base.MouseMove(sender, e);
if (!IsMouseDown)
{
DragRectangle = new Rect(-1, -1, 0, 0);
return;
}
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
var selectedRect = new Rect(MouseDownStartPosition, position);
var selectedLeds = ProfileViewModel.GetLedsInRectangle(selectedRect);
// Unless shift is held down, clear the current selection
if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
ProfileViewModel.SelectedLeds.Clear();
ProfileViewModel.SelectedLeds.AddRange(selectedLeds.Except(ProfileViewModel.SelectedLeds));
DragRectangle = selectedRect;
}
}
}
@@ -0,0 +1,45 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Visualization.Tools.SelectionToolView"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="Shape" x:Key="ShowIfMouseDown">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseDown}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1" To="0" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Canvas Background="Transparent" Width="50" Height="50">
<Rectangle Style="{StaticResource ShowIfMouseDown}"
Width="{Binding DragRectangle.Width}"
Height="{Binding DragRectangle.Height}"
Canvas.Left="{Binding DragRectangle.X}"
Canvas.Top="{Binding DragRectangle.Y}"
Stroke="{DynamicResource PrimaryHueLightBrush}"
StrokeDashArray="4 4"
StrokeThickness="1"
Opacity="0">
<Rectangle.Fill>
<SolidColorBrush Color="{DynamicResource Primary400}" Opacity="0.25" />
</Rectangle.Fill>
</Rectangle>
</Canvas>
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
/// <summary>
/// Interaction logic for SelectionToolView.xaml
/// </summary>
public partial class SelectionToolView : UserControl
{
public SelectionToolView()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,97 @@
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Artemis.Core.Models.Profile;
using Artemis.Core.Services.Interfaces;
using Artemis.UI.Properties;
using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
public class SelectionToolViewModel : VisualizationToolViewModel
{
private readonly IRenderElementService _renderElementService;
private Rect _dragRectangle;
public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, IRenderElementService renderElementService)
: base(profileViewModel, profileEditorService)
{
_renderElementService = renderElementService;
using (var stream = new MemoryStream(Resources.aero_crosshair))
{
Cursor = new Cursor(stream);
}
}
public Rect DragRectangle
{
get => _dragRectangle;
set => SetAndNotify(ref _dragRectangle, value);
}
public override void MouseUp(object sender, MouseButtonEventArgs e)
{
base.MouseUp(sender, e);
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
var selectedRect = new Rect(MouseDownStartPosition, position);
// Get selected LEDs
var selectedLeds = ProfileViewModel.GetLedsInRectangle(selectedRect);
// Apply the selection to the selected layer layer
if (ProfileEditorService.SelectedProfileElement is Layer layer)
{
// If shift is held down, add to the selection instead of replacing it
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
layer.AddLeds(selectedLeds.Except(layer.Leds));
else
{
layer.ClearLeds();
layer.AddLeds(selectedLeds);
}
ProfileEditorService.UpdateSelectedProfileElement();
}
// If no layer selected, apply it to a new layer in the selected folder
else if (ProfileEditorService.SelectedProfileElement is Folder folder)
{
var newLayer = _renderElementService.CreateLayer(folder.Profile, folder, "New layer");
newLayer.AddLeds(selectedLeds);
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
ProfileEditorService.UpdateSelectedProfileElement();
}
// If no folder selected, apply it to a new layer in the root folder
else
{
var rootFolder = ProfileEditorService.SelectedProfile.GetRootFolder();
var newLayer = _renderElementService.CreateLayer(rootFolder.Profile, rootFolder, "New layer");
newLayer.AddLeds(selectedLeds);
ProfileEditorService.ChangeSelectedProfileElement(newLayer);
ProfileEditorService.UpdateSelectedProfileElement();
}
}
public override void MouseMove(object sender, MouseEventArgs e)
{
base.MouseMove(sender, e);
if (!IsMouseDown)
{
DragRectangle = new Rect(-1, -1, 0, 0);
return;
}
var position = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
var selectedRect = new Rect(MouseDownStartPosition, position);
var selectedLeds = ProfileViewModel.GetLedsInRectangle(selectedRect);
// Unless shift is held down, clear the current selection
if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
ProfileViewModel.SelectedLeds.Clear();
ProfileViewModel.SelectedLeds.AddRange(selectedLeds.Except(ProfileViewModel.SelectedLeds));
DragRectangle = selectedRect;
}
}
}
@@ -0,0 +1,10 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Visualization.Tools.ViewpointMoveToolView"
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:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.Visualization.Tools"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid />
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
/// <summary>
/// Interaction logic for ViewpointMoveToolView.xaml
/// </summary>
public partial class ViewpointMoveToolView : UserControl
{
public ViewpointMoveToolView()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,20 @@
using System.Windows.Input;
using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
public class ViewpointMoveToolViewModel : VisualizationToolViewModel
{
public ViewpointMoveToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService)
{
Cursor = Cursors.Hand;
ProfileViewModel.PanZoomViewModel.LastPanPosition = null;
}
public override void MouseMove(object sender, MouseEventArgs e)
{
base.MouseMove(sender, e);
ProfileViewModel.PanZoomViewModel.ProcessMouseMove(sender, e);
}
}
}
@@ -0,0 +1,88 @@
using System;
using System.Windows;
using System.Windows.Input;
using Artemis.UI.Shared.Services.Interfaces;
namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
{
public abstract class VisualizationToolViewModel : CanvasViewModel
{
private Cursor _cursor;
private bool _isMouseDown;
private Point _mouseDownStartPosition;
protected VisualizationToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService)
{
// Not relevant for visualization tools as they overlay the entire canvas
X = 0;
Y = 0;
ProfileViewModel = profileViewModel;
ProfileEditorService = profileEditorService;
Cursor = Cursors.Arrow;
}
public ProfileViewModel ProfileViewModel { get; }
public IProfileEditorService ProfileEditorService { get; }
public Cursor Cursor
{
get => _cursor;
protected set => SetAndNotify(ref _cursor, value);
}
public bool IsMouseDown
{
get => _isMouseDown;
protected set => SetAndNotify(ref _isMouseDown, value);
}
public Point MouseDownStartPosition
{
get => _mouseDownStartPosition;
protected set => SetAndNotify(ref _mouseDownStartPosition, value);
}
public virtual void MouseDown(object sender, MouseButtonEventArgs e)
{
IsMouseDown = true;
MouseDownStartPosition = ProfileViewModel.PanZoomViewModel.GetRelativeMousePosition(sender, e);
}
public virtual void MouseUp(object sender, MouseButtonEventArgs e)
{
IsMouseDown = false;
}
public virtual void MouseMove(object sender, MouseEventArgs e)
{
}
public virtual void MouseWheel(object sender, MouseWheelEventArgs e)
{
}
public virtual void KeyUp(KeyEventArgs e)
{
}
public virtual void KeyDown(KeyEventArgs e)
{
}
protected Rect GetSquareRectBetweenPoints(Point start, Point end)
{
// Find the shortest side
var size = Math.Min(Math.Abs(start.X - end.X), Math.Abs(start.Y - end.Y));
// There's probably a very elegant way to do this, and this is not it
if (end.X < start.X && end.Y < start.Y)
return new Rect(start.X - size, start.Y - size, size, size);
if (end.X < start.X)
return new Rect(start.X - size, Math.Min(start.Y, end.Y), size, size);
if (end.Y < start.Y)
return new Rect(Math.Min(start.X, end.X), start.Y - size, size, size);
return new Rect(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y), size, size);
}
}
}