From aac46a72b848dd0bcd1173d6654d546de23270af Mon Sep 17 00:00:00 2001 From: Kyle Boone Date: Thu, 24 Oct 2019 18:32:11 -0700 Subject: [PATCH 1/3] Change to "notebook"-like behavior --- js/src/mpl_widget.css | 7 ++++--- js/src/mpl_widget.js | 14 +++----------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/js/src/mpl_widget.css b/js/src/mpl_widget.css index 8442bd5e..90c0eda1 100644 --- a/js/src/mpl_widget.css +++ b/js/src/mpl_widget.css @@ -1,6 +1,7 @@ .jupyter-matplotlib { - height: 500px; flex: 1 1 auto; + width: auto; + height: auto; } /* Toolbar */ @@ -13,8 +14,8 @@ /* Figure */ .jupyter-matplotlib-figure { - width: 100%; - height: 100%; + width: auto; + height: auto; } .jupyter-matplotlib-canvas_div { diff --git a/js/src/mpl_widget.js b/js/src/mpl_widget.js index bf8d2777..8e780fe1 100644 --- a/js/src/mpl_widget.js +++ b/js/src/mpl_widget.js @@ -231,6 +231,9 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ this.rubberband_canvas.setAttribute('width', width); this.rubberband_canvas.setAttribute('height', height); + + this.canvas_div.style.width = width + 'px'; + this.canvas_div.style.height = height + 'px'; }, send_message: function(type, message = {}) { @@ -363,17 +366,6 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ } }, - processPhosphorMessage: function(msg) { - MPLCanvasView.__super__.processPhosphorMessage.apply(this, arguments); - - switch (msg.type) { - case 'resize': - // case 'after-show': - this.request_resize(); - break; - } - }, - mouse_event: function(name) { var that = this; var last_update = 0; From 18157b0e75e484fc0bf673d7c2fd08c1e885e7e8 Mon Sep 17 00:00:00 2001 From: Kyle Boone Date: Tue, 29 Oct 2019 17:30:22 -0700 Subject: [PATCH 2/3] Sync the widget layout interface with matplotlib --- js/src/mpl_widget.css | 6 ++++ js/src/mpl_widget.js | 79 +++++++++++++++++++++++++++++++++++++++---- js/src/utils.js | 35 ++++++++++++++++++- 3 files changed, 113 insertions(+), 7 deletions(-) diff --git a/js/src/mpl_widget.css b/js/src/mpl_widget.css index 90c0eda1..bc5d0b52 100644 --- a/js/src/mpl_widget.css +++ b/js/src/mpl_widget.css @@ -6,8 +6,13 @@ /* Toolbar */ +.jupyter-matplotlib-toolbar { + flex: 0 0 auto; +} + .jupyter-matplotlib-button { width: calc(var(--jp-widgets-inline-width-tiny) / 2 - 2px); + overflow: hidden; padding: 0; } @@ -16,6 +21,7 @@ .jupyter-matplotlib-figure { width: auto; height: auto; + overflow: hidden; } .jupyter-matplotlib-canvas_div { diff --git a/js/src/mpl_widget.js b/js/src/mpl_widget.js index 8e780fe1..32b81822 100644 --- a/js/src/mpl_widget.js +++ b/js/src/mpl_widget.js @@ -56,8 +56,6 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ that.model_events(); - window.addEventListener('resize', that.request_resize.bind(that)); - that.send_initialization_message(); }); }, @@ -112,6 +110,8 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ this.el.appendChild(this.toolbar_view.el); } } + + this.request_resize(); }, clear: function() { @@ -123,6 +123,8 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ _init_header: function() { this.header = document.createElement('div'); this.header.style.textAlign = 'center'; + this.header.style.flexGrow = 0; + this.header.style.flexShrink = 0; this.header.classList = 'jupyter-widgets widget-label'; this.figure.appendChild(this.header); }, @@ -212,15 +214,57 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ _init_footer: function() { this.footer = document.createElement('div'); this.footer.style.textAlign = 'center'; + this.header.style.flexGrow = 0; + this.header.style.flexShrink = 0; this.footer.classList = 'jupyter-widgets widget-label'; this.figure.appendChild(this.footer); }, + _calculate_decorations_size: function() { + // Calculate the size of the decorations on the figure. + var decorations_width = 0; + var decorations_height = 0; + + // Toolbar size + var toolbar_position = this.model.get('toolbar_position'); + if (toolbar_position == 'top' || toolbar_position == 'bottom') { + decorations_height += utils.get_full_size(this.toolbar_view.el).height; + } else { + decorations_width += utils.get_full_size(this.toolbar_view.el).width; + } + + // Label sizes + decorations_height += utils.get_full_size(this.header).height; + decorations_height += utils.get_full_size(this.footer).height; + + // Margins on the canvas + var canvas_div_margins = utils.get_margin_size(this.canvas_div); + decorations_width += canvas_div_margins.width; + decorations_height += canvas_div_margins.height; + + // Margins on the figure div + var figure_margins = utils.get_margin_size(this.figure); + decorations_width += figure_margins.width; + decorations_height += figure_margins.height; + + return { + width: decorations_width, + height: decorations_height + }; + }, + request_resize: function() { - // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client, - // which will in turn request a refresh of the image. - var rect = this.canvas_div.getBoundingClientRect(); - this.send_message('resize', {'width': rect.width, 'height': rect.height}); + // Using the given widget size, figure out how big the canvas should be. + var decorations_size = this._calculate_decorations_size(); + + var new_canvas_width = this.el.clientWidth - decorations_size.width; + var new_canvas_height = this.el.clientHeight - decorations_size.height; + + // Ensure that the canvas size is a positive number. + new_canvas_width = new_canvas_width < 1 ? 1 : new_canvas_width; + new_canvas_height = new_canvas_height < 1 ? 1 : new_canvas_height; + + this.send_message('resize', {'width': new_canvas_width, 'height': new_canvas_height}); }, _resize_canvas: function(width, height) { @@ -234,6 +278,13 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ this.canvas_div.style.width = width + 'px'; this.canvas_div.style.height = height + 'px'; + + // Figure out the widget size. + var decorations_size = this._calculate_decorations_size(); + + // Reset the widget size to adapt to this figure. + this.el.style.width = width + decorations_size.width + 'px'; + this.el.style.height = height + decorations_size.height + 'px'; }, send_message: function(type, message = {}) { @@ -366,6 +417,22 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ } }, + processPhosphorMessage: function(msg) { + MPLCanvasView.__super__.processPhosphorMessage.apply(this, arguments); + + switch (msg.type) { + case 'resize': + // Ensure that the image already exists. We ignore the very first call to + // resize because we want the widget to adapt to the figure size set in + // matplotlib. + if (this.image.src) { + this.request_resize(); + } + break; + } + }, + + mouse_event: function(name) { var that = this; var last_update = 0; diff --git a/js/src/utils.js b/js/src/utils.js index de0a6629..b96270e3 100644 --- a/js/src/utils.js +++ b/js/src/utils.js @@ -41,8 +41,41 @@ function get_simple_keys(original) { }, {}); } +/* + * Return the total size of the margins for an element in both width and height. + */ +function get_margin_size(el) { + var style = getComputedStyle(el); + + var margin_width = parseFloat(style.marginLeft) + parseFloat(style.marginRight); + var margin_height = parseFloat(style.marginTop) + parseFloat(style.marginBottom); + + return { + width: margin_width, + height: margin_height, + }; +} + + +/* + * Return the full size of an element, including margins. + */ +function get_full_size(el) { + var margin_size = get_margin_size(el); + + var full_width = el.scrollWidth + margin_size.width; + var full_height = el.scrollHeight + margin_size.height; + + return { + width: full_width, + height: full_height, + }; +} + module.exports = { offset: offset, get_mouse_position: get_mouse_position, - get_simple_keys: get_simple_keys + get_simple_keys: get_simple_keys, + get_margin_size: get_margin_size, + get_full_size: get_full_size, } From a688ecee3bd8ee1360b4ca91ef8648d6037e9a9c Mon Sep 17 00:00:00 2001 From: Kyle Boone Date: Wed, 30 Oct 2019 11:10:34 -0700 Subject: [PATCH 3/3] Only allow resizes after the original matplotlib figure has been created. --- js/src/mpl_widget.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/js/src/mpl_widget.js b/js/src/mpl_widget.js index 32b81822..d62f66cd 100644 --- a/js/src/mpl_widget.js +++ b/js/src/mpl_widget.js @@ -254,6 +254,13 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ }, request_resize: function() { + // Ensure that the image already exists. We ignore the first calls to resize + // because we want the widget to first adapt to the figure size set in + // matplotlib. + if (!this.image.src) { + return; + } + // Using the given widget size, figure out how big the canvas should be. var decorations_size = this._calculate_decorations_size(); @@ -422,12 +429,7 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ switch (msg.type) { case 'resize': - // Ensure that the image already exists. We ignore the very first call to - // resize because we want the widget to adapt to the figure size set in - // matplotlib. - if (this.image.src) { - this.request_resize(); - } + this.request_resize(); break; } },