Profile tree - Implemented proper copy/pasting

This commit is contained in:
Robert committed 2020-12-01 19:08:10 +01:00
1 parent f694f39219
commit aae4e71d8e
21 files changed
+524 -294

No files matched your search

@@ -34,10 +34,11 @@
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}">
<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="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}"/>
<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>
<b:Interaction.Behaviors>
<behaviors:TreeViewSelectionBehavior ExpandSelected="True" SelectedItem="{Binding SelectedTreeItem}" />
@@ -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>
<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();
}