Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

220
LINES

< > BotCompany Repo | #1034387 // JPaintTool - it's totally like MS Paint

JavaX fragment (include) [tags: use-pretranspiled]

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)