-
Notifications
You must be signed in to change notification settings - Fork 14
Design note Keyboard and mouse events
During normal music editing, the controller handles keyboard events and dispatches them to the tracker or the editor. Tracker handles navigation keys and the editor handles music keys. It also handles the '/' key that brings up menus.
It also handles mouse events that occur inside the music svg. This is used to select musical elements. Ribbon buttons and keyboard commands often work on selections.
When menus, modal dialogs and the text editor widgets are active, we want all the keyboard and mouse events to go to them. In order to hand control of the mouse and keyboard off, we use promises. Menus sometimes create dialogs, in this case there are 2 promises. One is resolved when the menu is dismissed. The dialog pends on this, and then takes control via another promise. (Usually we don't need to unbind pointer events since there is an overlay that prevents the music svg from receiving those).
When a keyboard sequence bring up the 'slash' menu, the controller creates the menu manager. The menu manager takes over the keyboard and creates a menu based on the next command. It passes the menu promise into the menu when it is created, and the menu resolves the promise when it is dismissed. (DI stands for dependency injection - the controller has a notifier interface that is passed to the menus or dialogs).
- controller.evKey('/') => suiMenuManager.slashMenuMode (this) => (DI)controller.unbindKeyboardForModal(closeMenuPromise)
- suiMenuManager => eventSource.bindKeydownHandler(this)... menu stuff happens
- menu.complete => eventSource.unbindKeyboardHandler(this) => closeMenuPromise.resolve() => ...controller.then(bindEvents)
When a dialog is created from a menu, e.g. the file dialogs, the closeMenu promise is passed from the menu option to the dialog. The dialog also pends on closeMenuPromise. Controller rebinds keyboard events:
- suiMenuManager.slashMenuMode => (DI)controller.unbindKeyboardForModal(closeMenuPromise)
- ... menu selects the load option
- SuiLoadFileDialog.complete() => resolve closeMenuPromise
- resolve part 1:controller.then => controller.bindEvents
- resolve part 2: SuiLoadFileDialog.then => (DI)controller.unbindKeyboardForDialog(this)
- Dialog does it's thing
- dialog.complete => resolve => controller.then(bindEvents)
Dialogs can be created by the ribbon, keyboard events, or from selectable music elements (such is dialogs for dynamics and lines).
During text edit mode, the text edit widget grabs focus and keyboard for the text entry. This requires the text dialogs to have 2 different event 'modes': One when the text widget is active, and all keyboard events are handled by the text widget. When we are not editing the text directly, the dialog box handles the usual font settings, etc.
This example uses he lyric dialog but the text creation widget is similar.
- dialog.display => (DI)controller.unbindKeyboardForModal.
- dialog.lyricEditComponentControl.startEditSession => returns promise
- lyricEditComponent => eventSource.bindKeydownHandler(this) ... text is edited
- evButton(edit complete button) => lyricEditCompnonentControl.resolve()
- dialog.startEditSession.then()=> eventSource.bindKeydownHandler(this) ... dialog controls
- when dialog is dismissed it resolves the promise for the controller and the controller takes over.
That is a bit of an oversimplification, because there are several objects involved with the text editor. But that is the general event flow.
How is the world treating you?
If you want to use Smoosic to create music right-away.
If you want to take Smoosic home with you and make it your own. (needs update)