Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Digest new menu API #110

Open
Ducasse opened this issue Jun 20, 2024 · 1 comment
Open

Digest new menu API #110

Ducasse opened this issue Jun 20, 2024 · 1 comment

Comments

@Ducasse
Copy link
Member

Ducasse commented Jun 20, 2024

First the motivation: After implementing keybindings and context menus in spec, I started to be increasingly unhappy with it since both are ways to define actions to be applied to a widget, but one is “visible” and the other is not, and it just implements a keybinding (keybinding that needs to be displayed in the context menu). So internally we had : a menu that was displaying keybindings but not reacting to them. And then at other side, the keybindings.
The repeated pattern that emerged is that you always were doing something like this :

mypresenter
      contextMenu: [ self commanderActionGroup asMenuPresenter ];
      contextKeybindings: self commanderActionGroup asKMCategory.

This was annoying because it looks (is) inefficient and the fact the implementation was split is prone to errors (you could declare keybindings in a menu that are not later in the real bindings and vice-versa).
So I unified both, by using commander as the glue, but creating an API so people is not forced to create commander classes for each menu.

The new vocabulary is simple:

Presenters now understand actionGroup: message that recives a SpCommandGroup from commander. Then the rest is resolved by commander: title, description, tooltip, icon and shortcut.
To make the mechanism easy to use and not require the creation of a class for each action, there are several utilities :

Instantiable generic groups and actions

There is a SpActionGroup class and a SpAction class that can be used directly in the presenter, e.g.

  myPresenter
      actionGroup: (SpActionGroup new
          add: (SpAction new  
              name: 'Load'; 
              description: 'Load a STON product description.';
              iconName: #smallOpen; 
              action: [ self doLoad ];
              yourself);
          add: (SpAction new 
              name: 'Reset'; 
              description: 'Reset current product (cleans the destination).';
              iconName: #smallDelete; 
              shortcutKey: $x ctrl;
              action: [ self doReset ];
              actionEnabled: [ self isResetEnabled ];
              yourself);
          add: (SpAction new 
              name: 'Open'; 
              description: 'Open product directory (if exists).';
              shortcutKey: $o ctrl;
              beShortcutOnly;
              action: [ self doOpen ];
              yourself);
          yourself)

There are a couple things to notice here:

beShortcutOnly defines if the action will be displayed in the context menu or not (there are few reasons why you would do that, but you can 😉)
there is vocabulary to execute a block action:, but also to ask if that action should be enabled or not actionEnabled:.
If you think this is too verbose, there are several convenience methods (class and instance) to make this easier.

Scripting mode

I also added a family of methods to make this more scriptable, so you can write the same example as before in this way:

myPresenter
      actionGroupWith: [ :group | group
          addActionWith: [ :act | act 
              name: 'Load'; 
              description: 'Load a STON product description.';
              iconName: #smallOpen; 
              action: [ self doLoad ] ];
          addActionWith: [ :act | act 
              name: 'Reset'; 
              description: 'Reset current product (cleans the destination).';
              iconName: #smallDelete;
              shortcutKey: $x ctrl;
              action: [ self doReset ];
              actionEnabled: [ self isResetEnabled ] ];
          addShortcutWith: [ :act | act 
              shortcutKey: $o ctrl;
              action: [ self doOpen ] ] ]

Commander integration

Since this is just commander with a spice, you can also do:

myPresenter
      actionGroup: self myCommanderGroup

Drawbacks

Of course, this is not absent of drawbacks,

the most important of this is that right now, there is no way the context menu can be calculated on the fly while displaying it.
This is implementable, but I do not think is desirable: Every Human Interfase Guideline in earth says context needs to be always visible, with the not-valid options disabled. This helps people to know what they can do, even if they cannot do it that moment 🙂
This creates a dependency with Commander. I do not think this is a problem, Commander is important in the UI environment. Also, the fact that we have SpActionGroup and SpAction as entry points means we can cut-off Commander when we want.

Status

This is not yet operational in Spec-Morphic (not hard to implement, just not done yet), but completely functional in Spec-Gtk4

@koendehondt
Copy link
Collaborator

We decided not to include this because it is not ready in Pharo 12.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants