diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 6f4fd1d1c..6b30eb6c5 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -33,7 +33,13 @@ namespace Artemis.Core
Parent.AddChild(this);
}
- internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) : base(parent.Profile)
+ ///
+ /// Creates a new instance of the class based on the provided folder entity
+ ///
+ /// The profile the folder belongs to
+ /// The parent of the folder
+ /// The entity of the folder
+ public Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) : base(parent.Profile)
{
FolderEntity = folderEntity;
EntityId = folderEntity.Id;
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 51da2b65f..6c82080fc 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -122,29 +122,6 @@ namespace Artemis.Core
internal override RenderElementEntity RenderElementEntity => LayerEntity;
- ///
- /// Creates a deep copy of the layer
- ///
- /// The newly created copy
- public Layer CreateCopy()
- {
- if (Parent == null)
- throw new ArtemisCoreException("Cannot create a copy of a layer without a parent");
-
- LayerEntity entityCopy = CoreJson.DeserializeObject(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;
- }
-
///
public override List GetAllLayerProperties()
{
@@ -399,7 +376,14 @@ namespace Artemis.Core
}
finally
{
- canvas.Restore();
+ try
+ {
+ canvas.Restore();
+ }
+ catch
+ {
+ // ignored
+ }
Renderer.Close();
}
}
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index a8e21ce76..51c47682a 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -12,8 +12,8 @@ namespace Artemis.Core
///
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; }
+ ///
+ /// Gets the profile entity this profile uses for persistent storage
+ ///
+ public ProfileEntity ProfileEntity { get; internal set; }
internal Stack UndoStack { get; set; }
internal Stack RedoStack { get; set; }
@@ -118,6 +121,19 @@ namespace Artemis.Core
return $"[Profile] {nameof(Name)}: {Name}, {nameof(IsActivated)}: {IsActivated}, {nameof(Module)}: {Module}";
}
+ ///
+ /// Populates all the LEDs on the elements in this profile
+ ///
+ /// The currently active surface that contains the LEDs
+ public void PopulateLeds(ArtemisSurface surface)
+ {
+ if (Disposed)
+ throw new ObjectDisposedException("Profile");
+
+ foreach (Layer layer in GetAllLayers())
+ layer.PopulateLeds(surface);
+ }
+
///
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
///
diff --git a/src/Artemis.Core/Utilities/CoreJson.cs b/src/Artemis.Core/Utilities/CoreJson.cs
index b4e0ff0db..c4a79588d 100644
--- a/src/Artemis.Core/Utilities/CoreJson.cs
+++ b/src/Artemis.Core/Utilities/CoreJson.cs
@@ -5,7 +5,10 @@ using Newtonsoft.Json;
namespace Artemis.Core
{
- internal static class CoreJson
+ ///
+ /// A static helper class that serializes and deserializes JSON with the Artemis Core JSON settings
+ ///
+ public static class CoreJson
{
#region Serialize
diff --git a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
index d8f7ded16..8e9cd1db2 100644
--- a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
@@ -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 LayerEffects { get; set; }
public List PropertyEntities { get; set; }
public List ExpandedPropertyGroups { get; set; }
diff --git a/src/Artemis.Storage/Entities/Profile/FolderEntity.cs b/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
index 57614e6e9..7060ee6e0 100644
--- a/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/FolderEntity.cs
@@ -14,9 +14,6 @@ namespace Artemis.Storage.Entities.Profile
ExpandedPropertyGroups = new List();
}
- 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; }
diff --git a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs
index 9b8f447e4..60dd2071c 100644
--- a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs
@@ -15,9 +15,6 @@ namespace Artemis.Storage.Entities.Profile
ExpandedPropertyGroups = new List();
}
- 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; }
diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
index fda32c11f..e279f7df1 100644
--- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
@@ -92,46 +92,6 @@ namespace Artemis.UI.Shared.Services
/// The current module the profile editor is initialized for
ProfileModule? GetCurrentModule();
- ///
- /// Occurs when a new profile is selected
- ///
- event EventHandler ProfileSelected;
-
- ///
- /// Occurs then the currently selected profile is updated
- ///
- event EventHandler SelectedProfileUpdated;
-
- ///
- /// Occurs when a new profile element is selected
- ///
- event EventHandler ProfileElementSelected;
-
- ///
- /// Occurs when the currently selected profile element is updated
- ///
- event EventHandler SelectedProfileElementUpdated;
-
- ///
- /// Occurs when the currently selected data binding layer property is changed
- ///
- event EventHandler SelectedDataBindingChanged;
-
- ///
- /// Occurs when the current editor time is changed
- ///
- event EventHandler CurrentTimeChanged;
-
- ///
- /// Occurs when the pixels per second (zoom level) is changed
- ///
- event EventHandler PixelsPerSecondChanged;
-
- ///
- /// Occurs when the profile preview has been updated
- ///
- event EventHandler ProfilePreviewUpdated;
-
///
/// Registers a new property input view model used in the profile editor for the generic type defined in
///
@@ -174,5 +134,66 @@ namespace Artemis.UI.Shared.Services
///
///
PropertyInputViewModel? CreatePropertyInputViewModel(LayerProperty layerProperty);
+
+ ///
+ /// Duplicates the provided profile element placing it in the same folder and one position higher
+ ///
+ /// The profile element to duplicate
+ /// The duplicated profile element
+ ProfileElement? DuplicateProfileElement(ProfileElement profileElement);
+
+ ///
+ /// Copies the provided profile element onto the clipboard
+ ///
+ /// The profile element to copy
+ void CopyProfileElement(ProfileElement profileElement);
+
+ ///
+ /// Pastes a render profile element from the clipboard into the target folder
+ ///
+ /// The folder to paste the render element in to
+ /// The position at which to paste the element
+ /// The pasted render element
+ ProfileElement? PasteProfileElement(Folder target, int position);
+
+ ///
+ /// Occurs when a new profile is selected
+ ///
+ event EventHandler ProfileSelected;
+
+ ///
+ /// Occurs then the currently selected profile is updated
+ ///
+ event EventHandler SelectedProfileUpdated;
+
+ ///
+ /// Occurs when a new profile element is selected
+ ///
+ event EventHandler ProfileElementSelected;
+
+ ///
+ /// Occurs when the currently selected profile element is updated
+ ///
+ event EventHandler SelectedProfileElementUpdated;
+
+ ///
+ /// Occurs when the currently selected data binding layer property is changed
+ ///
+ event EventHandler SelectedDataBindingChanged;
+
+ ///
+ /// Occurs when the current editor time is changed
+ ///
+ event EventHandler CurrentTimeChanged;
+
+ ///
+ /// Occurs when the pixels per second (zoom level) is changed
+ ///
+ event EventHandler PixelsPerSecondChanged;
+
+ ///
+ /// Occurs when the profile preview has been updated
+ ///
+ event EventHandler ProfilePreviewUpdated;
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/Models/FolderClipboardModel.cs b/src/Artemis.UI.Shared/Services/Models/FolderClipboardModel.cs
new file mode 100644
index 000000000..9511e5299
--- /dev/null
+++ b/src/Artemis.UI.Shared/Services/Models/FolderClipboardModel.cs
@@ -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();
+ Layers = new List();
+ 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();
+ Layers = new List();
+ }
+
+ public FolderEntity? FolderEntity { get; set; }
+ public List Folders { get; set; }
+ public List 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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
index d7fb126d0..493a9e287 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
@@ -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 _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();
_kernel = kernel;
+ _logger = logger;
+ _profileService = profileService;
+ _coreService = coreService;
+ _surfaceService = surfaceService;
+ _registeredPropertyEditors = new List();
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 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)
+ {
+ if (!(profileElement.Parent is Folder parent))
+ return null;
+
+ object? clipboardModel = null;
+ switch (profileElement)
{
- SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.Path);
- OnSelectedDataBindingChanged();
+ 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? ProfileSelected;
@@ -396,6 +477,5 @@ namespace Artemis.UI.Shared.Services
}
#endregion
-
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Utilities/JsonClipboard.cs b/src/Artemis.UI.Shared/Utilities/JsonClipboard.cs
new file mode 100644
index 000000000..20276bcc2
--- /dev/null
+++ b/src/Artemis.UI.Shared/Utilities/JsonClipboard.cs
@@ -0,0 +1,58 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Windows;
+using Newtonsoft.Json;
+
+namespace Artemis.UI.Shared
+{
+ ///
+ /// Provides access to the clipboard via JSON-serialized objects
+ ///
+ public static class JsonClipboard
+ {
+ private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
+
+ ///
+ /// Sets the provided object on the clipboard
+ ///
+ /// The object to set on the clipboard
+ public static void SetObject(object clipboardObject)
+ {
+ string json = JsonConvert.SerializeObject(clipboardObject, JsonSettings);
+ Clipboard.SetData("Artemis", json);
+ }
+
+ ///
+ /// If set, gets the current object off of the clipboard
+ ///
+ /// The object that is on the clipboard, if none is set .
+ public static object? GetData()
+ {
+ string? json = Clipboard.GetData("Artemis")?.ToString();
+ return json != null ? JsonConvert.DeserializeObject(json, JsonSettings) : null;
+ }
+
+ ///
+ /// If set, gets the current object of type off of the clipboard
+ ///
+ /// The type of object to get
+ ///
+ /// The object that is on the clipboard. If none is set or not of type ,
+ /// .
+ ///
+ [return: MaybeNull]
+ public static T GetData()
+ {
+ object? data = GetData();
+ return data is T castData ? castData : default;
+ }
+
+ ///
+ /// Determines whether the clipboard currently contains Artemis data
+ ///
+ /// if the clipboard contains Artemis data, otherwise
+ public static bool ContainsArtemisData()
+ {
+ return Clipboard.ContainsData("Artemis");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs b/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs
index 11de9ea70..b01c00e4b 100644
--- a/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs
+++ b/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs
@@ -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,14 +124,12 @@ namespace Artemis.UI.Behaviors
// Recurse into children
if (recurse)
- {
foreach (object subitem in item.Items)
{
TreeViewItem tvi = item.ItemContainerGenerator.ContainerFromItem(subitem) as TreeViewItem;
if (tvi != null)
UpdateTreeViewItem(tvi, true);
}
- }
}
// Update state of all items
@@ -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);
diff --git a/src/Artemis.UI/Extensions/ScreenExtensions.cs b/src/Artemis.UI/Extensions/ScreenExtensions.cs
new file mode 100644
index 000000000..dd9277b30
--- /dev/null
+++ b/src/Artemis.UI/Extensions/ScreenExtensions.cs
@@ -0,0 +1,20 @@
+using Stylet;
+
+namespace Artemis.UI.Extensions
+{
+ public static class ScreenExtensions
+ {
+ public static T FindScreenOfType(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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeView.xaml b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeView.xaml
index 33414f284..f3f8ea165 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeView.xaml
@@ -34,10 +34,11 @@
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}">
-
-
-
-
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
index 72db66f48..4e65def4a 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
@@ -13,8 +13,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{
public class ProfileTreeViewModel : Conductor, 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);
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml
index 7e10a1d49..bf68ab782 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml
@@ -13,17 +13,6 @@
-
-
-
+
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
index 65f09b121..14c1d87a0 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
@@ -13,17 +13,28 @@
-