Files
2026-07-13 13:13:17 +08:00

201 lines
7.9 KiB
C#

using ImGuiNET;
using T3.Core.Utils;
using T3.Editor.Gui.Interaction.Snapping;
using T3.Editor.Gui.MagGraph.Model;
using T3.Editor.Gui.MagGraph.States;
using T3.Editor.Gui.Styling;
using T3.Editor.Gui.UiHelpers;
using T3.Editor.UiModel;
using T3.Editor.UiModel.Commands;
using T3.Editor.UiModel.Selection;
using T3.Editor.UiModel.Commands.Graph;
namespace T3.Editor.Gui.MagGraph.Interaction;
internal static class SectionResizing
{
[Flags]
internal enum Handles
{
None = 0,
Left = 1 << 0,
Right = 1 << 1,
Top = 1 << 2,
Bottom = 1 << 3,
TopLeft = Top | Left,
TopRight = Top | Right,
BottomLeft = Bottom | Left,
BottomRight = Bottom | Right,
}
/// <summary>
/// The edges dragged by the current interaction. Set by the graph view's resize
/// handles before entering the ResizeSection state.
/// </summary>
internal static Handles ActiveHandles = Handles.BottomRight;
internal static void Draw(GraphUiContext context)
{
_snapHandlerY.DrawSnapIndicator(context.View, UiColors.ForegroundFull.Fade(0.1f));
_snapHandlerX.DrawSnapIndicator(context.View, UiColors.ForegroundFull.Fade(0.1f));
// Setup...
var instViewSymbolUi = context?.ProjectView?.InstView?.SymbolUi;
if (instViewSymbolUi == null)
return;
var sectionId = context.ActiveSectionId;
if (!context.Layout.Sections.TryGetValue(sectionId, out var magSection))
{
context.ActiveSectionId = Guid.Empty;
context.StateMachine.SetState(GraphStates.Default, context);
return;
}
var section = magSection.Section;
// Start dragging...
{
var started = context.ActiveSectionId != _draggedSectionId;
if (started)
{
_draggedSectionId = context.ActiveSectionId;
_startBoundsMin = section.PosOnCanvas;
_startBoundsMax = section.PosOnCanvas + section.Size;
_startMousePosInCanvas = context.View.InverseTransformPositionFloat(ImGui.GetMousePos());
_moveCommand = new ModifyCanvasElementsCommand(instViewSymbolUi, [section], context.Selector);
}
if (started)
return;
}
ImGui.SetMouseCursor(GetCursorForHandles(ActiveHandles));
// Update dragging...
{
var mousePosInCanvas = context.View.InverseTransformPositionFloat(ImGui.GetMousePos());
var dragDelta = mousePosInCanvas - _startMousePosInCanvas;
var min = _startBoundsMin;
var max = _startBoundsMax;
if ((ActiveHandles & Handles.Left) != 0)
{
var newEdge = SnapEdge(_snapHandlerX, _startBoundsMin.X + dragDelta.X, context, magSection, context.View.Scale.X,
RasterSnapAttractor.Directions.Horizontal);
min.X = MathF.Min(newEdge, max.X - MagGraphItem.GridSize.X);
}
else if ((ActiveHandles & Handles.Right) != 0)
{
var newEdge = SnapEdge(_snapHandlerX, _startBoundsMax.X + dragDelta.X, context, magSection, context.View.Scale.X,
RasterSnapAttractor.Directions.Horizontal);
max.X = MathF.Max(newEdge, min.X + MagGraphItem.GridSize.X);
}
if ((ActiveHandles & Handles.Top) != 0)
{
var newEdge = SnapEdge(_snapHandlerY, _startBoundsMin.Y + dragDelta.Y, context, magSection, context.View.Scale.Y,
RasterSnapAttractor.Directions.Vertical);
min.Y = MathF.Min(newEdge, max.Y - MagGraphItem.GridSize.Y);
}
else if ((ActiveHandles & Handles.Bottom) != 0)
{
var newEdge = SnapEdge(_snapHandlerY, _startBoundsMax.Y + dragDelta.Y, context, magSection, context.View.Scale.Y,
RasterSnapAttractor.Directions.Vertical);
max.Y = MathF.Max(newEdge, min.Y + MagGraphItem.GridSize.Y);
}
section.PosOnCanvas = min;
section.Size = max - min;
}
// Complete dragging...
var completed = ImGui.IsMouseReleased(ImGuiMouseButton.Left);
if (completed)
{
var wasDragging = ImGui.GetMouseDragDelta(ImGuiMouseButton.Left).LengthSquared() > UserSettings.Config.ClickThreshold;
if (wasDragging)
{
// Resizing changes only this frame's rect - it never moves other ops or
// frames. Ownership adjusts via derivation on the layout refresh.
_moveCommand.StoreCurrentValues();
UndoRedoStack.Add(_moveCommand);
context.Layout.FlagStructureAsChanged();
}
else
{
_moveCommand.Undo();
if (context.Selector.IsNodeSelected(section))
{
if (ImGui.GetIO().KeyShift)
{
context.Selector.DeselectNode(section, null);
}
}
else
{
if (!ImGui.GetIO().KeyShift)
context.Selector.Clear();
context.Selector.AddSelection(section);
}
}
context.StateMachine.SetState(GraphStates.Default, context);
_draggedSectionId = Guid.Empty;
_moveCommand = null;
ActiveHandles = Handles.None;
}
}
internal static ImGuiMouseCursor GetCursorForHandles(Handles handles)
{
return handles switch
{
Handles.TopLeft or Handles.BottomRight => ImGuiMouseCursor.ResizeNWSE,
Handles.TopRight or Handles.BottomLeft => ImGuiMouseCursor.ResizeNESW,
Handles.Left or Handles.Right => ImGuiMouseCursor.ResizeEW,
Handles.Top or Handles.Bottom => ImGuiMouseCursor.ResizeNS,
_ => ImGuiMouseCursor.Arrow,
};
}
private static float SnapEdge(ValueSnapHandler snapHandler, float edgeValue, GraphUiContext context, MagGraphSection magSection,
float canvasScale, RasterSnapAttractor.Directions direction)
{
if (snapHandler.TryCheckForSnapping(edgeValue, out var snappedToSection, canvasScale * 0.25f,
[magSection],
context.Layout.Sections.Values))
{
return (float)snappedToSection;
}
var minGrid = MathF.Min(MagGraphItem.GridSize.X, MagGraphItem.GridSize.Y);
if (snapHandler.TryCheckForSnapping(edgeValue, out var snappedToGrid, canvasScale * 0.25f,
[],
[new RasterSnapAttractor
{
Canvas = context.View,
GridSize = Vector2.One * minGrid,
Direction = direction
}]))
{
return (float)snappedToGrid;
}
return edgeValue;
}
private static Guid _draggedSectionId = Guid.Empty;
private static Vector2 _startBoundsMin;
private static Vector2 _startBoundsMax;
private static Vector2 _startMousePosInCanvas;
private static ModifyCanvasElementsCommand _moveCommand;
private static readonly ValueSnapHandler _snapHandlerX = new(SnapResult.Orientations.Horizontal);
private static readonly ValueSnapHandler _snapHandlerY = new(SnapResult.Orientations.Vertical);
}