sclass JPaintTool is Swingable { settableWithVar File autoPersistFile; settableWithVar double autoPersistInterval = 10.0; settableWithVar brushColor = Color.black; transient settableWithChange volatile bool dirty; transient gettable BufferedImage image; transient ImageSurface imageSurface; transient bool autoPersistInstalled; transient ReliableSingleThread rstPersist = new(r _persist); transient JPanel buttons; transient ImageSurfaceScribbleTool scribbler; transient JComboBox cbImageSize; transient settableWithVar S status; transient simplyCached void init() { imageSurface = makeImageSurface(); cbImageSize = jTypedComboBox(imageSizes()); onChange(cbImageSize, l1 setImageSize); if (image == null) { setImage(makeNewImage()); if (autoPersistFile != null) { pcall { var img = loadImage2(autoPersistFile); if (img != null) setImage(img); else set dirty; } if (!autoPersistInstalled) { set autoPersistInstalled; awtEvery(imageSurface, autoPersistInterval, rstPersist); bindToComponent(imageSurface, null, rstPersist); } } } } BufferedImage makeNewImage() { ret main newImage(defaultImageSize()); } WidthAndHeight defaultImageSize() { ret getSelected(cbImageSize); } visualize { init(); ret withTopMargin(northCenterAndSouthWithMargin( buttons = jcenteredline(flattenToList( jbutton("New image", rThread newImage), cbImageSize, map colorSelectButton(colors()) )), jscroll_center_borderless(imageSurface), bottomPanel = centerAndEast( jVarLabel(varStatus()), autoPersistFile == null ? jpanel() : withLeftMargin(new JFilePathButton(autoPersistFile).visualize())); } void setImage(BufferedImage image) { this.image = image; imageSurface.setImage(image); revalidate(getParent(imageSurface)); // XXX } swappable ImageSurface makeImageSurface() { var is = pixelatedImageSurface(); //is.setAutoZoomToDisplay(true); //is.specialPurposed = true; is.zoomable(false); is.removeAllTools(); scribbler = new ImageSurfaceScribbleTool(is); scribbler.onPainted(-> { set dirty; }); varBrushColor().onChangeAndNow(color -> scribbler.setColor(color)); ret is; } void _persist { if (autoPersistFile != null && dirty) { dirty = false; savePNGVerbose(autoPersistFile, image); } } void newImage { rstPersist!; //if (dirty && !swingConfirm(imageSurface, "Discard image")) ret; setImage(makeNewImage()); dirty = false; } JButton colorSelectButton(Color color) { ret jImageButton(main newImage(16, color), "Select color to draw with", -> setBrushColor(color)); } L colors() { ret colorPaletteByBits(1); // 8 primary colors } L imageSizes() { ret mapLL sixteenToNine_p( 240, 360, 480, 720 ); } void setImageSize(WidthAndHeight size) { if (!sameSize(getImage(), size)) newImage(); } }