mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Profile tree - Implemented proper copy/pasting
This commit is contained in:
parent
f694f39219
commit
aae4e71d8e
@ -33,7 +33,13 @@ namespace Artemis.Core
|
||||
Parent.AddChild(this);
|
||||
}
|
||||
|
||||
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) : base(parent.Profile)
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Folder" /> class based on the provided folder entity
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile the folder belongs to</param>
|
||||
/// <param name="parent">The parent of the folder</param>
|
||||
/// <param name="folderEntity">The entity of the folder</param>
|
||||
public Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) : base(parent.Profile)
|
||||
{
|
||||
FolderEntity = folderEntity;
|
||||
EntityId = folderEntity.Id;
|
||||
|
||||
@ -122,29 +122,6 @@ namespace Artemis.Core
|
||||
|
||||
internal override RenderElementEntity RenderElementEntity => LayerEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep copy of the layer
|
||||
/// </summary>
|
||||
/// <returns>The newly created copy</returns>
|
||||
public Layer CreateCopy()
|
||||
{
|
||||
if (Parent == null)
|
||||
throw new ArtemisCoreException("Cannot create a copy of a layer without a parent");
|
||||
|
||||
LayerEntity entityCopy = CoreJson.DeserializeObject<LayerEntity>(CoreJson.SerializeObject(LayerEntity, true), true)!;
|
||||
entityCopy.Id = Guid.NewGuid();
|
||||
entityCopy.Name += " - Copy";
|
||||
|
||||
Layer copy = new Layer(Profile, Parent, entityCopy);
|
||||
if (LayerBrush?.Descriptor != null)
|
||||
copy.ChangeLayerBrush(LayerBrush.Descriptor);
|
||||
copy.AddLeds(Leds);
|
||||
|
||||
Parent.AddChild(copy, Order + 1);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override List<ILayerProperty> GetAllLayerProperties()
|
||||
{
|
||||
@ -398,8 +375,15 @@ namespace Artemis.Core
|
||||
canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
canvas.Restore();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
Renderer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,8 +12,8 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public sealed class Profile : ProfileElement
|
||||
{
|
||||
private bool _isActivated;
|
||||
private readonly object _lock = new object();
|
||||
private bool _isActivated;
|
||||
|
||||
internal Profile(ProfileModule module, string name) : base(null!)
|
||||
{
|
||||
@ -57,7 +57,10 @@ namespace Artemis.Core
|
||||
private set => SetAndNotify(ref _isActivated, value);
|
||||
}
|
||||
|
||||
internal ProfileEntity ProfileEntity { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the profile entity this profile uses for persistent storage
|
||||
/// </summary>
|
||||
public ProfileEntity ProfileEntity { get; internal set; }
|
||||
|
||||
internal Stack<string> UndoStack { get; set; }
|
||||
internal Stack<string> RedoStack { get; set; }
|
||||
@ -118,6 +121,19 @@ namespace Artemis.Core
|
||||
return $"[Profile] {nameof(Name)}: {Name}, {nameof(IsActivated)}: {IsActivated}, {nameof(Module)}: {Module}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates all the LEDs on the elements in this profile
|
||||
/// </summary>
|
||||
/// <param name="surface">The currently active surface that contains the LEDs</param>
|
||||
public void PopulateLeds(ArtemisSurface surface)
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
foreach (Layer layer in GetAllLayers())
|
||||
layer.PopulateLeds(surface);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
@ -196,15 +212,6 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
|
||||
internal void PopulateLeds(ArtemisSurface surface)
|
||||
{
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
foreach (Layer layer in GetAllLayers())
|
||||
layer.PopulateLeds(surface);
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -5,7 +5,10 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
internal static class CoreJson
|
||||
/// <summary>
|
||||
/// A static helper class that serializes and deserializes JSON with the Artemis Core JSON settings
|
||||
/// </summary>
|
||||
public static class CoreJson
|
||||
{
|
||||
#region Serialize
|
||||
|
||||
|
||||
@ -6,6 +6,9 @@ namespace Artemis.Storage.Entities.Profile.Abstract
|
||||
{
|
||||
public abstract class RenderElementEntity
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
public List<LayerEffectEntity> LayerEffects { get; set; }
|
||||
public List<PropertyEntity> PropertyEntities { get; set; }
|
||||
public List<string> ExpandedPropertyGroups { get; set; }
|
||||
|
||||
@ -14,9 +14,6 @@ namespace Artemis.Storage.Entities.Profile
|
||||
ExpandedPropertyGroups = new List<string>();
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
public int Order { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
@ -15,9 +15,6 @@ namespace Artemis.Storage.Entities.Profile
|
||||
ExpandedPropertyGroups = new List<string>();
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
public int Order { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
@ -92,46 +92,6 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <returns>The current module the profile editor is initialized for</returns>
|
||||
ProfileModule? GetCurrentModule();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a new profile is selected
|
||||
/// </summary>
|
||||
event EventHandler<ProfileEventArgs> ProfileSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs then the currently selected profile is updated
|
||||
/// </summary>
|
||||
event EventHandler<ProfileEventArgs> SelectedProfileUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a new profile element is selected
|
||||
/// </summary>
|
||||
event EventHandler<RenderProfileElementEventArgs> ProfileElementSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the currently selected profile element is updated
|
||||
/// </summary>
|
||||
event EventHandler<RenderProfileElementEventArgs> SelectedProfileElementUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the currently selected data binding layer property is changed
|
||||
/// </summary>
|
||||
event EventHandler SelectedDataBindingChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the current editor time is changed
|
||||
/// </summary>
|
||||
event EventHandler CurrentTimeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the pixels per second (zoom level) is changed
|
||||
/// </summary>
|
||||
event EventHandler PixelsPerSecondChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the profile preview has been updated
|
||||
/// </summary>
|
||||
event EventHandler ProfilePreviewUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new property input view model used in the profile editor for the generic type defined in
|
||||
/// <see cref="PropertyInputViewModel{T}" />
|
||||
@ -174,5 +134,66 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <typeparamref name="T" />
|
||||
/// </summary>
|
||||
PropertyInputViewModel<T>? CreatePropertyInputViewModel<T>(LayerProperty<T> layerProperty);
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates the provided profile element placing it in the same folder and one position higher
|
||||
/// </summary>
|
||||
/// <param name="profileElement">The profile element to duplicate</param>
|
||||
/// <returns>The duplicated profile element</returns>
|
||||
ProfileElement? DuplicateProfileElement(ProfileElement profileElement);
|
||||
|
||||
/// <summary>
|
||||
/// Copies the provided profile element onto the clipboard
|
||||
/// </summary>
|
||||
/// <param name="profileElement">The profile element to copy</param>
|
||||
void CopyProfileElement(ProfileElement profileElement);
|
||||
|
||||
/// <summary>
|
||||
/// Pastes a render profile element from the clipboard into the target folder
|
||||
/// </summary>
|
||||
/// <param name="target">The folder to paste the render element in to</param>
|
||||
/// <param name="position">The position at which to paste the element</param>
|
||||
/// <returns>The pasted render element</returns>
|
||||
ProfileElement? PasteProfileElement(Folder target, int position);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a new profile is selected
|
||||
/// </summary>
|
||||
event EventHandler<ProfileEventArgs> ProfileSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs then the currently selected profile is updated
|
||||
/// </summary>
|
||||
event EventHandler<ProfileEventArgs> SelectedProfileUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a new profile element is selected
|
||||
/// </summary>
|
||||
event EventHandler<RenderProfileElementEventArgs> ProfileElementSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the currently selected profile element is updated
|
||||
/// </summary>
|
||||
event EventHandler<RenderProfileElementEventArgs> SelectedProfileElementUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the currently selected data binding layer property is changed
|
||||
/// </summary>
|
||||
event EventHandler SelectedDataBindingChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the current editor time is changed
|
||||
/// </summary>
|
||||
event EventHandler CurrentTimeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the pixels per second (zoom level) is changed
|
||||
/// </summary>
|
||||
event EventHandler PixelsPerSecondChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the profile preview has been updated
|
||||
/// </summary>
|
||||
event EventHandler ProfilePreviewUpdated;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.Models
|
||||
{
|
||||
internal class FolderClipboardModel
|
||||
{
|
||||
public FolderClipboardModel(Folder folder)
|
||||
{
|
||||
FolderEntity = folder.FolderEntity;
|
||||
Folders = new List<FolderEntity>();
|
||||
Layers = new List<LayerEntity>();
|
||||
foreach (Folder allFolder in folder.GetAllFolders())
|
||||
Folders.Add(allFolder.FolderEntity);
|
||||
foreach (Layer allLayer in folder.GetAllLayers())
|
||||
Layers.Add(allLayer.LayerEntity);
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - For JSON.NET
|
||||
public FolderClipboardModel()
|
||||
{
|
||||
FolderEntity = null;
|
||||
Folders = new List<FolderEntity>();
|
||||
Layers = new List<LayerEntity>();
|
||||
}
|
||||
|
||||
public FolderEntity? FolderEntity { get; set; }
|
||||
public List<FolderEntity> Folders { get; set; }
|
||||
public List<LayerEntity> Layers { get; set; }
|
||||
public bool HasBeenPasted { get; set; }
|
||||
|
||||
public Folder Paste(Profile profile, ProfileElement parent)
|
||||
{
|
||||
if (FolderEntity == null)
|
||||
throw new ArtemisSharedUIException("Couldn't paste folder because FolderEntity deserialized as null");
|
||||
if (HasBeenPasted)
|
||||
throw new ArtemisSharedUIException("Clipboard model can only be pasted once");
|
||||
|
||||
HasBeenPasted = true;
|
||||
|
||||
// Generate new GUIDs
|
||||
ReplaceGuid(FolderEntity);
|
||||
foreach (FolderEntity folderEntity in Folders)
|
||||
ReplaceGuid(folderEntity);
|
||||
foreach (LayerEntity layerEntity in Layers)
|
||||
ReplaceGuid(layerEntity);
|
||||
|
||||
// Inject the pasted elements into the profile
|
||||
profile.ProfileEntity.Folders.AddRange(Folders);
|
||||
profile.ProfileEntity.Layers.AddRange(Layers);
|
||||
|
||||
// Let the folder initialize and load as usual
|
||||
FolderEntity.Name += " - copy";
|
||||
Folder folder = new Folder(profile, parent, FolderEntity);
|
||||
return folder;
|
||||
}
|
||||
|
||||
private void ReplaceGuid(RenderElementEntity parent)
|
||||
{
|
||||
Guid old = parent.Id;
|
||||
parent.Id = Guid.NewGuid();
|
||||
|
||||
foreach (FolderEntity child in Folders)
|
||||
{
|
||||
if (child.ParentId == old)
|
||||
child.ParentId = parent.Id;
|
||||
}
|
||||
|
||||
foreach (LayerEntity child in Layers)
|
||||
{
|
||||
if (child.ParentId == old)
|
||||
child.ParentId = parent.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,9 @@ using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.UI.Shared.Services.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
using Serilog;
|
||||
@ -14,23 +17,25 @@ namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
internal class ProfileEditorService : IProfileEditorService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly List<PropertyInputRegistration> _registeredPropertyEditors;
|
||||
private readonly object _selectedProfileElementLock = new object();
|
||||
private readonly object _selectedProfileLock = new object();
|
||||
private TimeSpan _currentTime;
|
||||
private int _pixelsPerSecond;
|
||||
private readonly IKernel _kernel;
|
||||
|
||||
public ProfileEditorService(IProfileService profileService, IKernel kernel, ILogger logger, ICoreService coreService)
|
||||
public ProfileEditorService(IKernel kernel, ILogger logger, IProfileService profileService, ICoreService coreService, ISurfaceService surfaceService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
_logger = logger;
|
||||
_coreService = coreService;
|
||||
_registeredPropertyEditors = new List<PropertyInputRegistration>();
|
||||
_kernel = kernel;
|
||||
_logger = logger;
|
||||
_profileService = profileService;
|
||||
_coreService = coreService;
|
||||
_surfaceService = surfaceService;
|
||||
_registeredPropertyEditors = new List<PropertyInputRegistration>();
|
||||
|
||||
PixelsPerSecond = 100;
|
||||
}
|
||||
@ -41,6 +46,30 @@ namespace Artemis.UI.Shared.Services
|
||||
Execute.PostToUIThread(OnProfilePreviewUpdated);
|
||||
}
|
||||
|
||||
private void ReloadProfile()
|
||||
{
|
||||
if (SelectedProfile == null)
|
||||
return;
|
||||
|
||||
// Trigger a profile change
|
||||
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
|
||||
// Trigger a selected element change
|
||||
RenderProfileElement? previousSelectedProfileElement = SelectedProfileElement;
|
||||
if (SelectedProfileElement is Folder folder)
|
||||
SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId);
|
||||
else if (SelectedProfileElement is Layer layer)
|
||||
SelectedProfileElement = SelectedProfile.GetAllLayers().FirstOrDefault(l => l.EntityId == layer.EntityId);
|
||||
OnSelectedProfileElementChanged(new RenderProfileElementEventArgs(SelectedProfileElement, previousSelectedProfileElement));
|
||||
// Trigger selected data binding change
|
||||
if (SelectedDataBinding != null)
|
||||
{
|
||||
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.Path);
|
||||
OnSelectedDataBindingChanged();
|
||||
}
|
||||
|
||||
UpdateProfilePreview();
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<PropertyInputRegistration> RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly();
|
||||
public Profile? SelectedProfile { get; private set; }
|
||||
public RenderProfileElement? SelectedProfileElement { get; private set; }
|
||||
@ -107,7 +136,7 @@ namespace Artemis.UI.Shared.Services
|
||||
return;
|
||||
|
||||
_profileService.UpdateProfile(SelectedProfile, true);
|
||||
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile));
|
||||
OnSelectedProfileUpdated(new ProfileEventArgs(SelectedProfile));
|
||||
UpdateProfilePreview();
|
||||
}
|
||||
}
|
||||
@ -206,7 +235,7 @@ namespace Artemis.UI.Shared.Services
|
||||
if (supportedType.IsGenericParameter)
|
||||
{
|
||||
if (supportedType.BaseType == null)
|
||||
throw new ArtemisSharedUIException($"Generic property input VM type must have a type constraint");
|
||||
throw new ArtemisSharedUIException("Generic property input VM type must have a type constraint");
|
||||
supportedType = supportedType.BaseType;
|
||||
}
|
||||
|
||||
@ -258,11 +287,9 @@ namespace Artemis.UI.Shared.Services
|
||||
}
|
||||
|
||||
if (snapToCurrentTime)
|
||||
{
|
||||
// Snap to the current time
|
||||
if (Math.Abs(time.TotalMilliseconds - CurrentTime.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return CurrentTime;
|
||||
}
|
||||
|
||||
if (snapTimes != null)
|
||||
{
|
||||
@ -289,9 +316,13 @@ namespace Artemis.UI.Shared.Services
|
||||
viewModelType = registration.ViewModelType.MakeGenericType(layerProperty.GetType().GenericTypeArguments);
|
||||
}
|
||||
else if (registration != null)
|
||||
{
|
||||
viewModelType = registration.ViewModelType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (viewModelType == null)
|
||||
return null;
|
||||
@ -308,30 +339,80 @@ namespace Artemis.UI.Shared.Services
|
||||
return SelectedProfile?.Module;
|
||||
}
|
||||
|
||||
private void ReloadProfile()
|
||||
{
|
||||
if (SelectedProfile == null)
|
||||
return;
|
||||
#region Copy/paste
|
||||
|
||||
// Trigger a profile change
|
||||
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
|
||||
// Trigger a selected element change
|
||||
RenderProfileElement? previousSelectedProfileElement = SelectedProfileElement;
|
||||
if (SelectedProfileElement is Folder folder)
|
||||
SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId);
|
||||
else if (SelectedProfileElement is Layer layer)
|
||||
SelectedProfileElement = SelectedProfile.GetAllLayers().FirstOrDefault(l => l.EntityId == layer.EntityId);
|
||||
OnSelectedProfileElementChanged(new RenderProfileElementEventArgs(SelectedProfileElement, previousSelectedProfileElement));
|
||||
// Trigger selected data binding change
|
||||
if (SelectedDataBinding != null)
|
||||
public ProfileElement? DuplicateProfileElement(ProfileElement profileElement)
|
||||
{
|
||||
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.Path);
|
||||
OnSelectedDataBindingChanged();
|
||||
if (!(profileElement.Parent is Folder parent))
|
||||
return null;
|
||||
|
||||
object? clipboardModel = null;
|
||||
switch (profileElement)
|
||||
{
|
||||
case Folder folder:
|
||||
{
|
||||
clipboardModel = CoreJson.DeserializeObject(CoreJson.SerializeObject(new FolderClipboardModel(folder), true), true);
|
||||
break;
|
||||
}
|
||||
case Layer layer:
|
||||
clipboardModel = CoreJson.DeserializeObject(CoreJson.SerializeObject(layer.LayerEntity, true), true);
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateProfilePreview();
|
||||
return clipboardModel == null ? null : PasteClipboardData(clipboardModel, parent, profileElement.Order);
|
||||
}
|
||||
|
||||
public void CopyProfileElement(ProfileElement profileElement)
|
||||
{
|
||||
switch (profileElement)
|
||||
{
|
||||
case Folder folder:
|
||||
{
|
||||
FolderClipboardModel clipboardModel = new FolderClipboardModel(folder);
|
||||
JsonClipboard.SetObject(clipboardModel);
|
||||
break;
|
||||
}
|
||||
case Layer layer:
|
||||
JsonClipboard.SetObject(layer.LayerEntity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public ProfileElement? PasteProfileElement(Folder target, int position)
|
||||
{
|
||||
object? clipboardObject = JsonClipboard.GetData();
|
||||
return clipboardObject != null ? PasteClipboardData(clipboardObject, target, position) : null;
|
||||
}
|
||||
|
||||
private RenderProfileElement? PasteClipboardData(object clipboardObject, Folder target, int position)
|
||||
{
|
||||
RenderProfileElement? pasted = null;
|
||||
switch (clipboardObject)
|
||||
{
|
||||
case FolderClipboardModel folderClipboardModel:
|
||||
pasted = folderClipboardModel.Paste(target.Profile, target);
|
||||
target.AddChild(pasted, position);
|
||||
break;
|
||||
case LayerEntity layerEntity:
|
||||
layerEntity.Id = Guid.NewGuid();
|
||||
layerEntity.Name += " - copy";
|
||||
pasted = new Layer(target.Profile, target, layerEntity);
|
||||
target.AddChild(pasted, position);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pasted != null)
|
||||
{
|
||||
target.Profile.PopulateLeds(_surfaceService.ActiveSurface);
|
||||
UpdateSelectedProfile();
|
||||
ChangeSelectedProfileElement(pasted);
|
||||
}
|
||||
|
||||
return pasted;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<ProfileEventArgs>? ProfileSelected;
|
||||
@ -396,6 +477,5 @@ namespace Artemis.UI.Shared.Services
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
58
src/Artemis.UI.Shared/Utilities/JsonClipboard.cs
Normal file
58
src/Artemis.UI.Shared/Utilities/JsonClipboard.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Windows;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the clipboard via JSON-serialized objects
|
||||
/// </summary>
|
||||
public static class JsonClipboard
|
||||
{
|
||||
private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
|
||||
|
||||
/// <summary>
|
||||
/// Sets the provided object on the clipboard
|
||||
/// </summary>
|
||||
/// <param name="clipboardObject">The object to set on the clipboard</param>
|
||||
public static void SetObject(object clipboardObject)
|
||||
{
|
||||
string json = JsonConvert.SerializeObject(clipboardObject, JsonSettings);
|
||||
Clipboard.SetData("Artemis", json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If set, gets the current object off of the clipboard
|
||||
/// </summary>
|
||||
/// <returns>The object that is on the clipboard, if none is set <see langword="null" />.</returns>
|
||||
public static object? GetData()
|
||||
{
|
||||
string? json = Clipboard.GetData("Artemis")?.ToString();
|
||||
return json != null ? JsonConvert.DeserializeObject(json, JsonSettings) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If set, gets the current object of type <typeparamref name="T" /> off of the clipboard
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to get</typeparam>
|
||||
/// <returns>
|
||||
/// The object that is on the clipboard. If none is set or not of type <typeparamref name="T" />,
|
||||
/// <see langword="null" />.
|
||||
/// </returns>
|
||||
[return: MaybeNull]
|
||||
public static T GetData<T>()
|
||||
{
|
||||
object? data = GetData();
|
||||
return data is T castData ? castData : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the clipboard currently contains Artemis data
|
||||
/// </summary>
|
||||
/// <returns><see langword="true" /> if the clipboard contains Artemis data, otherwise <see langword="false" /></returns>
|
||||
public static bool ContainsArtemisData()
|
||||
{
|
||||
return Clipboard.ContainsData("Artemis");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,9 +104,16 @@ namespace Artemis.UI.Behaviors
|
||||
if (SelectedItem == model && !item.IsSelected)
|
||||
{
|
||||
item.IsSelected = true;
|
||||
// Focus the newly selected item as if was clicked
|
||||
item.Focus();
|
||||
if (ExpandSelected)
|
||||
item.IsExpanded = true;
|
||||
}
|
||||
else if (SelectedItem == model && !item.IsFocused)
|
||||
{
|
||||
// Focus the newly selected item as if was clicked
|
||||
item.Focus();
|
||||
}
|
||||
// If the selected item is a parent of this model - expand
|
||||
else
|
||||
{
|
||||
@ -117,7 +124,6 @@ namespace Artemis.UI.Behaviors
|
||||
|
||||
// Recurse into children
|
||||
if (recurse)
|
||||
{
|
||||
foreach (object subitem in item.Items)
|
||||
{
|
||||
TreeViewItem tvi = item.ItemContainerGenerator.ContainerFromItem(subitem) as TreeViewItem;
|
||||
@ -125,7 +131,6 @@ namespace Artemis.UI.Behaviors
|
||||
UpdateTreeViewItem(tvi, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update state of all items
|
||||
private void UpdateAllTreeViewItems()
|
||||
@ -143,11 +148,9 @@ namespace Artemis.UI.Behaviors
|
||||
private void UpdateTreeViewItemStyle()
|
||||
{
|
||||
if (AssociatedObject.ItemContainerStyle == null)
|
||||
{
|
||||
AssociatedObject.ItemContainerStyle = new Style(
|
||||
typeof(TreeViewItem),
|
||||
Application.Current.TryFindResource(typeof(TreeViewItem)) as Style);
|
||||
}
|
||||
|
||||
if (!AssociatedObject.ItemContainerStyle.Setters.Contains(_treeViewItemEventSetter))
|
||||
AssociatedObject.ItemContainerStyle.Setters.Add(_treeViewItemEventSetter);
|
||||
|
||||
20
src/Artemis.UI/Extensions/ScreenExtensions.cs
Normal file
20
src/Artemis.UI/Extensions/ScreenExtensions.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Extensions
|
||||
{
|
||||
public static class ScreenExtensions
|
||||
{
|
||||
public static T FindScreenOfType<T>(this Screen screen) where T : Screen
|
||||
{
|
||||
Screen parent = screen.Parent as Screen;
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent is T match)
|
||||
return match;
|
||||
parent = parent.Parent as Screen;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,6 +36,7 @@
|
||||
<TreeView.InputBindings>
|
||||
<KeyBinding Key="F2" Command="{s:Action RenameElement}" s:View.ActionTarget="{Binding SelectedTreeItem}" />
|
||||
<KeyBinding Key="Delete" Command="{s:Action DeleteElement}" s:View.ActionTarget="{Binding SelectedTreeItem}" />
|
||||
<KeyBinding Key="D" Modifiers="Control" Command="{s:Action DuplicateElement}" s:View.ActionTarget="{Binding SelectedTreeItem}" />
|
||||
<KeyBinding Key="C" Modifiers="Control" Command="{s:Action CopyElement}" s:View.ActionTarget="{Binding SelectedTreeItem}" />
|
||||
<KeyBinding Key="V" Modifiers="Control" Command="{s:Action PasteElement}" s:View.ActionTarget="{Binding SelectedTreeItem}" />
|
||||
</TreeView.InputBindings>
|
||||
|
||||
@ -13,8 +13,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
{
|
||||
public class ProfileTreeViewModel : Conductor<FolderViewModel>, IProfileEditorPanelViewModel, IDropTarget
|
||||
{
|
||||
private readonly IProfileTreeVmFactory _profileTreeVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly IProfileTreeVmFactory _profileTreeVmFactory;
|
||||
private TreeItemViewModel _selectedTreeItem;
|
||||
private bool _updatingTree;
|
||||
|
||||
@ -41,6 +41,73 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
public void AddFolder()
|
||||
{
|
||||
ActiveItem?.AddFolder();
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
public void AddLayer()
|
||||
{
|
||||
ActiveItem?.AddLayer();
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
Subscribe();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
Unsubscribe();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
private void CreateRootFolderViewModel()
|
||||
{
|
||||
_updatingTree = true;
|
||||
ProfileElement firstChild = _profileEditorService.SelectedProfile?.Children?.FirstOrDefault();
|
||||
if (!(firstChild is Folder folder))
|
||||
{
|
||||
ActivateItem(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ActivateItem(_profileTreeVmFactory.FolderViewModel(folder));
|
||||
_updatingTree = false;
|
||||
}
|
||||
|
||||
private static DragDropType GetDragDropType(IDropInfo dropInfo)
|
||||
{
|
||||
TreeItemViewModel source = (TreeItemViewModel) dropInfo.Data;
|
||||
TreeItemViewModel target = (TreeItemViewModel) dropInfo.TargetItem;
|
||||
if (source == target)
|
||||
return DragDropType.None;
|
||||
|
||||
TreeItemViewModel parent = target;
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent == source)
|
||||
return DragDropType.None;
|
||||
parent = parent.Parent as TreeItemViewModel;
|
||||
}
|
||||
|
||||
switch (dropInfo.InsertPosition)
|
||||
{
|
||||
case RelativeInsertPosition.AfterTargetItem | RelativeInsertPosition.TargetItemCenter:
|
||||
case RelativeInsertPosition.BeforeTargetItem | RelativeInsertPosition.TargetItemCenter:
|
||||
return target.SupportsChildren ? DragDropType.Add : DragDropType.None;
|
||||
case RelativeInsertPosition.BeforeTargetItem:
|
||||
return DragDropType.InsertBefore;
|
||||
case RelativeInsertPosition.AfterTargetItem:
|
||||
return DragDropType.InsertAfter;
|
||||
default:
|
||||
return DragDropType.None;
|
||||
}
|
||||
}
|
||||
|
||||
public void DragOver(IDropInfo dropInfo)
|
||||
{
|
||||
DragDropType dragDropType = GetDragDropType(dropInfo);
|
||||
@ -84,80 +151,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
Subscribe();
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
public void AddFolder()
|
||||
{
|
||||
ActiveItem?.AddFolder();
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Global - Called from view
|
||||
public void AddLayer()
|
||||
{
|
||||
ActiveItem?.AddLayer();
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
Subscribe();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
Unsubscribe();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
private void CreateRootFolderViewModel()
|
||||
{
|
||||
_updatingTree = true;
|
||||
ProfileElement firstChild = _profileEditorService.SelectedProfile?.Children?.FirstOrDefault();
|
||||
if (!(firstChild is Folder folder))
|
||||
{
|
||||
ActivateItem(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ActivateItem(_profileTreeVmFactory.FolderViewModel(folder));
|
||||
_updatingTree = false;
|
||||
|
||||
// Auto-select the first layer
|
||||
// if (_profileEditorService.SelectedProfile != null && SelectedTreeItem == null)
|
||||
// {
|
||||
// if (_profileEditorService.SelectedProfile.GetRootFolder().Children.FirstOrDefault() is RenderProfileElement firstElement)
|
||||
// Execute.PostToUIThread(() => _profileEditorService.ChangeSelectedProfileElement(firstElement));
|
||||
// }
|
||||
}
|
||||
|
||||
private static DragDropType GetDragDropType(IDropInfo dropInfo)
|
||||
{
|
||||
TreeItemViewModel source = (TreeItemViewModel) dropInfo.Data;
|
||||
TreeItemViewModel target = (TreeItemViewModel) dropInfo.TargetItem;
|
||||
if (source == target)
|
||||
return DragDropType.None;
|
||||
|
||||
TreeItemViewModel parent = target;
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent == source)
|
||||
return DragDropType.None;
|
||||
parent = parent.Parent as TreeItemViewModel;
|
||||
}
|
||||
|
||||
switch (dropInfo.InsertPosition)
|
||||
{
|
||||
case RelativeInsertPosition.AfterTargetItem | RelativeInsertPosition.TargetItemCenter:
|
||||
case RelativeInsertPosition.BeforeTargetItem | RelativeInsertPosition.TargetItemCenter:
|
||||
return target.SupportsChildren ? DragDropType.Add : DragDropType.None;
|
||||
case RelativeInsertPosition.BeforeTargetItem:
|
||||
return DragDropType.InsertBefore;
|
||||
case RelativeInsertPosition.AfterTargetItem:
|
||||
return DragDropType.InsertAfter;
|
||||
default:
|
||||
return DragDropType.None;
|
||||
}
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void Subscribe()
|
||||
@ -187,7 +180,9 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
|
||||
ActiveItem.UpdateProfileElements();
|
||||
_updatingTree = false;
|
||||
if (e.RenderProfileElement == null)
|
||||
{
|
||||
SelectedTreeItem = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
TreeItemViewModel match = ActiveItem.GetAllChildren().FirstOrDefault(vm => vm.ProfileElement == e.RenderProfileElement);
|
||||
|
||||
@ -13,17 +13,6 @@
|
||||
<StackPanel Margin="-10" Background="Transparent">
|
||||
<StackPanel.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Rename" Command="{s:Action RenameElement}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="RenameBox" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Delete" Command="{s:Action DeleteElement}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="TrashCan" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Add new folder" Command="{s:Action AddFolder}">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="CreateNewFolder" />
|
||||
@ -34,6 +23,33 @@
|
||||
<materialDesign:PackIcon Kind="LayersPlus" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Duplicate" Command="{s:Action DuplicateElement}" InputGestureText="Ctrl+D">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="ContentDuplicate" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Copy" Command="{s:Action CopyElement}" InputGestureText="Ctrl+C">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="ContentCopy" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Paste" Command="{s:Action PasteElement}" InputGestureText="Ctrl+V">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="ContentPaste" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Rename" Command="{s:Action RenameElement}" InputGestureText="F2">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="RenameBox" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Delete" Command="{s:Action DeleteElement}" InputGestureText="Del">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="TrashCan" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</StackPanel.ContextMenu>
|
||||
<Grid Margin="10">
|
||||
|
||||
@ -13,17 +13,28 @@
|
||||
<StackPanel Margin="-10" Background="Transparent">
|
||||
<StackPanel.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Rename" Command="{s:Action RenameElement}">
|
||||
<MenuItem Header="Duplicate" Command="{s:Action DuplicateElement}" InputGestureText="Ctrl+D">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="RenameBox" />
|
||||
<materialDesign:PackIcon Kind="ContentDuplicate" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Duplicate" Command="{s:Action DuplicateElement}">
|
||||
<MenuItem Header="Copy" Command="{s:Action CopyElement}" InputGestureText="Ctrl+C">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="ContentCopy" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Delete" Command="{s:Action DeleteElement}">
|
||||
<MenuItem Header="Paste" Command="{s:Action PasteElement}" InputGestureText="Ctrl+V">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="ContentPaste" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Rename" Command="{s:Action RenameElement}" InputGestureText="F2">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="RenameBox" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Delete" Command="{s:Action DeleteElement}" InputGestureText="Del">
|
||||
<MenuItem.Icon>
|
||||
<materialDesign:PackIcon Kind="TrashCan" />
|
||||
</MenuItem.Icon>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Shared.Services;
|
||||
@ -8,8 +7,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
{
|
||||
public class LayerViewModel : TreeItemViewModel
|
||||
{
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
|
||||
public LayerViewModel(ProfileElement layer,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDialogService dialogService,
|
||||
@ -18,15 +15,6 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
ISurfaceService surfaceService) :
|
||||
base(layer, profileEditorService, dialogService, profileTreeVmFactory, layerBrushService, surfaceService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
}
|
||||
|
||||
public void DuplicateElement()
|
||||
{
|
||||
Layer layer = Layer.CreateCopy();
|
||||
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
_profileEditorService.ChangeSelectedProfileElement(layer);
|
||||
}
|
||||
|
||||
public Layer Layer => ProfileElement as Layer;
|
||||
|
||||
@ -5,12 +5,11 @@ using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Extensions;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.UI.Screens.ProfileEditor.Dialogs;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Utilities;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
@ -178,36 +177,44 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
ProfileElement newSelection = null;
|
||||
if (ProfileElement.Parent != null)
|
||||
{
|
||||
int index = ProfileElement.Parent.Children.IndexOf(ProfileElement);
|
||||
// If there is a next element, select that
|
||||
if (index < ProfileElement.Parent.Children.Count - 1)
|
||||
newSelection = ProfileElement.Parent.Children[index + 1];
|
||||
// Otherwise select the previous element
|
||||
else if (index > 0)
|
||||
newSelection = ProfileElement.Parent.Children[index - 1];
|
||||
// And if that's not there, fall back to the parent
|
||||
else
|
||||
newSelection = ProfileElement.Parent;
|
||||
}
|
||||
|
||||
// Farewell, cruel world
|
||||
TreeItemViewModel parent = (TreeItemViewModel) Parent;
|
||||
ProfileElement.Parent?.RemoveChild(ProfileElement);
|
||||
parent.RemoveExistingElement(this);
|
||||
|
||||
_profileEditorService.UpdateSelectedProfile();
|
||||
_profileEditorService.ChangeSelectedProfileElement(null);
|
||||
_profileEditorService.ChangeSelectedProfileElement(newSelection as RenderProfileElement);
|
||||
}
|
||||
|
||||
public void DuplicateElement()
|
||||
{
|
||||
_profileEditorService.DuplicateProfileElement(ProfileElement);
|
||||
}
|
||||
|
||||
public void CopyElement()
|
||||
{
|
||||
if (ProfileElement is Layer layer)
|
||||
JsonClipboard.SetObject(layer.LayerEntity);
|
||||
else if (ProfileElement is Folder folder)
|
||||
JsonClipboard.SetObject(folder.FolderEntity);
|
||||
_profileEditorService.CopyProfileElement(ProfileElement);
|
||||
}
|
||||
|
||||
public void PasteElement()
|
||||
{
|
||||
object? clipboardObject = JsonClipboard.GetData();
|
||||
if (clipboardObject is LayerEntity layerEntity)
|
||||
{
|
||||
layerEntity.Id = Guid.NewGuid();
|
||||
layerEntity.Name += " - copy";
|
||||
Layer pasted = new Layer(ProfileElement.Profile, ProfileElement.Parent, layerEntity);
|
||||
ProfileElement.Parent.AddChild(pasted, ProfileElement.Order - 1);
|
||||
}
|
||||
else if (clipboardObject is FolderEntity folderEntity)
|
||||
{
|
||||
}
|
||||
if (ProfileElement.Parent is Folder parent)
|
||||
_profileEditorService.PasteProfileElement(parent, ProfileElement.Order);
|
||||
}
|
||||
|
||||
public void UpdateProfileElements()
|
||||
@ -217,19 +224,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
foreach (TreeItemViewModel treeItemViewModel in toRemove)
|
||||
Items.Remove(treeItemViewModel);
|
||||
|
||||
// Order the children
|
||||
List<TreeItemViewModel> vmsList = Items.OrderBy(v => v.ProfileElement.Order).ToList();
|
||||
for (int index = 0; index < vmsList.Count; index++)
|
||||
{
|
||||
TreeItemViewModel profileElementViewModel = vmsList[index];
|
||||
if (Items.IndexOf(profileElementViewModel) != index)
|
||||
((BindableCollection<TreeItemViewModel>) Items).Move(Items.IndexOf(profileElementViewModel), index);
|
||||
}
|
||||
|
||||
// Ensure every child element has an up-to-date VM
|
||||
if (ProfileElement.Children == null)
|
||||
return;
|
||||
|
||||
// Add missing children
|
||||
List<TreeItemViewModel> newChildren = new List<TreeItemViewModel>();
|
||||
foreach (ProfileElement profileElement in ProfileElement.Children.OrderBy(c => c.Order))
|
||||
if (profileElement is Folder folder)
|
||||
@ -243,15 +238,15 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree.TreeItem
|
||||
newChildren.Add(_profileTreeVmFactory.LayerViewModel(layer));
|
||||
}
|
||||
|
||||
if (!newChildren.Any())
|
||||
return;
|
||||
|
||||
// Add the new children in one call, prevent extra UI events
|
||||
foreach (TreeItemViewModel treeItemViewModel in newChildren)
|
||||
{
|
||||
treeItemViewModel.UpdateProfileElements();
|
||||
Items.Add(treeItemViewModel);
|
||||
}
|
||||
|
||||
// Order the children
|
||||
((BindableCollection<TreeItemViewModel>) Items).Sort(i => i.ProfileElement.Order);
|
||||
}
|
||||
|
||||
public void EnableToggled()
|
||||
|
||||
@ -382,6 +382,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
else
|
||||
_previousSelectedLayer = null;
|
||||
|
||||
ApplyActiveProfile();
|
||||
UpdateLedsDimStatus();
|
||||
UpdateCanSelectEditTool();
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
using System.Windows;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.UI.Utilities
|
||||
{
|
||||
public static class JsonClipboard
|
||||
{
|
||||
private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
|
||||
|
||||
public static void SetObject(object clipboardObject)
|
||||
{
|
||||
string json = JsonConvert.SerializeObject(clipboardObject, JsonSettings);
|
||||
Clipboard.SetData("Artemis", json);
|
||||
}
|
||||
|
||||
public static object GetData()
|
||||
{
|
||||
string json = Clipboard.GetData("Artemis")?.ToString();
|
||||
if (json != null)
|
||||
return JsonConvert.DeserializeObject(json, JsonSettings);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static T GetData<T>()
|
||||
{
|
||||
object data = GetData();
|
||||
return data is T castData ? castData : default;
|
||||
}
|
||||
|
||||
public static bool ContainsArtemisData()
|
||||
{
|
||||
return Clipboard.ContainsData("Artemis");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user