Skip to content

Commit

Permalink
Add initial version of selectables' ordering (nodes, groups and links)
Browse files Browse the repository at this point in the history
  • Loading branch information
zHaytam committed Oct 15, 2022
1 parent 8d6d9c7 commit e2b6779
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 32 deletions.
4 changes: 3 additions & 1 deletion samples/SharedDemo/Demos/Groups/Grouping.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected override void OnInitialized()
var node2 = NewNode(250, 250);
var node3 = NewNode(500, 100);
var node4 = NewNode(700, 350);
BlazorDiagram.Nodes.Add(new[] { node1, node2, node3, node4 });
BlazorDiagram.Nodes.Add(new[] { node1, node2, node3 });

BlazorDiagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)));
BlazorDiagram.Links.Add(new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)));
Expand All @@ -29,6 +29,8 @@ protected override void OnInitialized()
var group1 = BlazorDiagram.Groups.Group(node1, node2);
var group2 = BlazorDiagram.Groups.Group(group1, node3);

BlazorDiagram.Nodes.Add(node4);

BlazorDiagram.Links.Add(new LinkModel(group2, node4));
}

Expand Down
14 changes: 7 additions & 7 deletions samples/SharedDemo/Demos/Nodes/SvgDemo.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,19 @@ private void InitializeDiagram()
var node3 = NewNode(500, 100);
var node4 = NewNode(700, 350);
_blazorDiagram.Nodes.Add(new[] { node1, node2, node3, node4 });

var controls1 = _blazorDiagram.Controls.AddFor(node4);
controls1.Add(new RemoveControl(1, 0));
controls1.Add(new BoundaryControl());

var group1 = _blazorDiagram.Groups.Add(new SvgGroupModel(new[] { node1, node2 }));
var group2 = _blazorDiagram.Groups.Add(new SvgGroupModel(new[] { group1, node3 }));

_blazorDiagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)));
_blazorDiagram.Links.Add(new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)));
_blazorDiagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)));
var link = _blazorDiagram.Links.Add(new LinkModel(group2, node4));

var group1 = _blazorDiagram.Groups.Add(new SvgGroupModel(new[] { node1, node2 }));
var group2 = _blazorDiagram.Groups.Add(new SvgGroupModel(new[] { group1, node3 }));
var controls1 = _blazorDiagram.Controls.AddFor(node4);
controls1.Add(new RemoveControl(1, 0));
controls1.Add(new BoundaryControl());

var link = _blazorDiagram.Links.Add(new LinkModel(group2, node4));
var controls2 = _blazorDiagram.Controls.AddFor(link);
controls2.Add(new RemoveControl(1, 0));
controls2.Add(new BoundaryControl());
Expand Down
90 changes: 89 additions & 1 deletion src/Blazor.Diagrams.Core/Diagram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Blazor.Diagrams.Core.Extensions;
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Layers;
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Core.Events;
using System;
Expand All @@ -21,6 +20,8 @@ namespace Blazor.Diagrams.Core
public abstract class Diagram
{
private readonly Dictionary<Type, Behavior> _behaviors;
private readonly List<SelectableModel> _orderedSelectables;
private bool _suspendSorting;

public event Action<Model?, PointerEventArgs>? PointerDown;
public event Action<Model?, PointerEventArgs>? PointerMove;
Expand All @@ -41,12 +42,21 @@ public abstract class Diagram
protected Diagram()
{
_behaviors = new Dictionary<Type, Behavior>();
_orderedSelectables = new List<SelectableModel>();

Nodes = new NodeLayer(this);
Links = new LinkLayer(this);
Groups = new GroupLayer(this);
Controls = new ControlsLayer();

Nodes.Added += OnSelectableAdded;
Links.Added += OnSelectableAdded;
Groups.Added += OnSelectableAdded;

Nodes.Removed += OnSelectableRemoved;
Links.Removed += OnSelectableRemoved;
Groups.Removed += OnSelectableRemoved;

RegisterBehavior(new SelectionBehavior(this));
RegisterBehavior(new DragMovablesBehavior(this));
RegisterBehavior(new DragNewLinkBehavior(this));
Expand All @@ -67,6 +77,7 @@ protected Diagram()
public Point Pan { get; private set; } = Point.Zero;
public double Zoom { get; private set; } = 1;
public bool SuspendRefresh { get; set; }
public IReadOnlyList<SelectableModel> OrderedSelectables => _orderedSelectables;

public void Refresh()
{
Expand Down Expand Up @@ -277,6 +288,83 @@ public Point GetScreenPoint(double clientX, double clientY)
return new Point(Zoom * clientX + Container.Left + Pan.X, Zoom * clientY + Container.Top + Pan.Y);
}

#region Ordering

public void SendToBack(SelectableModel model)
{
var minOrder = GetMinOrder();
if (model.Order == minOrder)
return;

if (!_orderedSelectables.Remove(model))
return;

_orderedSelectables.Insert(0, model);

// Todo: can optimize this by only updating the order of items before model
Batch(() =>
{
_suspendSorting = true;
for (var i = 0; i < _orderedSelectables.Count; i++)
{
_orderedSelectables[i].Order = i + 1;
}
_suspendSorting = false;
});
}

public void SendToFront(SelectableModel model)
{
var maxOrder = GetMaxOrder();
if (model.Order == maxOrder)
return;

if (!_orderedSelectables.Remove(model))
return;

_orderedSelectables.Add(model);

_suspendSorting = true;
model.Order = maxOrder + 1;
_suspendSorting = false;
Refresh();
}

public int GetMinOrder()
{
return _orderedSelectables.Count > 0 ? _orderedSelectables[0].Order : 0;
}

public int GetMaxOrder()
{
return _orderedSelectables.Count > 0 ? _orderedSelectables[^1].Order : 0;
}

private void OnSelectableAdded(SelectableModel model)
{
var maxOrder = GetMaxOrder();
_orderedSelectables.Add(model);
model.Order = maxOrder + 1;
model.OrderChanged += OnModelOrderChanged;
}

private void OnSelectableRemoved(SelectableModel model)
{
model.OrderChanged -= OnModelOrderChanged;
_orderedSelectables.Remove(model);
}

private void OnModelOrderChanged(Model model)
{
if (_suspendSorting)
return;

_orderedSelectables.Sort((a, b) => a.Order.CompareTo(b.Order));
Refresh();
}

#endregion

#region Events

public void TriggerPointerDown(Model? model, PointerEventArgs e) => PointerDown?.Invoke(model, e);
Expand Down
24 changes: 21 additions & 3 deletions src/Blazor.Diagrams.Core/Models/Base/SelectableModel.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
namespace Blazor.Diagrams.Core.Models.Base
using System;

namespace Blazor.Diagrams.Core.Models.Base
{
public abstract class SelectableModel : Model
{
public SelectableModel() { }
private int _order;

public event Action<SelectableModel>? OrderChanged;

public SelectableModel(string id) : base(id) { }
protected SelectableModel() { }

protected SelectableModel(string id) : base(id) { }

public bool Selected { get; internal set; }
public int Order
{
get => _order;
set
{
if (value == Order)
return;

_order = value;
OrderChanged?.Invoke(this);
}
}
}
}
45 changes: 25 additions & 20 deletions src/Blazor.Diagrams/Components/DiagramCanvas.razor
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,41 @@
@onwheel:stopPropagation>

<svg class="diagram-svg-layer" style="@GetLayerStyle(BlazorDiagram.Options.LinksLayerOrder)">
@foreach (var node in BlazorDiagram.Nodes.OfType<SvgNodeModel>().Where(n => n.Group == null))
{
<NodeRenderer @key="node" Node="node"/>
}

@foreach (var group in BlazorDiagram.Groups.OfType<SvgGroupModel>().Where(n => n.Group == null))
@foreach (var model in BlazorDiagram.OrderedSelectables)
{
<GroupRenderer @key="group" Group="group"/>
}

@foreach (var link in BlazorDiagram.Links)
{
<LinkRenderer @key="link" Link="link"/>
if (model is SvgNodeModel node && node.Group == null)
{
<NodeRenderer @key="node" Node="node" />
}
else if (model is SvgGroupModel group && group.Group == null)
{
<GroupRenderer @key="group" Group="group" />
}
else if (model is BaseLinkModel link)
{
<LinkRenderer @key="link" Link="link" />
}
}

<ControlsLayerRenderer Svg="@true"></ControlsLayerRenderer>
</svg>

<div class="diagram-html-layer" style="@GetLayerStyle(BlazorDiagram.Options.NodesLayerOrder)">
@foreach (var group in BlazorDiagram.Groups.Where(n => n is not SvgGroupModel))
{
if (group.Group != null)
continue;

<GroupRenderer @key="group" Group="group"/>
}

@foreach (var node in BlazorDiagram.Nodes.Where(n => n is not SvgNodeModel && n.Group == null))
@foreach (var model in BlazorDiagram.OrderedSelectables)
{
<NodeRenderer @key="node" Node="node"/>
if (model is GroupModel group)
{
if (group.Group == null && group is not SvgGroupModel)
{
<GroupRenderer @key="group" Group="group" />
}
}
else if (model is NodeModel node && node.Group == null && node is not SvgNodeModel)
{
<NodeRenderer @key="node" Node="node" />
}
}

<ControlsLayerRenderer Svg="@false"></ControlsLayerRenderer>
Expand Down
Loading

0 comments on commit e2b6779

Please sign in to comment.