diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index 610a5aac..102a9fed 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -107,7 +107,13 @@ public void SetSize(double width, double height) if (newSize.Equals(_size) == true) return; + Size? oldSize = Size != null ? new Size(Size.Width, Size.Height) : null; + Size = newSize; + if (oldSize != null) + { + UpdatePortPositions(oldSize, newSize); + } Refresh(); RefreshLinks(); SizeChanging?.Invoke(this); @@ -156,6 +162,9 @@ public virtual void UpdatePositionSilently(double deltaX, double deltaY) public virtual bool CanAttachTo(ILinkable other) => other is not PortModel && other is not BaseLinkModel; + /// + /// Updates port positions when node position changes. + /// private void UpdatePortPositions(double deltaX, double deltaY) { // Save some JS calls and update ports directly here @@ -166,6 +175,21 @@ private void UpdatePortPositions(double deltaX, double deltaY) } } + /// + /// Updates port positions when node size changes. + /// + private void UpdatePortPositions(Size oldSize, Size newSize) + { + var deltaWidth = newSize.Width - oldSize.Width; + var deltaHeight = newSize.Height - oldSize.Height; + + foreach (var port in _ports) + { + port.SetPortPositionOnNodeSizeChanged(deltaWidth, deltaHeight); + port.RefreshLinks(); + } + } + protected void TriggerMoving() { Moving?.Invoke(this); diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs index a0c85081..ef206856 100644 --- a/src/Blazor.Diagrams.Core/Models/PortModel.cs +++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs @@ -67,4 +67,38 @@ public virtual bool CanAttachTo(ILinkable other) void ILinkable.AddLink(BaseLinkModel link) => _links.Add(link); void ILinkable.RemoveLink(BaseLinkModel link) => _links.Remove(link); + + public virtual void SetPortPositionOnNodeSizeChanged(double deltaWidth, double deltaHeight) + { + switch (Alignment) + { + case PortAlignment.Top: + Position = new Point(Position.X + deltaWidth / 2, Position.Y); + break; + case PortAlignment.TopRight: + Position = new Point(Position.X + deltaWidth, Position.Y); + break; + case PortAlignment.TopLeft: + Position = new Point(Position.X, Position.Y); + break; + case PortAlignment.Right: + Position = new Point(Position.X + deltaWidth, Position.Y + deltaHeight / 2); + break; + case PortAlignment.Left: + Position = new Point(Position.X, Position.Y + deltaHeight / 2); + break; + case PortAlignment.Bottom: + Position = new Point(Position.X + deltaWidth / 2, Position.Y + deltaHeight); + break; + case PortAlignment.BottomRight: + Position = new Point(Position.X + deltaWidth, Position.Y + deltaHeight); + break; + case PortAlignment.BottomLeft: + Position = new Point(Position.X, Position.Y + deltaHeight); + break; + default: + Position = new Point(Position.X, Position.Y); + break; + } + } } diff --git a/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj b/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj index 11c8ea2c..ee63923f 100644 --- a/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj +++ b/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj @@ -1,4 +1,4 @@ - + net6.0 diff --git a/tests/Blazor.Diagrams.Core.Tests/Models/NodeModelTest.cs b/tests/Blazor.Diagrams.Core.Tests/Models/NodeModelTest.cs new file mode 100644 index 00000000..b27edc99 --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Models/NodeModelTest.cs @@ -0,0 +1,53 @@ +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; +using Moq; +using Xunit; + +namespace Blazor.Diagrams.Core.Tests.Models +{ + public class NodeModelTest + { + [Fact] + public void UpdatePortOnSetPosition() + { + var node = new NodeModel(position: new Point(100, 100)); + node.Size = new Size(100, 100); + + var port = new PortModel(node, PortAlignment.BottomLeft, new Point(50, 50)); + node.AddPort(port); + + var newX = 200; + var newY = 300; + + //Act + node.SetPosition(newX, newY); + + //Assert + Assert.Equal(150, port.Position.X); + Assert.Equal(250, port.Position.Y); + } + + [Fact] + public void SetPortPositionOnNodeSizeChangedIsCalledOnSetSize() + { + // Arrange + var oldWidth = 100.0; + var oldHeight = 100.0; + var newWidth = 500.0; + var newHeight = 700.0; + var deltaX = newWidth - oldWidth; + var deltaY = newHeight - oldHeight; + + var node = new NodeModel(new Point(100, 100)) { Size = new Size(oldWidth, oldHeight) }; + var portMock = new Mock(node, PortAlignment.BottomLeft, null, null); + + node.AddPort(portMock.Object); + + // Act + node.SetSize(newWidth, newHeight); + + // Assert + portMock.Verify(m => m.SetPortPositionOnNodeSizeChanged(deltaX, deltaY), Times.Once); + } + } +} diff --git a/tests/Blazor.Diagrams.Core.Tests/Models/PortModelTest.cs b/tests/Blazor.Diagrams.Core.Tests/Models/PortModelTest.cs new file mode 100644 index 00000000..d4dc57bf --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Models/PortModelTest.cs @@ -0,0 +1,33 @@ +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; +using Xunit; + +namespace Blazor.Diagrams.Core.Tests.Models +{ + public class PortModelTest + { + [Theory] + [InlineData(PortAlignment.Top, 50, 0)] + [InlineData(PortAlignment.TopLeft, 0, 0)] + [InlineData(PortAlignment.TopRight, 100, 0)] + [InlineData(PortAlignment.Bottom, 50, 100)] + [InlineData(PortAlignment.BottomLeft, 0, 100)] + [InlineData(PortAlignment.BottomRight, 100, 100)] + [InlineData(PortAlignment.Left, 0, 50)] + [InlineData(PortAlignment.Right, 100, 50)] + public void SetPortPositionOnNodeSizeChangedCalculatesCorrectPosition(PortAlignment alignment, double expectedXPosition, double expectedYPosition) + { + // Arrange + var node = new NodeModel(); + var port = new PortModel(node, alignment, new Point(0, 0)); + node.Size = new Size(100, 100); + + // Act + port.SetPortPositionOnNodeSizeChanged(100, 100); + + // Assert + Assert.Equal(expectedXPosition, port.Position.X); + Assert.Equal(expectedYPosition, port.Position.Y); + } + } +}