Skip to content

Commit a76bf90

Browse files
authored
Merge pull request #249 from martinRenou/keyboard_events
Add keyboard events
2 parents 2df4ebe + 18ff62e commit a76bf90

File tree

4 files changed

+65
-5
lines changed

4 files changed

+65
-5
lines changed

docs/source/events.rst

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,24 @@ Those methods take a callback function as single argument, this callback functio
1010

1111
.. code:: Python
1212
13+
from ipywidgets import Output
14+
15+
out = Output()
16+
17+
@out.capture()
1318
def handle_mouse_move(x, y):
14-
# Do something
15-
pass
19+
print('Mouse move event:', x, y)
1620
1721
canvas.on_mouse_move(handle_mouse_move)
1822
23+
@out.capture()
1924
def handle_mouse_down(x, y):
20-
# Do something else
21-
pass
25+
print('Mouse down event:', x, y)
2226
2327
canvas.on_mouse_down(handle_mouse_down)
2428
29+
display(out)
30+
2531
Built-in touch events
2632
---------------------
2733

@@ -41,6 +47,23 @@ Those methods take a callback function as single argument, this callback functio
4147
.. note::
4248
Please open an issue or a Pull Request if you want more events to be supported by ipycanvas
4349

50+
Keyboard events
51+
---------------
52+
53+
.. code:: Python
54+
55+
from ipywidgets import Output
56+
57+
out = Output()
58+
59+
@out.capture()
60+
def on_keyboard_event(key, shift_key, ctrl_key, meta_key):
61+
print('Keyboard event:', key, shift_key, ctrl_key, meta_key)
62+
63+
canvas.on_key_down(on_keyboard_event)
64+
65+
display(out)
66+
4467
ipyevents
4568
---------
4669

ipycanvas/canvas.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ class Canvas(_CanvasBase):
531531
_touch_move_callbacks = Instance(CallbackDispatcher, ())
532532
_touch_cancel_callbacks = Instance(CallbackDispatcher, ())
533533

534+
_key_down_callbacks = Instance(CallbackDispatcher, ())
535+
534536
ATTRS = {
535537
"fill_style": 0,
536538
"stroke_style": 1,
@@ -1408,6 +1410,10 @@ def on_touch_cancel(self, callback, remove=False):
14081410
"""Register a callback that will be called on touch cancel."""
14091411
self._touch_cancel_callbacks.register_callback(callback, remove=remove)
14101412

1413+
def on_key_down(self, callback, remove=False):
1414+
"""Register a callback that will be called on keyboard event."""
1415+
self._key_down_callbacks.register_callback(callback, remove=remove)
1416+
14111417
def __setattr__(self, name, value):
14121418
super(Canvas, self).__setattr__(name, value)
14131419

@@ -1464,6 +1470,14 @@ def _handle_frontend_event(self, _, content, buffers):
14641470
[(touch["x"], touch["y"]) for touch in content["touches"]]
14651471
)
14661472

1473+
if content.get("event", "") == "key_down":
1474+
self._key_down_callbacks(
1475+
content["key"],
1476+
content["shift_key"],
1477+
content["ctrl_key"],
1478+
content["meta_key"],
1479+
)
1480+
14671481
def _draw_polygons_or_linesegments(
14681482
self,
14691483
cmd,
@@ -1636,6 +1650,10 @@ def on_touch_cancel(self, callback, remove=False):
16361650
"""Register a callback that will be called on touch cancel."""
16371651
self._canvases[-1].on_touch_cancel(callback, remove=remove)
16381652

1653+
def on_key_down(self, callback, remove=False):
1654+
"""Register a callback that will be called on keyboard event."""
1655+
self._canvases[-1].on_key_down(callback, remove=remove)
1656+
16391657
def clear(self):
16401658
"""Clear the Canvas."""
16411659
for layer in self._canvases:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"url": "https://github.com/martinRenou/ipycanvas"
3131
},
3232
"scripts": {
33-
"build": "yarn run build:lib && yarn run build:nbextension",
33+
"build": "yarn run build:lib && yarn run build:nbextension && yarn run build:labextension",
3434
"build:lib": "tsc",
3535
"build:nbextension": "webpack",
3636
"build:labextension": "jupyter labextension build .",

src/widget.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,9 @@ class CanvasView extends DOMWidgetView {
10361036
this.el.addEventListener('touchend', { handleEvent: this.onTouchEnd.bind(this) });
10371037
this.el.addEventListener('touchmove', { handleEvent: this.onTouchMove.bind(this) });
10381038
this.el.addEventListener('touchcancel', { handleEvent: this.onTouchCancel.bind(this) });
1039+
this.el.addEventListener('keydown', { handleEvent: this.onKeyDown.bind(this) });
1040+
1041+
this.el.setAttribute('tabindex', '0');
10391042

10401043
this.updateCanvas();
10411044
}
@@ -1059,6 +1062,9 @@ class CanvasView extends DOMWidgetView {
10591062
}
10601063

10611064
private onMouseDown(event: MouseEvent) {
1065+
// Bring focus to the canvas element, so keyboard events can be triggered
1066+
this.el.focus();
1067+
10621068
this.model.send({ event: 'mouse_down', ...this.getCoordinates(event) }, {});
10631069
}
10641070

@@ -1090,6 +1096,19 @@ class CanvasView extends DOMWidgetView {
10901096
this.model.send({ event: 'touch_cancel', touches: touches.map(this.getCoordinates.bind(this)) }, {});
10911097
}
10921098

1099+
private onKeyDown(event: KeyboardEvent) {
1100+
event.preventDefault();
1101+
event.stopPropagation();
1102+
1103+
this.model.send({
1104+
event: 'key_down',
1105+
key: event.key,
1106+
shift_key: event.shiftKey,
1107+
ctrl_key: event.ctrlKey,
1108+
meta_key: event.metaKey
1109+
}, {});
1110+
}
1111+
10931112
protected getCoordinates(event: MouseEvent | Touch) {
10941113
const rect = this.el.getBoundingClientRect();
10951114

0 commit comments

Comments
 (0)