Skip to content

Commit c402f78

Browse files
authored
Merge pull request #24325 from meeseeksmachine/auto-backport-of-pr-24095-on-v3.6.x
Backport PR #24095 on branch v3.6.x (nb/webagg: Move mouse events to outer canvas div)
2 parents 2347d60 + 3568a9d commit c402f78

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

+54-51
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)