From 968772cbada68a076800bff47323254198dd097f Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 6 Mar 2026 22:54:17 +0100 Subject: [PATCH 01/13] resize node in all directions --- Source/Editor/Surface/ResizableSurfaceNode.cs | 119 +++++++++++++----- Source/Editor/Surface/SurfaceComment.cs | 16 +-- 2 files changed, 95 insertions(+), 40 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 259c29836..2a1f549fe 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -1,19 +1,22 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.Surface { /// - /// Visject Surface node control that cna be resized. + /// Visject Surface node control that can be resized. /// /// [HideInEditor] public class ResizableSurfaceNode : SurfaceNode { + private Float2 _resizeWeight; private Float2 _startResizingSize; - private Float2 _startResizingCornerOffset; + private Float2 _surfaceMouseLocation; + private bool _isMouseOverResizeBorder; /// /// Indicates whether the node is currently being resized. @@ -30,11 +33,6 @@ namespace FlaxEditor.Surface /// protected Float2 _sizeMin = new Float2(240, 160); - /// - /// Node resizing rectangle bounds. - /// - protected Rectangle _resizeButtonRect; - private Float2 SizeValue { get => (Float2)Values[_sizeValueIndex]; @@ -50,7 +48,7 @@ namespace FlaxEditor.Surface /// public override bool CanSelect(ref Float2 location) { - return base.CanSelect(ref location) && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location); + return base.CanSelect(ref location);// && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location); } /// @@ -79,15 +77,9 @@ namespace FlaxEditor.Surface { base.Draw(); - if (Surface.CanEdit) + if (Surface.CanEdit && (_isResizing || _isMouseOverResizeBorder)) { - var style = Style.Current; - if (_isResizing) - { - Render2D.FillRectangle(_resizeButtonRect, style.Selection); - Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); - } - Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); + Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 2f); } } @@ -109,46 +101,120 @@ namespace FlaxEditor.Surface base.OnEndMouseCapture(); } + private bool UpdateIsOverResizeBorder(Float2 location) + { + const float ResizeBorderHalfWidth = 20f; + const float ResizeOnAxisThreshold = 0.85f; + + var nodeRect = new Rectangle(Float2.Zero, Size); + bool onBorder = nodeRect.MakeExpanded(ResizeBorderHalfWidth).Contains(location); + var inNodeRect = nodeRect.MakeExpanded(-ResizeBorderHalfWidth); + bool inNode = inNodeRect.Contains(location); + + Float2 rawResizeWeight = (location - nodeRect.Center) / (nodeRect.Size * 0.5f); + _resizeWeight = new Float2(Mathf.Abs(rawResizeWeight.X) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.X) : 0, + Mathf.Abs(rawResizeWeight.Y) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.Y) : 0); + + _isMouseOverResizeBorder = onBorder && !inNode; + return onBorder && !inNode; + } + + private CursorType ResizeWeightToCursorType() + { + if ((_resizeWeight.X == 1 && _resizeWeight.Y == 0) || (_resizeWeight.X == -1 && _resizeWeight.Y == 0)) + return CursorType.SizeWE; + if ((_resizeWeight.X == 0 && _resizeWeight.Y == 1) || (_resizeWeight.X == 0 && _resizeWeight.Y == -1)) + return CursorType.SizeNS; + if ((_resizeWeight.X == -1 && _resizeWeight.Y == -1) || (_resizeWeight.X == 1 && _resizeWeight.Y == 1)) + return CursorType.SizeNWSE; + if ((_resizeWeight.X == 1 && _resizeWeight.Y == -1) || (_resizeWeight.X == -1 && _resizeWeight.Y == 1)) + return CursorType.SizeNESW; + return CursorType.Default; + } + /// public override bool OnMouseDown(Float2 location, MouseButton button) { if (base.OnMouseDown(location, button)) return true; - if (button == MouseButton.Left && _resizeButtonRect.Contains(ref location) && Surface.CanEdit) + if (button == MouseButton.Left && UpdateIsOverResizeBorder(location)) { // Start resizing + UpdateSurfaceMouseLocation(); _isResizing = true; _startResizingSize = Size; - _startResizingCornerOffset = Size - location; StartMouseCapture(); - Cursor = CursorType.SizeNWSE; return true; } return false; } + private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) + { + var pointOrigin = control.Parent ?? control; + var startPos = pointOrigin.PointFromParent(this, start); + var endPos = pointOrigin.PointFromParent(this, end); + return endPos - startPos; + } + + private void UpdateSurfaceMouseLocation() + { + _surfaceMouseLocation = Surface.PointFromScreen(Input.MouseScreenPosition); + } + /// public override void OnMouseMove(Float2 location) { if (_isResizing) { + var resizeAxisAbs = _resizeWeight.Absolute; + var resizeAxisPos = Float2.Clamp(_resizeWeight, Float2.Zero, Float2.One); + var resizeAxisNeg = Float2.Clamp(-_resizeWeight, Float2.Zero, Float2.One); + + var delta = PointToParent(Surface, location) - _surfaceMouseLocation; + // TODO: scale/size snapping? + delta *= resizeAxisAbs; + + var moveLocation = _surfaceMouseLocation + delta; + + // TODO: Do I need GetControlDelta? + var uiControlDelta = GetControlDelta(this, ref _surfaceMouseLocation, ref moveLocation); var emptySize = CalculateNodeSize(0, 0); - var size = Float2.Max(location - emptySize + _startResizingCornerOffset, _sizeMin); - Resize(size.X, size.Y); + Location += uiControlDelta * resizeAxisNeg; + Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; + Size = new Float2(Mathf.Max(Size.X, _sizeMin.X), Mathf.Max(Size.Y, _sizeMin.Y)); + SizeValue = Size - emptySize; + SizeValue = new Float2(Mathf.Max(SizeValue.X, _sizeMin.X), Mathf.Max(SizeValue.Y, _sizeMin.Y)); + CalculateNodeSize(Size.X, Size.Y); + UpdateSurfaceMouseLocation(); + } + else if (UpdateIsOverResizeBorder(location)) + { + Cursor = ResizeWeightToCursorType(); } else { + Cursor = CursorType.Default; base.OnMouseMove(location); } } + /// + public override void OnMouseLeave() + { + Cursor = CursorType.Default; + _isMouseOverResizeBorder = false; + base.OnMouseLeave(); + } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { if (button == MouseButton.Left && _isResizing) { + Cursor = CursorType.Default; EndResizing(); return true; } @@ -156,19 +222,8 @@ namespace FlaxEditor.Surface return base.OnMouseUp(location, button); } - /// - protected override void UpdateRectangles() - { - base.UpdateRectangles(); - - const float buttonMargin = Constants.NodeCloseButtonMargin; - const float buttonSize = Constants.NodeCloseButtonSize; - _resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin - 4, buttonSize, buttonSize); - } - private void EndResizing() { - Cursor = CursorType.Default; EndMouseCapture(); _isResizing = false; if (_startResizingSize != Size) diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index a76fa245d..aa0b0e811 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -130,7 +130,7 @@ namespace FlaxEditor.Surface _headerRect = new Rectangle(0, 0, Width, headerSize); _closeButtonRect = new Rectangle(Width - buttonSize - buttonMargin, buttonMargin, buttonSize, buttonSize); _colorButtonRect = new Rectangle(_closeButtonRect.Left - buttonSize - buttonMargin, buttonMargin, buttonSize, buttonSize); - _resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin, buttonSize, buttonSize); + //_resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin, buttonSize, buttonSize); _renameTextBox.Width = Width; _renameTextBox.Height = headerSize; } @@ -189,12 +189,12 @@ namespace FlaxEditor.Surface Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); // Resize button - if (_isResizing) - { - Render2D.FillRectangle(_resizeButtonRect, style.Selection); - Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); - } - Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); + //if (_isResizing) + //{ + // Render2D.FillRectangle(_resizeButtonRect, style.Selection); + // Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); + //} + //Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); } // Selection outline @@ -229,7 +229,7 @@ namespace FlaxEditor.Surface /// public override bool ContainsPoint(ref Float2 location, bool precise) { - return _headerRect.Contains(ref location) || _resizeButtonRect.Contains(ref location); + return _headerRect.Contains(ref location);// || _resizeButtonRect.Contains(ref location); } /// From 2d3d836103f1cea3593750c075c97ed260c93219 Mon Sep 17 00:00:00 2001 From: Saas Date: Sat, 7 Mar 2026 16:24:05 +0100 Subject: [PATCH 02/13] I need something to diff against --- Source/Editor/Surface/ResizableSurfaceNode.cs | 378 ++++++++++-------- Source/Editor/Surface/SurfaceComment.cs | 2 +- 2 files changed, 217 insertions(+), 163 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 2a1f549fe..ed79c90d2 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -13,15 +13,204 @@ namespace FlaxEditor.Surface [HideInEditor] public class ResizableSurfaceNode : SurfaceNode { - private Float2 _resizeWeight; - private Float2 _startResizingSize; - private Float2 _surfaceMouseLocation; - private bool _isMouseOverResizeBorder; + private class ResizeBorder : Control + { + private const float BorderWidth = 15f; + private const float ResizeOnAxisThreshold = 0.85f; - /// - /// Indicates whether the node is currently being resized. - /// - protected bool _isResizing; + public VisjectSurface Surface; + public ResizableSurfaceNode ResizeableNode; + + private Float2 _surfaceMouseLocation; + private Float2 startResizingSize; + + public bool IsMouseOverResizeBorder { get; private set; } + public bool IsResizing { get; private set; } + + public Action StartResize; + public Action EndResize; + + public Float2 ResizeWeight { get; private set; } + public CursorType CursorType + { + get + { + if ((ResizeWeight.X == 1 && ResizeWeight.Y == 0) || (ResizeWeight.X == -1 && ResizeWeight.Y == 0)) + return CursorType.SizeWE; + if ((ResizeWeight.X == 0 && ResizeWeight.Y == 1) || (ResizeWeight.X == 0 && ResizeWeight.Y == -1)) + return CursorType.SizeNS; + if ((ResizeWeight.X == -1 && ResizeWeight.Y == -1) || (ResizeWeight.X == 1 && ResizeWeight.Y == 1)) + return CursorType.SizeNWSE; + if ((ResizeWeight.X == 1 && ResizeWeight.Y == -1) || (ResizeWeight.X == -1 && ResizeWeight.Y == 1)) + return CursorType.SizeNESW; + + return CursorType.Default; + } + } + + + + public ResizeBorder(VisjectSurface surface, ResizableSurfaceNode resizeableNode) + { + Surface = surface; + ResizeableNode = resizeableNode; + } + + /// + /// Updates location and size to match the resizeable node with the additional padding. + /// + /// The node size. + /// The node location. + public void MatchResizeableNode(Float2 nodeSize, Float2 nodeLocation) + { + Size = nodeSize + new Float2(BorderWidth); + Location = nodeLocation - new Float2(BorderWidth * 0.5f); + } + + private void UpdateSurfaceMouseLocation() + { + _surfaceMouseLocation = Surface.PointFromScreen(Input.MouseScreenPosition); + } + + private void UpdateResizeFlags(Float2 location) + { + var borderRect = Bounds with { Location = Float2.Zero }; + bool onBorder = borderRect.Contains(location); + + Float2 rawResizeWeight = (location - borderRect.Center) / (borderRect.Size * 0.5f); + ResizeWeight = new Float2(Mathf.Abs(rawResizeWeight.X) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.X) : 0, + Mathf.Abs(rawResizeWeight.Y) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.Y) : 0); + + IsMouseOverResizeBorder = onBorder && !ResizeableNode.IsMouseOver; + } + + private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) + { + var pointOrigin = control.Parent ?? control; + var startPos = pointOrigin.PointFromParent(ResizeableNode, start); + var endPos = pointOrigin.PointFromParent(ResizeableNode, end); + return endPos - startPos; + } + + private void EndResizing() + { + EndMouseCapture(); + IsResizing = false; + if (startResizingSize != ResizeableNode.Size) + { + var emptySize = ResizeableNode.CalculateNodeSize(0, 0); + ResizeableNode.SizeValue = ResizeableNode.Size - emptySize; + Surface.MarkAsEdited(false); + } + } + + public override void OnMouseLeave() + { + Cursor = CursorType.Default; + IsMouseOverResizeBorder = false; + base.OnMouseLeave(); + } + + /// + public override void OnMouseMove(Float2 location) + { + if (!IsResizing) + { + UpdateResizeFlags(location); + } + else + { + var resizeAxisAbs = ResizeWeight.Absolute; + var resizeAxisPos = Float2.Clamp(ResizeWeight, Float2.Zero, Float2.One); + var resizeAxisNeg = Float2.Clamp(-ResizeWeight, Float2.Zero, Float2.One); + + var currentSurfaceMouse = Surface.PointFromScreen(Input.MouseScreenPosition); + var delta = currentSurfaceMouse - _surfaceMouseLocation; + // TODO: scale/size snapping? + delta *= resizeAxisAbs; + + var moveLocation = _surfaceMouseLocation + delta; + + // TODO: Do I need GetControlDelta? + var uiControlDelta = GetControlDelta(this, ref _surfaceMouseLocation, ref moveLocation); + var emptySize = ResizeableNode.CalculateNodeSize(0, 0); + ResizeableNode.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; + Float2 oldSize = ResizeableNode.Size; + ResizeableNode.Size = new Float2(Mathf.Max(ResizeableNode.Size.X, ResizeableNode.sizeMin.X), Mathf.Max(ResizeableNode.Size.Y, ResizeableNode.sizeMin.Y)); + if (oldSize == ResizeableNode.Size) // Only move if size wasn't clamped + { + ResizeableNode.Location += uiControlDelta * resizeAxisNeg; + } + ResizeableNode.SizeValue = ResizeableNode.Size - emptySize; + ResizeableNode.SizeValue = new Float2(Mathf.Max(ResizeableNode.SizeValue.X, ResizeableNode.sizeMin.X), Mathf.Max(ResizeableNode.SizeValue.Y, ResizeableNode.sizeMin.Y)); + ResizeableNode.CalculateNodeSize(ResizeableNode.Size.X, ResizeableNode.Size.Y); + UpdateSurfaceMouseLocation(); + MatchResizeableNode(ResizeableNode.Size, ResizeableNode.Location); + } + + if (IsMouseOverResizeBorder) + Cursor = CursorType; + else + Cursor = CursorType.Default; + + + + base.OnMouseMove(location); + } + + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (button == MouseButton.Left && IsMouseOverResizeBorder && !IsResizing) + { + // Start resizing + UpdateSurfaceMouseLocation(); + IsResizing = true; + startResizingSize = ResizeableNode.Size; + StartMouseCapture(); + return true; + } + + return base.OnMouseDown(location, button); + } + + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (button == MouseButton.Left && IsResizing) + { + Cursor = CursorType.Default; + EndResizing(); + return true; + } + + return base.OnMouseUp(location, button); + } + + /// + public override void OnLostFocus() + { + if (IsResizing) + EndResizing(); + + base.OnLostFocus(); + } + + /// + public override void OnEndMouseCapture() + { + if (IsResizing) + EndResizing(); + + base.OnEndMouseCapture(); + } + + //public override void Draw() + //{ + // Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Color.Blue, 1f); + //} + } + + + private ResizeBorder resizeBorder; /// /// Index of the Float2 value in the node values list to store node size. @@ -31,7 +220,7 @@ namespace FlaxEditor.Surface /// /// Minimum node size. /// - protected Float2 _sizeMin = new Float2(240, 160); + protected Float2 sizeMin = new Float2(240, 160); private Float2 SizeValue { @@ -43,12 +232,27 @@ namespace FlaxEditor.Surface public ResizableSurfaceNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) : base(id, context, nodeArch, groupArch) { + CullChildren = false; + ClipChildren = false; + resizeBorder = new ResizeBorder(Surface, this) + { + Parent = Surface.SurfaceRoot, + }; + + resizeBorder.MatchResizeableNode(Size, Location); + } + + /// + protected override void OnLocationChanged() + { + resizeBorder.MatchResizeableNode(Size, Location); + base.OnLocationChanged(); } /// public override bool CanSelect(ref Float2 location) { - return base.CanSelect(ref location);// && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location); + return base.CanSelect(ref location); } /// @@ -59,6 +263,7 @@ namespace FlaxEditor.Surface if (Surface != null && Surface.GridSnappingEnabled) size = Surface.SnapToGrid(size, true); Resize(size.X, size.Y); + resizeBorder.MatchResizeableNode(Size, Location); base.OnSurfaceLoaded(action); } @@ -77,161 +282,10 @@ namespace FlaxEditor.Surface { base.Draw(); - if (Surface.CanEdit && (_isResizing || _isMouseOverResizeBorder)) + if (Surface.CanEdit && (resizeBorder.IsResizing || resizeBorder.IsMouseOverResizeBorder)) { Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 2f); } } - - /// - public override void OnLostFocus() - { - if (_isResizing) - EndResizing(); - - base.OnLostFocus(); - } - - /// - public override void OnEndMouseCapture() - { - if (_isResizing) - EndResizing(); - - base.OnEndMouseCapture(); - } - - private bool UpdateIsOverResizeBorder(Float2 location) - { - const float ResizeBorderHalfWidth = 20f; - const float ResizeOnAxisThreshold = 0.85f; - - var nodeRect = new Rectangle(Float2.Zero, Size); - bool onBorder = nodeRect.MakeExpanded(ResizeBorderHalfWidth).Contains(location); - var inNodeRect = nodeRect.MakeExpanded(-ResizeBorderHalfWidth); - bool inNode = inNodeRect.Contains(location); - - Float2 rawResizeWeight = (location - nodeRect.Center) / (nodeRect.Size * 0.5f); - _resizeWeight = new Float2(Mathf.Abs(rawResizeWeight.X) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.X) : 0, - Mathf.Abs(rawResizeWeight.Y) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.Y) : 0); - - _isMouseOverResizeBorder = onBorder && !inNode; - return onBorder && !inNode; - } - - private CursorType ResizeWeightToCursorType() - { - if ((_resizeWeight.X == 1 && _resizeWeight.Y == 0) || (_resizeWeight.X == -1 && _resizeWeight.Y == 0)) - return CursorType.SizeWE; - if ((_resizeWeight.X == 0 && _resizeWeight.Y == 1) || (_resizeWeight.X == 0 && _resizeWeight.Y == -1)) - return CursorType.SizeNS; - if ((_resizeWeight.X == -1 && _resizeWeight.Y == -1) || (_resizeWeight.X == 1 && _resizeWeight.Y == 1)) - return CursorType.SizeNWSE; - if ((_resizeWeight.X == 1 && _resizeWeight.Y == -1) || (_resizeWeight.X == -1 && _resizeWeight.Y == 1)) - return CursorType.SizeNESW; - return CursorType.Default; - } - - /// - public override bool OnMouseDown(Float2 location, MouseButton button) - { - if (base.OnMouseDown(location, button)) - return true; - - if (button == MouseButton.Left && UpdateIsOverResizeBorder(location)) - { - // Start resizing - UpdateSurfaceMouseLocation(); - _isResizing = true; - _startResizingSize = Size; - StartMouseCapture(); - return true; - } - - return false; - } - - private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) - { - var pointOrigin = control.Parent ?? control; - var startPos = pointOrigin.PointFromParent(this, start); - var endPos = pointOrigin.PointFromParent(this, end); - return endPos - startPos; - } - - private void UpdateSurfaceMouseLocation() - { - _surfaceMouseLocation = Surface.PointFromScreen(Input.MouseScreenPosition); - } - - /// - public override void OnMouseMove(Float2 location) - { - if (_isResizing) - { - var resizeAxisAbs = _resizeWeight.Absolute; - var resizeAxisPos = Float2.Clamp(_resizeWeight, Float2.Zero, Float2.One); - var resizeAxisNeg = Float2.Clamp(-_resizeWeight, Float2.Zero, Float2.One); - - var delta = PointToParent(Surface, location) - _surfaceMouseLocation; - // TODO: scale/size snapping? - delta *= resizeAxisAbs; - - var moveLocation = _surfaceMouseLocation + delta; - - // TODO: Do I need GetControlDelta? - var uiControlDelta = GetControlDelta(this, ref _surfaceMouseLocation, ref moveLocation); - var emptySize = CalculateNodeSize(0, 0); - Location += uiControlDelta * resizeAxisNeg; - Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; - Size = new Float2(Mathf.Max(Size.X, _sizeMin.X), Mathf.Max(Size.Y, _sizeMin.Y)); - SizeValue = Size - emptySize; - SizeValue = new Float2(Mathf.Max(SizeValue.X, _sizeMin.X), Mathf.Max(SizeValue.Y, _sizeMin.Y)); - CalculateNodeSize(Size.X, Size.Y); - UpdateSurfaceMouseLocation(); - } - else if (UpdateIsOverResizeBorder(location)) - { - Cursor = ResizeWeightToCursorType(); - } - else - { - Cursor = CursorType.Default; - base.OnMouseMove(location); - } - } - - /// - public override void OnMouseLeave() - { - Cursor = CursorType.Default; - _isMouseOverResizeBorder = false; - base.OnMouseLeave(); - } - - /// - public override bool OnMouseUp(Float2 location, MouseButton button) - { - if (button == MouseButton.Left && _isResizing) - { - Cursor = CursorType.Default; - EndResizing(); - return true; - } - - return base.OnMouseUp(location, button); - } - - private void EndResizing() - { - EndMouseCapture(); - _isResizing = false; - if (_startResizingSize != Size) - { - var emptySize = CalculateNodeSize(0, 0); - SizeValue = Size - emptySize; - Surface.MarkAsEdited(false); - } - } } } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index aa0b0e811..05e89b705 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -56,7 +56,7 @@ namespace FlaxEditor.Surface : base(id, context, nodeArch, groupArch) { _sizeValueIndex = 2; // Index of the Size stored in Values array - _sizeMin = new Float2(140.0f, Constants.NodeHeaderSize); + sizeMin = new Float2(140.0f, Constants.NodeHeaderSize); _renameTextBox = new TextBox(false, 0, 0, Width) { Height = Constants.NodeHeaderSize, From 2805c0dd7b9f69ee8bb9d4e6e7b949d3c21a51b8 Mon Sep 17 00:00:00 2001 From: Saas Date: Sat, 7 Mar 2026 23:48:24 +0100 Subject: [PATCH 03/13] please squash commit history before merge I need sth to diff against again --- Source/Editor/Surface/ResizableSurfaceNode.cs | 167 +++++++++++------- Source/Editor/Surface/SurfaceComment.cs | 13 +- 2 files changed, 103 insertions(+), 77 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index ed79c90d2..6fa7225c5 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -13,58 +13,73 @@ namespace FlaxEditor.Surface [HideInEditor] public class ResizableSurfaceNode : SurfaceNode { - private class ResizeBorder : Control + /// + /// Helper class for that handles mouse interactions resizing the node itself. + /// + protected class ResizeBorder : Control { + /// + /// Distance to each of the 4 node edges that the cursor has to be so the user can resize along the direction of the edge. + /// private const float BorderWidth = 15f; - private const float ResizeOnAxisThreshold = 0.85f; - - public VisjectSurface Surface; - public ResizableSurfaceNode ResizeableNode; + private readonly VisjectSurface Surface; + private readonly ResizableSurfaceNode ResizableNode; private Float2 _surfaceMouseLocation; private Float2 startResizingSize; + /// + /// True if the mouse is at the border of the resizable node and not further away from the border than . + /// public bool IsMouseOverResizeBorder { get; private set; } + + /// + /// True if is being resized. + /// public bool IsResizing { get; private set; } - public Action StartResize; - public Action EndResize; + /// + /// The direction in which to resize the node. Should be either -1, 0 or 1 on both axes. + /// + public Float2 ResizeDirection { get; private set; } - public Float2 ResizeWeight { get; private set; } + /// + /// The type of cursor to show to hint to the user that they can resize the node in a given direction. + /// public CursorType CursorType { get { - if ((ResizeWeight.X == 1 && ResizeWeight.Y == 0) || (ResizeWeight.X == -1 && ResizeWeight.Y == 0)) + if ((ResizeDirection.X == 1 && ResizeDirection.Y == 0) || (ResizeDirection.X == -1 && ResizeDirection.Y == 0)) return CursorType.SizeWE; - if ((ResizeWeight.X == 0 && ResizeWeight.Y == 1) || (ResizeWeight.X == 0 && ResizeWeight.Y == -1)) + if ((ResizeDirection.X == 0 && ResizeDirection.Y == 1) || (ResizeDirection.X == 0 && ResizeDirection.Y == -1)) return CursorType.SizeNS; - if ((ResizeWeight.X == -1 && ResizeWeight.Y == -1) || (ResizeWeight.X == 1 && ResizeWeight.Y == 1)) + if ((ResizeDirection.X == -1 && ResizeDirection.Y == -1) || (ResizeDirection.X == 1 && ResizeDirection.Y == 1)) return CursorType.SizeNWSE; - if ((ResizeWeight.X == 1 && ResizeWeight.Y == -1) || (ResizeWeight.X == -1 && ResizeWeight.Y == 1)) + if ((ResizeDirection.X == 1 && ResizeDirection.Y == -1) || (ResizeDirection.X == -1 && ResizeDirection.Y == 1)) return CursorType.SizeNESW; return CursorType.Default; } } - - public ResizeBorder(VisjectSurface surface, ResizableSurfaceNode resizeableNode) + /// + public ResizeBorder(VisjectSurface surface, ResizableSurfaceNode resizableNode) { Surface = surface; - ResizeableNode = resizeableNode; + ResizableNode = resizableNode; } /// - /// Updates location and size to match the resizeable node with the additional padding. + /// Updates location and size to match the resizable node with the additional padding. /// /// The node size. /// The node location. - public void MatchResizeableNode(Float2 nodeSize, Float2 nodeLocation) + public void MatchResizableNode(Float2 nodeSize, Float2 nodeLocation) { - Size = nodeSize + new Float2(BorderWidth); - Location = nodeLocation - new Float2(BorderWidth * 0.5f); + Size = nodeSize + new Float2(BorderWidth * 2); + Location = nodeLocation - new Float2(BorderWidth); } private void UpdateSurfaceMouseLocation() @@ -72,23 +87,27 @@ namespace FlaxEditor.Surface _surfaceMouseLocation = Surface.PointFromScreen(Input.MouseScreenPosition); } - private void UpdateResizeFlags(Float2 location) + private void UpdateResizeFlags(Float2 mouseLocation) { var borderRect = Bounds with { Location = Float2.Zero }; - bool onBorder = borderRect.Contains(location); + bool onBorder = borderRect.Contains(mouseLocation); + // Check this the way we do because some resizeable nodes (like comments) have an implementation of `Control.ContainsPoint` + // that does not check for the size you would think they have based on their visual appearance + bool inNode = borderRect.MakeExpanded(-BorderWidth * 2f).Contains(mouseLocation); - Float2 rawResizeWeight = (location - borderRect.Center) / (borderRect.Size * 0.5f); - ResizeWeight = new Float2(Mathf.Abs(rawResizeWeight.X) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.X) : 0, - Mathf.Abs(rawResizeWeight.Y) >= ResizeOnAxisThreshold ? Mathf.Sign(rawResizeWeight.Y) : 0); + Float2 rawResizeDirection = (mouseLocation - borderRect.Center); + var nodeHalfSizeNoBorder = ResizableNode.Size * 0.5f - BorderWidth; + ResizeDirection = new Float2(Mathf.Abs(rawResizeDirection.X) >= nodeHalfSizeNoBorder.X ? Mathf.Sign(rawResizeDirection.X) : 0, + Mathf.Abs(rawResizeDirection.Y) >= nodeHalfSizeNoBorder.Y ? Mathf.Sign(rawResizeDirection.Y) : 0); - IsMouseOverResizeBorder = onBorder && !ResizeableNode.IsMouseOver; + IsMouseOverResizeBorder = false;// onBorder && !inNode; } private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) { var pointOrigin = control.Parent ?? control; - var startPos = pointOrigin.PointFromParent(ResizeableNode, start); - var endPos = pointOrigin.PointFromParent(ResizeableNode, end); + var startPos = pointOrigin.PointFromParent(ResizableNode, start); + var endPos = pointOrigin.PointFromParent(ResizableNode, end); return endPos - startPos; } @@ -96,14 +115,15 @@ namespace FlaxEditor.Surface { EndMouseCapture(); IsResizing = false; - if (startResizingSize != ResizeableNode.Size) + if (startResizingSize != ResizableNode.Size) { - var emptySize = ResizeableNode.CalculateNodeSize(0, 0); - ResizeableNode.SizeValue = ResizeableNode.Size - emptySize; + var emptySize = ResizableNode.CalculateNodeSize(0, 0); + ResizableNode.SizeValue = ResizableNode.Size - emptySize; Surface.MarkAsEdited(false); } } + /// public override void OnMouseLeave() { Cursor = CursorType.Default; @@ -120,44 +140,53 @@ namespace FlaxEditor.Surface } else { - var resizeAxisAbs = ResizeWeight.Absolute; - var resizeAxisPos = Float2.Clamp(ResizeWeight, Float2.Zero, Float2.One); - var resizeAxisNeg = Float2.Clamp(-ResizeWeight, Float2.Zero, Float2.One); + var resizeAxisAbs = ResizeDirection.Absolute; + var resizeAxisPos = Float2.Clamp(ResizeDirection, Float2.Zero, Float2.One); + var resizeAxisNeg = Float2.Clamp(-ResizeDirection, Float2.Zero, Float2.One); var currentSurfaceMouse = Surface.PointFromScreen(Input.MouseScreenPosition); var delta = currentSurfaceMouse - _surfaceMouseLocation; + // TODO: scale/size snapping? delta *= resizeAxisAbs; var moveLocation = _surfaceMouseLocation + delta; - // TODO: Do I need GetControlDelta? var uiControlDelta = GetControlDelta(this, ref _surfaceMouseLocation, ref moveLocation); - var emptySize = ResizeableNode.CalculateNodeSize(0, 0); - ResizeableNode.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; - Float2 oldSize = ResizeableNode.Size; - ResizeableNode.Size = new Float2(Mathf.Max(ResizeableNode.Size.X, ResizeableNode.sizeMin.X), Mathf.Max(ResizeableNode.Size.Y, ResizeableNode.sizeMin.Y)); - if (oldSize == ResizeableNode.Size) // Only move if size wasn't clamped - { - ResizeableNode.Location += uiControlDelta * resizeAxisNeg; - } - ResizeableNode.SizeValue = ResizeableNode.Size - emptySize; - ResizeableNode.SizeValue = new Float2(Mathf.Max(ResizeableNode.SizeValue.X, ResizeableNode.sizeMin.X), Mathf.Max(ResizeableNode.SizeValue.Y, ResizeableNode.sizeMin.Y)); - ResizeableNode.CalculateNodeSize(ResizeableNode.Size.X, ResizeableNode.Size.Y); + var emptySize = ResizableNode.CalculateNodeSize(0, 0); + + + // TODO: Fix: Can't move comments anymore + + // TODO: If resize is blocked by min size and the user tries to increase the size again, wait until !blocked by min size to apply delta again + // To do this, just record pos when starting to block by min size and if (cursorLocation > min) { ResizeAgain() } + + ResizableNode.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; + ResizableNode.Size = new Float2(Mathf.Max(ResizableNode.Size.X, ResizableNode.sizeMin.X), Mathf.Max(ResizableNode.Size.Y, ResizableNode.sizeMin.Y)); + ResizableNode.Location += uiControlDelta * resizeAxisNeg; + + // Only move if size wasn't clamped + + + //Debug.Log($"OLD: {oldSize} NEW: {ResizableNode.Size}"); + + ResizableNode.SizeValue = ResizableNode.Size - emptySize; + ResizableNode.SizeValue = new Float2(Mathf.Max(ResizableNode.SizeValue.X, ResizableNode.sizeMin.X), Mathf.Max(ResizableNode.SizeValue.Y, ResizableNode.sizeMin.Y)); + ResizableNode.CalculateNodeSize(ResizableNode.Size.X, ResizableNode.Size.Y); + UpdateSurfaceMouseLocation(); - MatchResizeableNode(ResizeableNode.Size, ResizeableNode.Location); + MatchResizableNode(ResizableNode.Size, ResizableNode.Location); } if (IsMouseOverResizeBorder) Cursor = CursorType; else Cursor = CursorType.Default; - - base.OnMouseMove(location); } + /// public override bool OnMouseDown(Float2 location, MouseButton button) { if (button == MouseButton.Left && IsMouseOverResizeBorder && !IsResizing) @@ -165,7 +194,7 @@ namespace FlaxEditor.Surface // Start resizing UpdateSurfaceMouseLocation(); IsResizing = true; - startResizingSize = ResizeableNode.Size; + startResizingSize = ResizableNode.Size; StartMouseCapture(); return true; } @@ -173,6 +202,7 @@ namespace FlaxEditor.Surface return base.OnMouseDown(location, button); } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { if (button == MouseButton.Left && IsResizing) @@ -203,14 +233,17 @@ namespace FlaxEditor.Surface base.OnEndMouseCapture(); } - //public override void Draw() - //{ - // Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Color.Blue, 1f); - //} + /// + public override void Draw() + { + Render2D.DrawRectangle(Bounds with { Location = Float2.Zero }, Color.Green, 1f); + } } - - private ResizeBorder resizeBorder; + /// + /// Represents the border control used for resizing the associated element. + /// + protected ResizeBorder ResizeBorderControl; /// /// Index of the Float2 value in the node values list to store node size. @@ -232,28 +265,26 @@ namespace FlaxEditor.Surface public ResizableSurfaceNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) : base(id, context, nodeArch, groupArch) { - CullChildren = false; - ClipChildren = false; - resizeBorder = new ResizeBorder(Surface, this) + ResizeBorderControl = new ResizeBorder(Surface, this) { Parent = Surface.SurfaceRoot, }; - resizeBorder.MatchResizeableNode(Size, Location); + ResizeBorderControl.MatchResizableNode(Size, Location); } /// protected override void OnLocationChanged() { - resizeBorder.MatchResizeableNode(Size, Location); + ResizeBorderControl.MatchResizableNode(Size, Location); base.OnLocationChanged(); } - /// - public override bool CanSelect(ref Float2 location) - { - return base.CanSelect(ref location); - } + ///// + //public override bool CanSelect(ref Float2 location) + //{ + // return base.CanSelect(ref location); + //} /// public override void OnSurfaceLoaded(SurfaceNodeActions action) @@ -263,7 +294,7 @@ namespace FlaxEditor.Surface if (Surface != null && Surface.GridSnappingEnabled) size = Surface.SnapToGrid(size, true); Resize(size.X, size.Y); - resizeBorder.MatchResizeableNode(Size, Location); + ResizeBorderControl.MatchResizableNode(Size, Location); base.OnSurfaceLoaded(action); } @@ -282,9 +313,9 @@ namespace FlaxEditor.Surface { base.Draw(); - if (Surface.CanEdit && (resizeBorder.IsResizing || resizeBorder.IsMouseOverResizeBorder)) + if (Surface.CanEdit && (ResizeBorderControl.IsResizing || ResizeBorderControl.IsMouseOverResizeBorder)) { - Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 2f); + Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 0.5f); } } } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 05e89b705..3a8416fd3 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -130,7 +130,6 @@ namespace FlaxEditor.Surface _headerRect = new Rectangle(0, 0, Width, headerSize); _closeButtonRect = new Rectangle(Width - buttonSize - buttonMargin, buttonMargin, buttonSize, buttonSize); _colorButtonRect = new Rectangle(_closeButtonRect.Left - buttonSize - buttonMargin, buttonMargin, buttonSize, buttonSize); - //_resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin, buttonSize, buttonSize); _renameTextBox.Width = Width; _renameTextBox.Height = headerSize; } @@ -188,13 +187,9 @@ namespace FlaxEditor.Surface // Color button Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - // Resize button - //if (_isResizing) - //{ - // Render2D.FillRectangle(_resizeButtonRect, style.Selection); - // Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); - //} - //Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey); + // Resize + if (ResizeBorderControl.IsResizing || ResizeBorderControl.IsMouseOverResizeBorder) + Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 0.5f); } // Selection outline @@ -229,7 +224,7 @@ namespace FlaxEditor.Surface /// public override bool ContainsPoint(ref Float2 location, bool precise) { - return _headerRect.Contains(ref location);// || _resizeButtonRect.Contains(ref location); + return _headerRect.Contains(ref location); } /// From 1105e58b066819fac0feb8d3bda321d08144a3c5 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 8 Mar 2026 14:53:48 +0100 Subject: [PATCH 04/13] fix not being able to move comments nodes --- Source/Editor/Surface/SurfaceComment.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 3a8416fd3..c257dddd1 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -81,12 +81,16 @@ namespace FlaxEditor.Surface if (Values.Length < 4) { if (IndexInParent > 0) + { IndexInParent = 0; + ResizeBorderControl.IndexInParent = - 1; + } OrderValue = IndexInParent; } else if (OrderValue != -1) { IndexInParent = OrderValue; + ResizeBorderControl.IndexInParent = OrderValue - 1; } } From 2c8ade42888cb2520fc63328bfb8e34d8fb80660 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 8 Mar 2026 23:11:22 +0100 Subject: [PATCH 05/13] fix comment order and not being able to move around comment nodes --- Source/Editor/Surface/ResizableSurfaceNode.cs | 38 ++++++++++++------- Source/Editor/Surface/SurfaceComment.cs | 30 +++++++-------- Source/Editor/Surface/SurfaceRootControl.cs | 6 +-- .../Editor/Surface/VisjectSurfaceContext.cs | 8 ++-- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 6fa7225c5..ea9ceec82 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -16,7 +16,7 @@ namespace FlaxEditor.Surface /// /// Helper class for that handles mouse interactions resizing the node itself. /// - protected class ResizeBorder : Control + public class ResizeBorder : ContainerControl { /// /// Distance to each of the 4 node edges that the cursor has to be so the user can resize along the direction of the edge. @@ -24,10 +24,14 @@ namespace FlaxEditor.Surface private const float BorderWidth = 15f; private readonly VisjectSurface Surface; - private readonly ResizableSurfaceNode ResizableNode; private Float2 _surfaceMouseLocation; private Float2 startResizingSize; + /// + /// The resizable node that this controls. + /// + public readonly ResizableSurfaceNode ResizableNode; + /// /// True if the mouse is at the border of the resizable node and not further away from the border than . /// @@ -63,7 +67,6 @@ namespace FlaxEditor.Surface } } - /// public ResizeBorder(VisjectSurface surface, ResizableSurfaceNode resizableNode) { @@ -100,7 +103,7 @@ namespace FlaxEditor.Surface ResizeDirection = new Float2(Mathf.Abs(rawResizeDirection.X) >= nodeHalfSizeNoBorder.X ? Mathf.Sign(rawResizeDirection.X) : 0, Mathf.Abs(rawResizeDirection.Y) >= nodeHalfSizeNoBorder.Y ? Mathf.Sign(rawResizeDirection.Y) : 0); - IsMouseOverResizeBorder = false;// onBorder && !inNode; + IsMouseOverResizeBorder = onBorder && !inNode; } private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) @@ -186,6 +189,13 @@ namespace FlaxEditor.Surface base.OnMouseMove(location); } + /// + public override void OnMouseEnter(Float2 location) + { + Cursor = CursorType.Default; + base.OnMouseEnter(location); + } + /// public override bool OnMouseDown(Float2 location, MouseButton button) { @@ -243,7 +253,7 @@ namespace FlaxEditor.Surface /// /// Represents the border control used for resizing the associated element. /// - protected ResizeBorder ResizeBorderControl; + public ResizeBorder ResizeBorderControl; /// /// Index of the Float2 value in the node values list to store node size. @@ -270,6 +280,7 @@ namespace FlaxEditor.Surface Parent = Surface.SurfaceRoot, }; + Parent = ResizeBorderControl; ResizeBorderControl.MatchResizableNode(Size, Location); } @@ -280,13 +291,7 @@ namespace FlaxEditor.Surface base.OnLocationChanged(); } - ///// - //public override bool CanSelect(ref Float2 location) - //{ - // return base.CanSelect(ref location); - //} - - /// + /// public override void OnSurfaceLoaded(SurfaceNodeActions action) { // Reapply the curve node size @@ -314,9 +319,14 @@ namespace FlaxEditor.Surface base.Draw(); if (Surface.CanEdit && (ResizeBorderControl.IsResizing || ResizeBorderControl.IsMouseOverResizeBorder)) - { Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 0.5f); - } + } + + /// + public override void OnDestroy() + { + ResizeBorderControl.Parent = null; + base.OnDestroy(); } } } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index c257dddd1..73f280d48 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -81,16 +81,12 @@ namespace FlaxEditor.Surface if (Values.Length < 4) { if (IndexInParent > 0) - { IndexInParent = 0; - ResizeBorderControl.IndexInParent = - 1; - } OrderValue = IndexInParent; } else if (OrderValue != -1) { - IndexInParent = OrderValue; - ResizeBorderControl.IndexInParent = OrderValue - 1; + ResizeBorderControl.IndexInParent = OrderValue; } } @@ -103,8 +99,8 @@ namespace FlaxEditor.Surface Color = ColorValue = Color.FromHSV(new Random().NextFloat(0, 360), 0.7f, 0.25f, 0.8f); if (OrderValue == -1) - OrderValue = Context.CommentCount - 1; - IndexInParent = OrderValue; + OrderValue = Context.CommentCount; + ResizeBorderControl.IndexInParent = OrderValue; } /// @@ -333,25 +329,25 @@ namespace FlaxEditor.Surface { cmOrder.ContextMenu.AddButton("Bring Forward", () => { - if (IndexInParent < Context.CommentCount - 1) - IndexInParent++; - OrderValue = IndexInParent; + if (ResizeBorderControl.IndexInParent < Context.CommentCount - 1) + ResizeBorderControl.IndexInParent++; + OrderValue = ResizeBorderControl.IndexInParent; }); cmOrder.ContextMenu.AddButton("Bring to Front", () => { - IndexInParent = Context.CommentCount - 1; - OrderValue = IndexInParent; + ResizeBorderControl.IndexInParent = Context.CommentCount - 1; + OrderValue = ResizeBorderControl.IndexInParent; }); cmOrder.ContextMenu.AddButton("Send Backward", () => { - if (IndexInParent > 0) - IndexInParent--; - OrderValue = IndexInParent; + if (ResizeBorderControl.IndexInParent > 0) + ResizeBorderControl.IndexInParent--; + OrderValue = ResizeBorderControl.IndexInParent; }); cmOrder.ContextMenu.AddButton("Send to Back", () => { - IndexInParent = 0; - OrderValue = IndexInParent; + ResizeBorderControl.IndexInParent = 0; + OrderValue = ResizeBorderControl.IndexInParent; }); } } diff --git a/Source/Editor/Surface/SurfaceRootControl.cs b/Source/Editor/Surface/SurfaceRootControl.cs index 15d4c43cc..c7639a27d 100644 --- a/Source/Editor/Surface/SurfaceRootControl.cs +++ b/Source/Editor/Surface/SurfaceRootControl.cs @@ -46,10 +46,10 @@ namespace FlaxEditor.Surface { var child = _children[i]; - if (child is SurfaceComment && child.Visible) + if (child is ResizableSurfaceNode.ResizeBorder border && border.ResizableNode is SurfaceComment comment2 && comment2.Visible) { - Render2D.PushTransform(ref child._cachedTransform); - child.Draw(); + Render2D.PushTransform(ref comment2._cachedTransform); + comment2.Draw(); Render2D.PopTransform(); } } diff --git a/Source/Editor/Surface/VisjectSurfaceContext.cs b/Source/Editor/Surface/VisjectSurfaceContext.cs index 0d10aa230..be91e02ca 100644 --- a/Source/Editor/Surface/VisjectSurfaceContext.cs +++ b/Source/Editor/Surface/VisjectSurfaceContext.cs @@ -80,8 +80,9 @@ namespace FlaxEditor.Surface var result = new List(); for (int i = 0; i < RootControl.Children.Count; i++) { - if (RootControl.Children[i] is SurfaceComment comment) - result.Add(comment); + var child = RootControl.Children[i]; + if (child is ResizableSurfaceNode.ResizeBorder border && border.ResizableNode is SurfaceComment comment2) + result.Add(comment2); } return result; } @@ -101,7 +102,8 @@ namespace FlaxEditor.Surface int count = 0; for (int i = 0; i < RootControl.Children.Count; i++) { - if (RootControl.Children[i] is SurfaceComment) + var child = RootControl.Children[i]; + if (child is ResizableSurfaceNode.ResizeBorder border && border.ResizableNode is SurfaceComment) count++; } return count; From 20bfc209c59c2f5c86c228749c23262b7ee9a335 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 8 Mar 2026 23:29:20 +0100 Subject: [PATCH 06/13] fix auto move new comment to back --- Source/Editor/Surface/VisjectSurface.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index e3bb94bcc..69c44ab98 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -810,10 +810,11 @@ namespace FlaxEditor.Surface int lowestCommentOrder = int.MaxValue; for (int i = 0; i < selection.Count; i++) { - if (selection[i] is not SurfaceComment || selection[i].IndexInParent >= lowestCommentOrder) - continue; - hasCommentsSelected = true; - lowestCommentOrder = selection[i].IndexInParent; + if (selection[i] is ResizableSurfaceNode node && node is SurfaceComment && node.ResizeBorderControl.IndexInParent < lowestCommentOrder) + { + hasCommentsSelected = true; + lowestCommentOrder = node.ResizeBorderControl.IndexInParent; + } } return _context.CreateComment(ref surfaceArea, string.IsNullOrEmpty(text) ? "Comment" : text, new Color(1.0f, 1.0f, 1.0f, 0.2f), hasCommentsSelected ? lowestCommentOrder : -1); From 681b303f2dc8b144308e449bbb1ef9e40b9461bc Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 9 Mar 2026 17:04:06 +0100 Subject: [PATCH 07/13] fix cursor shape when there are nested comments --- Source/Editor/Surface/ResizableSurfaceNode.cs | 65 +++++++++++-------- Source/Editor/Surface/SurfaceComment.cs | 2 + Source/Editor/Surface/VisjectSurface.cs | 1 + 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index ea9ceec82..26c64c382 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -23,10 +23,15 @@ namespace FlaxEditor.Surface /// private const float BorderWidth = 15f; - private readonly VisjectSurface Surface; + private readonly VisjectSurface _surface; private Float2 _surfaceMouseLocation; private Float2 startResizingSize; + /// + /// Wether to ignore the surface index in parent when updating the cursor type. Set to false for nodes that have order like . + /// + internal bool IgnoreSurfaceIndex = true; + /// /// The resizable node that this controls. /// @@ -67,10 +72,14 @@ namespace FlaxEditor.Surface } } - /// + /// + /// Creates a new instance of . + /// + /// The surface. + /// The this controls. public ResizeBorder(VisjectSurface surface, ResizableSurfaceNode resizableNode) { - Surface = surface; + _surface = surface; ResizableNode = resizableNode; } @@ -87,7 +96,7 @@ namespace FlaxEditor.Surface private void UpdateSurfaceMouseLocation() { - _surfaceMouseLocation = Surface.PointFromScreen(Input.MouseScreenPosition); + _surfaceMouseLocation = _surface.PointFromScreen(Input.MouseScreenPosition); } private void UpdateResizeFlags(Float2 mouseLocation) @@ -122,18 +131,10 @@ namespace FlaxEditor.Surface { var emptySize = ResizableNode.CalculateNodeSize(0, 0); ResizableNode.SizeValue = ResizableNode.Size - emptySize; - Surface.MarkAsEdited(false); + _surface.MarkAsEdited(false); } } - /// - public override void OnMouseLeave() - { - Cursor = CursorType.Default; - IsMouseOverResizeBorder = false; - base.OnMouseLeave(); - } - /// public override void OnMouseMove(Float2 location) { @@ -147,7 +148,7 @@ namespace FlaxEditor.Surface var resizeAxisPos = Float2.Clamp(ResizeDirection, Float2.Zero, Float2.One); var resizeAxisNeg = Float2.Clamp(-ResizeDirection, Float2.Zero, Float2.One); - var currentSurfaceMouse = Surface.PointFromScreen(Input.MouseScreenPosition); + var currentSurfaceMouse = _surface.PointFromScreen(Input.MouseScreenPosition); var delta = currentSurfaceMouse - _surfaceMouseLocation; // TODO: scale/size snapping? @@ -159,8 +160,6 @@ namespace FlaxEditor.Surface var emptySize = ResizableNode.CalculateNodeSize(0, 0); - // TODO: Fix: Can't move comments anymore - // TODO: If resize is blocked by min size and the user tries to increase the size again, wait until !blocked by min size to apply delta again // To do this, just record pos when starting to block by min size and if (cursorLocation > min) { ResizeAgain() } @@ -168,23 +167,26 @@ namespace FlaxEditor.Surface ResizableNode.Size = new Float2(Mathf.Max(ResizableNode.Size.X, ResizableNode.sizeMin.X), Mathf.Max(ResizableNode.Size.Y, ResizableNode.sizeMin.Y)); ResizableNode.Location += uiControlDelta * resizeAxisNeg; - // Only move if size wasn't clamped - - - //Debug.Log($"OLD: {oldSize} NEW: {ResizableNode.Size}"); - ResizableNode.SizeValue = ResizableNode.Size - emptySize; ResizableNode.SizeValue = new Float2(Mathf.Max(ResizableNode.SizeValue.X, ResizableNode.sizeMin.X), Mathf.Max(ResizableNode.SizeValue.Y, ResizableNode.sizeMin.Y)); + ResizableNode.CalculateNodeSize(ResizableNode.Size.X, ResizableNode.Size.Y); UpdateSurfaceMouseLocation(); MatchResizableNode(ResizableNode.Size, ResizableNode.Location); } - if (IsMouseOverResizeBorder) - Cursor = CursorType; - else - Cursor = CursorType.Default; + // Update the cursor shape + if (_surface.resizeableNodeIndexInParent <= IndexInParent || IgnoreSurfaceIndex) + { + if (!IgnoreSurfaceIndex) + _surface.resizeableNodeIndexInParent = IndexInParent; + + if (IsMouseOverResizeBorder) + Cursor = CursorType; + else + Cursor = CursorType.Default; + } base.OnMouseMove(location); } @@ -196,6 +198,15 @@ namespace FlaxEditor.Surface base.OnMouseEnter(location); } + /// + public override void OnMouseLeave() + { + Cursor = CursorType.Default; + IsMouseOverResizeBorder = false; + _surface.resizeableNodeIndexInParent = -1; // Will get updated by MouseMove again to match current index + base.OnMouseLeave(); + } + /// public override bool OnMouseDown(Float2 location, MouseButton button) { @@ -291,10 +302,10 @@ namespace FlaxEditor.Surface base.OnLocationChanged(); } - /// + /// public override void OnSurfaceLoaded(SurfaceNodeActions action) { - // Reapply the curve node size + // Reapply the node size var size = SizeValue; if (Surface != null && Surface.GridSnappingEnabled) size = Surface.SnapToGrid(size, true); diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 73f280d48..4966c83b3 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -65,6 +65,8 @@ namespace FlaxEditor.Surface EndEditOnClick = false, // We have to handle this ourselves, otherwise the textbox instantly loses focus when double-clicking the header HorizontalAlignment = TextAlignment.Center, }; + + ResizeBorderControl.IgnoreSurfaceIndex = false; } /// diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index 69c44ab98..352bc1ebe 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -62,6 +62,7 @@ namespace FlaxEditor.Surface private int _selectedConnectionIndex; internal int _isUpdatingBoxTypes; + internal int resizeableNodeIndexInParent = -1; /// /// True if surface supports implicit casting of the FlaxEngine.Object types into Boolean value (as simple validate check). From 8488183558b97f4e6ae8a322f0c41a4cf99065d5 Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 9 Mar 2026 18:29:56 +0100 Subject: [PATCH 08/13] don't show resize hints when connecting --- Source/Editor/Surface/ResizableSurfaceNode.cs | 4 ++-- Source/Editor/Surface/SurfaceComment.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 26c64c382..08f0afe5d 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -177,7 +177,7 @@ namespace FlaxEditor.Surface } // Update the cursor shape - if (_surface.resizeableNodeIndexInParent <= IndexInParent || IgnoreSurfaceIndex) + if ((_surface.resizeableNodeIndexInParent <= IndexInParent || IgnoreSurfaceIndex) && !_surface.IsConnecting) { if (!IgnoreSurfaceIndex) _surface.resizeableNodeIndexInParent = IndexInParent; @@ -329,7 +329,7 @@ namespace FlaxEditor.Surface { base.Draw(); - if (Surface.CanEdit && (ResizeBorderControl.IsResizing || ResizeBorderControl.IsMouseOverResizeBorder)) + if (Surface.CanEdit && !Surface.IsConnecting && (ResizeBorderControl.IsResizing || ResizeBorderControl.IsMouseOverResizeBorder)) Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 0.5f); } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 4966c83b3..a8f3849d5 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -190,7 +190,7 @@ namespace FlaxEditor.Surface Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); // Resize - if (ResizeBorderControl.IsResizing || ResizeBorderControl.IsMouseOverResizeBorder) + if ((ResizeBorderControl.IsResizing || ResizeBorderControl.IsMouseOverResizeBorder) && !Surface.IsConnecting) Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.Foreground, 0.5f); } From 5ba8cddd65d6d812447b36fc182b71bec612c981 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 17 Mar 2026 19:57:03 +0100 Subject: [PATCH 09/13] improve resizing to behave more like expected --- Source/Editor/Surface/ResizableSurfaceNode.cs | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 08f0afe5d..2e684a59a 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -24,11 +24,12 @@ namespace FlaxEditor.Surface private const float BorderWidth = 15f; private readonly VisjectSurface _surface; - private Float2 _surfaceMouseLocation; + private Float2 _lastSurfaceMouseLoc; private Float2 startResizingSize; + private Float2 noClampedSize; /// - /// Wether to ignore the surface index in parent when updating the cursor type. Set to false for nodes that have order like . + /// Whether to ignore the surface index in parent when updating the cursor type. Set to false for nodes that have order like . /// internal bool IgnoreSurfaceIndex = true; @@ -94,16 +95,11 @@ namespace FlaxEditor.Surface Location = nodeLocation - new Float2(BorderWidth); } - private void UpdateSurfaceMouseLocation() - { - _surfaceMouseLocation = _surface.PointFromScreen(Input.MouseScreenPosition); - } - private void UpdateResizeFlags(Float2 mouseLocation) { var borderRect = Bounds with { Location = Float2.Zero }; bool onBorder = borderRect.Contains(mouseLocation); - // Check this the way we do because some resizeable nodes (like comments) have an implementation of `Control.ContainsPoint` + // Check this the way we do because some resizable nodes (like comments) have an implementation of `Control.ContainsPoint` // that does not check for the size you would think they have based on their visual appearance bool inNode = borderRect.MakeExpanded(-BorderWidth * 2f).Contains(mouseLocation); @@ -115,7 +111,7 @@ namespace FlaxEditor.Surface IsMouseOverResizeBorder = onBorder && !inNode; } - private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) + private Float2 GetControlDelta(Control control, Float2 start, Float2 end) { var pointOrigin = control.Parent ?? control; var startPos = pointOrigin.PointFromParent(ResizableNode, start); @@ -148,32 +144,30 @@ namespace FlaxEditor.Surface var resizeAxisPos = Float2.Clamp(ResizeDirection, Float2.Zero, Float2.One); var resizeAxisNeg = Float2.Clamp(-ResizeDirection, Float2.Zero, Float2.One); - var currentSurfaceMouse = _surface.PointFromScreen(Input.MouseScreenPosition); - var delta = currentSurfaceMouse - _surfaceMouseLocation; + var currentSurfaceMouseLoc = _surface.PointFromScreen(Input.MouseScreenPosition); + var delta = currentSurfaceMouseLoc - _lastSurfaceMouseLoc; - // TODO: scale/size snapping? + // TODO: Snapping delta *= resizeAxisAbs; + var moveLocation = currentSurfaceMouseLoc; + var uiControlDelta = GetControlDelta(this, _lastSurfaceMouseLoc, moveLocation); - var moveLocation = _surfaceMouseLocation + delta; - - var uiControlDelta = GetControlDelta(this, ref _surfaceMouseLocation, ref moveLocation); + // This ensures that the node is not resized below min size, but also keeps the resize behavior like expected (which just Max() -ing the value does not) var emptySize = ResizableNode.CalculateNodeSize(0, 0); + var minSize = ResizableNode.sizeMin; + noClampedSize = noClampedSize + uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; + if (noClampedSize.X < minSize.X && noClampedSize.X < ResizableNode.Size.X) + resizeAxisAbs.X = resizeAxisPos.X = resizeAxisNeg.X = 0f; + if (noClampedSize.Y < minSize.Y && noClampedSize.Y < ResizableNode.Size.Y) + resizeAxisAbs.Y = resizeAxisPos.Y = resizeAxisNeg.Y = 0f; - - // TODO: If resize is blocked by min size and the user tries to increase the size again, wait until !blocked by min size to apply delta again - // To do this, just record pos when starting to block by min size and if (cursorLocation > min) { ResizeAgain() } - - ResizableNode.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; - ResizableNode.Size = new Float2(Mathf.Max(ResizableNode.Size.X, ResizableNode.sizeMin.X), Mathf.Max(ResizableNode.Size.Y, ResizableNode.sizeMin.Y)); ResizableNode.Location += uiControlDelta * resizeAxisNeg; - ResizableNode.SizeValue = ResizableNode.Size - emptySize; - ResizableNode.SizeValue = new Float2(Mathf.Max(ResizableNode.SizeValue.X, ResizableNode.sizeMin.X), Mathf.Max(ResizableNode.SizeValue.Y, ResizableNode.sizeMin.Y)); ResizableNode.CalculateNodeSize(ResizableNode.Size.X, ResizableNode.Size.Y); - - UpdateSurfaceMouseLocation(); MatchResizableNode(ResizableNode.Size, ResizableNode.Location); + + _lastSurfaceMouseLoc = currentSurfaceMouseLoc; } // Update the cursor shape @@ -213,7 +207,8 @@ namespace FlaxEditor.Surface if (button == MouseButton.Left && IsMouseOverResizeBorder && !IsResizing) { // Start resizing - UpdateSurfaceMouseLocation(); + _lastSurfaceMouseLoc = _surface.PointFromScreen(Input.MouseScreenPosition); + noClampedSize = ResizableNode.Size; IsResizing = true; startResizingSize = ResizableNode.Size; StartMouseCapture(); From 1d18220971d873493813adb3c05009dfb49edd24 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 17 Mar 2026 20:17:05 +0100 Subject: [PATCH 10/13] remove debug draw --- Source/Editor/Surface/ResizableSurfaceNode.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 2e684a59a..8b06d2966 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -248,12 +248,6 @@ namespace FlaxEditor.Surface base.OnEndMouseCapture(); } - - /// - public override void Draw() - { - Render2D.DrawRectangle(Bounds with { Location = Float2.Zero }, Color.Green, 1f); - } } /// From 826b7841e6b95a59fc8f6f497d3bbc13d49b2567 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 17 Mar 2026 20:22:56 +0100 Subject: [PATCH 11/13] actually resize the node lol --- Source/Editor/Surface/ResizableSurfaceNode.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 8b06d2966..edb6eac18 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -161,6 +161,7 @@ namespace FlaxEditor.Surface if (noClampedSize.Y < minSize.Y && noClampedSize.Y < ResizableNode.Size.Y) resizeAxisAbs.Y = resizeAxisPos.Y = resizeAxisNeg.Y = 0f; + ResizableNode.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; ResizableNode.Location += uiControlDelta * resizeAxisNeg; ResizableNode.SizeValue = ResizableNode.Size - emptySize; From 7bed68948b40845afa9bbeab4349cbafd68a9e18 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 17 Mar 2026 20:28:55 +0100 Subject: [PATCH 12/13] no resizing in non editable Visject surfaces --- Source/Editor/Surface/ResizableSurfaceNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index edb6eac18..9d44b325b 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -138,7 +138,7 @@ namespace FlaxEditor.Surface { UpdateResizeFlags(location); } - else + else if (_surface.CanEdit) { var resizeAxisAbs = ResizeDirection.Absolute; var resizeAxisPos = Float2.Clamp(ResizeDirection, Float2.Zero, Float2.One); @@ -172,7 +172,7 @@ namespace FlaxEditor.Surface } // Update the cursor shape - if ((_surface.resizeableNodeIndexInParent <= IndexInParent || IgnoreSurfaceIndex) && !_surface.IsConnecting) + if ((_surface.resizeableNodeIndexInParent <= IndexInParent || IgnoreSurfaceIndex) && !_surface.IsConnecting && _surface.CanEdit) { if (!IgnoreSurfaceIndex) _surface.resizeableNodeIndexInParent = IndexInParent; From 7fc1d525a50119980521d8174a5e2c45fa2aeee8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Jun 2026 12:06:26 +0200 Subject: [PATCH 13/13] Postmerge #4010 --- Source/Editor/Surface/ResizableSurfaceNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/ResizableSurfaceNode.cs b/Source/Editor/Surface/ResizableSurfaceNode.cs index 2bb30e0dd..e435e82bf 100644 --- a/Source/Editor/Surface/ResizableSurfaceNode.cs +++ b/Source/Editor/Surface/ResizableSurfaceNode.cs @@ -154,7 +154,7 @@ namespace FlaxEditor.Surface // This ensures that the node is not resized below min size, but also keeps the resize behavior like expected (which just Max() -ing the value does not) var emptySize = ResizableNode.CalculateNodeSize(0, 0); - var minSize = ResizableNode.sizeMin; + var minSize = ResizableNode._sizeMin; noClampedSize = noClampedSize + uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; if (noClampedSize.X < minSize.X && noClampedSize.X < ResizableNode.Size.X) resizeAxisAbs.X = resizeAxisPos.X = resizeAxisNeg.X = 0f; @@ -264,7 +264,7 @@ namespace FlaxEditor.Surface /// /// Minimum node size. /// - protected Float2 sizeMin = new Float2(240, 160); + protected Float2 _sizeMin = new Float2(240, 160); private Float2 SizeValue {