diff --git a/site/Site/Pages/Documentation/Controls/Customization.razor b/site/Site/Pages/Documentation/Controls/Customization.razor index fd33dcf7a..6211efd70 100644 --- a/site/Site/Pages/Documentation/Controls/Customization.razor +++ b/site/Site/Pages/Documentation/Controls/Customization.razor @@ -156,6 +156,7 @@ public class AlertControl : ExecutableControl cNode2.Title = "Hover on me"; _cDiagram.Controls.AddFor(cNode1, ControlsType.OnSelection).Add(new NodeInformationControl()); _cDiagram.Controls.AddFor(cNode2, ControlsType.OnHover).Add(new NodeInformationControl()); + _cDiagram.Controls.AddFor(cNode2, ControlsType.AlwaysOn).Add(new NodeInformationControl()); _cDiagram.SelectModel(cNode1, false); // Executable Control diff --git a/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs b/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs index 05148b5e6..19172c02e 100644 --- a/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs +++ b/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs @@ -5,57 +5,49 @@ namespace Blazor.Diagrams.Core.Controls; public class ControlsBehavior : Behavior { - public ControlsBehavior(Diagram diagram) : base(diagram) - { - Diagram.PointerEnter += OnPointerEnter; - Diagram.PointerLeave += OnPointerLeave; - Diagram.SelectionChanged += OnSelectionChanged; - } - - private void OnSelectionChanged(SelectableModel model) - { - var controls = Diagram.Controls.GetFor(model); - if (controls is not { Type: ControlsType.OnSelection }) - return; - - if (model.Selected) - { - controls.Show(); - } - else - { - controls.Hide(); - } - } - - private void OnPointerEnter(Model? model, PointerEventArgs e) - { - if (model == null) - return; - - var controls = Diagram.Controls.GetFor(model); - if (controls is not { Type: ControlsType.OnHover }) - return; - - controls.Show(); - } - - private void OnPointerLeave(Model? model, PointerEventArgs e) - { - if (model == null) - return; - - var controls = Diagram.Controls.GetFor(model); - if (controls is not { Type: ControlsType.OnHover }) - return; - - controls.Hide(); - } - - public override void Dispose() - { - Diagram.PointerEnter -= OnPointerEnter; - Diagram.PointerLeave -= OnPointerLeave; - Diagram.SelectionChanged -= OnSelectionChanged; - } + public ControlsBehavior(Diagram diagram) : base(diagram) + { + Diagram.PointerEnter += OnPointerEnter; + Diagram.PointerLeave += OnPointerLeave; + Diagram.SelectionChanged += OnSelectionChanged; + } + + private void OnSelectionChanged(SelectableModel model) + { + var controls = Diagram.Controls.GetFor(model, ControlsType.OnSelection); + if (controls is null) + return; + + if (model.Selected) + { + controls.Show(); + } + else + { + controls.Hide(); + } + } + + private void OnPointerEnter(Model? model, PointerEventArgs e) + { + if (model == null) + return; + + Diagram.Controls.GetFor(model, ControlsType.OnHover)?.Show(); + } + + private void OnPointerLeave(Model? model, PointerEventArgs e) + { + if (model == null) + return; + + Diagram.Controls.GetFor(model, ControlsType.OnHover)?.Hide(); + } + + public override void Dispose() + { + Diagram.PointerEnter -= OnPointerEnter; + Diagram.PointerLeave -= OnPointerLeave; + Diagram.SelectionChanged -= OnSelectionChanged; + } } \ No newline at end of file diff --git a/src/Blazor.Diagrams.Core/Controls/ControlsContainer.cs b/src/Blazor.Diagrams.Core/Controls/ControlsContainer.cs index 5ebc16b46..e741e2726 100644 --- a/src/Blazor.Diagrams.Core/Controls/ControlsContainer.cs +++ b/src/Blazor.Diagrams.Core/Controls/ControlsContainer.cs @@ -1,6 +1,4 @@ -using System; using System.Collections; -using System.Collections.Generic; using Blazor.Diagrams.Core.Models.Base; namespace Blazor.Diagrams.Core.Controls; @@ -16,17 +14,19 @@ public ControlsContainer(Model model, ControlsType type = ControlsType.OnSelecti { Model = model; Type = type; + + Visible = type == ControlsType.AlwaysOn; } public Model Model { get; } - public ControlsType Type { get; set; } + public ControlsType Type { get; init; } public bool Visible { get; private set; } public void Show() { if (Visible) return; - + Visible = true; VisibilityChanged?.Invoke(Model); } @@ -35,7 +35,7 @@ public void Hide() { if (!Visible) return; - + Visible = false; VisibilityChanged?.Invoke(Model); } diff --git a/src/Blazor.Diagrams.Core/Controls/ControlsLayer.cs b/src/Blazor.Diagrams.Core/Controls/ControlsLayer.cs index 39a331d76..f098f177a 100644 --- a/src/Blazor.Diagrams.Core/Controls/ControlsLayer.cs +++ b/src/Blazor.Diagrams.Core/Controls/ControlsLayer.cs @@ -1,61 +1,107 @@ -using System; -using System.Collections.Generic; using Blazor.Diagrams.Core.Models.Base; namespace Blazor.Diagrams.Core.Controls; public class ControlsLayer { - private readonly Dictionary _containers; + private readonly Dictionary<(Model Model, ControlsType Type), ControlsContainer> _containers = new(); public event Action? ChangeCaused; - public ControlsLayer() - { - _containers = new Dictionary(); - } - - public IReadOnlyCollection Models => _containers.Keys; + public IEnumerable Models => _containers.Keys.Select(key => key.Model); + public IEnumerable<(Model Model, ControlsType Type)> ContainersKeys => _containers.Keys; public ControlsContainer AddFor(Model model, ControlsType type = ControlsType.OnSelection) { - if (_containers.ContainsKey(model)) - return _containers[model]; - - var container = new ControlsContainer(model, type); + var key = (model, type); + if (_containers.TryGetValue(key, out ControlsContainer? container)) + return container; + + container = new(model, type); container.VisibilityChanged += OnVisibilityChanged; container.ControlsChanged += RefreshIfVisible; model.Changed += RefreshIfVisible; - _containers.Add(model, container); + + _containers.Add(key, container); + return container; } - public ControlsContainer? GetFor(Model model) + public ControlsContainer? GetFor(Model model, ControlsType type) { - return _containers.TryGetValue(model, out var container) ? container : null; + _containers.TryGetValue((model, type), out ControlsContainer? container); + + return container; } - public bool RemoveFor(Model model) + /// + /// Will return ALL registered containers for model. Null if no containers registered + /// + /// + /// + public IReadOnlyCollection? GetFor(Model model) + { + List? containers = new(); + foreach (ControlsType type in (ControlsType[])Enum.GetValues(typeof(ControlsType))) + { + if (_containers.TryGetValue((model, type), out ControlsContainer? container)) + containers.Add(container); + } + + if (containers.Count == 0) + return null; + + return containers.AsReadOnly(); + } + + public bool RemoveFor(Model model, ControlsType type) { - if (!_containers.TryGetValue(model, out var container)) + var key = (model, type); + if (!_containers.TryGetValue(key, out var container)) return false; - + container.VisibilityChanged -= OnVisibilityChanged; container.ControlsChanged -= RefreshIfVisible; model.Changed -= RefreshIfVisible; - _containers.Remove(model); + _containers.Remove(key); ChangeCaused?.Invoke(model); return true; } - public bool AreVisibleFor(Model model) => GetFor(model)?.Visible ?? false; + public bool RemoveFor(Model model) + { + bool removed = false; + foreach (ControlsType type in (ControlsType[])Enum.GetValues(typeof(ControlsType))) + { + var key = (model, type); + if (_containers.TryGetValue(key, out var container)) + { + container.VisibilityChanged -= OnVisibilityChanged; + container.ControlsChanged -= RefreshIfVisible; + model.Changed -= RefreshIfVisible; + _containers.Remove(key); + ChangeCaused?.Invoke(model); + removed = true; + } + + } + + return removed; + } + + public bool AreVisibleFor(Model model, ControlsType type) => GetFor(model, type)?.Visible ?? false; private void RefreshIfVisible(Model cause) { - if (!AreVisibleFor(cause)) - return; - - ChangeCaused?.Invoke(cause); + foreach (ControlsType type in (ControlsType[])Enum.GetValues(typeof(ControlsType))) + { + if (AreVisibleFor(cause, type)) + { + ChangeCaused?.Invoke(cause); + return; + } + } + } private void OnVisibilityChanged(Model cause) => ChangeCaused?.Invoke(cause); diff --git a/src/Blazor.Diagrams.Core/Controls/ControlsType.cs b/src/Blazor.Diagrams.Core/Controls/ControlsType.cs index 5997e72c9..641a79fdd 100644 --- a/src/Blazor.Diagrams.Core/Controls/ControlsType.cs +++ b/src/Blazor.Diagrams.Core/Controls/ControlsType.cs @@ -3,5 +3,6 @@ namespace Blazor.Diagrams.Core.Controls; public enum ControlsType { OnHover, - OnSelection + OnSelection, + AlwaysOn } \ No newline at end of file diff --git a/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs index b1e8983e4..db9c93b3f 100644 --- a/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs +++ b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs @@ -1,6 +1,5 @@ using Blazor.Diagrams.Core.Anchors; using Blazor.Diagrams.Core.Models.Base; -using System.Linq; namespace Blazor.Diagrams.Core.Layers; @@ -30,7 +29,7 @@ protected override void OnItemRemoved(BaseLinkModel link) link.TargetChanged -= OnLinkTargetChanged; Diagram.Controls.RemoveFor(link); - Remove(link.Links.ToList()); + Remove(link.Links); } private static void OnLinkSourceChanged(BaseLinkModel link, Anchor old, Anchor @new) diff --git a/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor b/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor index 7146c2316..ea336bc75 100644 --- a/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor +++ b/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor @@ -1,33 +1,33 @@ -@foreach (var model in BlazorDiagram.Controls.Models) +@foreach (var key in BlazorDiagram.Controls.ContainersKeys) { - var controls = BlazorDiagram.Controls.GetFor(model)!; - if (!controls.Visible || controls.Count == 0) - continue; + var controls = BlazorDiagram.Controls.GetFor(key.Model, key.Type)!; + if (!controls.Visible || controls.Count == 0) + continue; - if (Svg && model.IsSvg()) - { - - @foreach (var control in controls) - { - var position = control.GetPosition(model); - if (position == null) - continue; + if (Svg && key.Model.IsSvg()) + { + + @foreach (var control in controls) + { + var position = control.GetPosition(key.Model); + if (position == null) + continue; - @RenderControl(model, control, position, true) - } - - } - else if (!Svg && !model.IsSvg()) - { -
- @foreach (var control in controls) - { - var position = control.GetPosition(model); - if (position == null) - continue; + @RenderControl(key.Model, control, position, true) + } + + } + else if (!Svg && !key.Model.IsSvg()) + { +
+ @foreach (var control in controls) + { + var position = control.GetPosition(key.Model); + if (position == null) + continue; - @RenderControl(model, control, position, false) - } -
- } + @RenderControl(key.Model, control, position, false) + } +
+ } } \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor.cs b/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor.cs index e3e8f4a64..420617cd4 100644 --- a/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor.cs +++ b/src/Blazor.Diagrams/Components/Controls/ControlsLayerRenderer.razor.cs @@ -26,7 +26,7 @@ public void Dispose() protected override void OnInitialized() { BlazorDiagram.Controls.ChangeCaused += OnControlsChangeCaused; - } + } protected override bool ShouldRender() { @@ -47,38 +47,35 @@ private void OnControlsChangeCaused(Model cause) } private RenderFragment RenderControl(Model model, Control control, Point position, bool svg) - { - var componentType = BlazorDiagram.GetComponent(control.GetType()); - if (componentType == null) - throw new BlazorDiagramsException( - $"A component couldn't be found for the user action {control.GetType().Name}"); + { + var componentType = BlazorDiagram.GetComponent(control.GetType()) ?? throw new BlazorDiagramsException($"A component couldn't be found for the user action {control.GetType().Name}"); - return builder => - { - builder.OpenElement(0, svg ? "g" : "div"); - builder.AddAttribute(1, "class", - $"{(control is ExecutableControl ? "executable " : "")}diagram-control {control.GetType().Name}"); - if (svg) - builder.AddAttribute(2, "transform", - $"translate({position.X.ToInvariantString()} {position.Y.ToInvariantString()})"); - else - builder.AddAttribute(2, "style", - $"top: {position.Y.ToInvariantString()}px; left: {position.X.ToInvariantString()}px"); + return builder => + { + builder.OpenElement(0, svg ? "g" : "div"); + builder.AddAttribute(1, "class", $"{(control is ExecutableControl ? "executable " : "")}diagram-control {control.GetType().Name}"); + if (svg) + { + builder.AddAttribute(2, "transform", $"translate({position.X.ToInvariantString()} {position.Y.ToInvariantString()})"); + } + else + { + builder.AddAttribute(3, "style", $"top: {position.Y.ToInvariantString()}px; left: {position.X.ToInvariantString()}px"); + } - if (control is ExecutableControl ec) - { - builder.AddAttribute(3, "onpointerdown", - EventCallback.Factory.Create(this, e => OnPointerDown(e, model, ec))); - builder.AddEventStopPropagationAttribute(4, "onpointerdown", true); - } + if (control is ExecutableControl ec) + { + builder.AddAttribute(4, "onpointerdown", EventCallback.Factory.Create(this, e => OnPointerDown(e, model, ec))); + builder.AddEventStopPropagationAttribute(5, "onpointerdown", true); + } - builder.OpenComponent(5, componentType); - builder.AddAttribute(6, "Control", control); - builder.AddAttribute(7, "Model", model); - builder.CloseComponent(); - builder.CloseElement(); - }; - } + builder.OpenComponent(6, componentType); + builder.AddAttribute(7, "Control", control); + builder.AddAttribute(8, "Model", model); + builder.CloseComponent(); + builder.CloseElement(); + }; + } private async Task OnPointerDown(PointerEventArgs e, Model model, ExecutableControl control) {