From 8692bac8578e24ab472ea7b6aaa2b82f82c57bd3 Mon Sep 17 00:00:00 2001 From: Gulam Nabi Azad Date: Tue, 28 May 2024 13:11:59 +0530 Subject: [PATCH 1/4] WI00737678 : Remove DragNodeBehavior --- .../Behaviors/DragMovablesBehavior.cs | 171 ++++++++++++------ src/Blazor.Diagrams.Core/Models/NodeModel.cs | 32 +++- 2 files changed, 148 insertions(+), 55 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index 3a01eed7..ca6f9e06 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -1,54 +1,54 @@ -using Blazor.Diagrams.Core.Behaviors.Base; -using Blazor.Diagrams.Core.Events; -using Blazor.Diagrams.Core.Geometry; -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Events; using System; using System.Collections.Generic; +using Blazor.Diagrams.Core.Models; +using DiagramPoint = Blazor.Diagrams.Core.Geometry.Point; +using System.Linq; +using Blazor.Diagrams.Core.Behaviors.Base; namespace Blazor.Diagrams.Core.Behaviors; public class DragMovablesBehavior : Behavior { - private readonly Dictionary _initialPositions; + record NodeMoveablePositions(Point position) + { + public Dictionary ChildPositions { get; } = new(); + } + + private readonly Dictionary _initialPositions; private double? _lastClientX; private double? _lastClientY; private bool _moved; - private double _totalMovedX = 0; - private double _totalMovedY = 0; + double _totalMovedX = 0; + double _totalMovedY = 0; + + public const double CHILD_NODE_MIN_OFFSET_TOP = 40; + public const double CHILD_NODE_MIN_OFFSET_BOTTOM = 5; public DragMovablesBehavior(Diagram diagram) : base(diagram) { - _initialPositions = new Dictionary(); + _initialPositions = new Dictionary(); Diagram.PointerDown += OnPointerDown; Diagram.PointerMove += OnPointerMove; Diagram.PointerUp += OnPointerUp; - Diagram.PanChanged += OnPanChanged; } private void OnPointerDown(Model? model, PointerEventArgs e) { - if (model is not MovableModel) + if (model is not NodeModel) return; _initialPositions.Clear(); foreach (var sm in Diagram.GetSelectedModels()) { - if (sm is not MovableModel movable || movable.Locked) - continue; - - // Special case: groups without auto size on - if (sm is NodeModel node && node.Group != null && !node.Group.AutoSize) - continue; - - var position = movable.Position; - if (Diagram.Options.GridSnapToCenter && movable is NodeModel n) + if (sm is not NodeModel movable || movable.Locked) { - position = new Point(movable.Position.X + (n.Size?.Width ?? 0) / 2, - movable.Position.Y + (n.Size?.Height ?? 0) / 2); + continue; } - _initialPositions.Add(movable, position); + _initialPositions.Add(movable, new NodeMoveablePositions(movable.Position)); } _lastClientX = e.ClientX; @@ -58,50 +58,111 @@ private void OnPointerDown(Model? model, PointerEventArgs e) private void OnPointerMove(Model? model, PointerEventArgs e) { + if (!_moved) + { + foreach (var node in _initialPositions.Keys.ToArray()) + { + if (node is NodeModel jobNode) + { + var parent = jobNode.ParentNode; + while (parent != null) + { + if (_initialPositions.ContainsKey(parent)) + { + _initialPositions.Remove(jobNode); + break; + } + + parent = parent.ParentNode; + } + } + } + + foreach (var (node, positions) in _initialPositions) + { + if (node is NodeModel jobNode) + { + foreach (var child in jobNode.GetAllChildNodes()) + { + if (!child.Selected) + { + Diagram.SelectModel(child, false); + } + + positions.ChildPositions[child] = child.Position; + } + } + } + } if (_initialPositions.Count == 0 || _lastClientX == null || _lastClientY == null) + { return; + } _moved = true; - var deltaX = (e.ClientX - _lastClientX.Value) / Diagram.Zoom; - var deltaY = (e.ClientY - _lastClientY.Value) / Diagram.Zoom; - _totalMovedX += deltaX; - _totalMovedY += deltaY; + var dx = (e.ClientX - _lastClientX.Value) / Diagram.Zoom; + var dy = (e.ClientY - _lastClientY.Value) / Diagram.Zoom; + + _totalMovedX += dx; + _totalMovedY += dy; MoveNodes(model, _totalMovedX, _totalMovedY); _lastClientX = e.ClientX; _lastClientY = e.ClientY; - } - public void OnPanChanged(double deltaX, double deltaY) + + void MoveNodes(Model? model, double deltaX, double deltaY) { - if (_initialPositions.Count == 0 || _lastClientX == null || _lastClientY == null) - return; + foreach (var (node, positions) in _initialPositions) + { + SetPosition(node, positions.position.X + deltaX, positions.position.Y + deltaY); + deltaX = node.Position.X - positions.position.X; + deltaY = node.Position.Y - positions.position.Y; - _moved = true; + foreach (var (childNode, childPosition) in positions.ChildPositions) + { + SetPosition(childNode, childPosition.X + deltaX, childPosition.Y + deltaY); + } - _totalMovedX += deltaX; - _totalMovedY += deltaY; + if (node is NodeModel movableNode) + { + movableNode.TriggerMoving(); + } + } + } - MoveNodes(null, _totalMovedX, _totalMovedY); + void SetPosition(NodeModel node, double x, double y) + { + if (node is NodeModel nodeModel && nodeModel.ParentNode != null) + { + x = Clamp(x, nodeModel.Size?.Width, nodeModel.ParentNode.Position.X, nodeModel.ParentNode.Size?.Width); + var parentY = nodeModel.ParentNode.Position.Y + CHILD_NODE_MIN_OFFSET_TOP; + var parentH = nodeModel.ParentNode.Size?.Height - CHILD_NODE_MIN_OFFSET_TOP - CHILD_NODE_MIN_OFFSET_BOTTOM; + y = Clamp(y, nodeModel.Size?.Height, parentY, parentH); + + nodeModel.SetPosition(x, y); + } } - private void MoveNodes(Model? model, double deltaX, double deltaY) + double Clamp(double position, double? size, double? parentPosition, double? parentSize) { - foreach (var (movable, initialPosition) in _initialPositions) + var clamped = position; + + if (size != null && parentPosition != null && parentSize != null) { - var ndx = ApplyGridSize(deltaX + initialPosition.X); - var ndy = ApplyGridSize(deltaY + initialPosition.Y); - if (Diagram.Options.GridSnapToCenter && movable is NodeModel node) + if (position < parentPosition) { - node.SetPosition(ndx - (node.Size?.Width ?? 0) / 2, ndy - (node.Size?.Height ?? 0) / 2); + clamped = (double)parentPosition; } - else + else if (position + size > parentPosition + parentSize) { - movable.SetPosition(ndx, ndy); + clamped = (double)(parentPosition + parentSize - size); } } + + return clamped; } private void OnPointerUp(Model? model, PointerEventArgs e) @@ -111,11 +172,22 @@ private void OnPointerUp(Model? model, PointerEventArgs e) if (_moved) { - foreach (var (movable, _) in _initialPositions) + foreach (var (movable, childMovableNodes) in _initialPositions) { movable.TriggerMoved(); + if (movable is NodeModel movableNode) + { + foreach (var child in movableNode.GetAllChildNodes()) + { + if (childMovableNodes.ChildPositions.ContainsKey(child)) + { + child.TriggerMoved(); + } + } + } } } + _initialPositions.Clear(); _totalMovedX = 0; _totalMovedY = 0; @@ -123,21 +195,12 @@ private void OnPointerUp(Model? model, PointerEventArgs e) _lastClientY = null; } - private double ApplyGridSize(double n) - { - if (Diagram.Options.GridSize == null) - return n; - - var gridSize = Diagram.Options.GridSize.Value; - return gridSize * Math.Floor((n + gridSize / 2.0) / gridSize); - } - public override void Dispose() { _initialPositions.Clear(); + Diagram.PointerDown -= OnPointerDown; Diagram.PointerMove -= OnPointerMove; Diagram.PointerUp -= OnPointerUp; - Diagram.PanChanged -= OnPanChanged; } -} +} \ No newline at end of file diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index 102a9fed..53e65bf8 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -17,6 +17,8 @@ public class NodeModel : MovableModel, IHasBounds, IHasShape, ILinkable public event Action? SizeChanged; public event Action? Moving; + internal NodeModel? ParentNode; + public NodeModel(Point? position = null) : base(position) { } @@ -190,7 +192,7 @@ private void UpdatePortPositions(Size oldSize, Size newSize) } } - protected void TriggerMoving() + internal void TriggerMoving() { Moving?.Invoke(this); } @@ -198,4 +200,32 @@ protected void TriggerMoving() void ILinkable.AddLink(BaseLinkModel link) => _links.Add(link); void ILinkable.RemoveLink(BaseLinkModel link) => _links.Remove(link); + + internal List GetAllChildNodes() + { + var allChildren = new List(); + foreach (var child in childMovableNodes) + { + allChildren.Add(child); + if (child is NodeModel jobChild) + { + allChildren.AddRange(jobChild.GetAllChildNodes()); + } + } + return allChildren; + } + + internal void AddChildNode(NodeModel child) + { + child.ParentNode = this; + childMovableNodes.Add(child); + } + + internal void RemoveChildNode(NodeModel child) + { + childMovableNodes.Remove(child); + child.ParentNode = null; + } + + internal List childMovableNodes = new List(); } \ No newline at end of file From 185d1e66d953f247bcfe34688c211b1d55d8bc4f Mon Sep 17 00:00:00 2001 From: Gulam Nabi Azad Date: Wed, 29 May 2024 08:26:34 +0530 Subject: [PATCH 2/4] WI00737678 : Remove DragNodeBehavior-Update-1 --- src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index ca6f9e06..8c74936a 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -37,6 +37,7 @@ public DragMovablesBehavior(Diagram diagram) : base(diagram) private void OnPointerDown(Model? model, PointerEventArgs e) { + Console.WriteLine("On Pointer Down..."); if (model is not NodeModel) return; From 047efee15b0f71c3f61e0eabee0a4aef2474756f Mon Sep 17 00:00:00 2001 From: Gulam Nabi Azad Date: Wed, 29 May 2024 08:45:39 +0530 Subject: [PATCH 3/4] WI00737678 : Remove DragNodeBehavior-Update-2 --- .../Behaviors/DragMovablesBehaviorTests.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs index 813322dc..f71e6a5a 100644 --- a/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs +++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs @@ -141,24 +141,4 @@ public void Behavior_ShouldCallSetPosition_WhenGroupHasAutoSize() // Assert nodeMock.Verify(n => n.SetPosition(50, 50), Times.Once); } - - [Fact] - public void Behavior_ShouldCallSetPosition_WhenPanChanges() - { - // Arrange - var diagram = new TestDiagram(); - var nodeMock = new Mock(Point.Zero); - var node = diagram.Nodes.Add(nodeMock.Object); - diagram.SelectModel(node, false); - diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior(); - diagram.SetContainer(new Rectangle(0, 0, 100, 100)); - - // Act - diagram.TriggerPointerDown(node, - new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)); - diagram.TriggerWheel(new WheelEventArgs(100, 100, 0, 0, false, false, false, 100, 100, 0, 0)); - - // Assert - nodeMock.Verify(n => n.SetPosition(100, 100), Times.Once); - } } \ No newline at end of file From 3d5ff6b11a7adb9cca0f1a9135d32001e3e8804a Mon Sep 17 00:00:00 2001 From: Gulam Nabi Azad Date: Wed, 29 May 2024 10:06:53 +0530 Subject: [PATCH 4/4] WI00737678 : Remove DragNodeBehavior-Update-3 --- .../Behaviors/DragMovablesBehavior.cs | 44 +++++++++++++++++- .../Behaviors/DragMovablesBehaviorTests.cs | 46 ++++--------------- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index 8c74936a..0a20b82d 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -33,6 +33,7 @@ public DragMovablesBehavior(Diagram diagram) : base(diagram) Diagram.PointerDown += OnPointerDown; Diagram.PointerMove += OnPointerMove; Diagram.PointerUp += OnPointerUp; + Diagram.PanChanged += OnPanChanged; } private void OnPointerDown(Model? model, PointerEventArgs e) @@ -114,6 +115,45 @@ private void OnPointerMove(Model? model, PointerEventArgs e) _lastClientY = e.ClientY; } + public void OnPanChanged(double deltaX, double deltaY) + { + if (_initialPositions.Count == 0 || _lastClientX == null || _lastClientY == null) + return; + + _moved = true; + + _totalMovedX += deltaX; + _totalMovedY += deltaY; + + MoveNodesOnPan(null, _totalMovedX, _totalMovedY); + } + + private void MoveNodesOnPan(Model? model, double deltaX, double deltaY) + { + foreach (var (movable, initialPosition) in _initialPositions) + { + var ndx = ApplyGridSize(deltaX + initialPosition.position.X); + var ndy = ApplyGridSize(deltaY + initialPosition.position.Y); + if (Diagram.Options.GridSnapToCenter && movable is NodeModel node) + { + node.SetPosition(ndx - (node.Size?.Width ?? 0) / 2, ndy - (node.Size?.Height ?? 0) / 2); + } + else + { + movable.SetPosition(ndx, ndy); + } + } + } + + private double ApplyGridSize(double n) + { + if (Diagram.Options.GridSize == null) + return n; + + var gridSize = Diagram.Options.GridSize.Value; + return gridSize * Math.Floor((n + gridSize / 2.0) / gridSize); + } + void MoveNodes(Model? model, double deltaX, double deltaY) { foreach (var (node, positions) in _initialPositions) @@ -143,8 +183,9 @@ void SetPosition(NodeModel node, double x, double y) var parentH = nodeModel.ParentNode.Size?.Height - CHILD_NODE_MIN_OFFSET_TOP - CHILD_NODE_MIN_OFFSET_BOTTOM; y = Clamp(y, nodeModel.Size?.Height, parentY, parentH); - nodeModel.SetPosition(x, y); } + + node.SetPosition(x, y); } double Clamp(double position, double? size, double? parentPosition, double? parentSize) @@ -203,5 +244,6 @@ public override void Dispose() Diagram.PointerDown -= OnPointerDown; Diagram.PointerMove -= OnPointerMove; Diagram.PointerUp -= OnPointerUp; + Diagram.PanChanged -= OnPanChanged; } } \ No newline at end of file diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs index f71e6a5a..ade1ce20 100644 --- a/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs +++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragMovablesBehaviorTests.cs @@ -30,36 +30,6 @@ public void Behavior_ShouldCallSetPosition() nodeMock.Verify(n => n.SetPosition(50, 50), Times.Once); } - [Theory] - [InlineData(false, 0, 0, 45, 45)] - [InlineData(true, 0, 0, 35, 35)] - [InlineData(false, 3, 3, 45, 45)] - [InlineData(true, 3, 3, 50, 50)] - public void Behavior_SnapToGrid_ShouldCallSetPosition(bool gridSnapToCenter, double initialX, double initialY, double deltaX, double deltaY) - { - // Arrange - var diagram = new TestDiagram(new DiagramOptions - { - GridSize = 15, - GridSnapToCenter = gridSnapToCenter - }); - var nodeMock = new Mock(Point.Zero); - var node = diagram.Nodes.Add(nodeMock.Object); - node.Size = new Size(20, 20); - node.Position = new Point(initialX, initialY); - diagram.SelectModel(node, false); - - // Act - //Move 40px in X and Y - diagram.TriggerPointerDown(node, - new PointerEventArgs(20, 20, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)); - diagram.TriggerPointerMove(null, - new PointerEventArgs(60, 60, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)); - - // Assert - nodeMock.Verify(n => n.SetPosition(deltaX, deltaY), Times.Once); - } - [Fact] public void Behavior_ShouldTriggerMoved() { @@ -103,12 +73,12 @@ public void Behavior_ShouldNotTriggerMoved_WhenMovableDidntMove() } [Fact] - public void Behavior_ShouldNotCallSetPosition_WhenGroupHasNoAutoSize() + public void Behavior_ShouldCallSetPosition_WhenGroupHasAutoSize() { // Arrange var diagram = new TestDiagram(); var nodeMock = new Mock(Point.Zero); - var group = new GroupModel(new[] { nodeMock.Object }, autoSize: false); + var group = new GroupModel(new[] { nodeMock.Object }, autoSize: true); var node = diagram.Nodes.Add(nodeMock.Object); diagram.SelectModel(node, false); @@ -119,26 +89,26 @@ public void Behavior_ShouldNotCallSetPosition_WhenGroupHasNoAutoSize() new PointerEventArgs(150, 150, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)); // Assert - nodeMock.Verify(n => n.SetPosition(50, 50), Times.Never); + nodeMock.Verify(n => n.SetPosition(50, 50), Times.Once); } [Fact] - public void Behavior_ShouldCallSetPosition_WhenGroupHasAutoSize() + public void Behavior_ShouldCallSetPosition_WhenPanChanges() { // Arrange var diagram = new TestDiagram(); var nodeMock = new Mock(Point.Zero); - var group = new GroupModel(new[] { nodeMock.Object }, autoSize: true); var node = diagram.Nodes.Add(nodeMock.Object); diagram.SelectModel(node, false); + diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior(); + diagram.SetContainer(new Rectangle(0, 0, 100, 100)); // Act diagram.TriggerPointerDown(node, new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)); - diagram.TriggerPointerMove(null, - new PointerEventArgs(150, 150, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)); + diagram.TriggerWheel(new WheelEventArgs(100, 100, 0, 0, false, false, false, 100, 100, 0, 0)); // Assert - nodeMock.Verify(n => n.SetPosition(50, 50), Times.Once); + nodeMock.Verify(n => n.SetPosition(100, 100), Times.Once); } } \ No newline at end of file