diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs
index 609b7b5f7..32d75c71c 100644
--- a/Source/Editor/Surface/NodeArchetype.cs
+++ b/Source/Editor/Surface/NodeArchetype.cs
@@ -109,6 +109,7 @@ namespace FlaxEditor.Surface
///
public CreateCustomNodeFunc Create;
+ private Float2 _size;
///
/// Function for asynchronously loaded nodes to check if input ports are compatible, for filtering.
///
@@ -122,7 +123,17 @@ namespace FlaxEditor.Surface
///
/// Default initial size of the node.
///
- public Float2 Size;
+ public Float2 Size
+ {
+ get
+ {
+ return _size;
+ }
+ set
+ {
+ _size = VisjectSurface.RoundToGrid(value, true);
+ }
+ }
///
/// Custom set of flags.
diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs
index fe79ec3af..a6fc82d55 100644
--- a/Source/Editor/Surface/SurfaceUtils.cs
+++ b/Source/Editor/Surface/SurfaceUtils.cs
@@ -13,6 +13,9 @@ using FlaxEditor.Scripting;
using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
using FlaxEngine;
+using FlaxEditor.GUI;
+using FlaxEngine.GUI;
+using FlaxEditor.Options;
namespace FlaxEditor.Surface
{
@@ -556,5 +559,42 @@ namespace FlaxEditor.Surface
return true;
return AreScriptTypesEqualInner(left, right) || AreScriptTypesEqualInner(right, left);
}
+
+ // This might not be the greatest place to put this but I couldn't find anything better yet.
+ public static void VisjectCommonToolstripSetup(Editor editor, ToolStrip toolStrip, FlaxEditor.Undo undo,
+ Action save, Action showWholeGraph, Action toggleGridSnap, InputActionsContainer actionsContainer,
+ out ToolStripButton saveButton, out ToolStripButton undoButton, out ToolStripButton redoButton, out ToolStripButton gridSnapButton)
+ {
+ var inputOptions = editor.Options.Options.Input;
+
+ // Toolstrip
+ saveButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Save64, save).LinkTooltip("Save");
+ toolStrip.AddSeparator();
+ undoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
+ redoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
+ toolStrip.AddSeparator();
+ toolStrip.AddButton(editor.Icons.Search64, editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
+ toolStrip.AddButton(editor.Icons.CenterView64, showWholeGraph).LinkTooltip("Show whole graph");
+ gridSnapButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Stop64, toggleGridSnap).LinkTooltip("Toggle grid snapping for nodes.");
+ gridSnapButton.BackgroundColor = Style.Current.Background; // Default color for grid snap button.
+
+ // Setup input actions
+ actionsContainer.Add(options => options.Undo, undo.PerformUndo);
+ actionsContainer.Add(options => options.Redo, undo.PerformRedo);
+ actionsContainer.Add(options => options.Search, editor.ContentFinding.ShowSearch);
+ }
+
+ public static void ToggleSurfaceGridSnap(VisjectSurface surface, ToolStripButton gridSnapButton)
+ {
+ surface.GridSnappingEnabled = !surface.GridSnappingEnabled;
+ if (surface.GridSnappingEnabled)
+ {
+ gridSnapButton.BackgroundColor = Style.Current.BackgroundSelected;
+ }
+ else
+ {
+ gridSnapButton.BackgroundColor = Style.Current.Background;
+ }
+ }
}
}
diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs
index 9b6f86bcb..c540a2bd1 100644
--- a/Source/Editor/Surface/VisjectSurface.Input.cs
+++ b/Source/Editor/Surface/VisjectSurface.Input.cs
@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Options;
@@ -24,6 +25,7 @@ namespace FlaxEditor.Surface
private string _currentInputText = string.Empty;
private Float2 _movingNodesDelta;
+ private Float2 _gridRoundingDelta;
private HashSet _movingNodes;
private readonly Stack _inputBrackets = new Stack();
@@ -189,6 +191,29 @@ namespace FlaxEditor.Surface
}
}
+ ///
+ /// Round a visject coordinate point to the grid.
+ ///
+ /// The point to be rounded.
+ /// Round to ceiling instead?
+ ///
+ public static Float2 RoundToGrid(Float2 point, bool ceil = false)
+ {
+ Func round = x =>
+ {
+ double pointGridUnits = Math.Abs((double)x) / GridSize;
+ pointGridUnits = ceil ? Math.Ceiling(pointGridUnits) : Math.Floor(pointGridUnits);
+
+ return (float)Math.CopySign(pointGridUnits * GridSize, x);
+ };
+
+ Float2 pointToRound = point;
+ pointToRound.X = round(pointToRound.X);
+ pointToRound.Y = round(pointToRound.Y);
+
+ return pointToRound;
+ }
+
///
public override void OnMouseEnter(Float2 location)
{
@@ -256,18 +281,40 @@ namespace FlaxEditor.Surface
// Moving
else if (_isMovingSelection)
{
+ if (!GridSnappingEnabled)
+ _gridRoundingDelta = Float2.Zero; // Reset in case user toggled option between frames.
+
// Calculate delta (apply view offset)
var viewDelta = _rootControl.Location - _movingSelectionViewPos;
_movingSelectionViewPos = _rootControl.Location;
- var delta = location - _leftMouseDownPos - viewDelta;
- if (delta.LengthSquared > 0.01f)
+ var delta = location - _leftMouseDownPos - viewDelta + _gridRoundingDelta;
+ var deltaLengthSquared = delta.LengthSquared;
+
+ delta /= _targetScale;
+ if ((!GridSnappingEnabled || Math.Abs(delta.X) >= GridSize || (Math.Abs(delta.Y) >= GridSize))
+ && deltaLengthSquared > 0.01f)
{
- // Move selected nodes
- delta /= _targetScale;
+ if (GridSnappingEnabled)
+ {
+ Float2 unroundedDelta = delta;
+
+ delta = RoundToGrid(unroundedDelta);
+ _gridRoundingDelta = (unroundedDelta - delta) * _targetScale; // Standardize unit of the rounding delta, in case user zooms between node movements.
+ }
+
foreach (var node in _movingNodes)
+ {
+ if (GridSnappingEnabled)
+ {
+ Float2 unroundedLocation = node.Location;
+ node.Location = RoundToGrid(unroundedLocation);
+ }
+
node.Location += delta;
+ }
+
_leftMouseDownPos = location;
- _movingNodesDelta += delta;
+ _movingNodesDelta += delta; // TODO: Figure out how to handle undo for differing values of _gridRoundingDelta between selected nodes. For now it will be a small error in undo.
if (_movingNodes.Count > 0)
{
Cursor = CursorType.SizeAll;
diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs
index b3869f304..e4eb3269b 100644
--- a/Source/Editor/Surface/VisjectSurface.cs
+++ b/Source/Editor/Surface/VisjectSurface.cs
@@ -31,6 +31,16 @@ namespace FlaxEditor.Surface
///
protected SurfaceRootControl _rootControl;
+ ///
+ /// Is grid snapping enabled for this surface?
+ ///
+ public bool GridSnappingEnabled = false;
+
+ ///
+ /// The size of the snapping grid.
+ ///
+ public static readonly float GridSize = 20f;
+
private float _targetScale = 1.0f;
private float _moveViewWithMouseDragSpeed = 1.0f;
private bool _canEdit = true;
diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs
index 7da0d9707..ec3db7a9d 100644
--- a/Source/Editor/Surface/VisjectSurfaceWindow.cs
+++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs
@@ -894,6 +894,7 @@ namespace FlaxEditor.Surface
private readonly ToolStripButton _saveButton;
private readonly ToolStripButton _undoButton;
private readonly ToolStripButton _redoButton;
+ private readonly ToolStripButton _gridSnapButton;
private bool _showWholeGraphOnLoad = true;
///
@@ -999,19 +1000,9 @@ namespace FlaxEditor.Surface
}
_propertiesEditor.Modified += OnPropertyEdited;
- // Toolstrip
- _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
- _toolstrip.AddSeparator();
- _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
- _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
- _toolstrip.AddSeparator();
- _toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
- _toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
-
- // Setup input actions
- InputActions.Add(options => options.Undo, _undo.PerformUndo);
- InputActions.Add(options => options.Redo, _undo.PerformRedo);
- InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
+ SurfaceUtils.VisjectCommonToolstripSetup(editor, _toolstrip, _undo,
+ Save, ShowWholeGraph, ToggleGridSnap, InputActions,
+ out _saveButton, out _undoButton, out _redoButton, out _gridSnapButton);
}
private void OnUndoRedo(IUndoAction action)
@@ -1050,6 +1041,11 @@ namespace FlaxEditor.Surface
_surface.ShowWholeGraph();
}
+ private void ToggleGridSnap()
+ {
+ SurfaceUtils.ToggleSurfaceGridSnap(_surface, _gridSnapButton);
+ }
+
///
/// Refreshes temporary asset to see changes live when editing the surface.
///
diff --git a/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs b/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs
index 9618b30eb..1cdd07611 100644
--- a/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs
+++ b/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs
@@ -30,6 +30,7 @@ namespace FlaxEditor.Windows.Assets
private readonly ToolStripButton _saveButton;
private readonly ToolStripButton _undoButton;
private readonly ToolStripButton _redoButton;
+ private readonly ToolStripButton _gridSnapButton;
private bool _showWholeGraphOnLoad = true;
///
@@ -70,13 +71,9 @@ namespace FlaxEditor.Windows.Assets
_undo.ActionDone += OnUndoRedo;
// Toolstrip
- _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
- _toolstrip.AddSeparator();
- _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
- _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
- _toolstrip.AddSeparator();
- _toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
- _toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
+ SurfaceUtils.VisjectCommonToolstripSetup(editor, _toolstrip, _undo,
+ Save, ShowWholeGraph, ToggleGridSnap, InputActions,
+ out _saveButton, out _undoButton, out _redoButton, out _gridSnapButton);
// Panel
_panel = new Panel(ScrollBars.None)
@@ -85,11 +82,6 @@ namespace FlaxEditor.Windows.Assets
Offsets = new Margin(0, 0, _toolstrip.Bottom, 0),
Parent = this
};
-
- // Setup input actions
- InputActions.Add(options => options.Undo, _undo.PerformUndo);
- InputActions.Add(options => options.Redo, _undo.PerformRedo);
- InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
}
private void OnUndoRedo(IUndoAction action)
@@ -106,6 +98,11 @@ namespace FlaxEditor.Windows.Assets
_surface.ShowWholeGraph();
}
+ private void ToggleGridSnap()
+ {
+ SurfaceUtils.ToggleSurfaceGridSnap(_surface, _gridSnapButton);
+ }
+
///
/// Refreshes temporary asset to see changes live when editing the surface.
///
diff --git a/Source/Editor/Windows/Assets/VisualScriptWindow.cs b/Source/Editor/Windows/Assets/VisualScriptWindow.cs
index a7201e81c..3f742e09d 100644
--- a/Source/Editor/Windows/Assets/VisualScriptWindow.cs
+++ b/Source/Editor/Windows/Assets/VisualScriptWindow.cs
@@ -533,6 +533,7 @@ namespace FlaxEditor.Windows.Assets
private readonly ToolStripButton _saveButton;
private readonly ToolStripButton _undoButton;
private readonly ToolStripButton _redoButton;
+ private readonly ToolStripButton _gridSnapButton;
private Control[] _debugToolstripControls;
private bool _showWholeGraphOnLoad = true;
private bool _tmpAssetIsDirty;
@@ -597,13 +598,11 @@ namespace FlaxEditor.Windows.Assets
_propertiesEditor.Select(_properties);
// Toolstrip
- _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
- _toolstrip.AddSeparator();
- _undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
- _redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
- _toolstrip.AddSeparator();
- _toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
- _toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
+ SurfaceUtils.VisjectCommonToolstripSetup(editor, _toolstrip, _undo,
+ Save, ShowWholeGraph, ToggleGridSnap, InputActions,
+ out _saveButton, out _undoButton, out _redoButton, out _gridSnapButton);
+
+ // The rest of the toolstrip
_toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/scripting/visual/index.html")).LinkTooltip("See documentation to learn more");
_debugToolstripControls = new[]
@@ -643,9 +642,6 @@ namespace FlaxEditor.Windows.Assets
debugObjectPickerContainer.Parent = _toolstrip;
// Setup input actions
- InputActions.Add(options => options.Undo, _undo.PerformUndo);
- InputActions.Add(options => options.Redo, _undo.PerformRedo);
- InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
InputActions.Add(options => options.DebuggerContinue, OnDebuggerContinue);
InputActions.Add(options => options.DebuggerStepOver, OnDebuggerStepOver);
InputActions.Add(options => options.DebuggerStepOut, OnDebuggerStepOut);
@@ -687,6 +683,11 @@ namespace FlaxEditor.Windows.Assets
_surface.ShowWholeGraph();
}
+ private void ToggleGridSnap()
+ {
+ SurfaceUtils.ToggleSurfaceGridSnap(_surface, _gridSnapButton);
+ }
+
///
/// Refreshes temporary asset to see changes live when editing the surface.
///