Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System.Linq;
using Stride.Core.Annotations;
using Stride.Core.Assets.Editor.Quantum.NodePresenters.Keys;
using Stride.Core.Assets.Editor.ViewModel;
using Stride.Core.Annotations;
using Stride.Core.Reflection;
using Stride.Core.Presentation.Quantum.Presenters;
using Stride.Core.Reflection;

namespace Stride.Core.Assets.Editor.Quantum.NodePresenters.Updaters
{
internal sealed class CollectionPropertyNodeUpdater : AssetNodePresenterUpdaterBase
{
protected override void UpdateNode(IAssetNodePresenter node)
{
var memberNode = node as MemberNodePresenter;
MemberCollectionAttribute memberCollection;
if (memberNode != null && memberNode.IsEnumerable)
if (node is MemberNodePresenter memberNode && memberNode.IsEnumerable)
{
memberCollection = memberNode.MemberAttributes.OfType<MemberCollectionAttribute>().FirstOrDefault();
}
Expand All @@ -24,12 +22,38 @@ protected override void UpdateNode(IAssetNodePresenter node)
memberCollection = node.Descriptor.Attributes.OfType<MemberCollectionAttribute>().FirstOrDefault()
?? TypeDescriptorFactory.Default.AttributeRegistry.GetAttribute<MemberCollectionAttribute>(node.Type);
}

if (memberCollection != null)
{
if (memberCollection.CanReorderItems)
node.AttachedProperties.Add(CollectionData.ReorderCollectionItemKey, new ReorderCollectionItemViewModel());
if (memberCollection.ReadOnly)
{
node.AttachedProperties.Add(CollectionData.ReadOnlyCollectionKey, true);
}
}

// Check if this is an item within a reorderable collection
var parentNode = node.Parent;
if (parentNode != null && node is ItemNodePresenter)
{
// Dictionaries are not supported for reordering
if (DictionaryDescriptor.IsDictionary(parentNode.Type))
return;

MemberCollectionAttribute parentCollection;
if (parentNode is MemberNodePresenter parentMemberNode && parentMemberNode.IsEnumerable)
{
parentCollection = parentMemberNode.MemberAttributes.OfType<MemberCollectionAttribute>().FirstOrDefault();
}
else
{
parentCollection = parentNode.Descriptor.Attributes.OfType<MemberCollectionAttribute>().FirstOrDefault()
?? TypeDescriptorFactory.Default.AttributeRegistry.GetAttribute<MemberCollectionAttribute>(parentNode.Type);
}

if (parentCollection?.CanReorderItems == true)
{
node.AttachedProperties.Add(CollectionData.ReorderCollectionItemKey, new ReorderCollectionItemViewModel());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ protected override bool CanInitializeDrag(object originalSource)
if (node?.Parent == null)
return false;

if (!(TypeDescriptorFactory.Default.Find(node.Parent.Type) is CollectionDescriptor))
var parentDescriptor = TypeDescriptorFactory.Default.Find(node.Parent.Type);
if (!(parentDescriptor is CollectionDescriptor or ArrayDescriptor))
return false;

object data;
Expand All @@ -37,7 +38,8 @@ protected override IEnumerable<object> GetItemsToDrag(PropertyViewItem container
if (node?.Parent == null)
return Enumerable.Empty<object>();

if (!(TypeDescriptorFactory.Default.Find(node.Parent.Type) is CollectionDescriptor))
var parentDescriptor = TypeDescriptorFactory.Default.Find(node.Parent.Type);
if (!(parentDescriptor is CollectionDescriptor or ArrayDescriptor))
return Enumerable.Empty<object>();

object data;
Expand Down
11 changes: 11 additions & 0 deletions sources/editor/Stride.Core.Assets.Editor/View/CommonResources.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sd="http://schemas.stride3d.net/xaml/presentation"
xmlns:interactivity="clr-namespace:Stride.Core.Presentation.Interactivity;assembly=Stride.Core.Presentation.Wpf"
xmlns:view="clr-namespace:Stride.Core.Assets.Editor.View"
xmlns:viewModel="clr-namespace:Stride.Core.Assets.Editor.ViewModel"
xmlns:behaviors="clr-namespace:Stride.Core.Assets.Editor.View.Behaviors"
Expand All @@ -14,6 +15,16 @@
<ResourceDictionary Source="../Themes/ThemeSelector.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style TargetType="sd:PropertyViewItem" BasedOn="{StaticResource {x:Type sd:PropertyViewItem}}">
<Setter Property="interactivity:Interaction.Behaviors">
<Setter.Value>
<interactivity:BehaviorCollection>
<behaviors:PropertyViewItemDragDropBehavior CanDrag="True" CanInsert="True" DisplayInsertAdorner="True" />
</interactivity:BehaviorCollection>
</Setter.Value>
</Setter>
</Style>

<Style TargetType="view:DataGridEx">
<Setter Property="Background" Value="{StaticResource NormalBrush}" />
</Style>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,84 +1,188 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System.Collections.Generic;
using Stride.Core.Assets.Editor.Quantum.NodePresenters.Commands;
using Stride.Core.Assets.Editor.Quantum.NodePresenters.Keys;
using Stride.Core.Assets.Editor.View.Behaviors;
using Stride.Core.Presentation.Quantum.Presenters;
using Stride.Core.Presentation.Quantum.ViewModels;
using Stride.Core.Presentation.Services;
using Stride.Core.Reflection;

namespace Stride.Core.Assets.Editor.ViewModel
{
public class ReorderCollectionItemViewModel : IReorderItemViewModel
{

private NodeViewModel targetNode;

public bool CanInsertChildren(IReadOnlyCollection<object> children, InsertPosition position, AddChildModifiers modifiers, out string message)
{
// FIXME: This feature is disabled for now.
message = "";
return false;

//if (children.Count != 1)
//{
// message = "Only a single item can be moved at a time";
// return false;
//}

//var node = children.First() as NodeViewModel;
//if (node?.Parent == null || !(TypeDescriptorFactory.Default.Find(node.Parent.Type) is CollectionDescriptor))
//{
// message = "This item cannot be moved because it is not in a collection";
// return false;
//}

//if (node.Parent.Type != targetNode.Parent.Type)
//{
// message = "Invalid target location";
// return false;
//}

//object data;
//if (!node.AssociatedData.TryGetValue("ReorderCollectionItem", out data))
// return false;

//var sourceNode = (NodeViewModel)children.First();
//var sourceIndex = sourceNode.Index.Int;
//var targetIndex = targetNode.Index.Int;
//if (sourceIndex == targetIndex)
//{
// message = "The target position is the same that the current position";
// return false;
//}

//message = string.Format(position == InsertPosition.Before ? "Insert before {0}" : "Insert after {0}", targetNode.DisplayName);
//return node.Index.IsInt && data is IReorderItemViewModel;
if (children.Count != 1)
{
message = "Only a single item can be moved at a time";
return false;
}

var node = children.First() as NodeViewModel;
if (node?.Parent == null)
{
message = "This item cannot be moved because it is not in a collection";
return false;
}

var parentDescriptor = TypeDescriptorFactory.Default.Find(node.Parent.Type);
var isArray = parentDescriptor is ArrayDescriptor;
var isCollection = parentDescriptor is CollectionDescriptor collectionDescriptor &&
collectionDescriptor.HasRemoveAt && collectionDescriptor.HasInsert;

if (!isArray && !isCollection)
{
message = "This collection does not support reordering";
return false;
}

if (node.Parent != targetNode.Parent)
{
message = "Invalid target location";
return false;
}

object data;
if (!node.AssociatedData.TryGetValue(CollectionData.ReorderCollectionItem, out data))
{
message = "This item cannot be reordered";
return false;
}

var sourcePresenter = node.NodePresenters.FirstOrDefault() as ItemNodePresenter;
var targetPresenter = targetNode.NodePresenters.FirstOrDefault() as ItemNodePresenter;

if (sourcePresenter == null || targetPresenter == null ||
!sourcePresenter.Index.IsInt || !targetPresenter.Index.IsInt)
{
message = "Items with non-integer indices cannot be reordered";
return false;
}

var sourceIndex = sourcePresenter.Index.Int;
var targetIndex = targetPresenter.Index.Int;
if (sourceIndex == targetIndex)
{
message = "The target position is the same as the current position";
return false;
}

message = string.Format(position == InsertPosition.Before ? "Insert before {0}" : "Insert after {0}", targetNode.DisplayName);
return data is IReorderItemViewModel;
}

public void InsertChildren(IReadOnlyCollection<object> children, InsertPosition position, AddChildModifiers modifiers)
{
// FIXME: This feature is disabled for now.
//var sourceNode = (NodeViewModel)children.First();
//var sourceIndex = sourceNode.Index.Int;
//var targetIndex = targetNode.Index.Int;
//if (position == InsertPosition.After)
// ++targetIndex;

//if (sourceNode.Parent.NodeValue == targetNode.Parent.NodeValue && sourceIndex < targetIndex)
// --targetIndex;

//var moveCommand = (NodeCommandWrapperBase)sourceNode.Parent.GetCommand(MoveItemCommand.CommandName);
//if (moveCommand == null)
// return;

//var actionService = sourceNode.ServiceProvider.Get<IUndoRedoService>();
//using (var transaction = actionService.CreateTransaction())
//{
// moveCommand.Invoke(Tuple.Create(sourceIndex, targetIndex));
// actionService.SetName(transaction, $"Move item {sourceIndex}");
//}
var sourceNode = (NodeViewModel)children.First();
var sourcePresenter = sourceNode.NodePresenters.FirstOrDefault() as ItemNodePresenter;
var targetPresenter = targetNode.NodePresenters.FirstOrDefault() as ItemNodePresenter;

if (sourcePresenter == null || targetPresenter == null)
{
return;
}

var sourceIndex = sourcePresenter.Index.Int;
var targetIndex = targetPresenter.Index.Int;

if (position == InsertPosition.After)
{
++targetIndex;
}

if (sourceNode.Parent.NodeValue == targetNode.Parent.NodeValue && sourceIndex < targetIndex)
{
--targetIndex;
}

var actionService = sourceNode.ServiceProvider.Get<IUndoRedoService>();
var parentDescriptor = TypeDescriptorFactory.Default.Find(sourceNode.Parent.Type);

if (parentDescriptor is ArrayDescriptor)
{
ReorderArray(sourceNode, targetNode, sourceIndex, targetIndex, actionService);
}
else
{
var moveCommand = (NodePresenterCommandWrapper)sourceNode.GetCommand(MoveItemCommand.CommandName);
if (moveCommand == null)
{
return;
}

using var transaction = actionService.CreateTransaction();
moveCommand.Invoke(Tuple.Create(sourceIndex, targetIndex));
actionService.SetName(transaction, $"Move item {sourceIndex}");
}
}

private void ReorderArray(NodeViewModel sourceNode, NodeViewModel targetNode, int sourceIndex, int targetIndex, IUndoRedoService actionService)
{
using var transaction = actionService.CreateTransaction();

var parentNode = sourceNode.Parent;
var array = parentNode.NodeValue as Array;
if (array == null)
return;

var sourceValue = array.GetValue(sourceIndex);

if (sourceIndex < targetIndex)
{
for (int i = sourceIndex; i < targetIndex; i++)
{
var currentChild = parentNode.Children.FirstOrDefault(c =>
{
var presenter = c.NodePresenters.FirstOrDefault() as ItemNodePresenter;
return presenter?.Index.Int == i;
});
if (currentChild != null)
{
var presenter = currentChild.NodePresenters.FirstOrDefault() as ItemNodePresenter;
var nextValue = array.GetValue(i + 1);
presenter?.UpdateValue(nextValue);
}
}
}
else
{
for (int i = sourceIndex; i > targetIndex; i--)
{
var currentChild = parentNode.Children.FirstOrDefault(c =>
{
var presenter = c.NodePresenters.FirstOrDefault() as ItemNodePresenter;
return presenter?.Index.Int == i;
});
if (currentChild != null)
{
var presenter = currentChild.NodePresenters.FirstOrDefault() as ItemNodePresenter;
var prevValue = array.GetValue(i - 1);
presenter?.UpdateValue(prevValue);
}
}
}

var finalChild = parentNode.Children.FirstOrDefault(c =>
{
var presenter = c.NodePresenters.FirstOrDefault() as ItemNodePresenter;
return presenter?.Index.Int == targetIndex;
});
if (finalChild != null)
{
var presenter = finalChild.NodePresenters.FirstOrDefault() as ItemNodePresenter;
presenter?.UpdateValue(sourceValue);
}

actionService.SetName(transaction, $"Move item {sourceIndex}");
}

public void SetTargetNode(NodeViewModel node)
{
// FIXME: This feature is disabled for now.
//targetNode = node;
targetNode = node;
}
}
}