Add node alignment formatting options to visject

This commit is contained in:
Zode
2025-06-07 13:15:39 +03:00
committed by GitHub
parent eee4e55cf0
commit 1a77ba4552
3 changed files with 106 additions and 2 deletions

View File

@@ -0,0 +1,43 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.Surface
{
/// <summary>
/// Node Alignment type
/// </summary>
[HideInEditor]
public enum NodeAlignmentType
{
/// <summary>
/// Align nodes vertically to top, matching top-most node
/// </summary>
Top,
/// <summary>
/// Align nodes vertically to middle, using average of all nodes
/// </summary>
Middle,
/// <summary>
/// Align nodes vertically to bottom, matching bottom-most node
/// </summary>
Bottom,
/// <summary>
/// Align nodes horizontally to left, matching left-most node
/// </summary>
Left,
/// <summary>
/// Align nodes horizontally to center, using average of all nodes
/// </summary>
Center,
/// <summary>
/// Align nodes horizontally to right, matching right-most node
/// </summary>
Right,
}
}

View File

@@ -191,7 +191,14 @@ namespace FlaxEditor.Surface
private ContextMenuButton _cmCopyButton;
private ContextMenuButton _cmDuplicateButton;
private ContextMenuChildMenu _cmFormatNodesMenu;
private ContextMenuButton _cmFormatNodesConnectionButton;
private ContextMenuButton _cmAlignNodesTopButton;
private ContextMenuButton _cmAlignNodesMiddleButton;
private ContextMenuButton _cmAlignNodesBottomButton;
private ContextMenuButton _cmAlignNodesLeftButton;
private ContextMenuButton _cmAlignNodesCenterButton;
private ContextMenuButton _cmAlignNodesRightButton;
private ContextMenuButton _cmRemoveNodeConnectionsButton;
private ContextMenuButton _cmRemoveBoxConnectionsButton;
private readonly Float2 ContextMenuOffset = new Float2(5);
@@ -399,8 +406,20 @@ namespace FlaxEditor.Surface
}
menu.AddSeparator();
_cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => { FormatGraph(SelectedNodes); });
_cmFormatNodesConnectionButton.Enabled = CanEdit && HasNodesSelection;
_cmFormatNodesMenu = menu.AddChildMenu("Format node(s)");
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", () => { FormatGraph(SelectedNodes); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); });
_cmAlignNodesMiddleButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align middle", () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); });
_cmAlignNodesBottomButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align bottom", () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmAlignNodesLeftButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align left", () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); });
_cmAlignNodesCenterButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align center", () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); });
_cmAlignNodesRightButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align right", () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); });
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () =>
{

View File

@@ -282,5 +282,47 @@ namespace FlaxEditor.Surface
return maxOffset;
}
/// <summary>
/// Align given nodes on a graph using the given alignment type.
/// Ignores any potential overlap.
/// </summary>
/// <param name="nodes">List of nodes</param>
/// <param name="alignmentType">Alignemnt type</param>
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
{
if(nodes.Count <= 1)
return;
var undoActions = new List<MoveNodesAction>();
var boundingBox = GetNodesBounds(nodes);
for(int i = 0; i < nodes.Count; i++)
{
var centerY = boundingBox.Center.Y - (nodes[i].Height / 2);
var centerX = boundingBox.Center.X - (nodes[i].Width / 2);
var newLocation = alignmentType switch
{
NodeAlignmentType.Top => new Float2(nodes[i].Location.X, boundingBox.Top),
NodeAlignmentType.Middle => new Float2(nodes[i].Location.X, centerY),
NodeAlignmentType.Bottom => new Float2(nodes[i].Location.X, boundingBox.Bottom - nodes[i].Height),
NodeAlignmentType.Left => new Float2(boundingBox.Left, nodes[i].Location.Y),
NodeAlignmentType.Center => new Float2(centerX, nodes[i].Location.Y),
NodeAlignmentType.Right => new Float2(boundingBox.Right - nodes[i].Width, nodes[i].Location.Y),
_ => throw new NotImplementedException($"Unsupported node alignment type: {alignmentType}"),
};
var locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
if(Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
MarkAsEdited(false);
Undo?.AddAction(new MultiUndoAction(undoActions, $"Align nodes ({alignmentType})"));
}
}
}