From 58fb0d9fdb84e8de8920ce118d088cb507173c94 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Fri, 30 Oct 2020 23:46:28 +0100 Subject: [PATCH] Add the touch event handlers to the webagg and nbagg backends. --- lib/matplotlib/backend_bases.py | 15 +++++++ .../backends/backend_webagg_core.py | 12 ++++++ .../backends/web_backend/all_figures.html | 12 +++--- lib/matplotlib/backends/web_backend/js/mpl.js | 41 ++++++++++++++++++- .../backends/web_backend/single_figure.html | 6 ++- 5 files changed, 78 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 7f2ab50a56b2..1d98ac1779dd 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1976,6 +1976,21 @@ def enter_notify_event(self, guiEvent=None, xy=None): event = LocationEvent('figure_enter_event', self, x, y, guiEvent) self.callbacks.process('figure_enter_event', event) + def touch_start_event(self, touches, guiEvent=None): + # Placeholder implementation awaiting + # https://github.com/matplotlib/matplotlib/pull/8041. + pass + + def touch_move_event(self, touches, guiEvent=None): + # Placeholder implementation awaiting + # https://github.com/matplotlib/matplotlib/pull/8041. + pass + + def touch_end_event(self, touches, guiEvent=None): + # Placeholder implementation awaiting + # https://github.com/matplotlib/matplotlib/pull/8041. + pass + def inaxes(self, xy): """ Return the topmost visible `~.axes.Axes` containing the point *xy*. diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index d8016e537563..163cde1978de 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -291,6 +291,18 @@ def _handle_key(self, event): self.key_release_event(key, guiEvent=guiEvent) handle_key_press = handle_key_release = _handle_key + def _handle_touch(self, event): + e_type = event['type'] + guiEvent = event.get('guiEvent', None) + # TODO: Use event['touches'] to build up an appropriate backend event record. + if e_type == 'touch_start': + self.touch_start_event([], guiEvent=guiEvent) + elif e_type == 'touch_move': + self.touch_move_event([], guiEvent=guiEvent) + elif e_type == 'touch_end': + self.touch_end_event([], guiEvent=guiEvent) + handle_touch_start = handle_touch_move = handle_touch_end = _handle_touch + def handle_toolbar_button(self, event): # TODO: Be more suspicious of the input getattr(self.toolbar, event['name'])() diff --git a/lib/matplotlib/backends/web_backend/all_figures.html b/lib/matplotlib/backends/web_backend/all_figures.html index c29ee55f4efe..cb96cb25a578 100644 --- a/lib/matplotlib/backends/web_backend/all_figures.html +++ b/lib/matplotlib/backends/web_backend/all_figures.html @@ -24,11 +24,13 @@ main_div.appendChild(figure_div); var websocket_type = mpl.get_websocket_type(); var websocket = new websocket_type("{{ ws_uri }}" + fig_id + "/ws"); - var fig = new mpl.figure(fig_id, websocket, mpl_ondownload, figure_div); - - fig.focus_on_mouseover = true; - - fig.canvas.setAttribute("tabindex", fig_id); + var fig; + websocket.onopen = function () + { + fig = new mpl.figure(fig_id, websocket, mpl_ondownload, figure_div); + fig.focus_on_mouseover = true; + fig.canvas.setAttribute("tabindex", fig_id); + } } }; diff --git a/lib/matplotlib/backends/web_backend/js/mpl.js b/lib/matplotlib/backends/web_backend/js/mpl.js index 59cdadbea268..8ffbccb1245a 100644 --- a/lib/matplotlib/backends/web_backend/js/mpl.js +++ b/lib/matplotlib/backends/web_backend/js/mpl.js @@ -230,6 +230,12 @@ mpl.figure.prototype._init_canvas = function () { }; } + function on_touch_event_closure(name) { + return function (event) { + return fig.touch_event(event, name); + }; + } + rubberband_canvas.addEventListener( 'mousedown', on_mouse_event_closure('button_press') @@ -242,12 +248,10 @@ mpl.figure.prototype._init_canvas = function () { 'dblclick', on_mouse_event_closure('dblclick') ); - // Throttle sequential mouse events to 1 every 20ms. rubberband_canvas.addEventListener( 'mousemove', on_mouse_event_closure('motion_notify') ); - rubberband_canvas.addEventListener( 'mouseenter', on_mouse_event_closure('figure_enter') @@ -256,6 +260,18 @@ mpl.figure.prototype._init_canvas = function () { 'mouseleave', on_mouse_event_closure('figure_leave') ); + rubberband_canvas.addEventListener( + 'touchmove', + on_touch_event_closure('touch_move') + ); + rubberband_canvas.addEventListener( + 'touchenter', + on_touch_event_closure('touch_start') + ); + rubberband_canvas.addEventListener( + 'touchleave', + on_touch_event_closure('touch_end') + ); canvas_div.addEventListener('wheel', function (event) { if (event.deltaY < 0) { @@ -628,6 +644,27 @@ mpl.figure.prototype.mouse_event = function (event, name) { return false; }; +mpl.figure.prototype.touch_event = function (event, name) { + var touches = []; + for (var i = 0; i < event.changedTouches.length; i++) { + var canvas_pos = mpl.findpos(event.changedTouches[i]); + var x = canvas_pos.x * this.ratio; + var y = canvas_pos.y * this.ratio; + touches.push({x: x, y: y}); + } + + this.send_message(name, { + touches: touches + }); + + // TODO: preventDefault will stop the event having any further impact + // in the user's browser. This effectively will stop things like + // pinch-to-zoom etc. Sometimes this is desirable, but certainly + // not always. For now, err on the side of caution. + // event.preventDefault(); + return false; +}; + mpl.figure.prototype._key_event_extra = function (_event, _name) { // Handle any extra behaviour associated with a key event }; diff --git a/lib/matplotlib/backends/web_backend/single_figure.html b/lib/matplotlib/backends/web_backend/single_figure.html index 71fe451f6f14..6a5fe7c3f28a 100644 --- a/lib/matplotlib/backends/web_backend/single_figure.html +++ b/lib/matplotlib/backends/web_backend/single_figure.html @@ -20,9 +20,13 @@ var websocket_type = mpl.get_websocket_type(); var websocket = new websocket_type( "{{ ws_uri }}" + {{ str(fig_id) }} + "/ws"); - var fig = new mpl.figure( + var fig; + websocket.onopen = function () + { + fig = new mpl.figure( {{ str(fig_id) }}, websocket, mpl_ondownload, document.getElementById("figure")); + } } );