-
Notifications
You must be signed in to change notification settings - Fork 418
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
feat(keyboard): allow to override keyboard handlers #289
Conversation
f87b4c9
to
3684633
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few thoughts; not meant as a full review.
In general, if the goal should be allow to override keyboard handlers this solution accomplishes way to much / is to big and complicated.
If it should be to overhaul the existing keyboard implementation there is still room for improvement, i.e. make it simpler to react to combinations (CTRL+Y
, CTRL+SHIFT+Z
).
*/ | ||
export default function DiagramKeyBindings(keyboard, editorActions) { | ||
|
||
var moveCanvasLeft = MoveCanvasFactory('left', keyboard._config, editorActions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we plug in move canvas keyboard shortcuts via the MoveCanvas feature?
This would potentially allow us to easier override it (i.e. to restore the old behavior).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is actually done using the MoveCanvas
feature, see: https://github.com/bpmn-io/diagram-js/pull/289/files#diff-ab467edcc671fb2f7e5cb7a91b06437d
The factory function was added to keep code DRY as the only difference between the functions responsible for the movement is the direction
property passed to MoveCanvas
feature. Alternatively, we could move the factory function to DiagramKeyBindings.js
as a helper at the end of the file.
However, this does not seem to make it more difficult to override the listeners, because the way to do that is always the same: add listeners for the same keys but with higher priority.
Please let me know what you think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also go for a MoveCanvas
feature instead of a factory function. This would allow the injection of config and editorActions and would also allow the configuration of the feature specifically which I think makes sense because the speed is not the speed of the keyboard or anything, it's the speed of moving the canvas.
new Diagram({
keyboard: {
bindTo: document
},
moveCanvas: {
speed: 9000
}
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe even better?
new Diagram({
keyboard: {
bindTo: document
},
moveCanvas: {
keyboardMoveSpeed: 9000
}
});
lib/features/keyboard/Keyboard.js
Outdated
this._config = config || {}; | ||
this._config = assign({ | ||
speed: DEFAULT_SPEED, | ||
invertY: false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, the move canvas specifics belong to a move canvas keybinding if you ask me.
As an alternative we could make the detection implicit, i.e. apply it once we detect MacOS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The speed
was already there, but it wasn't clearly expressed in the config declaraction. As the fallback, the implementation used a magic number. Otherwise, we could have a separate configuration for each key binding. I think it is a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a better place to move the config is in #376. I'll change it, but in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. Speed shouldn't be part of the generic keyboard configuration. Other features might implement something that they want to do at a certain speed. If the configured speed doesn't work for two independent features they can't make use of it anyway.
lib/features/keyboard/Keyboard.js
Outdated
|
||
// run global listeners only if no listeners for specific keys reacted | ||
if (!eventBusResult) { | ||
forEach(this._globalListeners, function(listener) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Global listeners are being called with the code while local listeners are being called with the key? I'd rethink this behavior as I don't think it is intuitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it's not intuitive. We use keyCode
for globalListeners
only in order to preserve the backward compatibility, i.e. to not break for example the BpmnKeyBindings.
Alternatively, we could drop the old keyboard related behaviour as KeyboardEvent.keyCode
is already a deprecated
feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please investigate how much of a breaking change that is. We are breaking with this release anyway and it could make sense to break this feature for the reason you mentioned, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no blog post regarding the way to add a keyboard listener (one mention of the feature); the examples don't address this issue either.
There are two forum topics regarding the keyboard issues: 1, 2
The change to key
only would break BpmnKeyBindings
as it uses keyCode
. However, the feature itself needs refactor as it is a one big listener for each of Bpmn-js-specific key bindings.
lib/features/keyboard/Keyboard.js
Outdated
|
||
/** | ||
* A keyboard abstraction that may be activated and | ||
* deactivated by users at will, consuming key events | ||
* and triggering diagram actions. | ||
* | ||
* For pressed keys, keyboard fires an event according to following scheme: | ||
* `keyboard.keydown.[event.key || event.keyCode]` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When will this be called with the keyCode? I.e. how to separate the 1
key from the 1
keyCode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keyCode
will be used only for old browsers which don't support KeyboardEvent.key
. However, it's supported since IE9 so probably we should just get rid of the fallback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the fallback.
var moveCanvasRight = MoveCanvasFactory('right', keyboard._config, editorActions); | ||
var moveCanvasDown = MoveCanvasFactory('down', keyboard._config, editorActions); | ||
|
||
keyboard.addListener(copyKeys, DEFAULT_PRIORITY, copy); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we support something even more powerful for our users here:
keyboard.addCombo([ `ctrl+A`, `ArrowUp` ], doSomething)
This would be a true simplification for the API.
Inspired by hotshot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, I will have a look at that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a good candidate for a new feature. Not sure though, if we should add it in this PR or maybe a new one. What about if we add an issue for this?
Thank you for the feedback @nikku. Indeed, the PR seems to add a lot of code to the library. However, the previous implementation seemed to rely too much on hardcoded order of listeners, that's why I decided to use |
3684633
to
8fc097a
Compare
Removed |
d5fc320
to
97ec05d
Compare
If you want to move fast I suggest you to only apply the relevant changes to get priorities to the old behavior. It should be trivial to add priorities to the old Keyboard#addListener implementation. Even implementing the whole listener logic via the eventBus should be an easy and straight forward thing to do. Next up we could take our time to think about whether a better keyboard API makes sense, introduce breaking changes and so forth. |
Imagine every key event went via the eventBus. In order to get the hotshot behavior a simple addition could be a utility that matches key strokes (or event sequences) using a pattern: // add a cut listener with high priority
keyboard.addListener(5000, combo('ctrl+x', function(event) {
// `combo` utility ensures that this method is only called if key stroke matches combo
}));
// add listener for sequence of events
keyboard.addListener(5000, sequence('a b', function(event) {
// `sequence` utility ensures that this method is only called if two keys in sequence matched <a> and <b>
}));
// or register via eventBus directly
// do we actually need a distinction between key{up|down}?
// or is it enough to support a simple key event?
eventBus.on('keyboard.key{up,down}', 5000, function(event) {
// called on any key; may be wrapped to filter only relevant invocations
}); |
I totally agree separating the keyboard from the default key events. This should be a separate commit though, as it is a clear non-breaking refactoring. |
Okay, but this way we always trigger all (or almost all) the event listeners and let them pass silently, if they are not interested in the pressed key, correct? |
Yes. Did you benchmark if this is an issue? I assume it is not. |
a86cd2d
to
c130732
Compare
I've rewritten this PR so that we use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Final thought: What is going to be the user case to replace / override the the keyboard binding?
If users should provide a single keybinding service we could call the DiagramKeyBindings something like KeyboardBindings
instead.
c130732
to
65cba50
Compare
Applied changes suggested by @nikku. Thanks for the feedback :) |
690b2ea
to
8b1dcda
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good for me, what about you @philippfromme?
BREAKING CHANGE: keyboard listeners receive only the `KeyboardEvent` Instead of (keyCode, event), the keyboard event listeners will receive object with `KeyboardEvent` event as only property. * It is now possible to set priority of an event handler by passing (priority, listener) to `Keyboard#addListener`. * The default listeners are adjusted to handle events the new way.
BREAKING CHANGE: lifecycle listeners will no longer receive field listeners * Listeners are no longer stored inside Keyboard. * Field `listeners` was removed from lifecycle events. Closes #226
`KeyboardEvent.keyCode` is deprecated. Default listeners will use `KeyboardEvent.key` instead.
9313f60
to
9c78bfb
Compare
Added missing dependency. |
1d256ba
to
d3aed57
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few changes regarding coding style. Looks 🍰 now.
This PR allows to add listeners which override default keyboard handlers.
There are three ways to register a listener now using keyboard.addListener:
Closes #226