Skip to content

Commit 69c8198

Browse files
committed
Use Controls to implement node resizing (#10)
1 parent 11798a1 commit 69c8198

File tree

22 files changed

+876
-25
lines changed

22 files changed

+876
-25
lines changed

site/Site/Pages/Documentation/Controls/Overview.razor

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
@using Blazor.Diagrams.Core.Controls;
44
@using Blazor.Diagrams.Core.PathGenerators;
55
@using Blazor.Diagrams.Core.Positions;
6+
@using Blazor.Diagrams.Core.Positions.Resizing;
67
@using Blazor.Diagrams.Core.Routers;
78
@layout DocumentationLayout
89
@inherits DocumentationPage
@@ -135,6 +136,26 @@ Diagram.Controls.AddFor(SomeModel)
135136
</CascadingValue>
136137
</div>
137138

139+
<h3>Resize Control</h3>
140+
141+
<p>
142+
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 />
143+
There are four <code>ResizerProvider</code>s, one for each corner. Custom resizing behavior can be created by implementing <code>IResizerProvider</code>.
144+
</p>
145+
146+
<pre><code class="language-cs">
147+
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new BottomRightResizerProvider()));
148+
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new BottomLeftResizerProvider()));
149+
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new TopRightResizerProvider()));
150+
Diagram.Controls.AddFor(SomeModel).Add(new ResizeControl(new TopLeftResizerProvider()));
151+
</code></pre>
152+
153+
<div class="diagram-container" style="width: 100%; height: 300px;">
154+
<CascadingValue Value="_resizerDiagram" IsFixed="true">
155+
<DiagramCanvas></DiagramCanvas>
156+
</CascadingValue>
157+
</div>
158+
138159
<NavigationButtons NextTitle="Customization"
139160
NextLink="/documentation/controls-customization" />
140161

@@ -143,6 +164,7 @@ Diagram.Controls.AddFor(SomeModel)
143164
private BlazorDiagram _rDiagram = new();
144165
private BlazorDiagram _ahDiagram = new();
145166
private BlazorDiagram _dnlDiagram = new();
167+
private BlazorDiagram _resizerDiagram = new();
146168

147169
protected override void OnInitialized()
148170
{
@@ -198,5 +220,18 @@ Diagram.Controls.AddFor(SomeModel)
198220
_dnlDiagram.Controls.AddFor(dnlNode2).Add(new DragNewLinkControl(0, 0.5, offsetX: -20));
199221
_dnlDiagram.SelectModel(dnlNode1, false);
200222
_dnlDiagram.SelectModel(dnlNode2, false);
223+
224+
// Resize Control
225+
var resizeNode1 = _resizerDiagram.Nodes.Add(new NodeModel(new Point(100, 100)));
226+
var resizeNode2 = _resizerDiagram.Nodes.Add(new NodeModel(new Point(500, 150)));
227+
resizeNode1.Title = "Title";
228+
resizeNode2.Title = "Title";
229+
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new BottomRightResizerProvider()));
230+
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new BottomLeftResizerProvider()));
231+
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new TopRightResizerProvider()));
232+
_resizerDiagram.Controls.AddFor(resizeNode1, ControlsType.OnSelection).Add(new ResizeControl(new TopLeftResizerProvider()));
233+
_resizerDiagram.Controls.AddFor(resizeNode2, ControlsType.OnSelection).Add(new ResizeControl(new BottomRightResizerProvider()));
234+
_resizerDiagram.SelectModel(resizeNode1, false);
235+
_resizerDiagram.SelectModel(resizeNode2, false);
201236
}
202237
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Blazor.Diagrams.Core.Events;
2+
using Blazor.Diagrams.Core.Geometry;
3+
using Blazor.Diagrams.Core.Models.Base;
4+
using Blazor.Diagrams.Core.Positions.Resizing;
5+
using System.Threading.Tasks;
6+
7+
namespace Blazor.Diagrams.Core.Controls.Default
8+
{
9+
public class ResizeControl : ExecutableControl
10+
{
11+
private readonly IResizerProvider _resizeProvider;
12+
13+
public ResizeControl(IResizerProvider resizeProvider)
14+
{
15+
_resizeProvider = resizeProvider;
16+
}
17+
18+
public override Point? GetPosition(Model model) => _resizeProvider.GetPosition(model);
19+
20+
public string? Class => _resizeProvider.Class;
21+
22+
public override ValueTask OnPointerDown(Diagram diagram, Model model, PointerEventArgs e)
23+
{
24+
_resizeProvider.OnResizeStart(diagram, model, e);
25+
diagram.PointerMove += _resizeProvider.OnPointerMove;
26+
diagram.PointerUp += _resizeProvider.OnResizeEnd;
27+
diagram.PointerUp += (_, _) => OnResizeEnd(diagram);
28+
29+
return ValueTask.CompletedTask;
30+
}
31+
32+
void OnResizeEnd(Diagram diagram)
33+
{
34+
diagram.PointerMove -= _resizeProvider.OnPointerMove;
35+
diagram.PointerUp -= _resizeProvider.OnResizeEnd;
36+
}
37+
}
38+
}

src/Blazor.Diagrams.Core/Models/GroupModel.cs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,10 @@ public void AddChild(NodeModel child)
2727
{
2828
_children.Add(child);
2929
child.Group = this;
30-
child.SizeChanged += OnNodeChanged;
30+
child.SizeChanging += OnNodeChanged;
3131
child.Moving += OnNodeChanged;
3232

33-
if (UpdateDimensions())
34-
{
35-
Refresh();
36-
}
33+
UpdateDimensions();
3734
}
3835

3936
public void RemoveChild(NodeModel child)
@@ -42,14 +39,10 @@ public void RemoveChild(NodeModel child)
4239
return;
4340

4441
child.Group = null;
45-
child.SizeChanged -= OnNodeChanged;
42+
child.SizeChanging -= OnNodeChanged;
4643
child.Moving -= OnNodeChanged;
4744

48-
if (UpdateDimensions())
49-
{
50-
Refresh();
51-
RefreshLinks();
52-
}
45+
UpdateDimensions();
5346
}
5447

5548
public override void SetPosition(double x, double y)
@@ -83,7 +76,7 @@ public void Ungroup()
8376
foreach (var child in Children)
8477
{
8578
child.Group = null;
86-
child.SizeChanged -= OnNodeChanged;
79+
child.SizeChanging -= OnNodeChanged;
8780
child.Moving -= OnNodeChanged;
8881
}
8982

@@ -96,7 +89,7 @@ private void Initialize(IEnumerable<NodeModel> children)
9689
{
9790
_children.Add(child);
9891
child.Group = this;
99-
child.SizeChanged += OnNodeChanged;
92+
child.SizeChanging += OnNodeChanged;
10093
child.Moving += OnNodeChanged;
10194
}
10295

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

10699
private void OnNodeChanged(NodeModel node)
107100
{
108-
if (UpdateDimensions())
109-
{
110-
Refresh();
111-
}
101+
UpdateDimensions();
112102
}
113103

114104
private bool UpdateDimensions()
@@ -128,7 +118,7 @@ private bool UpdateDimensions()
128118
TriggerMoving();
129119
}
130120

131-
Size = new Size(bounds.Width + Padding * 2, bounds.Height + Padding * 2);
121+
SetSize(bounds.Width + Padding * 2, bounds.Height + Padding * 2);
132122
return true;
133123
}
134124
}

src/Blazor.Diagrams.Core/Models/NodeModel.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ public class NodeModel : MovableModel, IHasBounds, IHasShape, ILinkable
1111
private readonly List<PortModel> _ports = new();
1212
private readonly List<BaseLinkModel> _links = new();
1313
private Size? _size;
14+
public Size MinimumDimensions { get; set; } = new Size(0, 0);
1415

16+
public event Action<NodeModel>? SizeChanging;
1517
public event Action<NodeModel>? SizeChanged;
1618
public event Action<NodeModel>? Moving;
1719

@@ -28,11 +30,7 @@ public Size? Size
2830
get => _size;
2931
set
3032
{
31-
if (value?.Equals(_size) == true)
32-
return;
33-
3433
_size = value;
35-
SizeChanged?.Invoke(this);
3634
}
3735
}
3836
public bool ControlledSize { get; init; }
@@ -103,6 +101,23 @@ public override void SetPosition(double x, double y)
103101
Moving?.Invoke(this);
104102
}
105103

104+
public void SetSize(double width, double height)
105+
{
106+
var newSize = new Size(width, height);
107+
if (newSize.Equals(_size) == true)
108+
return;
109+
110+
Size = newSize;
111+
Refresh();
112+
RefreshLinks();
113+
SizeChanging?.Invoke(this);
114+
}
115+
116+
public void TriggerSizeChanged()
117+
{
118+
SizeChanged?.Invoke(this);
119+
}
120+
106121
public virtual void UpdatePositionSilently(double deltaX, double deltaY)
107122
{
108123
base.SetPosition(Position.X + deltaX, Position.Y + deltaY);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using Blazor.Diagrams.Core.Events;
2+
using Blazor.Diagrams.Core.Geometry;
3+
using Blazor.Diagrams.Core.Models;
4+
using Blazor.Diagrams.Core.Models.Base;
5+
6+
namespace Blazor.Diagrams.Core.Positions.Resizing
7+
{
8+
public class BottomLeftResizerProvider : IResizerProvider
9+
{
10+
public string? Class => "bottomleft";
11+
12+
private Size _originalSize = null!;
13+
private Point _originalPosition = null!;
14+
private Point _originalMousePosition = null!;
15+
private NodeModel _nodeModel = null!;
16+
17+
public Point? GetPosition(Model model)
18+
{
19+
if (model is NodeModel nodeModel && nodeModel.Size is not null)
20+
{
21+
return new Point(nodeModel.Position.X - 5, nodeModel.Position.Y + nodeModel.Size.Height + 5);
22+
}
23+
return null;
24+
}
25+
26+
public void OnResizeStart(Diagram diagram, Model model, PointerEventArgs eventArgs)
27+
{
28+
if (model is NodeModel nodeModel)
29+
{
30+
_originalPosition = new Point(nodeModel.Position.X, nodeModel.Position.Y);
31+
_originalMousePosition = new Point(eventArgs.ClientX, eventArgs.ClientY);
32+
_originalSize = nodeModel.Size!;
33+
_nodeModel = nodeModel;
34+
}
35+
}
36+
37+
public void OnPointerMove(Model? model, PointerEventArgs args)
38+
{
39+
if (_nodeModel is null)
40+
{
41+
return;
42+
}
43+
44+
var height = _originalSize.Height + (args.ClientY - _originalMousePosition.Y);
45+
var width = _originalSize.Width - (args.ClientX - _originalMousePosition.X);
46+
47+
var positionX = _originalPosition.X + (args.ClientX - _originalMousePosition.X);
48+
var positionY = _originalPosition.Y;
49+
50+
if (width < _nodeModel.MinimumDimensions.Width)
51+
{
52+
width = _nodeModel.MinimumDimensions.Width;
53+
positionX = _nodeModel.Position.X;
54+
}
55+
if (height < _nodeModel.MinimumDimensions.Height)
56+
{
57+
height = _nodeModel.MinimumDimensions.Height;
58+
positionY = _nodeModel.Position.Y;
59+
}
60+
61+
_nodeModel.SetPosition(positionX, positionY);
62+
_nodeModel.SetSize(width, height);
63+
}
64+
65+
public void OnResizeEnd(Model? model, PointerEventArgs args)
66+
{
67+
_nodeModel?.TriggerSizeChanged();
68+
_originalSize = null!;
69+
_originalPosition = null!;
70+
_originalMousePosition = null!;
71+
_nodeModel = null!;
72+
}
73+
74+
}
75+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Blazor.Diagrams.Core.Events;
2+
using Blazor.Diagrams.Core.Geometry;
3+
using Blazor.Diagrams.Core.Models;
4+
using Blazor.Diagrams.Core.Models.Base;
5+
6+
namespace Blazor.Diagrams.Core.Positions.Resizing
7+
{
8+
public class BottomRightResizerProvider : IResizerProvider
9+
{
10+
public string? Class => "bottomright";
11+
12+
private Size _originalSize = null!;
13+
private Point _originalMousePosition = null!;
14+
private NodeModel _nodeModel = null!;
15+
16+
public Point? GetPosition(Model model)
17+
{
18+
if (model is NodeModel nodeModel && nodeModel.Size is not null)
19+
{
20+
return new Point(nodeModel.Position.X + nodeModel.Size.Width + 5, nodeModel.Position.Y + nodeModel.Size.Height + 5);
21+
}
22+
return null;
23+
}
24+
25+
public void OnResizeStart(Diagram diagram, Model model, PointerEventArgs eventArgs)
26+
{
27+
if (model is NodeModel nodeModel)
28+
{
29+
_originalMousePosition = new Point(eventArgs.ClientX, eventArgs.ClientY);
30+
_originalSize = nodeModel.Size!;
31+
_nodeModel = nodeModel;
32+
}
33+
}
34+
35+
public void OnPointerMove(Model? model, PointerEventArgs args)
36+
{
37+
if (_nodeModel is null)
38+
{
39+
return;
40+
}
41+
42+
var height = _originalSize.Height + (args.ClientY - _originalMousePosition.Y);
43+
var width = _originalSize.Width + (args.ClientX - _originalMousePosition.X);
44+
45+
if (width < _nodeModel.MinimumDimensions.Width)
46+
{
47+
width = _nodeModel.MinimumDimensions.Width;
48+
}
49+
if (height < _nodeModel.MinimumDimensions.Height)
50+
{
51+
height = _nodeModel.MinimumDimensions.Height;
52+
}
53+
54+
_nodeModel.SetSize(width, height);
55+
}
56+
57+
public void OnResizeEnd(Model? model, PointerEventArgs args)
58+
{
59+
_nodeModel?.TriggerSizeChanged();
60+
_originalSize = null!;
61+
_originalMousePosition = null!;
62+
_nodeModel = null!;
63+
}
64+
65+
}
66+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Blazor.Diagrams.Core.Events;
2+
using Blazor.Diagrams.Core.Models;
3+
using Blazor.Diagrams.Core.Models.Base;
4+
5+
namespace Blazor.Diagrams.Core.Positions.Resizing
6+
{
7+
public interface IResizerProvider : IPositionProvider
8+
{
9+
public string? Class { get; }
10+
public void OnResizeStart(Diagram diagram, Model model, PointerEventArgs eventArgs);
11+
public void OnPointerMove(Model? model, PointerEventArgs args);
12+
public void OnResizeEnd(Model? model, PointerEventArgs args);
13+
}
14+
}

0 commit comments

Comments
 (0)