// FIXME: consider using the cut/paste mechanism for moving entities protected virtual void MoveChildren([NotNull] IReadOnlyCollection <object> children, int index) { if (children.Count == 0) { return; } var moved = false; // Save the selection to restore it after the operation var selection = Editor.SelectedContent.ToList(); // Clear the selection Editor.ClearSelection(); using (var transaction = Editor.UndoRedoService.CreateTransaction()) { foreach (var child in children) { if (child is UIElementViewModel element) { // Some of the elements we're moving might already be children of this object, let's count for their removal in the insertion index. var elementIndex = Children.IndexOf(element); if (elementIndex >= 0 && elementIndex < index) { --index; } // Hierarchy must be cloned before removing the elements! // Note: if the source asset is different than the current asset, we need to generate new ids. var flags = element.Asset == Asset ? SubHierarchyCloneFlags.None : SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects; var hierarchy = UIAssetPropertyGraph.CloneSubHierarchies(element.Asset.Session.AssetNodeContainer, element.Asset.Asset, element.Id.ObjectId.Yield(), flags, out Dictionary <Guid, Guid> idRemapping); // Remove from previous asset element.Asset.AssetHierarchyPropertyGraph.RemovePartFromAsset(element.UIElementDesign); // Get the id of the new element if (!idRemapping.TryGetValue(element.Id.ObjectId, out Guid partId)) { partId = element.Id.ObjectId; } // Insert it in the new parent, or as root if the new parent is null. Asset.AssetHierarchyPropertyGraph.AddPartToAsset(hierarchy.Parts, hierarchy.Parts[partId], (this as UIElementViewModel)?.AssetSideUIElement, index++); moved = true; continue; } if (child is IUIElementFactory factory) { var childHierarchy = factory.Create(UIAsset); Asset.AssetHierarchyPropertyGraph.AddPartToAsset(childHierarchy.Parts, childHierarchy.Parts[childHierarchy.RootParts.Single().Id], (this as UIElementViewModel)?.AssetSideUIElement, index++); } } Editor.UndoRedoService.SetName(transaction, $"{(moved ? "Move" : "Add")} {children.Count} element{(children.Count > 1 ? "s" : string.Empty)} to {GetDropLocationName()}"); } // Fixup selection since adding/inserting may create new viewmodels Editor.FixupAndRestoreSelection(selection, children); }
/// <summary> /// Successively copy the properties from the <paramref name="sourcePanel"/> to the <paramref name="targetPanel"/>, then swap the panels in the parent /// and finally move the children from the <paramref name="sourcePanel"/> to the <paramref name="targetPanel"/>. /// </summary> private void CopySwapExchange([NotNull] PanelViewModel sourcePanel, [NotNull] UIElementDesign targetPanel, [CanBeNull] Func <IEnumerable <UIElementViewModel>, IEnumerable <UIElementViewModel> > childSorter = null) { var targetPanelElement = targetPanel.UIElement as Panel; if (targetPanelElement == null) { throw new ArgumentException(@"The target element must be a Panel", nameof(targetPanel)); } // Clone common properties CopyCommonProperties(Editor.NodeContainer, sourcePanel.AssetSidePanel, targetPanelElement); // Initialize the new hierarchy of elements that starts from the target and contains all the children IEnumerable <UIElementViewModel> children = sourcePanel.Children.ToList(); var hierarchy = UIAssetPropertyGraph.CloneSubHierarchies(Asset.Session.AssetNodeContainer, Asset.Asset, children.Select(c => c.Id.ObjectId), SubHierarchyCloneFlags.None, out _); hierarchy.RootParts.Add(targetPanel.UIElement); hierarchy.Parts.Add(targetPanel); // Remove all children from the source panel. foreach (var child in children) { Asset.AssetHierarchyPropertyGraph.RemovePartFromAsset(child.UIElementDesign); } // Swap panels in the parent var elementParent = Parent as UIElementViewModel; var rootParent = Parent as UIRootViewModel; if (rootParent != null) { // special case of RootElement rootParent.ReplaceRootElement(sourcePanel, hierarchy, targetPanelElement.Id); } else if (elementParent != null) { // Remove current panel from Parent var index = elementParent.Children.IndexOf(sourcePanel); Asset.AssetHierarchyPropertyGraph.RemovePartFromAsset(sourcePanel.UIElementDesign); Asset.AssetHierarchyPropertyGraph.AddPartToAsset(hierarchy.Parts, targetPanel, elementParent.AssetSideUIElement, index); } else { throw new InvalidOperationException(); } // Sort the children list before re-inserting them in the target panel. children = childSorter?.Invoke(children) ?? children; // Then populate the target panel foreach (var child in children) { Asset.InsertUIElement(hierarchy.Parts, hierarchy.Parts[child.Id.ObjectId], targetPanelElement); } }
private void GroupInto(IUIElementFactory factory) { var targetPanelType = (factory as UIElementFromSystemLibrary)?.Type; if (targetPanelType == null) { throw new NotSupportedException("Grouping elements into a user library type isn't supported."); } if (!typeof(Panel).IsAssignableFrom(targetPanelType)) { throw new ArgumentException(@"The target type isn't a panel", nameof(targetPanelType)); } if (SelectedContent.Count == 0) { return; } // Ensure that the selected elements are sibling. var allParents = SelectedItems.Select(x => x.Parent).OfType <UIHierarchyItemViewModel>().ToList(); var parent = allParents[0]; if (allParents.Any(x => x != parent)) { throw new InvalidOperationException("This operation can only be executed on a selection of sibling elements."); } using (var transaction = UndoRedoService.CreateTransaction()) { var children = SelectedItems.ToList(); // Create the new panel into which we'll insert the selection var newPanel = (Panel)Activator.CreateInstance(targetPanelType); var newPanelDesign = new UIElementDesign(newPanel); // Create a hierarchy containing all children and the panel var hierarchy = UIAssetPropertyGraph.CloneSubHierarchies(Asset.Session.AssetNodeContainer, Asset.Asset, children.Select(c => c.Id.ObjectId), SubHierarchyCloneFlags.None, out _); hierarchy.RootParts.Add(newPanel); hierarchy.Parts.Add(newPanelDesign); // Remove all children from their partDesign panel. foreach (var child in children) { child.Asset.AssetHierarchyPropertyGraph.RemovePartFromAsset(child.UIElementDesign); } // Add the new panel in place of the selected content. parent.Asset.InsertUIElement(hierarchy.Parts, newPanelDesign, (parent as UIElementViewModel)?.AssetSideUIElement); // Add the children into the new panel. foreach (var child in children) { parent.Asset.InsertUIElement(hierarchy.Parts, hierarchy.Parts[child.Id.ObjectId], newPanel); } UndoRedoService.SetName(transaction, $"Group into {targetPanelType.Name}"); } }
public AssetCompositeHierarchyData <UIElementDesign, UIElement> Create(UIAssetBase targetAsset) { // Create a clone without linking to the library var flags = SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects | SubHierarchyCloneFlags.RemoveOverrides; var clonedHierarchy = UIAssetPropertyGraph.CloneSubHierarchies(library.Session.AssetNodeContainer, library.Asset, id.Yield(), flags, out Dictionary <Guid, Guid> _); foreach (var part in clonedHierarchy.Parts.Values) { // Reset name property part.UIElement.Name = null; part.Base = null; } return(clonedHierarchy); }
private void Ungroup() { // Move all children of this panel into the parent panel, if available: // - if parent is a root, this action should be ignored // - if parent is ContentControl and this panel has more than one child, this action should be ignored if (Parent is UIRootViewModel || (Parent is ContentControlViewModel && Children.Count > 1)) { return; } var parentElement = (Parent as UIElementViewModel)?.AssetSideUIElement; if (parentElement == null) { return; } // FIXME: should be similar to "move all children" then "delete previous panel" using (var transaction = Editor.UndoRedoService.CreateTransaction()) { var children = Children.ToList(); var hierarchy = UIAssetPropertyGraph.CloneSubHierarchies(Asset.Session.AssetNodeContainer, Asset.Asset, children.Select(c => c.Id.ObjectId), SubHierarchyCloneFlags.None, out _); // Remove all children from this panel. foreach (var child in children) { Asset.AssetHierarchyPropertyGraph.RemovePartFromAsset(child.UIElementDesign); } // Remove the current panel. Asset.AssetHierarchyPropertyGraph.RemovePartFromAsset(UIElementDesign); // Add the children into the parent panel. foreach (var child in children) { // TODO: might need smart rules for dependency properties (maybe use the same rules than when changing the layout type) Asset.InsertUIElement(hierarchy.Parts, hierarchy.Parts[child.Id.ObjectId], parentElement); } Editor.UndoRedoService.SetName(transaction, $"Ungroup {children.Count} elements"); } }