8000 nb/webagg: Move mouse events to outer canvas div · matplotlib/matplotlib@5950bad · GitHub
[go: up one dir, main page]

Skip to content

Commit 5950bad

Browse files
committed
nb/webagg: Move mouse events to outer canvas div
This fixes the resize grip on WebKit, which is a bit buggy [1, 2]. We additionally need to ignore pointer events on both canvases, even though their `z-index` puts the `div` on top. For mouse events, we no longer prevent the default handler in most browsers, because that will block the resize grip from working now that it is handling events from the same `div`. Due to the same bugs in WebKit, we _do_ still need to prevent the default handler there, or else any mouse drags (i.e., for pan and zoom), will cause the browser to try and select the canvas. The mouse position calculation is somewhat simplified as well. To ensure keyboard events make it to the canvas `div`, it now has a `tabindex`. [1] https://bugs.webkit.org/show_bug.cgi?id=144526 [2] https://bugs.webkit.org/show_bug.cgi?id=181818
1 parent 6ed3ab1 commit 5950bad

File tree

1 file changed

+54
-51
lines changed
  • lib/matplotlib/backends/web_backend/js

1 file changed

+54
-51
lines changed

lib/matplotlib/backends/web_backend/js/mpl.js

Lines changed: 54 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ mpl.figure.prototype._init_canvas = function () {
112112
var fig = this;
113113

114114
var canvas_div = (this.canvas_div = document.createElement('div'));
115+
canvas_div.setAttribute('tabindex', '0');
115116
canvas_div.setAttribute(
116117
'style',
117118
'border: 1px solid #ddd;' +
@@ -122,7 +123,8 @@ mpl.figure.prototype._init_canvas = function () {
122123
'outline: 0;' +
123124
'overflow: hidden;' +
124125
'position: relative;' +
125-
'resize: both;'
126+
'resize: both;' +
127+
'z-index: 2;'
126128
);
127129

128130
function on_keyboard_event_closure(name) {
@@ -145,7 +147,13 @@ mpl.figure.prototype._init_canvas = function () {
145147

146148
var canvas = (this.canvas = document.createElement('canvas'));
147149
canvas.classList.add('mpl-canvas');
148-
canvas.setAttribute('style', 'box-sizing: content-box;');
150+
canvas.setAttribute(
151+
'style',
152+
'box-sizing: content-box;' +
153+
'pointer-events: none;' +
154+
'position: relative;' +
155+
'z-index: 0;'
156+
);
149157

150158
this.context = canvas.getContext('2d');
151159

@@ -165,7 +173,12 @@ mpl.figure.prototype._init_canvas = function () {
165173
));
166174
rubberband_canvas.setAttribute(
167175
'style',
168-
'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'
176+
'box-sizing: content-box;' +
177+
'left: 0;' +
178+
'pointer-events: none;' +
179+
'position: absolute;' +
180+
'top: 0;' +
181+
'z-index: 1;'
169182
);
170183

171184
// Apply a ponyfill if ResizeObserver is not implemented by browser.
@@ -215,10 +228,10 @@ mpl.figure.prototype._init_canvas = function () {
215228
canvas.setAttribute('width', width * fig.ratio);
216229
canvas.setAttribute('height', height * fig.ratio);
217230
}
218-
canvas.setAttribute(
219-
'style',
220-
'width: ' + width + 'px; height: ' + height + 'px;'
221-
);
231+
/* This rescales the canvas back to display pixels, so that it
232+
* appears correct on HiDPI screens. */
233+
canvas.style.width = width + 'px';
234+
canvas.style.height = height + 'px';
222235

223236
rubberband_canvas.setAttribute('width', width);
224237
rubberband_canvas.setAttribute('height', height);
@@ -234,34 +247,53 @@ mpl.figure.prototype._init_canvas = function () {
234247
this.resizeObserverInstance.observe(canvas_div);
235248

236249
function on_mouse_event_closure(name) {
237-
return function (event) {
238-
return fig.mouse_event(event, name);
239-
};
250+
/* User Agent sniffing is bad, but WebKit is busted:
251+
* https://bugs.webkit.org/show_bug.cgi?id=144526
252+
* https://bugs.webkit.org/show_bug.cgi?id=181818
253+
* The worst that happens here is that they get an extra browser
254+
* selection when dragging, if this check fails to catch them.
255+
*/
256+
var UA = navigator.userAgent;
257+
var isWebKit = /AppleWebKit/.test(UA) && !/Chrome/.test(UA);
258+
if(isWebKit) {
259+
return function (event) {
260+
/* This prevents the web browser from automatically changing to
261+
* the text insertion cursor when the button is pressed. We
262+
* want to control all of the cursor setting manually through
263+
* the 'cursor' event from matplotlib */
264+
event.preventDefault()
265+
return fig.mouse_event(event, name);
266+
};
267+
} else {
268+
return function (event) {
269+
return fig.mouse_event(event, name);
270+
};
271+
}
240272
}
241273

242-
rubberband_canvas.addEventListener(
274+
canvas_div.addEventListener(
243275
'mousedown',
244276
on_mouse_event_closure('button_press')
245277
);
246-
rubberband_canvas.addEventListener(
278+
canvas_div.addEventListener(
247279
'mouseup',
248280
on_mouse_event_closure('button_release')
249281
);
250-
rubberband_canvas.addEventListener(
282+
canvas_div.addEventListener(
251283
'dblclick',
252284
on_mouse_event_closure('dblclick')
253285
);
254286
// Throttle sequential mouse events to 1 every 20ms.
255-
rubberband_canvas.addEventListener(
287+
canvas_div.addEventListener(
256288
'mousemove',
257289
on_mouse_event_closure('motion_notify')
258290
);
259291

260-
rubberband_canvas.addEventListener(
292+
canvas_div.addEventListener(
261293
'mouseenter',
262294
on_mouse_event_closure('figure_enter')
263295
);
264-
rubberband_canvas.addEventListener(
296+
canvas_div.addEventListener(
265297
'mouseleave',
266298
on_mouse_event_closure('figure_leave')
267299
);
@@ -289,7 +321,7 @@ mpl.figure.prototype._init_canvas = function () {
289321
};
290322

291323
// Disable right mouse context menu.
292-
this.rubberband_canvas.addEventListener('contextmenu', function (_e) {
324+
canvas_div.addEventListener('contextmenu', function (_e) {
293325
event.preventDefault();
294326
return false;
295327
});
@@ -444,7 +476,7 @@ mpl.figure.prototype.handle_figure_label = function (fig, msg) {
444476
};
445477

446478
mpl.figure.prototype.handle_cursor = function (fig, msg) {
447-
fig.rubberband_canvas.style.cursor = msg['cursor'];
479+
fig.canvas_div.style.cursor = msg['cursor'];
448480
};
449481

450482
mpl.figure.prototype.handle_message = function (fig, msg) {
@@ -556,30 +588,6 @@ mpl.figure.prototype._make_on_message_function = function (fig) {
556588
};
557589
};
558590

559-
// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas
560-
mpl.findpos = function (e) {
561-
//this section is from http://www.quirksmode.org/js/events_properties.html
562-
var targ;
563-
if (!e) {
564-
e = window.event;
565-
}
566-
if (e.target) {
567-
targ = e.target;
568-
} else if (e.srcElement) {
569-
targ = e.srcElement;
570-
}
571-
if (targ.nodeType === 3) {
572-
// defeat Safari bug
573-
targ = targ.parentNode;
574-
}
575-
576-
// pageX,Y are the mouse positions relative to the document
577-
var boundingRect = targ.getBoundingClientRect();
578-
var x = e.pageX - (boundingRect.left + document.body.scrollLeft);
579-
var y = e.pageY - (boundingRect.top + document.body.scrollTop);
580-
581-
return { x: x, y: y };
582-
};
583591

584592
/*
585593
* return a copy of an object with only non-object keys
@@ -596,15 +604,15 @@ function simpleKeys(original) {
596604
}
597605

598606
mpl.figure.prototype.mouse_event = function (event, name) {
599-
var canvas_pos = mpl.findpos(event);
600-
601607
if (name === 'button_press') {
602608
this.canvas.focus();
603609
this.canvas_div.focus();
604610
}
605611

606-
var x = canvas_pos.x * this.ratio;
607-
var y = canvas_pos.y * this.ratio;
612+
// from https://stackoverflow.com/q/1114465
613+
var boundingRect = this.canvas.getBoundingClientRect();
614+
var x = (event.clientX - boundingRect.left) * this.ratio;
615+
var y = (event.clientY - boundingRect.top) * this.ratio;
608616

609617
this.send_message(name, {
610618
x: x,
@@ -614,11 +622,6 @@ mpl.figure.prototype.mouse_event = function (event, name) {
614622
guiEvent: simpleKeys(event),
615623
});
616624

617-
/* This prevents the web browser from automatically changing to
618-
* the text insertion cursor when the button is pressed. We want
619-
* to control all of the cursor setting manually through the
620-
* 'cursor' event from matplotlib */
621-
event.preventDefault();
622625
return false;
623626
};
624627

0 commit comments

Comments
 (0)
0