diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs
index b63d493c2..2422ef063 100644
--- a/src/Artemis.Core/Constants.cs
+++ b/src/Artemis.Core/Constants.cs
@@ -32,7 +32,7 @@ namespace Artemis.Core
///
/// The full path to the Artemis data folder
///
- public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis");
+ public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis.Avalonia");
///
/// The full path to the Artemis logs folder
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 11a34940c..1a8a26905 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -579,10 +579,8 @@ namespace Artemis.Core
SKRect bounds = customBounds ?? Bounds;
SKPoint positionProperty = Transform.Position.CurrentValue;
- // Start at the center of the shape
- SKPoint position = zeroBased
- ? new SKPoint(bounds.MidX - bounds.Left, bounds.MidY - Bounds.Top)
- : new SKPoint(bounds.MidX, bounds.MidY);
+ // Start at the top left of the shape
+ SKPoint position = zeroBased ? new SKPoint(0, 0) : new SKPoint(bounds.Left, bounds.Top);
// Apply translation
if (applyTranslation)
@@ -649,9 +647,9 @@ namespace Artemis.Core
SKPoint anchorPosition = GetLayerAnchorPosition(true, zeroBased, bounds);
SKPoint anchorProperty = Transform.AnchorPoint.CurrentValue;
- // Translation originates from the unscaled center of the shape and is tied to the anchor
- float x = anchorPosition.X - (zeroBased ? bounds.MidX - bounds.Left : bounds.MidX) - anchorProperty.X * bounds.Width;
- float y = anchorPosition.Y - (zeroBased ? bounds.MidY - bounds.Top : bounds.MidY) - anchorProperty.Y * bounds.Height;
+ // Translation originates from the top left of the shape and is tied to the anchor
+ float x = anchorPosition.X - (zeroBased ? 0 : bounds.Left) - anchorProperty.X * bounds.Width;
+ float y = anchorPosition.Y - (zeroBased ? 0 : bounds.Top) - anchorProperty.Y * bounds.Height;
SKMatrix transform = SKMatrix.Empty;
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
index 6bdf1fd6d..f671d6a07 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Artemis.Storage.Entities.Profile;
@@ -201,33 +202,33 @@ namespace Artemis.Core
/// An optional time to set the value add, if provided and property is using keyframes the value will be set to an new
/// or existing keyframe.
///
- /// The new keyframe if one was created.
+ /// The keyframe if one was created or updated.
public LayerPropertyKeyframe? SetCurrentValue(T value, TimeSpan? time)
{
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
- LayerPropertyKeyframe? newKeyframe = null;
+ LayerPropertyKeyframe? keyframe = null;
if (time == null || !KeyframesEnabled || !KeyframesSupported)
BaseValue = value;
else
{
// If on a keyframe, update the keyframe
- LayerPropertyKeyframe? currentKeyframe = Keyframes.FirstOrDefault(k => k.Position == time.Value);
+ keyframe = Keyframes.FirstOrDefault(k => k.Position == time.Value);
// Create a new keyframe if none found
- if (currentKeyframe == null)
+ if (keyframe == null)
{
- newKeyframe = new LayerPropertyKeyframe(value, time.Value, Easings.Functions.Linear, this);
- AddKeyframe(newKeyframe);
+ keyframe = new LayerPropertyKeyframe(value, time.Value, Easings.Functions.Linear, this);
+ AddKeyframe(keyframe);
}
else
- currentKeyframe.Value = value;
+ keyframe.Value = value;
}
// Force an update so that the base value is applied to the current value and
// keyframes/data bindings are applied using the new base value
ReapplyUpdate();
- return newKeyframe;
+ return keyframe;
}
///
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyPreview.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyPreview.cs
new file mode 100644
index 000000000..906c5058b
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyPreview.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Linq;
+
+namespace Artemis.Core;
+
+///
+/// Represents a container for a preview value of a that can be used to update and
+/// discard a temporary value.
+///
+/// The value type of the layer property.
+public sealed class LayerPropertyPreview : IDisposable
+{
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The layer property to apply the preview value to.
+ /// The time in the timeline at which the preview is applied.
+ public LayerPropertyPreview(LayerProperty layerProperty, TimeSpan time)
+ {
+ Property = layerProperty;
+ Time = time;
+ OriginalKeyframe = layerProperty.Keyframes.FirstOrDefault(k => k.Position == time);
+ OriginalValue = OriginalKeyframe != null ? OriginalKeyframe.Value : layerProperty.CurrentValue;
+ PreviewValue = OriginalValue;
+ }
+
+ ///
+ /// Gets the property this preview applies to.
+ ///
+ public LayerProperty Property { get; }
+
+ ///
+ /// Gets the original keyframe of the property at the time the preview was created.
+ ///
+ public LayerPropertyKeyframe? OriginalKeyframe { get; }
+
+ ///
+ /// Gets the original value of the property at the time the preview was created.
+ ///
+ public T OriginalValue { get; }
+
+ ///
+ /// Gets the time in the timeline at which the preview is applied.
+ ///
+ public TimeSpan Time { get; }
+
+ ///
+ /// Gets the keyframe that was created to preview the value.
+ ///
+ public LayerPropertyKeyframe? PreviewKeyframe { get; private set; }
+
+ ///
+ /// Gets the preview value.
+ ///
+ public T? PreviewValue { get; private set; }
+
+ ///
+ /// Updates the layer property to the given , keeping track of the original state of the
+ /// property.
+ ///
+ /// The value to preview.
+ public void Preview(T value)
+ {
+ PreviewValue = value;
+ PreviewKeyframe = Property.SetCurrentValue(value, Time);
+ }
+
+ ///
+ /// Discard the preview value and restores the original state of the property. The returned boolean can be used to
+ /// determine whether the preview value was different from the original value.
+ ///
+ /// if any changes where discarded; otherwise .
+ public bool DiscardPreview()
+ {
+ if (PreviewKeyframe != null && OriginalKeyframe == null)
+ {
+ Property.RemoveKeyframe(PreviewKeyframe);
+ return true;
+ }
+
+ Property.SetCurrentValue(OriginalValue, Time);
+ return !Equals(OriginalValue, PreviewValue); ;
+ }
+
+ ///
+ /// Discard the preview value and restores the original state of the property. The returned boolean can be used to
+ /// determine whether the preview value was different from the original value.
+ ///
+ public void Dispose()
+ {
+ DiscardPreview();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs b/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
index 9a78aba1c..2b2ab624b 100644
--- a/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
+++ b/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
@@ -12,13 +12,13 @@ namespace Artemis.Core
///
/// The point at which the shape is attached to its position
///
- [PropertyDescription(Description = "The point at which the shape is attached to its position", InputStepSize = 0.001f)]
+ [PropertyDescription(Description = "The point at which the shape is attached to its position", InputAffix = "%")]
public SKPointLayerProperty AnchorPoint { get; set; }
///
/// The position of the shape
///
- [PropertyDescription(Description = "The position of the shape", InputStepSize = 0.001f)]
+ [PropertyDescription(Description = "The position of the shape", InputAffix = "%")]
public SKPointLayerProperty Position { get; set; }
///
@@ -43,6 +43,8 @@ namespace Artemis.Core
protected override void PopulateDefaults()
{
Scale.DefaultValue = new SKSize(100, 100);
+ AnchorPoint.DefaultValue = new SKPoint(0.5f, 0.5f);
+ Position.DefaultValue = new SKPoint(0.5f, 0.5f);
Opacity.DefaultValue = 100;
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/EditToolViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/EditToolViewModel.cs
index 3f0e8fbde..381e3e42d 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/EditToolViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/EditToolViewModel.cs
@@ -420,7 +420,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools
private static SKPoint RoundPoint(SKPoint point, int decimals)
{
- return new((float) Math.Round(point.X, decimals, MidpointRounding.AwayFromZero), (float) Math.Round(point.Y, decimals, MidpointRounding.AwayFromZero));
+ return new SKPoint(
+ (float) Math.Round(point.X, decimals, MidpointRounding.AwayFromZero),
+ (float) Math.Round(point.Y, decimals, MidpointRounding.AwayFromZero)
+ );
}
private static SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot, bool includeScale)
diff --git a/src/Avalonia/Artemis.UI.Shared/Extensions/LayerExtensions.cs b/src/Avalonia/Artemis.UI.Shared/Extensions/LayerExtensions.cs
index a1d2cd5d9..9e4b874c0 100644
--- a/src/Avalonia/Artemis.UI.Shared/Extensions/LayerExtensions.cs
+++ b/src/Avalonia/Artemis.UI.Shared/Extensions/LayerExtensions.cs
@@ -14,6 +14,9 @@ public static class LayerExtensions
///
public static SKRect GetLayerBounds(this Layer layer)
{
+ if (!layer.Leds.Any())
+ return SKRect.Empty;
+
return new SKRect(
layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.X),
layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.Y),
@@ -32,8 +35,8 @@ public static class LayerExtensions
if (positionOverride != null)
positionProperty = positionOverride.Value;
- // Start at the center of the shape
- SKPoint position = new(layerBounds.MidX, layerBounds.MidY);
+ // Start at the top left of the shape
+ SKPoint position = new(layerBounds.Left, layerBounds.Top);
// Apply translation
position.X += positionProperty.X * layerBounds.Width;
@@ -59,7 +62,7 @@ public static class LayerExtensions
///
/// Returns a new point normalized to 0.0-1.0
///
- public static SKPoint GetScaledPoint(this Layer layer, SKPoint point, bool absolute)
+ public static SKPoint GetNormalizedPoint(this Layer layer, SKPoint point, bool absolute)
{
SKRect bounds = GetLayerBounds(layer);
if (absolute)
diff --git a/src/Avalonia/Artemis.UI.Shared/Providers/ICursorProvider.cs b/src/Avalonia/Artemis.UI.Shared/Providers/ICursorProvider.cs
new file mode 100644
index 000000000..a8bd9a768
--- /dev/null
+++ b/src/Avalonia/Artemis.UI.Shared/Providers/ICursorProvider.cs
@@ -0,0 +1,24 @@
+using Avalonia.Input;
+
+namespace Artemis.UI.Shared.Providers;
+
+///
+/// Represents a provider for custom cursors.
+///
+public interface ICursorProvider
+{
+ ///
+ /// A cursor that indicates a rotating.
+ ///
+ public Cursor Rotate { get; }
+
+ ///
+ /// A cursor that indicates dragging.
+ ///
+ public Cursor Drag { get; }
+
+ ///
+ /// A cursor that indicates dragging horizontally.
+ ///
+ public Cursor DragHorizontal { get; }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/Commands/ResetLayerProperty.cs b/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/Commands/ResetLayerProperty.cs
new file mode 100644
index 000000000..edc15e9ec
--- /dev/null
+++ b/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/Commands/ResetLayerProperty.cs
@@ -0,0 +1,52 @@
+using Artemis.Core;
+
+namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
+
+///
+/// Represents a profile editor command that can be used to reset a layer property to it's default value.
+///
+public class ResetLayerProperty : IProfileEditorCommand
+{
+ private readonly LayerProperty _layerProperty;
+ private readonly T _originalBaseValue;
+ private readonly bool _keyframesEnabled;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public ResetLayerProperty(LayerProperty layerProperty)
+ {
+ if (layerProperty.DefaultValue == null)
+ throw new ArtemisSharedUIException("Can't reset a layer property without a default value.");
+
+ _layerProperty = layerProperty;
+ _originalBaseValue = _layerProperty.BaseValue;
+ _keyframesEnabled = _layerProperty.KeyframesEnabled;
+ }
+
+ #region Implementation of IProfileEditorCommand
+
+ ///
+ public string DisplayName => "Reset layer property";
+
+ ///
+ public void Execute()
+ {
+ string json = CoreJson.SerializeObject(_layerProperty.DefaultValue, true);
+
+ if (_keyframesEnabled)
+ _layerProperty.KeyframesEnabled = false;
+
+ _layerProperty.SetCurrentValue(CoreJson.DeserializeObject(json)!, null);
+ }
+
+ ///
+ public void Undo()
+ {
+ _layerProperty.SetCurrentValue(_originalBaseValue, null);
+ if (_keyframesEnabled)
+ _layerProperty.KeyframesEnabled = true;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateLayerProperty.cs b/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateLayerProperty.cs
index d6179bb4e..794a64358 100644
--- a/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateLayerProperty.cs
+++ b/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/Commands/UpdateLayerProperty.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Artemis.Core;
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
@@ -12,6 +13,7 @@ public class UpdateLayerProperty : IProfileEditorCommand
private readonly T _newValue;
private readonly T _originalValue;
private readonly TimeSpan? _time;
+ private readonly bool _hasKeyframe;
private LayerPropertyKeyframe? _newKeyframe;
///
@@ -23,6 +25,7 @@ public class UpdateLayerProperty : IProfileEditorCommand
_originalValue = layerProperty.CurrentValue;
_newValue = newValue;
_time = time;
+ _hasKeyframe = _layerProperty.Keyframes.Any(k => k.Position == time);
DisplayName = $"Update {_layerProperty.PropertyDescription.Name ?? "property"}";
}
@@ -50,13 +53,19 @@ public class UpdateLayerProperty : IProfileEditorCommand
{
// If there was already a keyframe from a previous execute that was undone, put it back
if (_newKeyframe != null)
+ {
_layerProperty.AddKeyframe(_newKeyframe);
- else
+ return;
+ }
+
+ // If the layer had no keyframe yet but keyframes are enabled, a new keyframe will be returned
+ if (!_hasKeyframe && _layerProperty.KeyframesEnabled)
{
_newKeyframe = _layerProperty.SetCurrentValue(_newValue, _time);
- if (_newKeyframe != null)
- DisplayName = $"Add {_layerProperty.PropertyDescription.Name ?? "property"} keyframe";
+ DisplayName = $"Add {_layerProperty.PropertyDescription.Name ?? "property"} keyframe";
}
+ else
+ _layerProperty.SetCurrentValue(_newValue, _time);
}
///
diff --git a/src/Avalonia/Artemis.UI.Windows/App.axaml.cs b/src/Avalonia/Artemis.UI.Windows/App.axaml.cs
index 6d5b42adc..d527e4d2d 100644
--- a/src/Avalonia/Artemis.UI.Windows/App.axaml.cs
+++ b/src/Avalonia/Artemis.UI.Windows/App.axaml.cs
@@ -1,4 +1,7 @@
using Artemis.Core.Services;
+using Artemis.UI.Shared.Providers;
+using Artemis.UI.Windows.Ninject;
+using Artemis.UI.Windows.Providers;
using Artemis.UI.Windows.Providers.Input;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
@@ -13,7 +16,7 @@ namespace Artemis.UI.Windows
{
public override void Initialize()
{
- _kernel = ArtemisBootstrapper.Bootstrap(this);
+ _kernel = ArtemisBootstrapper.Bootstrap(this, new WindowsModule());
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this);
}
diff --git a/src/Avalonia/Artemis.UI.Windows/ApplicationStateManager.cs b/src/Avalonia/Artemis.UI.Windows/ApplicationStateManager.cs
index a3c1be22d..34c509005 100644
--- a/src/Avalonia/Artemis.UI.Windows/ApplicationStateManager.cs
+++ b/src/Avalonia/Artemis.UI.Windows/ApplicationStateManager.cs
@@ -55,7 +55,7 @@ namespace Artemis.UI.Windows
public bool FocusExistingInstance()
{
- _artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535d", out bool createdNew);
+ _artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535f", out bool createdNew);
if (createdNew)
return false;
diff --git a/src/Avalonia/Artemis.UI.Windows/Artemis.UI.Windows.csproj b/src/Avalonia/Artemis.UI.Windows/Artemis.UI.Windows.csproj
index 69e2d4bd1..436ab8302 100644
--- a/src/Avalonia/Artemis.UI.Windows/Artemis.UI.Windows.csproj
+++ b/src/Avalonia/Artemis.UI.Windows/Artemis.UI.Windows.csproj
@@ -8,6 +8,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair.png
new file mode 100644
index 000000000..9330d74ca
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair_minus.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair_minus.png
new file mode 100644
index 000000000..2845e6288
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair_minus.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair_plus.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair_plus.png
new file mode 100644
index 000000000..b2b0e2e78
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_crosshair_plus.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_drag.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_drag.png
new file mode 100644
index 000000000..7089b15ba
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_drag.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png
new file mode 100644
index 000000000..e222d8e91
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_pen_min.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_pen_min.png
new file mode 100644
index 000000000..b876e71d6
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_pen_min.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_pen_plus.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_pen_plus.png
new file mode 100644
index 000000000..0dcc55a98
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_pen_plus.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate.png
new file mode 100644
index 000000000..91f5728a5
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_bl.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_bl.png
new file mode 100644
index 000000000..e5853ff6a
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_bl.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_br.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_br.png
new file mode 100644
index 000000000..31275d930
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_br.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_tl.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_tl.png
new file mode 100644
index 000000000..3dd314a7a
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_tl.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_tr.png b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_tr.png
new file mode 100644
index 000000000..6c218cf7c
Binary files /dev/null and b/src/Avalonia/Artemis.UI.Windows/Assets/Cursors/aero_rotate_tr.png differ
diff --git a/src/Avalonia/Artemis.UI.Windows/Ninject/WindowsModule.cs b/src/Avalonia/Artemis.UI.Windows/Ninject/WindowsModule.cs
new file mode 100644
index 000000000..3a79ea6c3
--- /dev/null
+++ b/src/Avalonia/Artemis.UI.Windows/Ninject/WindowsModule.cs
@@ -0,0 +1,18 @@
+using Artemis.UI.Shared.Providers;
+using Artemis.UI.Windows.Providers;
+using Ninject.Modules;
+
+namespace Artemis.UI.Windows.Ninject;
+
+public class WindowsModule : NinjectModule
+{
+ #region Overrides of NinjectModule
+
+ ///
+ public override void Load()
+ {
+ Kernel!.Bind().To().InSingletonScope();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI.Windows/Providers/CursorProvider.cs b/src/Avalonia/Artemis.UI.Windows/Providers/CursorProvider.cs
new file mode 100644
index 000000000..97a53f032
--- /dev/null
+++ b/src/Avalonia/Artemis.UI.Windows/Providers/CursorProvider.cs
@@ -0,0 +1,22 @@
+using System;
+using Artemis.UI.Shared.Providers;
+using Avalonia;
+using Avalonia.Input;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+
+namespace Artemis.UI.Windows.Providers;
+
+public class CursorProvider : ICursorProvider
+{
+ public CursorProvider(IAssetLoader assetLoader)
+ {
+ Rotate = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_rotate.png"))), new PixelPoint(21, 10));
+ Drag = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3));
+ DragHorizontal = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5));
+ }
+
+ public Cursor Rotate { get; }
+ public Cursor Drag { get; }
+ public Cursor DragHorizontal { get; }
+}
\ No newline at end of file
diff --git a/src/Avalonia/Artemis.UI/ArtemisBootstrapper.cs b/src/Avalonia/Artemis.UI/ArtemisBootstrapper.cs
index be40ac024..a72152b46 100644
--- a/src/Avalonia/Artemis.UI/ArtemisBootstrapper.cs
+++ b/src/Avalonia/Artemis.UI/ArtemisBootstrapper.cs
@@ -11,6 +11,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Ninject;
+using Ninject.Modules;
using ReactiveUI;
using Splat.Ninject;
@@ -21,7 +22,7 @@ namespace Artemis.UI
private static StandardKernel? _kernel;
private static Application? _application;
- public static StandardKernel Bootstrap(Application application)
+ public static StandardKernel Bootstrap(Application application, params INinjectModule[] modules)
{
if (_application != null || _kernel != null)
throw new ArtemisUIException("UI already bootstrapped");
@@ -35,6 +36,7 @@ namespace Artemis.UI
_kernel.Load();
_kernel.Load();
_kernel.Load();
+ _kernel.Load(modules);
_kernel.UseNinjectDependencyResolver();
diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyViewModel.cs
index 5a5eb64c6..1f95bd0dc 100644
--- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyViewModel.cs
+++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Tree/TreePropertyViewModel.cs
@@ -1,4 +1,6 @@
using System;
+using System.Reactive;
+using System.Reactive.Linq;
using Artemis.Core;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.ProfileEditor;
@@ -27,11 +29,19 @@ internal class TreePropertyViewModel : ActivatableViewModelBase, ITreePropert
_profileEditorService.Time.Subscribe(t => _time = t).DisposeWith(d);
this.WhenAnyValue(vm => vm.LayerProperty.KeyframesEnabled).Subscribe(_ => this.RaisePropertyChanged(nameof(KeyframesEnabled))).DisposeWith(d);
});
+
+ ResetToDefault = ReactiveCommand.Create(ExecuteResetToDefault, Observable.Return(LayerProperty.DefaultValue != null));
+ }
+
+ private void ExecuteResetToDefault()
+ {
+ _profileEditorService.ExecuteCommand(new ResetLayerProperty(LayerProperty));
}
public LayerProperty LayerProperty { get; }
public PropertyViewModel PropertyViewModel { get; }
public PropertyInputViewModel? PropertyInputViewModel { get; }
+ public ReactiveCommand ResetToDefault { get; }
public bool KeyframesEnabled
{
@@ -62,4 +72,5 @@ internal class TreePropertyViewModel : ActivatableViewModelBase, ITreePropert
return depth;
}
-}
\ No newline at end of file
+}
+
diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs
index e409816dd..802c407a8 100644
--- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs
+++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs
@@ -30,14 +30,21 @@ public class StatusBarViewModel : ActivatableViewModelBase
this.WhenAnyValue(vm => vm.History)
.Select(h => h?.Undo ?? Observable.Never())
.Switch()
- .Subscribe(c => StatusMessage = c != null ? $"Undid '{c.DisplayName}'." : "Nothing to undo.");
+ .Subscribe(c =>
+ {
+ StatusMessage = c != null ? $"Undid '{c.DisplayName}'." : "Nothing to undo.";
+ ShowStatusMessage = true;
+ });
this.WhenAnyValue(vm => vm.History)
.Select(h => h?.Redo ?? Observable.Never())
.Switch()
- .Subscribe(c => StatusMessage = c != null ? $"Redid '{c.DisplayName}'." : "Nothing to redo.");
+ .Subscribe(c =>
+ {
+ StatusMessage = c != null ? $"Redid '{c.DisplayName}'." : "Nothing to redo.";
+ ShowStatusMessage = true;
+ });
- this.WhenAnyValue(vm => vm.StatusMessage).Subscribe(_ => ShowStatusMessage = true);
- this.WhenAnyValue(vm => vm.StatusMessage).Throttle(TimeSpan.FromSeconds(3)).Subscribe(_ => ShowStatusMessage = false);
+ this.WhenAnyValue(vm => vm.ShowStatusMessage).Where(v => v).Throttle(TimeSpan.FromSeconds(3)).Subscribe(_ => ShowStatusMessage = false);
}
public RenderProfileElement? ProfileElement => _profileElement?.Value;
diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml
index f0b7c4bdf..8a6ef6906 100644
--- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml
+++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml
@@ -5,155 +5,193 @@
xmlns:tools="clr-namespace:Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools.TransformToolView"
- x:DataType="tools:TransformToolViewModel">
+ x:DataType="tools:TransformToolViewModel"
+ ClipToBounds="False">
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+