Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Controls to implement node resizing #393

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions site/Site/Pages/Documentation/Controls/Overview.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@using Blazor.Diagrams.Core.Controls;
@using Blazor.Diagrams.Core.PathGenerators;
@using Blazor.Diagrams.Core.Positions;
@using Blazor.Diagrams.Core.Positions.Resizing;
@using Blazor.Diagrams.Core.Routers;
@layout DocumentationLayout
@inherits DocumentationPage
Expand Down Expand Up @@ -135,6 +136,26 @@ Diagram.Controls.AddFor(SomeModel)
</CascadingValue>
</div>

<h3>Resize Control</h3>

<p>
The <code>ResizeControl</code> adds a resizer which is a box that when dragged, can resize the node. The resizer position and movement of the node is controlled using a Resizer Provider.<br />
There are four <code>ResizerProvider</code>s, one for each corner. Custom resizing behavior can be created by inheriting and overriding <code>ResizerProvider</code>.
</p>

<pre><code class="language-cs">
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new BottomRightResizerProvider()));
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new BottomLeftResizerProvider()));
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new TopRightResizerProvider()));
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new TopLeftResizerProvider()));
</code></pre>

<div class="diagram-container" style="width: 100%; height: 300px;">
<CascadingValue Value="_resizerDiagram" IsFixed="true">
<DiagramCanvas></DiagramCanvas>
</CascadingValue>
</div>

<NavigationButtons NextTitle="Customization"
NextLink="/documentation/controls-customization" />

Expand All @@ -143,6 +164,7 @@ Diagram.Controls.AddFor(SomeModel)
private BlazorDiagram _rDiagram = new();
private BlazorDiagram _ahDiagram = new();
private BlazorDiagram _dnlDiagram = new();
private BlazorDiagram _resizerDiagram = new();

protected override void OnInitialized()
{
Expand Down Expand Up @@ -198,5 +220,21 @@ Diagram.Controls.AddFor(SomeModel)
_dnlDiagram.Controls.AddFor(dnlNode2).Add(new DragNewLinkControl(0, 0.5, offsetX: -20));
_dnlDiagram.SelectModel(dnlNode1, false);
_dnlDiagram.SelectModel(dnlNode2, false);

// Resize Control
var resizeNode1 = _resizerDiagram.Nodes.Add(new NodeModel(new Point(100, 100)));
var resizeNode2 = _resizerDiagram.Nodes.Add(new NodeModel(new Point(500, 150)));
var resizeNode1Port = resizeNode1.AddPort(PortAlignment.Right);
var resizeNode2Port = resizeNode2.AddPort(PortAlignment.Left);
_resizerDiagram.Links.Add(new LinkModel(resizeNode1Port, resizeNode2Port));
resizeNode1.Title = "Title";
resizeNode2.Title = "Title";
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new BottomRightResizerProvider()));
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new BottomLeftResizerProvider()));
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new TopRightResizerProvider()));
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new TopLeftResizerProvider()));
_resizerDiagram.Controls.AddFor(resizeNode2, ControlsType.OnSelection).Add(new ResizeControl(new BottomRightResizerProvider()));
_resizerDiagram.SelectModel(resizeNode1, false);
_resizerDiagram.SelectModel(resizeNode2, false);
}
}
38 changes: 38 additions & 0 deletions src/Blazor.Diagrams.Core/Controls/Default/ResizeControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Blazor.Diagrams.Core.Events;
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Core.Positions.Resizing;
using System.Threading.Tasks;

namespace Blazor.Diagrams.Core.Controls.Default
{
public class ResizeControl : ExecutableControl
{
private readonly ResizerProvider _resizeProvider;

public ResizeControl(ResizerProvider resizeProvider)
{
_resizeProvider = resizeProvider;
}

public override Point? GetPosition(Model model) => _resizeProvider.GetPosition(model);

public string? Class => _resizeProvider.Class;

public override ValueTask OnPointerDown(Diagram diagram, Model model, PointerEventArgs e)
{
_resizeProvider.OnResizeStart(diagram, model, e);
diagram.PointerMove += _resizeProvider.OnPointerMove;
diagram.PointerUp += _resizeProvider.OnResizeEnd;
diagram.PointerUp += (_, _) => OnResizeEnd(diagram);

return ValueTask.CompletedTask;
}

void OnResizeEnd(Diagram diagram)
{
diagram.PointerMove -= _resizeProvider.OnPointerMove;
diagram.PointerUp -= _resizeProvider.OnResizeEnd;
}
}
}
26 changes: 8 additions & 18 deletions src/Blazor.Diagrams.Core/Models/GroupModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,10 @@ public void AddChild(NodeModel child)
{
_children.Add(child);
child.Group = this;
child.SizeChanged += OnNodeChanged;
child.SizeChanging += OnNodeChanged;
child.Moving += OnNodeChanged;

if (UpdateDimensions())
{
Refresh();
}
UpdateDimensions();
}

public void RemoveChild(NodeModel child)
Expand All @@ -42,14 +39,10 @@ public void RemoveChild(NodeModel child)
return;

child.Group = null;
child.SizeChanged -= OnNodeChanged;
child.SizeChanging -= OnNodeChanged;
child.Moving -= OnNodeChanged;

if (UpdateDimensions())
{
Refresh();
RefreshLinks();
}
UpdateDimensions();
}

public override void SetPosition(double x, double y)
Expand Down Expand Up @@ -83,7 +76,7 @@ public void Ungroup()
foreach (var child in Children)
{
child.Group = null;
child.SizeChanged -= OnNodeChanged;
child.SizeChanging -= OnNodeChanged;
child.Moving -= OnNodeChanged;
}

Expand All @@ -96,7 +89,7 @@ private void Initialize(IEnumerable<NodeModel> children)
{
_children.Add(child);
child.Group = this;
child.SizeChanged += OnNodeChanged;
child.SizeChanging += OnNodeChanged;
child.Moving += OnNodeChanged;
}

Expand All @@ -105,10 +98,7 @@ private void Initialize(IEnumerable<NodeModel> children)

private void OnNodeChanged(NodeModel node)
{
if (UpdateDimensions())
{
Refresh();
}
UpdateDimensions();
}

private bool UpdateDimensions()
Expand All @@ -128,7 +118,7 @@ private bool UpdateDimensions()
TriggerMoving();
}

Size = new Size(bounds.Width + Padding * 2, bounds.Height + Padding * 2);
SetSize(bounds.Width + Padding * 2, bounds.Height + Padding * 2);
return true;
}
}
47 changes: 43 additions & 4 deletions src/Blazor.Diagrams.Core/Models/NodeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ public class NodeModel : MovableModel, IHasBounds, IHasShape, ILinkable
private readonly List<PortModel> _ports = new();
private readonly List<BaseLinkModel> _links = new();
private Size? _size;
public Size MinimumDimensions { get; set; } = new Size(0, 0);

public event Action<NodeModel>? SizeChanging;
public event Action<NodeModel>? SizeChanged;
public event Action<NodeModel>? Moving;

Expand All @@ -28,11 +30,7 @@ public Size? Size
get => _size;
set
{
if (value?.Equals(_size) == true)
return;

_size = value;
SizeChanged?.Invoke(this);
}
}
public bool ControlledSize { get; init; }
Expand Down Expand Up @@ -103,6 +101,29 @@ public override void SetPosition(double x, double y)
Moving?.Invoke(this);
}

public void SetSize(double width, double height)
{
var newSize = new Size(width, 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);
}

public void TriggerSizeChanged()
{
SizeChanged?.Invoke(this);
}

public virtual void UpdatePositionSilently(double deltaX, double deltaY)
{
base.SetPosition(Position.X + deltaX, Position.Y + deltaY);
Expand Down Expand Up @@ -141,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;

/// <summary>
/// Updates port positions when node position changes.
/// </summary>
private void UpdatePortPositions(double deltaX, double deltaY)
{
// Save some JS calls and update ports directly here
Expand All @@ -151,6 +175,21 @@ private void UpdatePortPositions(double deltaX, double deltaY)
}
}

/// <summary>
/// Updates port positions when node size changes.
/// </summary>
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);
Expand Down
34 changes: 34 additions & 0 deletions src/Blazor.Diagrams.Core/Models/PortModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;

namespace Blazor.Diagrams.Core.Positions.Resizing
{
public class BottomLeftResizerProvider : ResizerProvider
{
override public string? Class => "bottomleft";
override public bool ShouldChangeXPositionOnResize => true;
override public bool ShouldChangeYPositionOnResize => false;
override public bool ShouldAddTotalMovedX => false;
override public bool ShouldAddTotalMovedY => true;

public override Point? GetPosition(Model model)
{
if (model is NodeModel nodeModel && nodeModel.Size is not null)
{
return new Point(nodeModel.Position.X - 5, nodeModel.Position.Y + nodeModel.Size.Height + 5);
}
return null;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;

namespace Blazor.Diagrams.Core.Positions.Resizing
{
public class BottomRightResizerProvider : ResizerProvider
{
override public string? Class => "bottomright";
override public bool ShouldChangeXPositionOnResize => false;
override public bool ShouldChangeYPositionOnResize => false;
override public bool ShouldAddTotalMovedX => true;
override public bool ShouldAddTotalMovedY => true;

public override Point? GetPosition(Model model)
{
if (model is NodeModel nodeModel && nodeModel.Size is not null)
{
return new Point(nodeModel.Position.X + nodeModel.Size.Width + 5, nodeModel.Position.Y + nodeModel.Size.Height + 5);
}
return null;
}

}
}
Loading