diff --git a/src/Avalonia/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs b/src/Avalonia/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs index 49003ab86..6dd1d3ded 100644 --- a/src/Avalonia/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs +++ b/src/Avalonia/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs @@ -22,7 +22,7 @@ namespace Artemis.UI.Shared.DataModelVisualization set { if (Equals(value, _displayValue)) return; - this.RaiseAndSetIfChanged(ref _displayValue, value); + RaiseAndSetIfChanged(ref _displayValue, value); OnDisplayValueUpdated(); } } @@ -56,7 +56,7 @@ namespace Artemis.UI.Shared.DataModelVisualization public DataModelPropertyAttribute? PropertyDescription { get => _propertyDescription; - internal set => this.RaiseAndSetIfChanged(ref _propertyDescription, value); + internal set => RaiseAndSetIfChanged(ref _propertyDescription, value); } /// diff --git a/src/Avalonia/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs b/src/Avalonia/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs index 80578c565..40a265190 100644 --- a/src/Avalonia/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs +++ b/src/Avalonia/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs @@ -28,7 +28,7 @@ namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display public string Display { get => _display; - set => this.RaiseAndSetIfChanged(ref _display, value); + set => RaiseAndSetIfChanged(ref _display, value); } protected override void OnDisplayValueUpdated() diff --git a/src/Avalonia/Artemis.UI.Shared/Extensions/LayerExtensions.cs b/src/Avalonia/Artemis.UI.Shared/Extensions/LayerExtensions.cs new file mode 100644 index 000000000..2de45c517 --- /dev/null +++ b/src/Avalonia/Artemis.UI.Shared/Extensions/LayerExtensions.cs @@ -0,0 +1,109 @@ +using System.Linq; +using Artemis.Core; +using SkiaSharp; + +namespace Artemis.UI.Shared.Extensions; + +/// +/// Provides utilities when working with layers in UI elements. +/// +public static class LayerExtensions +{ + /// + /// Returns the layer's bounds in real coordinates. + /// + public static SKRect GetLayerBounds(this Layer layer) + { + return new SKRect( + layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.X), + layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.Y), + layer.Leds.Max(l => l.RgbLed.AbsoluteBoundary.Location.X + l.RgbLed.AbsoluteBoundary.Size.Width), + layer.Leds.Max(l => l.RgbLed.AbsoluteBoundary.Location.Y + l.RgbLed.AbsoluteBoundary.Size.Height) + ); + } + + /// + /// Returns the layer's anchor in real coordinates. + /// + public static SKPoint GetLayerAnchorPosition(this Layer layer, SKPoint? positionOverride = null) + { + SKRect layerBounds = GetLayerBounds(layer); + SKPoint positionProperty = layer.Transform.Position.CurrentValue; + if (positionOverride != null) + positionProperty = positionOverride.Value; + + // Start at the center of the shape + SKPoint position = new(layerBounds.MidX, layerBounds.MidY); + + // Apply translation + position.X += positionProperty.X * layerBounds.Width; + position.Y += positionProperty.Y * layerBounds.Height; + + return position; + } + + /// + /// Returns an absolute and scaled rectangular path for the given layer in real coordinates. + /// + public static SKPath GetLayerPath(this Layer layer, bool includeTranslation, bool includeScale, bool includeRotation, SKPoint? anchorOverride = null) + { + SKRect layerBounds = GetLayerBounds(layer); + + // Apply transformation like done by the core during layer rendering (same differences apply as in GetLayerTransformGroup) + SKPoint anchorPosition = GetLayerAnchorPosition(layer); + if (anchorOverride != null) + anchorPosition = anchorOverride.Value; + + SKPoint anchorProperty = layer.Transform.AnchorPoint.CurrentValue; + + // Translation originates from the unscaled center of the shape and is tied to the anchor + float x = anchorPosition.X - layerBounds.MidX - anchorProperty.X * layerBounds.Width; + float y = anchorPosition.Y - layerBounds.MidY - anchorProperty.Y * layerBounds.Height; + + SKPath path = new(); + path.AddRect(layerBounds); + if (includeTranslation) + path.Transform(SKMatrix.CreateTranslation(x, y)); + if (includeScale) + path.Transform(SKMatrix.CreateScale(layer.Transform.Scale.CurrentValue.Width / 100f, layer.Transform.Scale.CurrentValue.Height / 100f, anchorPosition.X, anchorPosition.Y)); + if (includeRotation) + path.Transform(SKMatrix.CreateRotationDegrees(layer.Transform.Rotation.CurrentValue, anchorPosition.X, anchorPosition.Y)); + + return path; + } + + /// + /// Returns a new point normalized to 0.0-1.0 + /// + public static SKPoint GetScaledPoint(this Layer layer, SKPoint point, bool absolute) + { + SKRect bounds = GetLayerBounds(layer); + if (absolute) + return new SKPoint( + 100 / bounds.Width * (point.X - bounds.Left) / 100, + 100 / bounds.Height * (point.Y - bounds.Top) / 100 + ); + + return new SKPoint( + 100 / bounds.Width * point.X / 100, + 100 / bounds.Height * point.Y / 100 + ); + } + + /// + /// Returns the offset from the given point to the top-left of the layer + /// + public static SKPoint GetDragOffset(this Layer layer, SKPoint dragStart) + { + // Figure out what the top left will be if the shape moves to the current cursor position + SKPoint scaledDragStart = GetScaledPoint(layer, dragStart, true); + SKPoint tempAnchor = GetLayerAnchorPosition(layer, scaledDragStart); + SKPoint tempTopLeft = GetLayerPath(layer, true, true, true, tempAnchor)[0]; + + // Get the shape's position + SKPoint topLeft = GetLayerPath(layer, true, true, true)[0]; + + // The difference between the two is the offset + return topLeft - tempTopLeft; + } +} \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI.Shared/Plugins/ScriptingProviders/ScriptEditorViewModel.cs b/src/Avalonia/Artemis.UI.Shared/Plugins/ScriptingProviders/ScriptEditorViewModel.cs index 65e1fc9b1..416d2614a 100644 --- a/src/Avalonia/Artemis.UI.Shared/Plugins/ScriptingProviders/ScriptEditorViewModel.cs +++ b/src/Avalonia/Artemis.UI.Shared/Plugins/ScriptingProviders/ScriptEditorViewModel.cs @@ -42,7 +42,7 @@ namespace Artemis.UI.Shared.ScriptingProviders public Script? Script { get => _script; - internal set => this.RaiseAndSetIfChanged(ref _script, value); + internal set => RaiseAndSetIfChanged(ref _script, value); } /// diff --git a/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs b/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs index 5d01eded7..bb841721c 100644 --- a/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs +++ b/src/Avalonia/Artemis.UI.Shared/Services/ProfileEditor/IToolViewModel.cs @@ -78,7 +78,7 @@ public abstract class ToolViewModel : ActivatableViewModelBase, IToolViewModel public bool IsSelected { get => _isSelected; - set => this.RaiseAndSetIfChanged(ref _isSelected, value); + set => RaiseAndSetIfChanged(ref _isSelected, value); } /// diff --git a/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs b/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs index 0d3059328..e89b30258 100644 --- a/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs +++ b/src/Avalonia/Artemis.UI.Shared/ViewModelBase.cs @@ -1,118 +1,142 @@ using System; -using System.Reactive.Disposables; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using Artemis.UI.Shared.Events; using FluentAvalonia.UI.Controls; +using JetBrains.Annotations; using ReactiveUI; using ReactiveUI.Validation.Helpers; -namespace Artemis.UI.Shared +namespace Artemis.UI.Shared; + +/// +/// Represents the base class for Artemis view models +/// +public abstract class ContentDialogViewModelBase : ReactiveValidationObject, IActivatableViewModel, IDisposable { /// - /// Represents the base class for Artemis view models + /// Gets the content dialog that hosts the view model /// - public abstract class ContentDialogViewModelBase : ReactiveValidationObject, IActivatableViewModel, IDisposable + public ContentDialog? ContentDialog { get; internal set; } + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) { - /// - /// Gets the content dialog that hosts the view model - /// - public ContentDialog? ContentDialog { get; internal set; } + } - #region Implementation of IActivatableViewModel + #region Implementation of IActivatableViewModel - /// - public ViewModelActivator Activator { get; } = new(); + /// + public ViewModelActivator Activator { get; } = new(); - #endregion + #endregion - #region IDisposable + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} - /// - /// Releases the unmanaged resources used by the object and optionally releases the managed resources. - /// - /// - /// to release both managed and unmanaged resources; - /// to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - } +/// +/// Represents the base class for Artemis view models +/// +public abstract class ViewModelValidationBase : ReactiveValidationObject +{ + private string? _displayName; - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + /// + /// Gets or sets the display name of the view model + /// + public string? DisplayName + { + get => _displayName; + set => this.RaiseAndSetIfChanged(ref _displayName, value); + } +} - #endregion +/// +/// Represents the base class for Artemis view models +/// +public abstract class ViewModelBase : ReactiveObject +{ + private string? _displayName; + + /// + /// Gets or sets the display name of the view model + /// + public string? DisplayName + { + get => _displayName; + set => RaiseAndSetIfChanged(ref _displayName, value); } /// - /// Represents the base class for Artemis view models + /// RaiseAndSetIfChanged fully implements a Setter for a read-write property on a ReactiveObject, using + /// CallerMemberName to raise the notification and the ref to the backing field to set the property. /// - public abstract class ViewModelValidationBase : ReactiveValidationObject + /// The type of the return value. + /// A Reference to the backing field for this property. + /// The new value. + /// + /// The name of the property, usually automatically provided through the CallerMemberName + /// attribute. + /// + /// The newly set value, normally discarded. + [NotifyPropertyChangedInvocator] + public TRet RaiseAndSetIfChanged(ref TRet backingField, TRet newValue, [CallerMemberName] string? propertyName = null) { - private string? _displayName; + if (propertyName is null) + throw new ArgumentNullException(nameof(propertyName)); - /// - /// Gets or sets the display name of the view model - /// - public string? DisplayName - { - get => _displayName; - set => this.RaiseAndSetIfChanged(ref _displayName, value); - } + if (EqualityComparer.Default.Equals(backingField, newValue)) + return newValue; + + this.RaisePropertyChanging(propertyName); + backingField = newValue; + this.RaisePropertyChanged(propertyName); + return newValue; + } +} + +/// +/// Represents the base class for Artemis view models that are interested in the activated event +/// +public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel +{ + /// + public ViewModelActivator Activator { get; } = new(); +} + +/// +/// Represents the base class for Artemis view models used to drive dialogs +/// +public abstract class DialogViewModelBase : ActivatableViewModelBase +{ + /// + /// Closes the dialog with the given + /// + /// The result of the dialog + public void Close(TResult result) + { + CloseRequested?.Invoke(this, new DialogClosedEventArgs(result)); } /// - /// Represents the base class for Artemis view models + /// Closes the dialog without a result /// - public abstract class ViewModelBase : ReactiveObject + public void Cancel() { - private string? _displayName; - - /// - /// Gets or sets the display name of the view model - /// - public string? DisplayName - { - get => _displayName; - set => this.RaiseAndSetIfChanged(ref _displayName, value); - } + CancelRequested?.Invoke(this, EventArgs.Empty); } - /// - /// Represents the base class for Artemis view models that are interested in the activated event - /// - public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel - { - /// - public ViewModelActivator Activator { get; } = new(); - } - - /// - /// Represents the base class for Artemis view models used to drive dialogs - /// - public abstract class DialogViewModelBase : ActivatableViewModelBase - { - /// - /// Closes the dialog with the given - /// - /// The result of the dialog - public void Close(TResult result) - { - CloseRequested?.Invoke(this, new DialogClosedEventArgs(result)); - } - - /// - /// Closes the dialog without a result - /// - public void Cancel() - { - CancelRequested?.Invoke(this, EventArgs.Empty); - } - - internal event EventHandler>? CloseRequested; - internal event EventHandler? CancelRequested; - } + internal event EventHandler>? CloseRequested; + internal event EventHandler? CancelRequested; } \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/Debugger/DebugViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Debugger/DebugViewModel.cs index d1b773f1a..f6bda99aa 100644 --- a/src/Avalonia/Artemis.UI/Screens/Debugger/DebugViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Debugger/DebugViewModel.cs @@ -38,7 +38,7 @@ namespace Artemis.UI.Screens.Debugger public NavigationViewItem? SelectedItem { get => _selectedItem; - set => this.RaiseAndSetIfChanged(ref _selectedItem, value); + set => RaiseAndSetIfChanged(ref _selectedItem, value); } private void NavigateToSelectedItem(NavigationViewItem item) diff --git a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs index 7c9fbf74b..9c62836b8 100644 --- a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugViewModel.cs @@ -59,13 +59,13 @@ namespace Artemis.UI.Screens.Debugger.DataModel public DataModelPropertiesViewModel? MainDataModel { get => _mainDataModel; - set => this.RaiseAndSetIfChanged(ref _mainDataModel, value); + set => RaiseAndSetIfChanged(ref _mainDataModel, value); } public string? PropertySearch { get => _propertySearch; - set => this.RaiseAndSetIfChanged(ref _propertySearch, value); + set => RaiseAndSetIfChanged(ref _propertySearch, value); } public bool SlowUpdates @@ -73,7 +73,7 @@ namespace Artemis.UI.Screens.Debugger.DataModel get => _slowUpdates; set { - this.RaiseAndSetIfChanged(ref _slowUpdates, value); + RaiseAndSetIfChanged(ref _slowUpdates, value); _updateTimer.Interval = _slowUpdates ? 500 : 25; } } @@ -85,7 +85,7 @@ namespace Artemis.UI.Screens.Debugger.DataModel get => _selectedModule; set { - this.RaiseAndSetIfChanged(ref _selectedModule, value); + RaiseAndSetIfChanged(ref _selectedModule, value); GetDataModel(); } } @@ -95,7 +95,7 @@ namespace Artemis.UI.Screens.Debugger.DataModel get => _isModuleFilterEnabled; set { - this.RaiseAndSetIfChanged(ref _isModuleFilterEnabled, value); + RaiseAndSetIfChanged(ref _isModuleFilterEnabled, value); if (!IsModuleFilterEnabled) SelectedModule = null; diff --git a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugMeasurementViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugMeasurementViewModel.cs index 4c1391c5d..37f6470fb 100644 --- a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugMeasurementViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugMeasurementViewModel.cs @@ -22,31 +22,31 @@ namespace Artemis.UI.Screens.Debugger.Performance public string? Last { get => _last; - set => this.RaiseAndSetIfChanged(ref _last, value); + set => RaiseAndSetIfChanged(ref _last, value); } public string? Average { get => _average; - set => this.RaiseAndSetIfChanged(ref _average, value); + set => RaiseAndSetIfChanged(ref _average, value); } public string? Min { get => _min; - set => this.RaiseAndSetIfChanged(ref _min, value); + set => RaiseAndSetIfChanged(ref _min, value); } public string? Max { get => _max; - set => this.RaiseAndSetIfChanged(ref _max, value); + set => RaiseAndSetIfChanged(ref _max, value); } public string? Percentile { get => _percentile; - set => this.RaiseAndSetIfChanged(ref _percentile, value); + set => RaiseAndSetIfChanged(ref _percentile, value); } public void Update() diff --git a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs index 1afef3043..d4e81011b 100644 --- a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs @@ -66,25 +66,25 @@ namespace Artemis.UI.Screens.Debugger.Performance public double CurrentFps { get => _currentFps; - set => this.RaiseAndSetIfChanged(ref _currentFps, value); + set => RaiseAndSetIfChanged(ref _currentFps, value); } public int RenderWidth { get => _renderWidth; - set => this.RaiseAndSetIfChanged(ref _renderWidth, value); + set => RaiseAndSetIfChanged(ref _renderWidth, value); } public int RenderHeight { get => _renderHeight; - set => this.RaiseAndSetIfChanged(ref _renderHeight, value); + set => RaiseAndSetIfChanged(ref _renderHeight, value); } public string? Renderer { get => _renderer; - set => this.RaiseAndSetIfChanged(ref _renderer, value); + set => RaiseAndSetIfChanged(ref _renderer, value); } private void HandleActivation() diff --git a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs index 51d1bf5a7..92fe7e804 100644 --- a/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs @@ -35,31 +35,31 @@ namespace Artemis.UI.Screens.Debugger.Render public Bitmap? CurrentFrame { get => _currentFrame; - set => this.RaiseAndSetIfChanged(ref _currentFrame, value); + set => RaiseAndSetIfChanged(ref _currentFrame, value); } public double CurrentFps { get => _currentFps; - set => this.RaiseAndSetIfChanged(ref _currentFps, value); + set => RaiseAndSetIfChanged(ref _currentFps, value); } public int RenderWidth { get => _renderWidth; - set => this.RaiseAndSetIfChanged(ref _renderWidth, value); + set => RaiseAndSetIfChanged(ref _renderWidth, value); } public int RenderHeight { get => _renderHeight; - set => this.RaiseAndSetIfChanged(ref _renderHeight, value); + set => RaiseAndSetIfChanged(ref _renderHeight, value); } public string? Renderer { get => _renderer; - set => this.RaiseAndSetIfChanged(ref _renderer, value); + set => RaiseAndSetIfChanged(ref _renderer, value); } private void HandleActivation() diff --git a/src/Avalonia/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs index 315e3744f..00d04d167 100644 --- a/src/Avalonia/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Device/DeviceSettingsViewModel.cs @@ -57,7 +57,7 @@ namespace Artemis.UI.Screens.Device public bool TogglingDevice { get => _togglingDevice; - set => this.RaiseAndSetIfChanged(ref _togglingDevice, value); + set => RaiseAndSetIfChanged(ref _togglingDevice, value); } public void IdentifyDevice() diff --git a/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabViewModel.cs index f11989e93..bb30a4238 100644 --- a/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DeviceLedsTabViewModel.cs @@ -60,7 +60,7 @@ namespace Artemis.UI.Screens.Device get => _isSelected; set { - if (!this.RaiseAndSetIfChanged(ref _isSelected, value)) + if (!RaiseAndSetIfChanged(ref _isSelected, value)) return; Apply(); } diff --git a/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DevicePropertiesTabViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DevicePropertiesTabViewModel.cs index 02050d9f7..aefb65d4f 100644 --- a/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DevicePropertiesTabViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Device/Tabs/DevicePropertiesTabViewModel.cs @@ -84,55 +84,55 @@ namespace Artemis.UI.Screens.Device public int X { get => _x; - set => this.RaiseAndSetIfChanged(ref _x, value); + set => RaiseAndSetIfChanged(ref _x, value); } public int Y { get => _y; - set => this.RaiseAndSetIfChanged(ref _y, value); + set => RaiseAndSetIfChanged(ref _y, value); } public float Scale { get => _scale; - set => this.RaiseAndSetIfChanged(ref _scale, value); + set => RaiseAndSetIfChanged(ref _scale, value); } public int Rotation { get => _rotation; - set => this.RaiseAndSetIfChanged(ref _rotation, value); + set => RaiseAndSetIfChanged(ref _rotation, value); } public float RedScale { get => _redScale; - set => this.RaiseAndSetIfChanged(ref _redScale, value); + set => RaiseAndSetIfChanged(ref _redScale, value); } public float GreenScale { get => _greenScale; - set => this.RaiseAndSetIfChanged(ref _greenScale, value); + set => RaiseAndSetIfChanged(ref _greenScale, value); } public float BlueScale { get => _blueScale; - set => this.RaiseAndSetIfChanged(ref _blueScale, value); + set => RaiseAndSetIfChanged(ref _blueScale, value); } public SKColor CurrentColor { get => _currentColor; - set => this.RaiseAndSetIfChanged(ref _currentColor, value); + set => RaiseAndSetIfChanged(ref _currentColor, value); } public bool DisplayOnDevices { get => _displayOnDevices; - set => this.RaiseAndSetIfChanged(ref _displayOnDevices, value); + set => RaiseAndSetIfChanged(ref _displayOnDevices, value); } // This solution won't scale well but I don't expect there to be many more categories. diff --git a/src/Avalonia/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs index 6a174e615..e7ba90cda 100644 --- a/src/Avalonia/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Device/Tabs/InputMappingsTabViewModel.cs @@ -37,7 +37,7 @@ namespace Artemis.UI.Screens.Device public ArtemisLed? SelectedLed { get => _selectedLed; - set => this.RaiseAndSetIfChanged(ref _selectedLed, value); + set => RaiseAndSetIfChanged(ref _selectedLed, value); } public ObservableCollection<(ArtemisLed, ArtemisLed)> InputMappings { get; } diff --git a/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesInstallDialogViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesInstallDialogViewModel.cs index 28fb0baff..c5240786f 100644 --- a/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesInstallDialogViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesInstallDialogViewModel.cs @@ -47,37 +47,37 @@ namespace Artemis.UI.Screens.Plugins public PluginPrerequisiteViewModel? ActivePrerequisite { get => _activePrerequisite; - set => this.RaiseAndSetIfChanged(ref _activePrerequisite, value); + set => RaiseAndSetIfChanged(ref _activePrerequisite, value); } public bool ShowProgress { get => _showProgress; - set => this.RaiseAndSetIfChanged(ref _showProgress, value); + set => RaiseAndSetIfChanged(ref _showProgress, value); } public bool ShowIntro { get => _showIntro; - set => this.RaiseAndSetIfChanged(ref _showIntro, value); + set => RaiseAndSetIfChanged(ref _showIntro, value); } public bool ShowFailed { get => _showFailed; - set => this.RaiseAndSetIfChanged(ref _showFailed, value); + set => RaiseAndSetIfChanged(ref _showFailed, value); } public bool ShowInstall { get => _showInstall; - set => this.RaiseAndSetIfChanged(ref _showInstall, value); + set => RaiseAndSetIfChanged(ref _showInstall, value); } public bool CanInstall { get => _canInstall; - set => this.RaiseAndSetIfChanged(ref _canInstall, value); + set => RaiseAndSetIfChanged(ref _canInstall, value); } public async Task Install() diff --git a/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesUninstallDialogViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesUninstallDialogViewModel.cs index f489d2726..16323dac4 100644 --- a/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesUninstallDialogViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Plugins/Dialogs/PluginPrerequisitesUninstallDialogViewModel.cs @@ -55,19 +55,19 @@ namespace Artemis.UI.Screens.Plugins public PluginPrerequisiteViewModel? ActivePrerequisite { get => _activePrerequisite; - set => this.RaiseAndSetIfChanged(ref _activePrerequisite, value); + set => RaiseAndSetIfChanged(ref _activePrerequisite, value); } public bool CanUninstall { get => _canUninstall; - set => this.RaiseAndSetIfChanged(ref _canUninstall, value); + set => RaiseAndSetIfChanged(ref _canUninstall, value); } public bool IsFinished { get => _isFinished; - set => this.RaiseAndSetIfChanged(ref _isFinished, value); + set => RaiseAndSetIfChanged(ref _isFinished, value); } public async Task Uninstall() diff --git a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginFeatureViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginFeatureViewModel.cs index 8a6678f3e..4e5a72da3 100644 --- a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginFeatureViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginFeatureViewModel.cs @@ -66,7 +66,7 @@ namespace Artemis.UI.Screens.Plugins public bool Enabling { get => _enabling; - set => this.RaiseAndSetIfChanged(ref _enabling, value); + set => RaiseAndSetIfChanged(ref _enabling, value); } public bool IsEnabled diff --git a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs index 85d617e1a..7fc226fc8 100644 --- a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginPrerequisiteViewModel.cs @@ -50,7 +50,7 @@ namespace Artemis.UI.Screens.Plugins public PluginPrerequisiteActionViewModel? ActiveAction { get => _activeAction; - set => this.RaiseAndSetIfChanged(ref _activeAction, value); + set => RaiseAndSetIfChanged(ref _activeAction, value); } public PluginPrerequisite PluginPrerequisite { get; } @@ -58,19 +58,19 @@ namespace Artemis.UI.Screens.Plugins public bool Installing { get => _installing; - set => this.RaiseAndSetIfChanged(ref _installing, value); + set => RaiseAndSetIfChanged(ref _installing, value); } public bool Uninstalling { get => _uninstalling; - set => this.RaiseAndSetIfChanged(ref _uninstalling, value); + set => RaiseAndSetIfChanged(ref _uninstalling, value); } public bool IsMet { get => _isMet; - set => this.RaiseAndSetIfChanged(ref _isMet, value); + set => RaiseAndSetIfChanged(ref _isMet, value); } public bool Busy => _busy.Value; diff --git a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginSettingsViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginSettingsViewModel.cs index 2334cfda6..6898bc122 100644 --- a/src/Avalonia/Artemis.UI/Screens/Plugins/PluginSettingsViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Plugins/PluginSettingsViewModel.cs @@ -78,13 +78,13 @@ namespace Artemis.UI.Screens.Plugins public Plugin Plugin { get => _plugin; - set => this.RaiseAndSetIfChanged(ref _plugin, value); + set => RaiseAndSetIfChanged(ref _plugin, value); } public bool Enabling { get => _enabling; - set => this.RaiseAndSetIfChanged(ref _enabling, value); + set => RaiseAndSetIfChanged(ref _enabling, value); } public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name; @@ -100,7 +100,7 @@ namespace Artemis.UI.Screens.Plugins get => _isSettingsPopupOpen; set { - if (!this.RaiseAndSetIfChanged(ref _isSettingsPopupOpen, value)) return; + if (!RaiseAndSetIfChanged(ref _isSettingsPopupOpen, value)) return; CheckPrerequisites(); } } @@ -108,13 +108,13 @@ namespace Artemis.UI.Screens.Plugins public bool CanInstallPrerequisites { get => _canInstallPrerequisites; - set => this.RaiseAndSetIfChanged(ref _canInstallPrerequisites, value); + set => RaiseAndSetIfChanged(ref _canInstallPrerequisites, value); } public bool CanRemovePrerequisites { get => _canRemovePrerequisites; - set => this.RaiseAndSetIfChanged(ref _canRemovePrerequisites, value); + set => RaiseAndSetIfChanged(ref _canRemovePrerequisites, value); } private void ExecuteOpenSettings() diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs index de0dd23f3..df9021b49 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/MenuBar/MenuBarViewModel.cs @@ -18,6 +18,6 @@ public class MenuBarViewModel : ActivatableViewModelBase public ProfileEditorHistory? History { get => _history; - set => this.RaiseAndSetIfChanged(ref _history, value); + set => RaiseAndSetIfChanged(ref _history, value); } } \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs index 25936d2a8..a07d4aa6f 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Playback/PlaybackViewModel.cs @@ -50,19 +50,19 @@ public class PlaybackViewModel : ActivatableViewModelBase public bool Repeating { get => _repeating; - set => this.RaiseAndSetIfChanged(ref _repeating, value); + set => RaiseAndSetIfChanged(ref _repeating, value); } public bool RepeatTimeline { get => _repeatTimeline; - set => this.RaiseAndSetIfChanged(ref _repeatTimeline, value); + set => RaiseAndSetIfChanged(ref _repeatTimeline, value); } public bool RepeatSegment { get => _repeatSegment; - set => this.RaiseAndSetIfChanged(ref _repeatSegment, value); + set => RaiseAndSetIfChanged(ref _repeatSegment, value); } public void PlayFromStart() diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs index 7cec467b0..fa6ffe96d 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/ProfileTreeViewModel.cs @@ -48,7 +48,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree public TreeItemViewModel? SelectedChild { get => _selectedChild; - set => this.RaiseAndSetIfChanged(ref _selectedChild, value); + set => RaiseAndSetIfChanged(ref _selectedChild, value); } private void SelectCurrentProfileElement(RenderProfileElement? element) diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs index b24e33450..86ecb14c6 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/ProfileTree/TreeItemViewModel.cs @@ -89,19 +89,19 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree public ProfileElement? ProfileElement { get => _profileElement; - set => this.RaiseAndSetIfChanged(ref _profileElement, value); + set => RaiseAndSetIfChanged(ref _profileElement, value); } public bool IsExpanded { get => _isExpanded; - set => this.RaiseAndSetIfChanged(ref _isExpanded, value); + set => RaiseAndSetIfChanged(ref _isExpanded, value); } public bool Renaming { get => _renaming; - set => this.RaiseAndSetIfChanged(ref _renaming, value); + set => RaiseAndSetIfChanged(ref _renaming, value); } public TreeItemViewModel? Parent { get; set; } @@ -118,7 +118,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree public string? RenameValue { get => _renameValue; - set => this.RaiseAndSetIfChanged(ref _renameValue, value); + set => RaiseAndSetIfChanged(ref _renameValue, value); } public async Task ShowBrokenStateExceptions() diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs index eb453dc1f..202d5f4df 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyGroupViewModel.cs @@ -64,19 +64,19 @@ public class PropertyGroupViewModel : ViewModelBase public bool IsVisible { get => _isVisible; - set => this.RaiseAndSetIfChanged(ref _isVisible, value); + set => RaiseAndSetIfChanged(ref _isVisible, value); } public bool IsExpanded { get => _isExpanded; - set => this.RaiseAndSetIfChanged(ref _isExpanded, value); + set => RaiseAndSetIfChanged(ref _isExpanded, value); } public bool HasChildren { get => _hasChildren; - set => this.RaiseAndSetIfChanged(ref _hasChildren, value); + set => RaiseAndSetIfChanged(ref _hasChildren, value); } public List GetAllKeyframeViewModels(bool expandedOnly) diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs index 3f6e00d25..36cbbd55f 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/PropertyViewModel.cs @@ -30,18 +30,18 @@ public class PropertyViewModel : ViewModelBase public bool IsVisible { get => _isVisible; - set => this.RaiseAndSetIfChanged(ref _isVisible, value); + set => RaiseAndSetIfChanged(ref _isVisible, value); } public bool IsHighlighted { get => _isHighlighted; - set => this.RaiseAndSetIfChanged(ref _isHighlighted, value); + set => RaiseAndSetIfChanged(ref _isHighlighted, value); } public bool IsExpanded { get => _isExpanded; - set => this.RaiseAndSetIfChanged(ref _isExpanded, value); + set => RaiseAndSetIfChanged(ref _isExpanded, value); } } \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs index be3aa871d..38bbf7940 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Keyframes/TimelineKeyframeViewModel.cs @@ -47,13 +47,13 @@ public class TimelineKeyframeViewModel : ActivatableViewModelBase, ITimelineK public double X { get => _x; - set => this.RaiseAndSetIfChanged(ref _x, value); + set => RaiseAndSetIfChanged(ref _x, value); } public string Timestamp { get => _timestamp; - set => this.RaiseAndSetIfChanged(ref _timestamp, value); + set => RaiseAndSetIfChanged(ref _timestamp, value); } public bool IsSelected => _isSelected?.Value ?? false; diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs index a78dace60..87cac2175 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/TimelineGroupViewModel.cs @@ -36,7 +36,7 @@ public class TimelineGroupViewModel : ActivatableViewModelBase public ObservableCollection? KeyframePositions { get => _keyframePositions; - set => this.RaiseAndSetIfChanged(ref _keyframePositions, value); + set => RaiseAndSetIfChanged(ref _keyframePositions, value); } private void UpdateKeyframePositions() 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 ea8d621ab..e409816dd 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/StatusBar/StatusBarViewModel.cs @@ -52,12 +52,12 @@ public class StatusBarViewModel : ActivatableViewModelBase public string? StatusMessage { get => _statusMessage; - set => this.RaiseAndSetIfChanged(ref _statusMessage, value); + set => RaiseAndSetIfChanged(ref _statusMessage, value); } public bool ShowStatusMessage { get => _showStatusMessage; - set => this.RaiseAndSetIfChanged(ref _showStatusMessage, value); + set => RaiseAndSetIfChanged(ref _showStatusMessage, value); } } \ No newline at end of file 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 d92f01b02..f0b7c4bdf 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 @@ -2,7 +2,160 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + 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"> - Welcome to Avalonia! + x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools.TransformToolView" + x:DataType="tools:TransformToolViewModel"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml.cs index 1149fe2ad..67562ca14 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolView.axaml.cs @@ -1,10 +1,20 @@ +using System; +using System.Linq; +using Avalonia; +using Avalonia.Controls.PanAndZoom; +using Avalonia.Input; +using Avalonia.LogicalTree; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; +using ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools; public class TransformToolView : ReactiveUserControl { + private ZoomBorder? _zoomBorder; + private PointerPoint _dragOffset; + public TransformToolView() { InitializeComponent(); @@ -14,4 +24,138 @@ public class TransformToolView : ReactiveUserControl { AvaloniaXamlLoader.Load(this); } + + #region Zoom + + /// + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + _zoomBorder = (ZoomBorder?)this.GetLogicalAncestors().FirstOrDefault(l => l is ZoomBorder); + if (_zoomBorder != null) + _zoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged; + base.OnAttachedToLogicalTree(e); + } + + /// + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if (_zoomBorder != null) + _zoomBorder.PropertyChanged -= ZoomBorderOnPropertyChanged; + base.OnDetachedFromLogicalTree(e); + } + + private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (e.Property != ZoomBorder.ZoomXProperty || _zoomBorder == null) + return; + + // TODO + } + + #endregion + + #region Rotation + + private void RotationOnPointerPressed(object? sender, PointerPressedEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + _dragOffset = e.GetCurrentPoint(_zoomBorder); + + e.Pointer.Capture(element); + e.Handled = true; + } + + private void RotationOnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || e.Pointer.Captured != element || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + e.Pointer.Capture(null); + e.Handled = true; + } + + private void RotationOnPointerMoved(object? sender, PointerEventArgs e) + { + IInputElement? element = (IInputElement?) sender; + if (element == null || e.Pointer.Captured != element || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + e.Handled = true; + } + + #endregion + + #region Movement + + private void MoveOnPointerPressed(object? sender, PointerPressedEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + _dragOffset = e.GetCurrentPoint(_zoomBorder); + + e.Pointer.Capture(element); + e.Handled = true; + } + + private void MoveOnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || e.Pointer.Captured != element || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + e.Pointer.Capture(null); + e.Handled = true; + } + + private void MoveOnPointerMoved(object? sender, PointerEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || e.Pointer.Captured != element || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + e.Handled = true; + } + + #endregion + + #region Resizing + + private void ResizeOnPointerPressed(object? sender, PointerPressedEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + _dragOffset = e.GetCurrentPoint(_zoomBorder); + + e.Pointer.Capture(element); + e.Handled = true; + } + + private void ResizeOnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || e.Pointer.Captured != element || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + e.Pointer.Capture(null); + e.Handled = true; + } + + private void ResizeOnPointerMoved(object? sender, PointerEventArgs e) + { + IInputElement? element = (IInputElement?)sender; + if (element == null || e.Pointer.Captured != element || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + + e.Handled = true; + } + + #endregion } \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs index 14ae1e5ec..e1b53e1e9 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Tools/TransformToolViewModel.cs @@ -1,21 +1,80 @@ -using System.Reactive.Linq; +using System; +using System.Reactive; +using System.Reactive.Linq; using Artemis.Core; +using Artemis.UI.Shared.Extensions; using Artemis.UI.Shared.Services.ProfileEditor; +using Avalonia; +using Avalonia.Controls.Mixins; using Material.Icons; using ReactiveUI; +using SkiaSharp; namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools; public class TransformToolViewModel : ToolViewModel { - private readonly ObservableAsPropertyHelper? _isEnabled; + private readonly ObservableAsPropertyHelper _isEnabled; + private RelativePoint _relativeAnchor; + private double _inverseRotation; + private ObservableAsPropertyHelper? _layer; + private double _rotation; + private Rect _shapeBounds; + private Point _anchor; /// public TransformToolViewModel(IProfileEditorService profileEditorService) { + // Not disposed when deactivated but when really disposed _isEnabled = profileEditorService.ProfileElement.Select(p => p is Layer).ToProperty(this, vm => vm.IsEnabled); + + this.WhenActivated(d => + { + _layer = profileEditorService.ProfileElement.Select(p => p as Layer).ToProperty(this, vm => vm.Layer).DisposeWith(d); + + this.WhenAnyValue(vm => vm.Layer) + .Select(l => l != null + ? Observable.FromEventPattern(x => l.RenderPropertiesUpdated += x, x => l.RenderPropertiesUpdated -= x) + : Observable.Never>()) + .Switch() + .Subscribe(_ => Update()) + .DisposeWith(d); + this.WhenAnyValue(vm => vm.Layer) + .Select(l => l != null + ? Observable.FromEventPattern(x => l.Transform.Position.CurrentValueSet += x, x => l.Transform.Position.CurrentValueSet -= x) + : Observable.Never>()) + .Switch() + .Subscribe(_ => Update()) + .DisposeWith(d); + this.WhenAnyValue(vm => vm.Layer) + .Select(l => l != null + ? Observable.FromEventPattern(x => l.Transform.Rotation.CurrentValueSet += x, x => l.Transform.Rotation.CurrentValueSet -= x) + : Observable.Never>()) + .Switch() + .Subscribe(_ => Update()) + .DisposeWith(d); + this.WhenAnyValue(vm => vm.Layer) + .Select(l => l != null + ? Observable.FromEventPattern(x => l.Transform.Scale.CurrentValueSet += x, x => l.Transform.Scale.CurrentValueSet -= x) + : Observable.Never>()) + .Switch() + .Subscribe(_ => Update()) + .DisposeWith(d); + this.WhenAnyValue(vm => vm.Layer) + .Select(l => l != null + ? Observable.FromEventPattern(x => l.Transform.AnchorPoint.CurrentValueSet += x, x => l.Transform.AnchorPoint.CurrentValueSet -= x) + : Observable.Never>()) + .Switch() + .Subscribe(_ => Update()) + .DisposeWith(d); + + this.WhenAnyValue(vm => vm.Layer).Subscribe(_ => Update()).DisposeWith(d); + profileEditorService.Time.Subscribe(_ => Update()).DisposeWith(d); + }); } + public Layer? Layer => _layer?.Value; + /// public override bool IsEnabled => _isEnabled?.Value ?? false; @@ -34,6 +93,36 @@ public class TransformToolViewModel : ToolViewModel /// public override string ToolTip => "Transform the shape of the current layer"; + public Rect ShapeBounds + { + get => _shapeBounds; + set => RaiseAndSetIfChanged(ref _shapeBounds, value); + } + + public double Rotation + { + get => _rotation; + set => RaiseAndSetIfChanged(ref _rotation, value); + } + + public double InverseRotation + { + get => _inverseRotation; + set => RaiseAndSetIfChanged(ref _inverseRotation, value); + } + + public RelativePoint RelativeAnchor + { + get => _relativeAnchor; + set => RaiseAndSetIfChanged(ref _relativeAnchor, value); + } + + public Point Anchor + { + get => _anchor; + set => RaiseAndSetIfChanged(ref _anchor, value); + } + /// protected override void Dispose(bool disposing) { @@ -42,4 +131,37 @@ public class TransformToolViewModel : ToolViewModel base.Dispose(disposing); } + + private void Update() + { + if (Layer == null) + return; + + SKPath path = new(); + path.AddRect(Layer.GetLayerBounds()); + path.Transform(Layer.GetTransformMatrix(false, true, true, false)); + + ShapeBounds = path.Bounds.ToRect(); + Rotation = Layer.Transform.Rotation.CurrentValue; + InverseRotation = Rotation * -1; + + // The middle of the element is 0.5/0.5 in Avalonia, in Artemis it's 0.0/0.0 so compensate for that below + SKPoint layerAnchor = Layer.Transform.AnchorPoint.CurrentValue; + RelativeAnchor = new RelativePoint(layerAnchor.X + 0.5, layerAnchor.Y + 0.5, RelativeUnit.Relative); + Anchor = new Point(ShapeBounds.Width * RelativeAnchor.Point.X, ShapeBounds.Height * RelativeAnchor.Point.Y); + } + + public enum ShapeControlPoint + { + TopLeft, + TopRight, + BottomRight, + BottomLeft, + TopCenter, + RightCenter, + BottomCenter, + LeftCenter, + LayerShape, + Anchor + } } \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorViewModel.cs index 0ed2ce175..43ca7c63c 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorViewModel.cs @@ -51,7 +51,7 @@ public class VisualEditorViewModel : ActivatableViewModelBase public ReadOnlyObservableCollection Tools { get => _tools; - set => this.RaiseAndSetIfChanged(ref _tools, value); + set => RaiseAndSetIfChanged(ref _tools, value); } private void CreateVisualizers(ProfileConfiguration? profileConfiguration) diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerShapeVisualizerViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerShapeVisualizerViewModel.cs index 24fc77b95..c180f00da 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerShapeVisualizerViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerShapeVisualizerViewModel.cs @@ -54,25 +54,25 @@ public class LayerShapeVisualizerViewModel : ActivatableViewModelBase, IVisualiz public Rect LayerBounds { get => _layerBounds; - private set => this.RaiseAndSetIfChanged(ref _layerBounds, value); + private set => RaiseAndSetIfChanged(ref _layerBounds, value); } public double X { get => _x; - set => this.RaiseAndSetIfChanged(ref _x, value); + set => RaiseAndSetIfChanged(ref _x, value); } public double Y { get => _y; - set => this.RaiseAndSetIfChanged(ref _y, value); + set => RaiseAndSetIfChanged(ref _y, value); } public Geometry? ShapeGeometry { get => _shapeGeometry; - set => this.RaiseAndSetIfChanged(ref _shapeGeometry, value); + set => RaiseAndSetIfChanged(ref _shapeGeometry, value); } public int Order => 2; diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerVisualizerViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerVisualizerViewModel.cs index 2750e4a8d..32fd84f96 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerVisualizerViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/Visualizers/LayerVisualizerViewModel.cs @@ -40,19 +40,19 @@ public class LayerVisualizerViewModel : ActivatableViewModelBase, IVisualizerVie public Rect LayerBounds { get => _layerBounds; - private set => this.RaiseAndSetIfChanged(ref _layerBounds, value); + private set => RaiseAndSetIfChanged(ref _layerBounds, value); } public double X { get => _x; - set => this.RaiseAndSetIfChanged(ref _x, value); + set => RaiseAndSetIfChanged(ref _x, value); } public double Y { get => _y; - set => this.RaiseAndSetIfChanged(ref _y, value); + set => RaiseAndSetIfChanged(ref _y, value); } public int Order => 1; diff --git a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs index 03b4aeaa0..a28bf40de 100644 --- a/src/Avalonia/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs @@ -70,7 +70,7 @@ namespace Artemis.UI.Screens.ProfileEditor public ReadOnlyObservableCollection Tools { get => _tools; - set => this.RaiseAndSetIfChanged(ref _tools, value); + set => RaiseAndSetIfChanged(ref _tools, value); } public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value; diff --git a/src/Avalonia/Artemis.UI/Screens/Root/RootViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Root/RootViewModel.cs index e88ca311d..59acf2659 100644 --- a/src/Avalonia/Artemis.UI/Screens/Root/RootViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Root/RootViewModel.cs @@ -77,13 +77,13 @@ namespace Artemis.UI.Screens.Root public SidebarViewModel? SidebarViewModel { get => _sidebarViewModel; - set => this.RaiseAndSetIfChanged(ref _sidebarViewModel, value); + set => RaiseAndSetIfChanged(ref _sidebarViewModel, value); } public ViewModelBase? TitleBarViewModel { get => _titleBarViewModel; - set => this.RaiseAndSetIfChanged(ref _titleBarViewModel, value); + set => RaiseAndSetIfChanged(ref _titleBarViewModel, value); } private void CurrentMainWindowOnClosed(object? sender, EventArgs e) diff --git a/src/Avalonia/Artemis.UI/Screens/Root/SplashViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Root/SplashViewModel.cs index e12ab63d6..d4f21eb52 100644 --- a/src/Avalonia/Artemis.UI/Screens/Root/SplashViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Root/SplashViewModel.cs @@ -30,7 +30,7 @@ namespace Artemis.UI.Screens.Root public string Status { get => _status; - set => this.RaiseAndSetIfChanged(ref _status, value); + set => RaiseAndSetIfChanged(ref _status, value); } private void OnPluginManagementServiceOnPluginManagementLoaded(object? sender, PluginEventArgs args) diff --git a/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/AboutTabViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/AboutTabViewModel.cs index c0641a06d..666bb5686 100644 --- a/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/AboutTabViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/AboutTabViewModel.cs @@ -27,31 +27,31 @@ namespace Artemis.UI.Screens.Settings public string? Version { get => _version; - set => this.RaiseAndSetIfChanged(ref _version, value); + set => RaiseAndSetIfChanged(ref _version, value); } public Bitmap? RobertProfileImage { get => _robertProfileImage; - set => this.RaiseAndSetIfChanged(ref _robertProfileImage, value); + set => RaiseAndSetIfChanged(ref _robertProfileImage, value); } public Bitmap? DarthAffeProfileImage { get => _darthAffeProfileImage; - set => this.RaiseAndSetIfChanged(ref _darthAffeProfileImage, value); + set => RaiseAndSetIfChanged(ref _darthAffeProfileImage, value); } public Bitmap? DrMeteorProfileImage { get => _drMeteorProfileImage; - set => this.RaiseAndSetIfChanged(ref _drMeteorProfileImage, value); + set => RaiseAndSetIfChanged(ref _drMeteorProfileImage, value); } public Bitmap? KaiProfileImage { get => _kaiProfileImage; - set => this.RaiseAndSetIfChanged(ref _kaiProfileImage, value); + set => RaiseAndSetIfChanged(ref _kaiProfileImage, value); } private async Task Activate() diff --git a/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs index 2777a26e5..39d3e3073 100644 --- a/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs @@ -46,7 +46,7 @@ namespace Artemis.UI.Screens.Settings public string? SearchPluginInput { get => _searchPluginInput; - set => this.RaiseAndSetIfChanged(ref _searchPluginInput, value); + set => RaiseAndSetIfChanged(ref _searchPluginInput, value); } public void OpenUrl(string url) diff --git a/src/Avalonia/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs index 06a005cea..366a3e90f 100644 --- a/src/Avalonia/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Sidebar/Dialogs/ProfileConfigurationEditViewModel.cs @@ -64,31 +64,31 @@ namespace Artemis.UI.Screens.Sidebar public ProfileConfiguration ProfileConfiguration { get => _profileConfiguration; - set => this.RaiseAndSetIfChanged(ref _profileConfiguration, value); + set => RaiseAndSetIfChanged(ref _profileConfiguration, value); } public string ProfileName { get => _profileName; - set => this.RaiseAndSetIfChanged(ref _profileName, value); + set => RaiseAndSetIfChanged(ref _profileName, value); } public ProfileConfigurationHotkeyMode HotkeyMode { get => _hotkeyMode; - set => this.RaiseAndSetIfChanged(ref _hotkeyMode, value); + set => RaiseAndSetIfChanged(ref _hotkeyMode, value); } public Hotkey? EnableHotkey { get => _enableHotkey; - set => this.RaiseAndSetIfChanged(ref _enableHotkey, value); + set => RaiseAndSetIfChanged(ref _enableHotkey, value); } public Hotkey? DisableHotkey { get => _disableHotkey; - set => this.RaiseAndSetIfChanged(ref _disableHotkey, value); + set => RaiseAndSetIfChanged(ref _disableHotkey, value); } public ObservableCollection Modules { get; } @@ -96,7 +96,7 @@ namespace Artemis.UI.Screens.Sidebar public ProfileModuleViewModel? SelectedModule { get => _selectedModule; - set => this.RaiseAndSetIfChanged(ref _selectedModule, value); + set => RaiseAndSetIfChanged(ref _selectedModule, value); } public async Task Import() @@ -183,31 +183,31 @@ namespace Artemis.UI.Screens.Sidebar public ProfileConfigurationIconType IconType { get => _iconType; - set => this.RaiseAndSetIfChanged(ref _iconType, value); + set => RaiseAndSetIfChanged(ref _iconType, value); } public ObservableCollection? MaterialIcons { get => _materialIcons; - set => this.RaiseAndSetIfChanged(ref _materialIcons, value); + set => RaiseAndSetIfChanged(ref _materialIcons, value); } public ProfileIconViewModel? SelectedMaterialIcon { get => _selectedMaterialIcon; - set => this.RaiseAndSetIfChanged(ref _selectedMaterialIcon, value); + set => RaiseAndSetIfChanged(ref _selectedMaterialIcon, value); } public Bitmap? SelectedBitmapSource { get => _selectedBitmapSource; - set => this.RaiseAndSetIfChanged(ref _selectedBitmapSource, value); + set => RaiseAndSetIfChanged(ref _selectedBitmapSource, value); } public SvgImage? SelectedSvgSource { get => _selectedSvgSource; - set => this.RaiseAndSetIfChanged(ref _selectedSvgSource, value); + set => RaiseAndSetIfChanged(ref _selectedSvgSource, value); } private void LoadIcon() diff --git a/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs index cc87bfda5..244b45660 100644 --- a/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs @@ -53,7 +53,7 @@ namespace Artemis.UI.Screens.Sidebar public SidebarProfileConfigurationViewModel? SelectedProfileConfiguration { get => _selectedProfileConfiguration; - set => this.RaiseAndSetIfChanged(ref _selectedProfileConfiguration, value); + set => RaiseAndSetIfChanged(ref _selectedProfileConfiguration, value); } public bool ShowItems diff --git a/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs index aa72a7b5e..8d788f49c 100644 --- a/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs @@ -89,13 +89,13 @@ namespace Artemis.UI.Screens.Sidebar public ArtemisDevice? HeaderDevice { get => _headerDevice; - set => this.RaiseAndSetIfChanged(ref _headerDevice, value); + set => RaiseAndSetIfChanged(ref _headerDevice, value); } public SidebarScreenViewModel? SelectedSidebarScreen { get => _selectedSidebarScreen; - set => this.RaiseAndSetIfChanged(ref _selectedSidebarScreen, value); + set => RaiseAndSetIfChanged(ref _selectedSidebarScreen, value); } public SidebarCategoryViewModel AddProfileCategoryViewModel(ProfileCategory profileCategory) diff --git a/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs b/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs index 947198734..c9f5db87a 100644 --- a/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/ListDeviceViewModel.cs @@ -20,13 +20,13 @@ namespace Artemis.UI.Screens.SurfaceEditor public bool IsSelected { get => _isSelected; - set => this.RaiseAndSetIfChanged(ref _isSelected, value); + set => RaiseAndSetIfChanged(ref _isSelected, value); } public SKColor Color { get => _color; - set => this.RaiseAndSetIfChanged(ref _color, value); + set => RaiseAndSetIfChanged(ref _color, value); } } } \ No newline at end of file diff --git a/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs b/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs index 7800ebf5c..5dcf03e62 100644 --- a/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/SurfaceEditor/SurfaceDeviceViewModel.cs @@ -53,7 +53,7 @@ namespace Artemis.UI.Screens.SurfaceEditor get => _selectionStatus; set { - this.RaiseAndSetIfChanged(ref _selectionStatus, value); + RaiseAndSetIfChanged(ref _selectionStatus, value); this.RaisePropertyChanged(nameof(Highlighted)); } } @@ -65,7 +65,7 @@ namespace Artemis.UI.Screens.SurfaceEditor public Cursor Cursor { get => _cursor; - set => this.RaiseAndSetIfChanged(ref _cursor, value); + set => RaiseAndSetIfChanged(ref _cursor, value); } public void StartMouseDrag(Point mouseStartPosition) diff --git a/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml b/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml index 2a4589ba1..e3a16ed34 100644 --- a/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml +++ b/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopView.axaml @@ -6,24 +6,26 @@ xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared" xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:attachedProperties="clr-namespace:Artemis.UI.Shared.AttachedProperties;assembly=Artemis.UI.Shared" + xmlns:workshop="clr-namespace:Artemis.UI.Screens.Workshop" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Artemis.UI.Screens.Workshop.WorkshopView"> + x:Class="Artemis.UI.Screens.Workshop.WorkshopView" + x:DataType="workshop:WorkshopViewModel"> Workshop!! :3 Notification tests - - - - @@ -36,6 +38,13 @@ + + + + + + + diff --git a/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopViewModel.cs b/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopViewModel.cs index 35b280461..95778a15f 100644 --- a/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopViewModel.cs +++ b/src/Avalonia/Artemis.UI/Screens/Workshop/WorkshopViewModel.cs @@ -1,6 +1,8 @@ using System.Reactive; +using System.Reactive.Linq; using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Interfaces; +using Avalonia.Input; using ReactiveUI; namespace Artemis.UI.Screens.Workshop @@ -8,10 +10,13 @@ namespace Artemis.UI.Screens.Workshop public class WorkshopViewModel : MainScreenViewModel { private readonly INotificationService _notificationService; + private StandardCursorType _selectedCursor; + private readonly ObservableAsPropertyHelper _cursor; public WorkshopViewModel(IScreen hostScreen, INotificationService notificationService) : base(hostScreen, "workshop") { _notificationService = notificationService; + _cursor = this.WhenAnyValue(vm => vm.SelectedCursor).Select(c => new Cursor(c)).ToProperty(this, vm => vm.Cursor); DisplayName = "Workshop"; ShowNotification = ReactiveCommand.Create(ExecuteShowNotification); @@ -19,6 +24,14 @@ namespace Artemis.UI.Screens.Workshop public ReactiveCommand ShowNotification { get; set; } + public StandardCursorType SelectedCursor + { + get => _selectedCursor; + set => RaiseAndSetIfChanged(ref _selectedCursor, value); + } + + public Cursor Cursor => _cursor.Value; + private void ExecuteShowNotification(NotificationSeverity severity) { _notificationService.CreateNotification().WithTitle("Test title").WithMessage("Test message").WithSeverity(severity).Show();