4 using System.Collections.Generic;
5 using System.Collections.Immutable;
7 using Microsoft.Xna.Framework;
17 internal sealed
class CircuitBoxMouseDragSnapshotHandler
19 public IEnumerable<CircuitBoxNode> Nodes
23 var cb = circuitBoxUi.CircuitBox;
25 foreach (var label
in cb.Labels) { yield
return label; }
26 foreach (var component
in cb.Components) { yield
return component; }
27 foreach (var node
in cb.InputOutputNodes) { yield
return node; }
31 private IReadOnlyList<CircuitBoxWire> Wires => circuitBoxUi.CircuitBox.Wires;
34 private ImmutableArray<CircuitBoxConnection> connections = ImmutableArray<CircuitBoxConnection>.Empty;
37 private ImmutableHashSet<CircuitBoxNode> lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty,
39 lastSelectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty,
41 moveAffectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
43 public Option<(CircuitBoxResizeDirection, CircuitBoxNode)> LastResizeAffectedNode = Option.None;
45 public ImmutableHashSet<CircuitBoxNode> GetLastComponentsUnderCursor() => lastNodesUnderCursor;
46 public ImmutableHashSet<CircuitBoxNode> GetMoveAffectedComponents() => moveAffectedComponents;
48 public Option<CircuitBoxConnection> LastConnectorUnderCursor = Option.None;
49 public Option<CircuitBoxWire> LastWireUnderCursor = Option.None;
54 public bool IsDragging {
get;
private set; }
59 public bool IsWiring {
get;
private set; }
64 public bool IsResizing {
get;
private set; }
66 private Vector2 startClick = Vector2.Zero;
67 private readonly CircuitBoxUI circuitBoxUi;
72 private const float dragTreshold = 16f;
74 public CircuitBoxMouseDragSnapshotHandler(CircuitBoxUI ui)
82 public void StartDragging()
84 Vector2 cursorPos = circuitBoxUi.GetCursorPosition();
85 SnapshotNodesUnderCursor(cursorPos);
86 SnapshotSelectedNodes();
87 SnapshotMoveAffectedNodes();
88 startClick = cursorPos;
91 public void ClearSnapshot()
93 lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty;
94 lastSelectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
95 moveAffectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
96 LastConnectorUnderCursor = Option.None;
97 LastWireUnderCursor = Option.None;
98 LastResizeAffectedNode = Option.None;
104 public void UpdateConnections()
106 var builder = ImmutableArray.CreateBuilder<CircuitBoxConnection>();
108 builder.AddRange(circuitBoxUi.CircuitBox.Inputs);
109 builder.AddRange(circuitBoxUi.CircuitBox.Outputs);
111 foreach (var node
in Nodes)
113 builder.AddRange(node.Connectors);
116 connections = builder.ToImmutable();
122 public Option<CircuitBoxConnection> FindConnectorUnderCursor(Vector2 cursorPos)
124 foreach (var connection
in connections)
126 if (connection.Contains(cursorPos))
128 return Option.Some(connection);
138 public Option<CircuitBoxWire> FindWireUnderCursor(Vector2 cursorPos)
140 foreach (CircuitBoxWire wire
in Wires)
142 if (wire is { IsSelected:
true, IsSelectedByMe:
false }) {
continue; }
143 if (wire.Renderer.Contains(cursorPos))
145 return Option.Some(wire);
155 public ImmutableHashSet<CircuitBoxNode> FindNodesUnderCursor(Vector2 cursorPos)
157 var builder = ImmutableHashSet.CreateBuilder<CircuitBoxNode>();
158 foreach (var node
in Nodes)
160 if (node is { IsSelected:
true, IsSelectedByMe:
false }) {
continue; }
161 if (node.Rect.Contains(cursorPos))
167 return builder.ToImmutable();
173 private void SnapshotNodesUnderCursor(Vector2 cursorPos)
175 lastNodesUnderCursor = FindNodesUnderCursor(cursorPos);
176 LastConnectorUnderCursor = FindConnectorUnderCursor(cursorPos);
177 LastWireUnderCursor = FindWireUnderCursor(cursorPos);
178 LastResizeAffectedNode = FindResizeBorderUnderCursor(lastNodesUnderCursor, cursorPos);
181 private Option<(CircuitBoxResizeDirection, CircuitBoxNode)> FindResizeBorderUnderCursor(ImmutableHashSet<CircuitBoxNode> nodes, Vector2 cursorPos)
183 if (!nodes.Any()) {
return Option.None; }
185 var node = circuitBoxUi.GetTopmostNode(nodes);
186 if (node is
null || !node.IsResizable) {
return Option.None; }
188 const float borderSize = 32f;
190 var rect = node.Rect;
191 RectangleF bottomBorder =
new(rect.X, rect.Top, rect.Width, borderSize);
192 RectangleF rightBorder =
new(rect.Right - borderSize, rect.Y, borderSize, rect.Height);
193 RectangleF leftBorder =
new(rect.X, rect.Y, borderSize, rect.Height);
195 bool hoverBottom = bottomBorder.Contains(cursorPos),
196 hoverRight = rightBorder.Contains(cursorPos),
197 hoverLeft = leftBorder.Contains(cursorPos);
199 var dir = CircuitBoxResizeDirection.None;
201 if (hoverBottom) { dir |= CircuitBoxResizeDirection.Down; }
202 if (hoverRight) { dir |= CircuitBoxResizeDirection.Right; }
203 if (hoverLeft) { dir |= CircuitBoxResizeDirection.Left; }
205 if (dir is CircuitBoxResizeDirection.None)
210 return Option.Some((dir, node));
218 private void SnapshotSelectedNodes()
220 lastSelectedComponents = Nodes.Where(
static n => n is { IsSelected:
true, IsSelectedByMe:
true }).ToImmutableHashSet();
226 private void SnapshotMoveAffectedNodes()
228 bool moveSelection = lastNodesUnderCursor.Any(node => lastSelectedComponents.Contains(node));
236 moveAffectedComponents = moveSelection
switch
238 true => lastSelectedComponents,
239 false => circuitBoxUi.GetTopmostNode(lastNodesUnderCursor)
switch
241 null => ImmutableHashSet<CircuitBoxNode>.Empty,
242 var node => ImmutableHashSet.Create(node)
247 public Vector2 GetDragAmount(Vector2 mousePos) => mousePos - startClick;
252 public void EndDragging()
254 startClick = Vector2.Zero;
258 lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty;
261 public void UpdateDrag(Vector2 cursorPos)
264 if (LastConnectorUnderCursor.IsNone())
270 if (lastNodesUnderCursor.IsEmpty)
275 if (LastResizeAffectedNode.IsNone())
281 if (startClick == Vector2.Zero)
289 if (circuitBoxUi.Locked) {
return; }
290 bool isDragThresholdExceeded = Vector2.DistanceSquared(startClick, cursorPos) > dragTreshold * dragTreshold;
292 if (LastConnectorUnderCursor.IsSome())
294 IsWiring |= isDragThresholdExceeded;
296 else if (LastResizeAffectedNode.IsSome())
298 IsResizing |= isDragThresholdExceeded;
302 IsDragging |= isDragThresholdExceeded;