Blazor-Diagrams / Blazor.Diagrams

A fully customizable and extensible all-purpose diagrams library for Blazor
https://blazor-diagrams.zhaytam.com
MIT License
921 stars 177 forks source link

Feature: Add `CanAttachFrom` Behavior #188

Closed wdcossey closed 1 year ago

wdcossey commented 2 years ago

While CanAttachTo is really helpful, sometimes it's beneficial to have the inverse, i.e. CanAttachFrom when you want to permit/deny attachment from a port.

It's the same logic (as CanAttachTo) but swapping the ports.

//PortModel.cs
public virtual bool CanAttachFrom(PortModel port)
    => port != this && !port.Locked && Parent != port.Parent;
//DragNewLinkBehavior.cs
if (!(model is PortModel port) || !port.CanAttachFrom(_ongoingLink.SourcePort!) || !_ongoingLink.SourcePort!.CanAttachTo(port))
{
    Diagram.Links.Remove(_ongoingLink);
    _ongoingLink = null;
    return;
}
zHaytam commented 2 years ago

Hello, thanks for the suggestion! It's indeed easy to implement as you showed. May I know what kind of use cases you'd like to use it in?

wdcossey commented 2 years ago

@zHaytam

I want to have multiple (and different) Ports per Node, essentially the ports can only attach to ports of the same type.

Over simplified example:

  1. Blue can only attach to blue.
  2. Red can only attach to red.
  3. Green can only attach to green. image

The CanAttachTo behaviour works but only in certain scenarios, it can be circumvented when connecting the ports in the reverse (backwards) order. i.e Using the example above a user can connect red to blue with the current CanAttachTo behaviour.

The code for CanAttachFrom is exactly the same as CanAttachTo, the only difference is the ports are swapped, thus permitting/denying the connection in the reverse order.

wdcossey commented 2 years ago

I have the code I posted above working as expected and will happily open a PR if you are happy.

Although the code is 99.9% the same it's better to have the logic separate and give the user the power to override the needed method(s).

The completed example of the code above can be applied to the ColoredPort sample, from samples\SharedDemo\Demos\CustomPort\ColoredPort.cs:

    public class ColoredPort : PortModel
    {
        public ColoredPort(NodeModel parent, PortAlignment alignment, bool isRed) : base(parent, alignment, null, null)
        {
            IsRed = isRed;
        }

        public bool IsRed { get; set; }

        public override bool CanAttachTo(PortModel port)
        {
            // Checks for same-node/port attachments
            if (!base.CanAttachTo(port))
                return false;

            // Only able to attach to the same port type
            if (!(port is ColoredPort cp))
                return false;

            return IsRed == cp.IsRed;
        }

        public override bool CanAttachFrom(PortModel port)
        {
            // Checks for same-node/port attachments
            if (!base.CanAttachFrom(port))
                return false;

            // Only able to attach to the same port type
            if (!(port is ColoredPort cp))
                return false;

            return IsRed == cp.IsRed;
        }
    }
zHaytam commented 2 years ago

Very sorry for the late reply, I thought I did a long time ago...

In your example (picture), how can the user connect red to blue? In CanAttachTo, you have both ports, so you should be able to test on both of them no?

public override bool CanAttachTo(PortModel port)
{
            // Checks for same-node/port attachments
            if (!base.CanAttachTo(port))
                return false;

            // Only able to attach to the same port type
            if (!(port is ColoredPort cp))
                return false;

            return IsRed == cp.IsRed || IsBlue == cp.IsBlue || IsGreen == cp.IsGreen;
}

This would work for all cases no? Do you have a running example that shows the issue?

zHaytam commented 1 year ago

In 3.0.0, another option was added for the links factory to return null in order to not create an ongoing link. This can also be used to do any checks from the source port or node and decide whether to create a link or not.