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

User Actions Feature #213

Closed zHaytam closed 1 year ago

zHaytam commented 1 year ago

Give the possibility for:

Examples of must-have actions:

Example in draw.io: image

TimDaborn commented 1 year ago

May I recommend that you adopt the command pattern (GoF). Command pattern - Wikipedia

Take for example the current DeleteSelectionBehavior

The behavior listens to the key down event for the associated key press, and the code to the action the delete is in the behavior’s keydown event

Now let’s say I want to add a menu option in my client UI to delete selected nodes. There is no way I can reuse the behavior’s Diagram_KeyDown event. It is private and takes a KeyboardEventArgs.

So, by separating the invokers (in this case the behavior) and creating another class for the command (the action of deletion), you would have something more flexible.

The DeleteSelectionBehavior accepts a command class in the constructor and if matches the delete key, calls the command’s execute method.

So, in my UI, I can now also call the delete command's execute method. In some future date, if you change the command to delete something else that derives from SelectableModel – I don’t need to make any changes in my code.

Now you could go a step further. The changed DeleteSelectionBehavior now just connects the keystroke and the command. It can renamed as keystrokeBehavior that accepts a keystroke and ideally an interface for a command, or perhaps, it refences a dictionary of keys/command interfaces for all the keystrokes. Keystrokes no longer need to be set in various options – it is part of the creating and registering the behaviors.

Now as a user, I can write my own commands and bind it to a keystroke using the supplied keystrokeBehavior class.

(As an aside, I would also let users supply the code to register behaviors – rather than have a set registered and the user unregister the ones they don’t like and register new ones. Less messy.)

Now, for example, a user could create their own DeleteCommand, derived from yours, and display a “do you want to delete” dialog, and still reference your underlying delete command.

A command interface, let’s call it ICommand, can also have other properties such as enable and an enabled changed action.

The Maui ICommand is defined thus:

public interface ICommand { public void Execute (Object parameter); public bool CanExecute (Object parameter); public event EventHandler CanExecuteChanged; }

See https://docs.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/commanding#icommands

(Just to clear, I am not suggesting you reference System.Windows.Input – just have a similar interface.)

This will be useful for the selection behaviour– when models are selected/deselected, a list of ICommands have their CanExecuteChanged events raised and the UI reacts accordingly. Thus if no models are selected, all the UI elements that require selected models are disabled.

zHaytam commented 1 year ago

Hello! I appreciate your opinion, thank you for taking the time to write that.

The command pattern does feel a little bit weird in the frontend, especially for UI actions that can be as complex as API calls and anything the library users want to do really. I don't want to use that pattern for this specific feature because it's not really needed, all actions are button clicks so a delegate or something will work just fine.

What I do like, however, is your generic shortcut behavior with the commands pattern. Something like this would be great to handle all shortcut use cases. This can easily let users assign commands to shortcuts.

zHaytam commented 1 year ago

The more I think about it, the more it does make sense to have commands as actions...