Libraryless. Click here for Pure Java version (20367L/129K).
sclass JPaintTool > MetaWithChangeListeners is Swingable, AutoCloseable { settableWithVar File autoPersistFile; settableWithVar double autoPersistInterval = 10.0; settableWithVar Color brushColor = Color.black; settableWithVar bool drawingAllowed = true; settable Color bgColor = Color.lightGray; // background color around image surface event imageChanged(BufferedImage image); transient settableWithChange volatile bool dirty; transient gettable BufferedImage image; transient gettable ImageSurface imageSurface; transient bool autoPersistInstalled; transient ReliableSingleThread rstPersist = new(r _persist); transient JPanel buttons; // tools transient new ImageSurfaceScribbleTool scribbler; transient new ImageSurfaceDragger dragger; transient new ImageSurfaceSelector selector; transient JComboBox<WidthAndHeight> cbImageSize; //transient settableWithVar S status; transient new ButtonGroup colorButtonGroup; transient new ButtonGroup toolSelectButtonGroup; transient simplyCached void init() { imageSurface = makeImageSurface(); imageSurface.onUserModifiedImage(image -> { print("userModifiedImage"); setImage(image); fireImageChanged(); }); cbImageSize = jTypedComboBox(imageSizes()); main onChange(cbImageSize, l1 setImageSize); printVars("JPaintTool.init", +image, +autoPersistFile); if (image == null) { setImage(makeNewImage()); if (autoPersistFile != null) { pcall { var img = loadImage2(autoPersistFile); if (img != null) { setImage(img); print("Image loaded"); } } if (!autoPersistInstalled) { set autoPersistInstalled; awtEvery(imageSurface, autoPersistInterval, rstPersist); bindToComponent(imageSurface, null, rstPersist); } } } varDrawingAllowed().onChangeAndNow(b -> scribbler.enabled(print(scribblerEnabled := b))); } BufferedImage makeNewImage() { ret main newImage(defaultImageSize()); } WidthAndHeight defaultImageSize() { ret getSelected(cbImageSize); } cachedVisualize { init(); var filePathLabel = new JFilePathLabel(autoPersistFile).nameOnly(); varAutoPersistFile().onChange(file -> filePathLabel.setFile(file)); var filePathLabelVis = filePathLabel.visualize(); componentPopupMenuItem(filePathLabel.button.visualize(), "Save image now", r { set dirty; rstPersist!; }); ret withTopMargin(northCenterAndSouthWithMargin( // north buttons buttons = jcenteredline(flattenToList( jbutton("New image", rThread newImage), withLabel("Size:", cbImageSize), map colorSelectButton(colors()), swing(-> new JImageSwitch(#1103077, #1103078, varDrawingAllowed()) .setToolTips( "Drawing allowed (click to change)", "Drawing not allowed (click to change)")), )), // image surface centerAndEastWithMargin( jscroll_borderless(setBackground(bgColor, jFullCenter(imageSurface))), //jscroll_center_borderless(imageSurface), vstackWithSpacing( makeToolSelectors() ) ), // south controls centerAndEast( withLeftMargin(bottomLeftControls()), autoPersistFile == null ? jpanel() : withSideMargin(withLabel("Image:", filePathLabelVis)) ) )); } void setImage(BufferedImage image) { this.image = image; imageSurface.setImage(image); imageChanged(image); } transient swappable ImageSurface makeImageSurface() { var is = pixelatedImageSurface(); //is.setAutoZoomToDisplay(true); is.specialPurposed = true; is.allowPaste(true); is.zoomable(false); is.removeAllTools(); //scribbler.onScribbleDone(scribble -> printStruct(scribble)); scribbler.onPainted(l0 fireImageChanged); varBrushColor().onChangeAndNow(color -> scribbler.setColor(color)); is.setTool(scribbler); ret is; } void fireImageChanged { set dirty; imageChanged(getImage()); } void _persist { if (autoPersistFile != null && dirty) { dirty = false; savePNGVerbose(autoPersistFile, image); } } // doesn't allow drawing immediately void loadImageProtected(File file) { loadImage(file); drawingAllowed(false); } // Note: also auto-persists to that file void loadImage(File file) { close(); // persist var img = loadImage2(file); autoPersistFile(file); setImage(img); } void newImage() ctex { close(); // persist //if (dirty && !swingConfirm(imageSurface, "Discard image")) ret; var file = createAutoPersistFile(); print("Created auto persist file: " + f2s(file)); autoPersistFile(file); setImage(makeNewImage()); drawingAllowed(true); dirty = false; } // swap to make a new file every time user clicks "New image" swappable File createAutoPersistFile() { ret autoPersistFile(); } JToggleButton colorSelectButton(Color color) { var btn = jImageToggleButton(main newImage(16, color), "Select color to draw with", -> setBrushColor(color), eq(brushColor(), color)); ret addToButtonGroup(colorButtonGroup, btn); } L<Color> colors() { ret colorPaletteByBits(1); // 8 primary colors } L<WidthAndHeight> imageSizes() { ret mapLL sixteenToNine_p( 240, 360, 480, 720 ); } void setImageSize(WidthAndHeight size) { if (!sameSize(getImage(), size)) newImage(); } close { rstPersist.triggerAndWait(); } transient swappable JComponent bottomLeftControls() { ret jpanel(); } void selectTool(ImageSurfaceMouseHandler tool) { imageSurface.setTool(tool); } JToggleButton toolSelectButton(ImageSurfaceMouseHandler tool, S imageID, S toolTip) { ret addToButtonGroup(toolSelectButtonGroup, jImageToggleButton(imageID, toolTip, -> selectTool(tool), imageSurface.hasTool(tool)); } L<JComponent> makeToolSelectors() { ret ll( instaToolTip(-> "Draw on image with left mouse button" + stringIf(!drawingAllowed, " (click lock icon to start)"), toolSelectButton(scribbler, #1103080, null)), toolSelectButton(dragger, #1103079, "Scroll image with left mouse button"), toolSelectButton(selector, #1103081, "Select rectangular area with left mouse button") ); } }
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1034387 |
Snippet name: | JPaintTool - it's totally like MS Paint |
Eternal ID of this version: | #1034387/93 |
Text MD5: | eac7f071cb23abd7156eef8cefb9b1f7 |
Transpilation MD5: | 3625af800b2dd13d063bee8619419ebc |
Author: | stefan |
Category: | javax / gui |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-03-20 18:34:40 |
Source code size: | 6769 bytes / 220 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 345 / 1006 |
Version history: | 92 change(s) |
Referenced in: | #1003674 - Standard Classes + Interfaces (LIVE continued in #1034167) |