diff --git a/packages/webr/R/canvas.R b/packages/webr/R/canvas.R
index a3558929..97bf366c 100644
--- a/packages/webr/R/canvas.R
+++ b/packages/webr/R/canvas.R
@@ -94,7 +94,8 @@ canvas_purge <- function() {
#' @param ... Arguments to be passed to the graphics device.
#' @export
canvas_install <- function(...) {
+ args <- as.list(match.call()[-1])
options(device = function() {
- webr::canvas(...)
+ do.call(webr::canvas, args)
})
}
diff --git a/src/repl/App.tsx b/src/repl/App.tsx
index 1e169627..59cf5c81 100644
--- a/src/repl/App.tsx
+++ b/src/repl/App.tsx
@@ -5,9 +5,9 @@ import Editor from './components/Editor';
import Plot from './components/Plot';
import Files from './components/Files';
import { Readline } from 'xterm-readline';
-import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { WebR } from '../webR/webr-main';
import { CanvasMessage, PagerMessage } from '../webR/webr-chan';
+import { Panel, PanelGroup, PanelResizeHandle, getPanelElement } from 'react-resizable-panels';
import './App.css';
const webR = new WebR({
@@ -33,6 +33,7 @@ export interface FilesInterface {
}
export interface PlotInterface {
+ resize: (direction: "width" | "height", px: number) => void;
newPlot: () => void;
drawImage: (img: ImageBitmap) => void;
}
@@ -49,6 +50,7 @@ const filesInterface: FilesInterface = {
};
const plotInterface: PlotInterface = {
+ resize: () => { return; },
newPlot: () => { return; },
drawImage: () => {
throw new Error('Unable to plot, plotting not initialised.');
@@ -71,11 +73,15 @@ async function handlePagerMessage(msg: PagerMessage) {
}
}
+const onPanelResize = (size: number) => {
+ plotInterface.resize("width", size * window.innerWidth / 100);
+};
+
function App() {
return (
-
+
-
+
-
+
@@ -107,7 +113,12 @@ void (async () => {
// Set the default graphics device and pager
await webR.evalRVoid('webr::pager_install()');
- await webR.evalRVoid('webr::canvas_install()');
+ await webR.evalRVoid(`
+ webr::canvas_install(
+ width = getOption("webr.fig.width", 504),
+ height = getOption("webr.fig.height", 504)
+ )
+ `);
// shim function from base R with implementations for webR
// see ?webr::shim_install for details.
diff --git a/src/repl/components/Plot.css b/src/repl/components/Plot.css
index eb191b8d..25ad95fb 100644
--- a/src/repl/components/Plot.css
+++ b/src/repl/components/Plot.css
@@ -1,17 +1,16 @@
.plot-background {
background-color: var(--bg-dark);
flex-grow: 1;
+ overflow: auto;
display: flex;
justify-content: center;
align-items: center;
- min-height: 0;
- min-width: 0;
}
.plot-container {
max-width: 90%;
max-height: 90%;
- aspect-ratio: 1 / 1;
+ overflow: hidden;
background-color: white;
}
diff --git a/src/repl/components/Plot.tsx b/src/repl/components/Plot.tsx
index b4ac3dfb..ff135684 100644
--- a/src/repl/components/Plot.tsx
+++ b/src/repl/components/Plot.tsx
@@ -2,16 +2,20 @@ import React from 'react';
import './Plot.css';
import { PlotInterface } from '../App';
import { FaArrowCircleLeft, FaArrowCircleRight, FaRegSave, FaTrashAlt } from 'react-icons/fa';
-import { Panel } from 'react-resizable-panels';
+import { Panel, getPanelElement } from 'react-resizable-panels';
+import { WebR } from '../../webR/webr-main';
export function Plot({
+ webR,
plotInterface,
}: {
+ webR: WebR;
plotInterface: PlotInterface;
}) {
const plotContainerRef = React.useRef
(null);
const canvasRef = React.useRef(null);
const canvasElements = React.useRef([]);
+ const plotSize = React.useRef<{width: number, height: number}>({width: 1008, height: 1008});
const [selectedCanvas, setSelectedCanvas] = React.useState(null);
// Register the current canvas with the plotting interface so that when the
@@ -28,13 +32,33 @@ export function Plot({
plotInterface.newPlot = () => {
const plotNumber = canvasElements.current.length + 1;
const canvas = document.createElement('canvas');
- canvas.setAttribute('width', '1008');
- canvas.setAttribute('height', '1008');
+ canvas.setAttribute('width', String(plotSize.current.width * 2));
+ canvas.setAttribute('height', String(plotSize.current.height * 2));
canvas.setAttribute('aria-label', `R Plot ${plotNumber}`);
canvasRef.current = canvas;
canvasElements.current.push(canvas);
setSelectedCanvas(plotNumber - 1);
};
+
+ // Resize the canvas() device when the plotting pane changes size
+ plotInterface.resize = (direction, px) => {
+ plotSize.current[direction] = Math.max(px / 1.5, 150);
+ void webR.init().then(async () => {
+ await webR.evalRVoid(`
+ # Close any active canvas devices
+ repeat {
+ devices <- dev.list()
+ idx <- which(names(devices) == "canvas")
+ if (length(idx) == 0) {
+ break
+ }
+ dev.off(devices[idx[1]])
+ }
+ # Set canvas size for future devices
+ options(webr.fig.width = ${plotSize.current.width}, webr.fig.height = ${plotSize.current.height})
+ `);
+ });
+ };
}, [plotInterface]);
// Update the plot container to display the currently selected canvas element
@@ -47,6 +71,7 @@ export function Plot({
} else {
const canvas = canvasElements.current[selectedCanvas];
plotContainerRef.current.replaceChildren(canvas);
+ plotContainerRef.current.style.aspectRatio = `${canvas.width} / ${canvas.height}`;
}
}, [selectedCanvas]);
@@ -66,9 +91,10 @@ export function Plot({
const nextPlot = () => setSelectedCanvas((selectedCanvas === null) ? null : selectedCanvas + 1);
const prevPlot = () => setSelectedCanvas((selectedCanvas === null) ? null : selectedCanvas - 1);
+ const onResize = (size:number) => plotInterface.resize("height", size * window.innerHeight / 100);
return (
-
+