diff --git a/js/src/mpl_widget.css b/js/src/mpl_widget.css index 8442bd5e..bc5d0b52 100644 --- a/js/src/mpl_widget.css +++ b/js/src/mpl_widget.css @@ -1,20 +1,27 @@ .jupyter-matplotlib { - height: 500px; flex: 1 1 auto; + width: auto; + height: auto; } /* 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; } /* Figure */ .jupyter-matplotlib-figure { - width: 100%; - height: 100%; + 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 3ab1fb87..ec5d926b 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,64 @@ 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}); + // 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(); + + 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) { @@ -231,6 +282,16 @@ 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'; + + // 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 = {}) { @@ -368,12 +429,12 @@ var MPLCanvasView = widgets.DOMWidgetView.extend({ switch (msg.type) { case 'resize': - // case 'after-show': 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, }