mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Profile editor - Added tools and layer visualizers
This commit is contained in:
parent
89beb92935
commit
bc1b44069c
@ -565,23 +565,24 @@ namespace Artemis.Core
|
||||
OnRenderPropertiesUpdated();
|
||||
}
|
||||
|
||||
internal SKPoint GetLayerAnchorPosition(bool applyTranslation, bool zeroBased)
|
||||
internal SKPoint GetLayerAnchorPosition(bool applyTranslation, bool zeroBased, SKRect? customBounds = null)
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
SKRect bounds = customBounds ?? Bounds;
|
||||
SKPoint positionProperty = Transform.Position.CurrentValue;
|
||||
|
||||
// Start at the center of the shape
|
||||
SKPoint position = zeroBased
|
||||
? new SKPointI(Bounds.MidX - Bounds.Left, Bounds.MidY - Bounds.Top)
|
||||
: new SKPointI(Bounds.MidX, Bounds.MidY);
|
||||
? new SKPoint(bounds.MidX - bounds.Left, bounds.MidY - Bounds.Top)
|
||||
: new SKPoint(bounds.MidX, bounds.MidY);
|
||||
|
||||
// Apply translation
|
||||
if (applyTranslation)
|
||||
{
|
||||
position.X += positionProperty.X * Bounds.Width;
|
||||
position.Y += positionProperty.Y * Bounds.Height;
|
||||
position.X += positionProperty.X * bounds.Width;
|
||||
position.Y += positionProperty.Y * bounds.Height;
|
||||
}
|
||||
|
||||
return position;
|
||||
@ -625,8 +626,9 @@ namespace Artemis.Core
|
||||
/// <param name="includeTranslation">Whether translation should be included</param>
|
||||
/// <param name="includeScale">Whether the scale should be included</param>
|
||||
/// <param name="includeRotation">Whether the rotation should be included</param>
|
||||
/// <param name="customBounds">Optional custom bounds to base the anchor on</param>
|
||||
/// <returns>The transformation matrix containing the current transformation settings</returns>
|
||||
public SKMatrix GetTransformMatrix(bool zeroBased, bool includeTranslation, bool includeScale, bool includeRotation)
|
||||
public SKMatrix GetTransformMatrix(bool zeroBased, bool includeTranslation, bool includeScale, bool includeRotation, SKRect? customBounds = null)
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
@ -634,15 +636,16 @@ namespace Artemis.Core
|
||||
if (Path == null)
|
||||
return SKMatrix.Empty;
|
||||
|
||||
SKRect bounds = customBounds ?? Bounds;
|
||||
SKSize sizeProperty = Transform.Scale.CurrentValue;
|
||||
float rotationProperty = Transform.Rotation.CurrentValue;
|
||||
|
||||
SKPoint anchorPosition = GetLayerAnchorPosition(true, zeroBased);
|
||||
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;
|
||||
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;
|
||||
|
||||
SKMatrix transform = SKMatrix.Empty;
|
||||
|
||||
|
||||
@ -4,9 +4,9 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
@ -36,6 +36,9 @@ namespace Artemis.UI.Linux
|
||||
controlledApplicationLifetime.Exit += (_, _) =>
|
||||
{
|
||||
RunForcedShutdownIfEnabled();
|
||||
|
||||
// Dispose plugins before disposing the kernel because plugins might access services during dispose
|
||||
kernel.Get<IPluginManagementService>().Dispose();
|
||||
kernel.Dispose();
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a profile editor command that can be used to change the LEDs of a layer.
|
||||
/// </summary>
|
||||
public class ChangeLayerLeds : IProfileEditorCommand
|
||||
{
|
||||
private readonly Layer _layer;
|
||||
private readonly List<ArtemisLed> _leds;
|
||||
private readonly List<ArtemisLed> _originalLeds;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ChangeLayerLeds" /> class.
|
||||
/// </summary>
|
||||
public ChangeLayerLeds(Layer layer, List<ArtemisLed> leds)
|
||||
{
|
||||
_layer = layer;
|
||||
_leds = leds;
|
||||
_originalLeds = new List<ArtemisLed>(_layer.Leds);
|
||||
}
|
||||
|
||||
#region Implementation of IProfileEditorCommand
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DisplayName => "Change layer LEDs";
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Execute()
|
||||
{
|
||||
_layer.ClearLeds();
|
||||
_layer.AddLeds(_leds);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Undo()
|
||||
{
|
||||
_layer.ClearLeds();
|
||||
_layer.AddLeds(_originalLeds);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -42,6 +42,11 @@ public interface IProfileEditorService : IArtemisSharedUIService
|
||||
/// </summary>
|
||||
IObservable<int> PixelsPerSecond { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a source list of all available editor tools.
|
||||
/// </summary>
|
||||
SourceList<IToolViewModel> Tools { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Connect to the observable list of keyframes and observe any changes starting with the list's initial items.
|
||||
/// </summary>
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
using Material.Icons;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.ProfileEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a profile editor tool.
|
||||
/// </summary>
|
||||
public interface IToolViewModel : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the tool is selected.
|
||||
/// </summary>
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the tool is enabled.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether or not this tool is exclusive.
|
||||
/// Exclusive tools deactivate any other exclusive tools when activated.
|
||||
/// </summary>
|
||||
public bool IsExclusive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether this tool should be shown in the toolbar.
|
||||
/// </summary>
|
||||
public bool ShowInToolbar { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the order in which this tool should appear in the toolbar.
|
||||
/// </summary>
|
||||
public int Order { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon which this tool should show in the toolbar.
|
||||
/// </summary>
|
||||
public MaterialIconKind Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tooltip which this tool should show in the toolbar.
|
||||
/// </summary>
|
||||
public string ToolTip { get; }
|
||||
}
|
||||
|
||||
public abstract class ToolViewModel : ActivatableViewModelBase, IToolViewModel
|
||||
{
|
||||
private bool _isSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#region Implementation of IToolViewModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set => this.RaiseAndSetIfChanged(ref _isSelected, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool IsEnabled { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool IsExclusive { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool ShowInToolbar { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract int Order { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract MaterialIconKind Icon { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string ToolTip { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
@ -9,6 +10,8 @@ using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.ProfileEditor;
|
||||
@ -43,9 +46,33 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
Playing = _playingSubject.AsObservable();
|
||||
SuspendedEditing = _suspendedEditingSubject.AsObservable();
|
||||
PixelsPerSecond = _pixelsPerSecondSubject.AsObservable();
|
||||
Tools = new SourceList<IToolViewModel>();
|
||||
Tools.Connect().AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Subscribe(set =>
|
||||
{
|
||||
IToolViewModel? changed = set.FirstOrDefault()?.Item.Current;
|
||||
if (changed == null)
|
||||
return;
|
||||
|
||||
// Disable all others if the changed one is selected and exclusive
|
||||
if (changed.IsSelected && changed.IsExclusive)
|
||||
{
|
||||
Tools.Edit(list =>
|
||||
{
|
||||
foreach (IToolViewModel toolViewModel in list.Where(t => t.IsExclusive && t != changed))
|
||||
toolViewModel.IsSelected = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public IObservable<bool> SuspendedEditing { get; }
|
||||
public IObservable<ProfileConfiguration?> ProfileConfiguration { get; }
|
||||
public IObservable<RenderProfileElement?> ProfileElement { get; }
|
||||
public IObservable<ProfileEditorHistory?> History { get; }
|
||||
public IObservable<TimeSpan> Time { get; }
|
||||
public IObservable<bool> Playing { get; }
|
||||
public IObservable<int> PixelsPerSecond { get; }
|
||||
public SourceList<IToolViewModel> Tools { get; }
|
||||
|
||||
private ProfileEditorHistory? GetHistory(ProfileConfiguration? profileConfiguration)
|
||||
{
|
||||
@ -87,13 +114,6 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
}
|
||||
}
|
||||
|
||||
public IObservable<ProfileConfiguration?> ProfileConfiguration { get; }
|
||||
public IObservable<RenderProfileElement?> ProfileElement { get; }
|
||||
public IObservable<ProfileEditorHistory?> History { get; }
|
||||
public IObservable<TimeSpan> Time { get; }
|
||||
public IObservable<bool> Playing { get; }
|
||||
public IObservable<int> PixelsPerSecond { get; }
|
||||
|
||||
public IObservable<IChangeSet<ILayerPropertyKeyframe>> ConnectToKeyframes()
|
||||
{
|
||||
return _selectedKeyframes.Connect();
|
||||
|
||||
@ -84,34 +84,10 @@ namespace Artemis.UI.Shared
|
||||
/// <summary>
|
||||
/// Represents the base class for Artemis view models that are interested in the activated event
|
||||
/// </summary>
|
||||
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel, IDisposable
|
||||
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected ActivatableViewModelBase()
|
||||
{
|
||||
this.WhenActivated(disposables => Disposable.Create(Dispose).DisposeWith(disposables));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ViewModelActivator Activator { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -39,6 +39,9 @@ namespace Artemis.UI.Windows
|
||||
controlledApplicationLifetime.Exit += (_, _) =>
|
||||
{
|
||||
RunForcedShutdownIfEnabled();
|
||||
|
||||
// Dispose plugins before disposing the kernel because plugins might access services during dispose
|
||||
kernel.Get<IPluginManagementService>().Dispose();
|
||||
kernel.Dispose();
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Artemis.UI.Windows": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--force-elevation --disable-forced-shutdown --pcmr"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,10 @@
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Ninject.InstanceProviders;
|
||||
using Artemis.UI.Screens;
|
||||
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Shared.PlatformSupport;
|
||||
using Ninject.Extensions.Conventions;
|
||||
@ -39,6 +41,14 @@ namespace Artemis.UI.Ninject
|
||||
.BindAllBaseClasses();
|
||||
});
|
||||
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.FromThisAssembly()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<IToolViewModel>()
|
||||
.BindAllInterfaces();
|
||||
});
|
||||
|
||||
// Bind UI factories
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
@ -65,8 +66,17 @@ namespace Artemis.UI.Screens.Device
|
||||
|
||||
this.WhenAnyValue(x => x.RedScale, x => x.GreenScale, x => x.BlueScale).Subscribe(_ => ApplyScaling());
|
||||
|
||||
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||
_coreService.FrameRendering += OnFrameRendering;
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Device.PropertyChanged += DeviceOnPropertyChanged;
|
||||
_coreService.FrameRendering += OnFrameRendering;
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_coreService.FrameRendering -= OnFrameRendering;
|
||||
Device.PropertyChanged -= DeviceOnPropertyChanged;
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public ArtemisDevice Device { get; }
|
||||
@ -235,18 +245,6 @@ namespace Artemis.UI.Screens.Device
|
||||
Device.BlueScale = _initialBlueScale;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_coreService.FrameRendering -= OnFrameRendering;
|
||||
Device.PropertyChanged -= DeviceOnPropertyChanged;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private bool GetCategory(DeviceCategory category)
|
||||
{
|
||||
return _categories.Contains(category);
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
@ -30,6 +31,15 @@ namespace Artemis.UI.Screens.Plugins
|
||||
|
||||
CanInstall = false;
|
||||
Task.Run(() => CanInstall = Prerequisites.Any(p => !p.PluginPrerequisite.IsMet()));
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_tokenSource?.Cancel();
|
||||
_tokenSource?.Dispose();
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public ObservableCollection<PluginPrerequisiteViewModel> Prerequisites { get; }
|
||||
@ -125,17 +135,5 @@ namespace Artemis.UI.Screens.Plugins
|
||||
{
|
||||
return await windowService.ShowDialogAsync<PluginPrerequisitesInstallDialogViewModel, bool>(("subjects", subjects));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_tokenSource?.Cancel();
|
||||
_tokenSource?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
@ -37,6 +38,15 @@ namespace Artemis.UI.Screens.Plugins
|
||||
|
||||
// Could be slow so take it off of the UI thread
|
||||
Task.Run(() => CanUninstall = Prerequisites.Any(p => p.PluginPrerequisite.IsMet()));
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_tokenSource?.Cancel();
|
||||
_tokenSource?.Dispose();
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public string CancelLabel { get; }
|
||||
@ -133,17 +143,5 @@ namespace Artemis.UI.Screens.Plugins
|
||||
{
|
||||
return await windowService.ShowDialogAsync<PluginPrerequisitesUninstallDialogViewModel, bool>(("subjects", subjects), ("cancelLabel", cancelLabel));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_tokenSource?.Cancel();
|
||||
_tokenSource?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
@ -36,12 +37,25 @@ namespace Artemis.UI.Screens.Plugins
|
||||
FeatureInfo = pluginFeatureInfo;
|
||||
ShowShield = FeatureInfo.Plugin.Info.RequiresAdmin && showShield;
|
||||
|
||||
_pluginManagementService.PluginFeatureEnabling += OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled += OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed += OnFeatureEnableStopped;
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_pluginManagementService.PluginFeatureEnabling += OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled += OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed += OnFeatureEnableStopped;
|
||||
|
||||
FeatureInfo.Plugin.Enabled += PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled += PluginOnToggled;
|
||||
FeatureInfo.Plugin.Enabled += PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled += PluginOnToggled;
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_pluginManagementService.PluginFeatureEnabling -= OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed -= OnFeatureEnableStopped;
|
||||
|
||||
FeatureInfo.Plugin.Enabled -= PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled -= PluginOnToggled;
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public PluginFeatureInfo FeatureInfo { get; }
|
||||
@ -99,22 +113,6 @@ namespace Artemis.UI.Screens.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_pluginManagementService.PluginFeatureEnabling -= OnFeatureEnabling;
|
||||
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnableStopped;
|
||||
_pluginManagementService.PluginFeatureEnableFailed -= OnFeatureEnableStopped;
|
||||
|
||||
FeatureInfo.Plugin.Enabled -= PluginOnToggled;
|
||||
FeatureInfo.Plugin.Disabled -= PluginOnToggled;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private async Task UpdateEnabled(bool enable)
|
||||
{
|
||||
if (IsEnabled == enable)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
@ -33,7 +34,12 @@ namespace Artemis.UI.Screens.Plugins
|
||||
this.WhenAnyValue(x => x.Installing, x => x.Uninstalling, (i, u) => i || u).ToProperty(this, x => x.Busy, out _busy);
|
||||
this.WhenAnyValue(x => x.ActiveAction, a => Actions.IndexOf(a!)).ToProperty(this, x => x.ActiveStepNumber, out _activeStepNumber);
|
||||
|
||||
PluginPrerequisite.PropertyChanged += PluginPrerequisiteOnPropertyChanged;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
PluginPrerequisite.PropertyChanged += PluginPrerequisiteOnPropertyChanged;
|
||||
Disposable.Create(() => PluginPrerequisite.PropertyChanged -= PluginPrerequisiteOnPropertyChanged).DisposeWith(d);
|
||||
});
|
||||
|
||||
// Could be slow so take it off of the UI thread
|
||||
Task.Run(() => IsMet = PluginPrerequisite.IsMet());
|
||||
@ -105,14 +111,6 @@ namespace Artemis.UI.Screens.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) PluginPrerequisite.PropertyChanged -= PluginPrerequisiteOnPropertyChanged;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void PluginPrerequisiteOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(PluginPrerequisite.CurrentAction))
|
||||
|
||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
@ -49,12 +50,23 @@ namespace Artemis.UI.Screens.Plugins
|
||||
foreach (PluginFeatureInfo pluginFeatureInfo in Plugin.Features)
|
||||
PluginFeatures.Add(_settingsVmFactory.CreatePluginFeatureViewModel(pluginFeatureInfo, false));
|
||||
|
||||
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
||||
|
||||
|
||||
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(x => x.IsEnabled).Select(isEnabled => isEnabled && Plugin.ConfigurationDialog != null));
|
||||
InstallPrerequisites = ReactiveCommand.CreateFromTask(ExecuteInstallPrerequisites, this.WhenAnyValue(x => x.CanInstallPrerequisites));
|
||||
RemovePrerequisites = ReactiveCommand.CreateFromTask<bool>(ExecuteRemovePrerequisites, this.WhenAnyValue(x => x.CanRemovePrerequisites));
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginToggled;
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> OpenSettings { get; }
|
||||
@ -237,17 +249,6 @@ namespace Artemis.UI.Screens.Plugins
|
||||
Utilities.OpenUrl(uri.ToString());
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginToggled;
|
||||
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginToggled;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void PluginManagementServiceOnPluginToggled(object? sender, PluginEventArgs e)
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(IsEnabled));
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools
|
||||
{
|
||||
public interface IToolViewModel
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools.SelectionAddToolView" ClipToBounds="False">
|
||||
<Grid>
|
||||
<controls:SelectionRectangle InputElement="{Binding $parent[ZoomBorder]}"
|
||||
BorderBrush="{DynamicResource SystemAccentColor}"
|
||||
BorderRadius="8"
|
||||
SelectionFinished="SelectionRectangle_OnSelectionFinished">
|
||||
<controls:SelectionRectangle.Background>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2"></SolidColorBrush>
|
||||
</controls:SelectionRectangle.Background>
|
||||
</controls:SelectionRectangle>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,24 @@
|
||||
using Artemis.UI.Shared.Events;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Skia;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
|
||||
public class SelectionAddToolView : ReactiveUserControl<SelectionAddToolViewModel>
|
||||
{
|
||||
public SelectionAddToolView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void SelectionRectangle_OnSelectionFinished(object? sender, SelectionRectangleEventArgs e)
|
||||
{
|
||||
ViewModel?.AddLedsInRectangle(e.Rectangle.ToSKRect());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Material.Icons;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
|
||||
public class SelectionAddToolViewModel : ToolViewModel
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly ObservableAsPropertyHelper<bool>? _isEnabled;
|
||||
private Layer? _layer;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SelectionAddToolViewModel(IProfileEditorService profileEditorService, IRgbService rgbService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_rgbService = rgbService;
|
||||
_isEnabled = profileEditorService.ProfileElement.Select(p => p is Layer).ToProperty(this, vm => vm.IsEnabled);
|
||||
|
||||
this.WhenActivated(d => profileEditorService.ProfileElement.Subscribe(p => _layer = p as Layer).DisposeWith(d));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsEnabled => _isEnabled?.Value ?? false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsExclusive => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool ShowInToolbar => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => 3;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override MaterialIconKind Icon => MaterialIconKind.SelectionDrag;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToolTip => "Add LEDs to the current layer";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
_isEnabled?.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public void AddLedsInRectangle(SKRect rect)
|
||||
{
|
||||
if (_layer == null)
|
||||
return;
|
||||
|
||||
List<ArtemisLed> leds = _rgbService.EnabledDevices.SelectMany(d => d.Leds).Where(l => l.AbsoluteRectangle.IntersectsWith(rect)).ToList();
|
||||
_profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
|
||||
xmlns:paz="clr-namespace:Avalonia.Controls.PanAndZoom;assembly=Avalonia.Controls.PanAndZoom"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools.SelectionRemoveToolView"
|
||||
ClipToBounds="False">
|
||||
<Grid>
|
||||
<controls:SelectionRectangle InputElement="{Binding $parent[paz:ZoomBorder]}"
|
||||
BorderBrush="{DynamicResource SystemAccentColor}"
|
||||
BorderRadius="8"
|
||||
SelectionFinished="SelectionRectangle_OnSelectionFinished">
|
||||
<controls:SelectionRectangle.Background>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorLight1}" Opacity="0.2"></SolidColorBrush>
|
||||
</controls:SelectionRectangle.Background>
|
||||
</controls:SelectionRectangle>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,24 @@
|
||||
using Artemis.UI.Shared.Events;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Skia;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
|
||||
public class SelectionRemoveToolView : ReactiveUserControl<SelectionRemoveToolViewModel>
|
||||
{
|
||||
public SelectionRemoveToolView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void SelectionRectangle_OnSelectionFinished(object? sender, SelectionRectangleEventArgs e)
|
||||
{
|
||||
ViewModel?.RemoveLedsInRectangle(e.Rectangle.ToSKRect());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Material.Icons;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
|
||||
public class SelectionRemoveToolViewModel : ToolViewModel
|
||||
{
|
||||
private readonly ObservableAsPropertyHelper<bool>? _isEnabled;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private Layer? _layer;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SelectionRemoveToolViewModel(IProfileEditorService profileEditorService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_isEnabled = profileEditorService.ProfileElement.Select(p => p is Layer).ToProperty(this, vm => vm.IsEnabled);
|
||||
this.WhenActivated(d => profileEditorService.ProfileElement.Subscribe(p => _layer = p as Layer).DisposeWith(d));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsEnabled => _isEnabled?.Value ?? false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsExclusive => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool ShowInToolbar => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => 3;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override MaterialIconKind Icon => MaterialIconKind.SelectOff;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToolTip => "Remove LEDs from the current layer";
|
||||
|
||||
public void RemoveLedsInRectangle(SKRect rect)
|
||||
{
|
||||
if (_layer == null)
|
||||
return;
|
||||
|
||||
List<ArtemisLed> leds = _layer.Leds.Except(_layer.Leds.Where(l => l.AbsoluteRectangle.IntersectsWith(rect))).ToList();
|
||||
_profileEditorService.ExecuteCommand(new ChangeLayerLeds(_layer, leds));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
_isEnabled?.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools.TransformToolView">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
@ -0,0 +1,17 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
|
||||
public class TransformToolView : ReactiveUserControl<TransformToolViewModel>
|
||||
{
|
||||
public TransformToolView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Material.Icons;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
|
||||
public class TransformToolViewModel : ToolViewModel
|
||||
{
|
||||
private readonly ObservableAsPropertyHelper<bool>? _isEnabled;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TransformToolViewModel(IProfileEditorService profileEditorService)
|
||||
{
|
||||
_isEnabled = profileEditorService.ProfileElement.Select(p => p is Layer).ToProperty(this, vm => vm.IsEnabled);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsEnabled => _isEnabled?.Value ?? false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsExclusive => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool ShowInToolbar => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => 3;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override MaterialIconKind Icon => MaterialIconKind.TransitConnectionVariant;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToolTip => "Transform the shape of the current layer";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
_isEnabled?.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,7 @@
|
||||
<ItemsControl Items="{CompiledBinding Tools}" ClipToBounds="False">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Canvas />
|
||||
<Grid />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
|
||||
@ -20,25 +20,26 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
||||
private readonly IProfileEditorVmFactory _vmFactory;
|
||||
private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration;
|
||||
private readonly SourceList<IVisualizerViewModel> _visualizers;
|
||||
private ReadOnlyObservableCollection<IToolViewModel> _tools;
|
||||
|
||||
public VisualEditorViewModel(IProfileEditorService profileEditorService, IRgbService rgbService, IProfileEditorVmFactory vmFactory)
|
||||
{
|
||||
_vmFactory = vmFactory;
|
||||
_visualizers = new SourceList<IVisualizerViewModel>();
|
||||
|
||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices);
|
||||
Tools = new ObservableCollection<IToolViewModel>();
|
||||
|
||||
_visualizers.Connect()
|
||||
.Sort(SortExpressionComparer<IVisualizerViewModel>.Ascending(vm => vm.Order))
|
||||
.Bind(out ReadOnlyObservableCollection<IVisualizerViewModel> visualizers)
|
||||
.Subscribe();
|
||||
|
||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices);
|
||||
Visualizers = visualizers;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
|
||||
profileEditorService.ProfileConfiguration.Subscribe(CreateVisualizers).DisposeWith(d);
|
||||
profileEditorService.Tools.Connect().AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Filter(t => t.IsSelected).Bind(out ReadOnlyObservableCollection<IToolViewModel> tools).Subscribe().DisposeWith(d);
|
||||
Tools = tools;
|
||||
});
|
||||
}
|
||||
|
||||
@ -46,7 +47,12 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
||||
|
||||
public ObservableCollection<ArtemisDevice> Devices { get; }
|
||||
public ReadOnlyObservableCollection<IVisualizerViewModel> Visualizers { get; }
|
||||
public ObservableCollection<IToolViewModel> Tools { get; }
|
||||
|
||||
public ReadOnlyObservableCollection<IToolViewModel> Tools
|
||||
{
|
||||
get => _tools;
|
||||
set => this.RaiseAndSetIfChanged(ref _tools, value);
|
||||
}
|
||||
|
||||
private void CreateVisualizers(ProfileConfiguration? profileConfiguration)
|
||||
{
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
public interface IVisualizerViewModel
|
||||
{
|
||||
int X { get; }
|
||||
int Y { get; }
|
||||
double X { get; }
|
||||
double Y { get; }
|
||||
int Order { get; }
|
||||
}
|
||||
@ -6,8 +6,7 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers.LayerShapeVisualizerView"
|
||||
x:DataType="visualizers:LayerShapeVisualizerViewModel"
|
||||
ClipToBounds="False"
|
||||
ZIndex="2">
|
||||
ClipToBounds="False">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Path.layer-visualizer">
|
||||
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
|
||||
|
||||
@ -7,13 +7,18 @@ using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Skia;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
|
||||
|
||||
public class LayerShapeVisualizerViewModel : ActivatableViewModelBase, IVisualizerViewModel
|
||||
{
|
||||
private ObservableAsPropertyHelper<bool>? _selected;
|
||||
private Rect _layerBounds;
|
||||
private double _x;
|
||||
private double _y;
|
||||
private Geometry? _shapeGeometry;
|
||||
|
||||
public LayerShapeVisualizerViewModel(Layer layer, IProfileEditorService profileEditorService)
|
||||
@ -40,13 +45,29 @@ public class LayerShapeVisualizerViewModel : ActivatableViewModelBase, IVisualiz
|
||||
|
||||
profileEditorService.Time.Subscribe(_ => UpdateTransform()).DisposeWith(d);
|
||||
Update();
|
||||
UpdateTransform();
|
||||
});
|
||||
}
|
||||
|
||||
public Layer Layer { get; }
|
||||
public bool Selected => _selected?.Value ?? false;
|
||||
public Rect LayerBounds => Layer.Bounds.ToRect();
|
||||
|
||||
public Rect LayerBounds
|
||||
{
|
||||
get => _layerBounds;
|
||||
private set => this.RaiseAndSetIfChanged(ref _layerBounds, value);
|
||||
}
|
||||
|
||||
public double X
|
||||
{
|
||||
get => _x;
|
||||
set => this.RaiseAndSetIfChanged(ref _x, value);
|
||||
}
|
||||
|
||||
public double Y
|
||||
{
|
||||
get => _y;
|
||||
set => this.RaiseAndSetIfChanged(ref _y, value);
|
||||
}
|
||||
|
||||
public Geometry? ShapeGeometry
|
||||
{
|
||||
@ -54,25 +75,43 @@ public class LayerShapeVisualizerViewModel : ActivatableViewModelBase, IVisualiz
|
||||
set => this.RaiseAndSetIfChanged(ref _shapeGeometry, value);
|
||||
}
|
||||
|
||||
public int Order => 2;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Layer.General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
|
||||
ShapeGeometry = new RectangleGeometry(new Rect(0, 0, Layer.Bounds.Width, Layer.Bounds.Height));
|
||||
else
|
||||
ShapeGeometry = new EllipseGeometry(new Rect(0, 0, Layer.Bounds.Width, Layer.Bounds.Height));
|
||||
UpdateLayerBounds();
|
||||
|
||||
this.RaisePropertyChanged(nameof(X));
|
||||
this.RaisePropertyChanged(nameof(Y));
|
||||
this.RaisePropertyChanged(nameof(LayerBounds));
|
||||
if (Layer.General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
|
||||
ShapeGeometry = new RectangleGeometry(LayerBounds);
|
||||
else
|
||||
ShapeGeometry = new EllipseGeometry(LayerBounds);
|
||||
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
private void UpdateLayerBounds()
|
||||
{
|
||||
// Create accurate bounds based on the RgbLeds and not the rounded ArtemisLeds
|
||||
SKPath path = new();
|
||||
foreach (ArtemisLed artemisLed in Layer.Leds)
|
||||
{
|
||||
path.AddRect(SKRect.Create(
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Location.X,
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Location.Y,
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Size.Width,
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Size.Height)
|
||||
);
|
||||
}
|
||||
|
||||
SKRect bounds = path.Bounds;
|
||||
LayerBounds = new Rect(0, 0, bounds.Width, bounds.Height);
|
||||
X = bounds.Left;
|
||||
Y = bounds.Top;
|
||||
}
|
||||
|
||||
private void UpdateTransform()
|
||||
{
|
||||
if (ShapeGeometry != null)
|
||||
ShapeGeometry.Transform = new MatrixTransform(Layer.GetTransformMatrix(true, true, true, true).ToMatrix());
|
||||
ShapeGeometry.Transform = new MatrixTransform(Layer.GetTransformMatrix(false, true, true, true, LayerBounds.ToSKRect()).ToMatrix());
|
||||
}
|
||||
|
||||
public int X => Layer.Bounds.Left;
|
||||
public int Y => Layer.Bounds.Top;
|
||||
public int Order => 2;
|
||||
}
|
||||
@ -6,7 +6,7 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers.LayerVisualizerView"
|
||||
x:DataType="visualizers:LayerVisualizerViewModel"
|
||||
ZIndex="1">
|
||||
ClipToBounds="False">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Path.layer-visualizer">
|
||||
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
|
||||
|
||||
@ -6,12 +6,16 @@ using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using ReactiveUI;
|
||||
using ShimSkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
|
||||
|
||||
public class LayerVisualizerViewModel : ActivatableViewModelBase, IVisualizerViewModel
|
||||
{
|
||||
private ObservableAsPropertyHelper<bool>? _selected;
|
||||
private Rect _layerBounds;
|
||||
private double _x;
|
||||
private double _y;
|
||||
|
||||
public LayerVisualizerViewModel(Layer layer, IProfileEditorService profileEditorService)
|
||||
{
|
||||
@ -25,21 +29,50 @@ public class LayerVisualizerViewModel : ActivatableViewModelBase, IVisualizerVie
|
||||
.Select(p => p == Layer)
|
||||
.ToProperty(this, vm => vm.Selected)
|
||||
.DisposeWith(d);
|
||||
|
||||
Update();
|
||||
});
|
||||
}
|
||||
|
||||
public Layer Layer { get; }
|
||||
public bool Selected => _selected?.Value ?? false;
|
||||
public Rect LayerBounds => new(0, 0, Layer.Bounds.Width, Layer.Bounds.Height);
|
||||
|
||||
public Rect LayerBounds
|
||||
{
|
||||
get => _layerBounds;
|
||||
private set => this.RaiseAndSetIfChanged(ref _layerBounds, value);
|
||||
}
|
||||
|
||||
public double X
|
||||
{
|
||||
get => _x;
|
||||
set => this.RaiseAndSetIfChanged(ref _x, value);
|
||||
}
|
||||
|
||||
public double Y
|
||||
{
|
||||
get => _y;
|
||||
set => this.RaiseAndSetIfChanged(ref _y, value);
|
||||
}
|
||||
|
||||
public int Order => 1;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
this.RaisePropertyChanged(nameof(X));
|
||||
this.RaisePropertyChanged(nameof(Y));
|
||||
this.RaisePropertyChanged(nameof(LayerBounds));
|
||||
}
|
||||
// Create accurate bounds based on the RgbLeds and not the rounded ArtemisLeds
|
||||
SKPath path = new();
|
||||
foreach (ArtemisLed artemisLed in Layer.Leds)
|
||||
{
|
||||
path.AddRect(SKRect.Create(
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Location.X,
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Location.Y,
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Size.Width,
|
||||
artemisLed.RgbLed.AbsoluteBoundary.Size.Height)
|
||||
);
|
||||
}
|
||||
|
||||
public int X => Layer.Bounds.Left;
|
||||
public int Y => Layer.Bounds.Top;
|
||||
public int Order => 1;
|
||||
LayerBounds = new Rect(0, 0, path.Bounds.Width, path.Bounds.Height);
|
||||
X = path.Bounds.Left;
|
||||
Y = path.Bounds.Top;
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:profileEditor="clr-namespace:Artemis.UI.Screens.ProfileEditor"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared.Services.ProfileEditor;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileEditorView"
|
||||
x:DataType="profileEditor:ProfileEditorViewModel">
|
||||
@ -45,7 +46,7 @@
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="{CompiledBinding TreeWidth.Value, Mode=TwoWay, Converter={StaticResource DoubleToGridLengthConverter}}"/>
|
||||
<ColumnDefinition Width="{CompiledBinding TreeWidth.Value, Mode=TwoWay, Converter={StaticResource DoubleToGridLengthConverter}}" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
@ -56,25 +57,23 @@
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="{CompiledBinding PropertiesHeight.Value, Mode=TwoWay, Converter={StaticResource DoubleToGridLengthConverter}}"/>
|
||||
<RowDefinition Height="{CompiledBinding PropertiesHeight.Value, Mode=TwoWay, Converter={StaticResource DoubleToGridLengthConverter}}" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Classes="card" Padding="0" Margin="4 0 4 4" ClipToBounds="True">
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<Border Grid.Column="0" Background="{DynamicResource CardStrokeColorDefaultSolidBrush}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<ToggleButton Classes="icon-button editor-sidebar-button">
|
||||
<avalonia:MaterialIcon Kind="HandLeft" />
|
||||
</ToggleButton>
|
||||
<ToggleButton Classes="icon-button editor-sidebar-button">
|
||||
<avalonia:MaterialIcon Kind="TransitConnectionVariant" />
|
||||
</ToggleButton>
|
||||
<ToggleButton Classes="icon-button editor-sidebar-button">
|
||||
<avalonia:MaterialIcon Kind="SelectionDrag" />
|
||||
</ToggleButton>
|
||||
<ToggleButton Classes="icon-button editor-sidebar-button">
|
||||
<avalonia:MaterialIcon Kind="SelectOff" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
<ItemsControl Items="{CompiledBinding Tools}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="shared:IToolViewModel">
|
||||
<ToggleButton Classes="icon-button editor-sidebar-button"
|
||||
ToolTip.Tip="{CompiledBinding ToolTip}"
|
||||
IsEnabled="{CompiledBinding IsEnabled}"
|
||||
IsChecked="{CompiledBinding IsSelected}">
|
||||
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" />
|
||||
</ToggleButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
<ContentControl Grid.Column="1" Content="{CompiledBinding VisualEditorViewModel}" />
|
||||
</Grid>
|
||||
@ -83,7 +82,7 @@
|
||||
<GridSplitter Grid.Row="1" Classes="editor-grid-splitter-horizontal" />
|
||||
|
||||
<Border Grid.Row="2" Classes="card card-condensed" Margin="4" Padding="0" ClipToBounds="True">
|
||||
<ContentControl Content="{CompiledBinding PropertiesViewModel}"/>
|
||||
<ContentControl Content="{CompiledBinding PropertiesViewModel}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
@ -91,9 +90,9 @@
|
||||
|
||||
<Grid Grid.Row="0" Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="{CompiledBinding ConditionsHeight.Value, Mode=TwoWay, Converter={StaticResource DoubleToGridLengthConverter}}"/>
|
||||
<RowDefinition Height="{CompiledBinding ConditionsHeight.Value, Mode=TwoWay, Converter={StaticResource DoubleToGridLengthConverter}}" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Classes="card card-condensed" Margin="4 0 4 4">
|
||||
<ContentControl Content="{CompiledBinding ProfileTreeViewModel}" />
|
||||
@ -106,6 +105,6 @@
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<ContentControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{CompiledBinding StatusBarViewModel}"/>
|
||||
<ContentControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{CompiledBinding StatusBarViewModel}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
@ -7,7 +8,10 @@ using Artemis.UI.Screens.ProfileEditor.ProfileTree;
|
||||
using Artemis.UI.Screens.ProfileEditor.Properties;
|
||||
using Artemis.UI.Screens.ProfileEditor.StatusBar;
|
||||
using Artemis.UI.Screens.ProfileEditor.VisualEditor;
|
||||
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using Ninject;
|
||||
using ReactiveUI;
|
||||
|
||||
@ -18,6 +22,7 @@ namespace Artemis.UI.Screens.ProfileEditor
|
||||
private readonly ISettingsService _settingsService;
|
||||
private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration;
|
||||
private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history;
|
||||
private ReadOnlyObservableCollection<IToolViewModel> _tools;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProfileEditorViewModel(IScreen hostScreen,
|
||||
@ -26,7 +31,7 @@ namespace Artemis.UI.Screens.ProfileEditor
|
||||
VisualEditorViewModel visualEditorViewModel,
|
||||
ProfileTreeViewModel profileTreeViewModel,
|
||||
ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel,
|
||||
MenuBarViewModel menuBarViewModel,
|
||||
MenuBarViewModel menuBarViewModel,
|
||||
PropertiesViewModel propertiesViewModel,
|
||||
StatusBarViewModel statusBarViewModel)
|
||||
: base(hostScreen, "profile-editor")
|
||||
@ -42,8 +47,18 @@ namespace Artemis.UI.Screens.ProfileEditor
|
||||
else
|
||||
MenuBarViewModel = menuBarViewModel;
|
||||
|
||||
this.WhenActivated(d => _profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d));
|
||||
this.WhenActivated(d => _history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d));
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
|
||||
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
|
||||
profileEditorService.Tools.Connect()
|
||||
.Filter(t => t.ShowInToolbar)
|
||||
.Sort(SortExpressionComparer<IToolViewModel>.Ascending(vm => vm.Order))
|
||||
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
|
||||
.Subscribe()
|
||||
.DisposeWith(d);
|
||||
Tools = tools;
|
||||
});
|
||||
}
|
||||
|
||||
public VisualEditorViewModel VisualEditorViewModel { get; }
|
||||
@ -52,6 +67,12 @@ namespace Artemis.UI.Screens.ProfileEditor
|
||||
public PropertiesViewModel PropertiesViewModel { get; }
|
||||
public StatusBarViewModel StatusBarViewModel { get; }
|
||||
|
||||
public ReadOnlyObservableCollection<IToolViewModel> Tools
|
||||
{
|
||||
get => _tools;
|
||||
set => this.RaiseAndSetIfChanged(ref _tools, value);
|
||||
}
|
||||
|
||||
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
|
||||
public ProfileEditorHistory? History => _history?.Value;
|
||||
public PluginSetting<double> TreeWidth => _settingsService.GetSetting("ProfileEditor.TreeWidth", 350.0);
|
||||
|
||||
@ -1,56 +1,61 @@
|
||||
using Artemis.Core;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.DefaultTypes.PropertyInput;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.PropertyInput;
|
||||
using DynamicData;
|
||||
|
||||
namespace Artemis.UI.Services
|
||||
namespace Artemis.UI.Services;
|
||||
|
||||
public class RegistrationService : IRegistrationService
|
||||
{
|
||||
public class RegistrationService : IRegistrationService
|
||||
private readonly IInputService _inputService;
|
||||
private readonly IPropertyInputService _propertyInputService;
|
||||
private bool _registeredBuiltInPropertyEditors;
|
||||
|
||||
public RegistrationService(IInputService inputService, IPropertyInputService propertyInputService, IProfileEditorService profileEditorService, IEnumerable<IToolViewModel> toolViewModels)
|
||||
{
|
||||
private readonly IInputService _inputService;
|
||||
private readonly IPropertyInputService _propertyInputService;
|
||||
private bool _registeredBuiltInPropertyEditors;
|
||||
_inputService = inputService;
|
||||
_propertyInputService = propertyInputService;
|
||||
|
||||
public RegistrationService(IInputService inputService, IPropertyInputService propertyInputService)
|
||||
{
|
||||
_inputService = inputService;
|
||||
_propertyInputService = propertyInputService;
|
||||
}
|
||||
public void RegisterBuiltInDataModelDisplays()
|
||||
{
|
||||
}
|
||||
profileEditorService.Tools.AddRange(toolViewModels);
|
||||
}
|
||||
|
||||
public void RegisterBuiltInDataModelInputs()
|
||||
{
|
||||
}
|
||||
public void RegisterBuiltInDataModelDisplays()
|
||||
{
|
||||
}
|
||||
|
||||
public void RegisterBuiltInPropertyEditors()
|
||||
{
|
||||
if (_registeredBuiltInPropertyEditors)
|
||||
return;
|
||||
public void RegisterBuiltInDataModelInputs()
|
||||
{
|
||||
}
|
||||
|
||||
_propertyInputService.RegisterPropertyInput<BrushPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<ColorGradientPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<FloatPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<IntPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<SKColorPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<SKPointPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<SKSizePropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput(typeof(EnumPropertyInputViewModel<>), Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<BoolPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<FloatRangePropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<IntRangePropertyInputViewModel>(Constants.CorePlugin);
|
||||
public void RegisterBuiltInPropertyEditors()
|
||||
{
|
||||
if (_registeredBuiltInPropertyEditors)
|
||||
return;
|
||||
|
||||
_registeredBuiltInPropertyEditors = true;
|
||||
}
|
||||
_propertyInputService.RegisterPropertyInput<BrushPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<ColorGradientPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<FloatPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<IntPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<SKColorPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<SKPointPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<SKSizePropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput(typeof(EnumPropertyInputViewModel<>), Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<BoolPropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<FloatRangePropertyInputViewModel>(Constants.CorePlugin);
|
||||
_propertyInputService.RegisterPropertyInput<IntRangePropertyInputViewModel>(Constants.CorePlugin);
|
||||
|
||||
public void RegisterControllers()
|
||||
{
|
||||
}
|
||||
_registeredBuiltInPropertyEditors = true;
|
||||
}
|
||||
|
||||
public void ApplyPreferredGraphicsContext()
|
||||
{
|
||||
}
|
||||
public void RegisterControllers()
|
||||
{
|
||||
}
|
||||
|
||||
public void ApplyPreferredGraphicsContext()
|
||||
{
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user