Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
simple paint App JavaFX
/*
* ===================================================
* contents
* ===================================================
* 00- Free draw
* 01- rubber
* 02- draw Line
* 03- draw Rectangele
* 04- draw Circle
* 05- draw Ellipse
* 06- Text
*
* ----------------------------------------------------
* Features
* ----------------------------------------------------
* - the ability to change Line color
* - the ability to change Fill color
* - the ability to change Line width
* - Undo & Redo
* - Open Image && save Image
*
* ____________________________________________________
* problems
* - undo & redo : not working with free draw and rubber
* - Line & Rect & Circ ... aren't be updated while drawing
* ===================================================
*/
package paint;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class Paint extends Application {
@Override
public void start(Stage primaryStage) {
Stack<Shape> undoHistory = new Stack();
Stack<Shape> redoHistory = new Stack();
/* ----------btns---------- */
ToggleButton drowbtn = new ToggleButton("Draw");
ToggleButton rubberbtn = new ToggleButton("Rubber");
ToggleButton linebtn = new ToggleButton("Line");
ToggleButton rectbtn = new ToggleButton("Rectange");
ToggleButton circlebtn = new ToggleButton("Circle");
ToggleButton elpslebtn = new ToggleButton("Ellipse");
ToggleButton textbtn = new ToggleButton("Text");
ToggleButton[] toolsArr = {drowbtn, rubberbtn, linebtn, rectbtn, circlebtn, elpslebtn, textbtn};
ToggleGroup tools = new ToggleGroup();
for (ToggleButton tool : toolsArr) {
tool.setMinWidth(90);
tool.setToggleGroup(tools);
tool.setCursor(Cursor.HAND);
}
ColorPicker cpLine = new ColorPicker(Color.BLACK);
ColorPicker cpFill = new ColorPicker(Color.TRANSPARENT);
TextArea text = new TextArea();
text.setPrefRowCount(1);
Slider slider = new Slider(1, 50, 3);
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);
Label line_color = new Label("Line Color");
Label fill_color = new Label("Fill Color");
Label line_width = new Label("3.0");
Button undo = new Button("Undo");
Button redo = new Button("Redo");
Button save = new Button("Save");
Button open = new Button("Open");
Button[] basicArr = {undo, redo, save, open};
for(Button btn : basicArr) {
btn.setMinWidth(90);
btn.setCursor(Cursor.HAND);
btn.setTextFill(Color.WHITE);
btn.setStyle("-fx-background-color: #666;");
}
save.setStyle("-fx-background-color: #80334d;");
open.setStyle("-fx-background-color: #80334d;");
VBox btns = new VBox(10);
btns.getChildren().addAll(drowbtn, rubberbtn, linebtn, rectbtn, circlebtn, elpslebtn,
textbtn, text, line_color, cpLine, fill_color, cpFill, line_width, slider, undo, redo, open, save);
btns.setPadding(new Insets(5));
btns.setStyle("-fx-background-color: #999");
btns.setPrefWidth(100);
/* ----------Drow Canvas---------- */
Canvas canvas = new Canvas(1080, 790);
GraphicsContext gc;
gc = canvas.getGraphicsContext2D();
gc.setLineWidth(1);
Line line = new Line();
Rectangle rect = new Rectangle();
Circle circ = new Circle();
Ellipse elps = new Ellipse();
canvas.setOnMousePressed(e->{
if(drowbtn.isSelected()) {
gc.setStroke(cpLine.getValue());
gc.beginPath();
gc.lineTo(e.getX(), e.getY());
}
else if(rubberbtn.isSelected()) {
double lineWidth = gc.getLineWidth();
gc.clearRect(e.getX() - lineWidth / 2, e.getY() - lineWidth / 2, lineWidth, lineWidth);
}
else if(linebtn.isSelected()) {
gc.setStroke(cpLine.getValue());
line.setStartX(e.getX());
line.setStartY(e.getY());
}
else if(rectbtn.isSelected()) {
gc.setStroke(cpLine.getValue());
gc.setFill(cpFill.getValue());
rect.setX(e.getX());
rect.setY(e.getY());
}
else if(circlebtn.isSelected()) {
gc.setStroke(cpLine.getValue());
gc.setFill(cpFill.getValue());
circ.setCenterX(e.getX());
circ.setCenterY(e.getY());
}
else if(elpslebtn.isSelected()) {
gc.setStroke(cpLine.getValue());
gc.setFill(cpFill.getValue());
elps.setCenterX(e.getX());
elps.setCenterY(e.getY());
}
else if(textbtn.isSelected()) {
gc.setLineWidth(1);
gc.setFont(Font.font(slider.getValue()));
gc.setStroke(cpLine.getValue());
gc.setFill(cpFill.getValue());
gc.fillText(text.getText(), e.getX(), e.getY());
gc.strokeText(text.getText(), e.getX(), e.getY());
}
});
canvas.setOnMouseDragged(e->{
if(drowbtn.isSelected()) {
gc.lineTo(e.getX(), e.getY());
gc.stroke();
}
else if(rubberbtn.isSelected()){
double lineWidth = gc.getLineWidth();
gc.clearRect(e.getX() - lineWidth / 2, e.getY() - lineWidth / 2, lineWidth, lineWidth);
}
});
canvas.setOnMouseReleased(e->{
if(drowbtn.isSelected()) {
gc.lineTo(e.getX(), e.getY());
gc.stroke();
gc.closePath();
}
else if(rubberbtn.isSelected()) {
double lineWidth = gc.getLineWidth();
gc.clearRect(e.getX() - lineWidth / 2, e.getY() - lineWidth / 2, lineWidth, lineWidth);
}
else if(linebtn.isSelected()) {
line.setEndX(e.getX());
line.setEndY(e.getY());
gc.strokeLine(line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY());
undoHistory.push(new Line(line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY()));
}
else if(rectbtn.isSelected()) {
rect.setWidth(Math.abs((e.getX() - rect.getX())));
rect.setHeight(Math.abs((e.getY() - rect.getY())));
//rect.setX((rect.getX() > e.getX()) ? e.getX(): rect.getX());
if(rect.getX() > e.getX()) {
rect.setX(e.getX());
}
//rect.setY((rect.getY() > e.getY()) ? e.getY(): rect.getY());
if(rect.getY() > e.getY()) {
rect.setY(e.getY());
}
gc.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
gc.strokeRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
undoHistory.push(new Rectangle(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()));
}
else if(circlebtn.isSelected()) {
circ.setRadius((Math.abs(e.getX() - circ.getCenterX()) + Math.abs(e.getY() - circ.getCenterY())) / 2);
if(circ.getCenterX() > e.getX()) {
circ.setCenterX(e.getX());
}
if(circ.getCenterY() > e.getY()) {
circ.setCenterY(e.getY());
}
gc.fillOval(circ.getCenterX(), circ.getCenterY(), circ.getRadius(), circ.getRadius());
gc.strokeOval(circ.getCenterX(), circ.getCenterY(), circ.getRadius(), circ.getRadius());
undoHistory.push(new Circle(circ.getCenterX(), circ.getCenterY(), circ.getRadius()));
}
else if(elpslebtn.isSelected()) {
elps.setRadiusX(Math.abs(e.getX() - elps.getCenterX()));
elps.setRadiusY(Math.abs(e.getY() - elps.getCenterY()));
if(elps.getCenterX() > e.getX()) {
elps.setCenterX(e.getX());
}
if(elps.getCenterY() > e.getY()) {
elps.setCenterY(e.getY());
}
gc.strokeOval(elps.getCenterX(), elps.getCenterY(), elps.getRadiusX(), elps.getRadiusY());
gc.fillOval(elps.getCenterX(), elps.getCenterY(), elps.getRadiusX(), elps.getRadiusY());
undoHistory.push(new Ellipse(elps.getCenterX(), elps.getCenterY(), elps.getRadiusX(), elps.getRadiusY()));
}
redoHistory.clear();
Shape lastUndo = undoHistory.lastElement();
lastUndo.setFill(gc.getFill());
lastUndo.setStroke(gc.getStroke());
lastUndo.setStrokeWidth(gc.getLineWidth());
});
// color picker
cpLine.setOnAction(e->{
gc.setStroke(cpLine.getValue());
});
cpFill.setOnAction(e->{
gc.setFill(cpFill.getValue());
});
// slider
slider.valueProperty().addListener(e->{
double width = slider.getValue();
if(textbtn.isSelected()){
gc.setLineWidth(1);
gc.setFont(Font.font(slider.getValue()));
line_width.setText(String.format("%.1f", width));
return;
}
line_width.setText(String.format("%.1f", width));
gc.setLineWidth(width);
});
/*------- Undo & Redo ------*/
// Undo
undo.setOnAction(e->{
if(!undoHistory.empty()){
gc.clearRect(0, 0, 1080, 790);
Shape removedShape = undoHistory.lastElement();
if(removedShape.getClass() == Line.class) {
Line tempLine = (Line) removedShape;
tempLine.setFill(gc.getFill());
tempLine.setStroke(gc.getStroke());
tempLine.setStrokeWidth(gc.getLineWidth());
redoHistory.push(new Line(tempLine.getStartX(), tempLine.getStartY(), tempLine.getEndX(), tempLine.getEndY()));
}
else if(removedShape.getClass() == Rectangle.class) {
Rectangle tempRect = (Rectangle) removedShape;
tempRect.setFill(gc.getFill());
tempRect.setStroke(gc.getStroke());
tempRect.setStrokeWidth(gc.getLineWidth());
redoHistory.push(new Rectangle(tempRect.getX(), tempRect.getY(), tempRect.getWidth(), tempRect.getHeight()));
}
else if(removedShape.getClass() == Circle.class) {
Circle tempCirc = (Circle) removedShape;
tempCirc.setStrokeWidth(gc.getLineWidth());
tempCirc.setFill(gc.getFill());
tempCirc.setStroke(gc.getStroke());
redoHistory.push(new Circle(tempCirc.getCenterX(), tempCirc.getCenterY(), tempCirc.getRadius()));
}
else if(removedShape.getClass() == Ellipse.class) {
Ellipse tempElps = (Ellipse) removedShape;
tempElps.setFill(gc.getFill());
tempElps.setStroke(gc.getStroke());
tempElps.setStrokeWidth(gc.getLineWidth());
redoHistory.push(new Ellipse(tempElps.getCenterX(), tempElps.getCenterY(), tempElps.getRadiusX(), tempElps.getRadiusY()));
}
Shape lastRedo = redoHistory.lastElement();
lastRedo.setFill(removedShape.getFill());
lastRedo.setStroke(removedShape.getStroke());
lastRedo.setStrokeWidth(removedShape.getStrokeWidth());
undoHistory.pop();
for(int i=0; i < undoHistory.size(); i++) {
Shape shape = undoHistory.elementAt(i);
if(shape.getClass() == Line.class) {
Line temp = (Line) shape;
gc.setLineWidth(temp.getStrokeWidth());
gc.setStroke(temp.getStroke());
gc.setFill(temp.getFill());
gc.strokeLine(temp.getStartX(), temp.getStartY(), temp.getEndX(), temp.getEndY());
}
else if(shape.getClass() == Rectangle.class) {
Rectangle temp = (Rectangle) shape;
gc.setLineWidth(temp.getStrokeWidth());
gc.setStroke(temp.getStroke());
gc.setFill(temp.getFill());
gc.fillRect(temp.getX(), temp.getY(), temp.getWidth(), temp.getHeight());
gc.strokeRect(temp.getX(), temp.getY(), temp.getWidth(), temp.getHeight());
}
else if(shape.getClass() == Circle.class) {
Circle temp = (Circle) shape;
gc.setLineWidth(temp.getStrokeWidth());
gc.setStroke(temp.getStroke());
gc.setFill(temp.getFill());
gc.fillOval(temp.getCenterX(), temp.getCenterY(), temp.getRadius(), temp.getRadius());
gc.strokeOval(temp.getCenterX(), temp.getCenterY(), temp.getRadius(), temp.getRadius());
}
else if(shape.getClass() == Ellipse.class) {
Ellipse temp = (Ellipse) shape;
gc.setLineWidth(temp.getStrokeWidth());
gc.setStroke(temp.getStroke());
gc.setFill(temp.getFill());
gc.fillOval(temp.getCenterX(), temp.getCenterY(), temp.getRadiusX(), temp.getRadiusY());
gc.strokeOval(temp.getCenterX(), temp.getCenterY(), temp.getRadiusX(), temp.getRadiusY());
}
}
} else {
System.out.println("there is no action to undo");
}
});
// Redo
redo.setOnAction(e->{
if(!redoHistory.empty()) {
Shape shape = redoHistory.lastElement();
gc.setLineWidth(shape.getStrokeWidth());
gc.setStroke(shape.getStroke());
gc.setFill(shape.getFill());
redoHistory.pop();
if(shape.getClass() == Line.class) {
Line tempLine = (Line) shape;
gc.strokeLine(tempLine.getStartX(), tempLine.getStartY(), tempLine.getEndX(), tempLine.getEndY());
undoHistory.push(new Line(tempLine.getStartX(), tempLine.getStartY(), tempLine.getEndX(), tempLine.getEndY()));
}
else if(shape.getClass() == Rectangle.class) {
Rectangle tempRect = (Rectangle) shape;
gc.fillRect(tempRect.getX(), tempRect.getY(), tempRect.getWidth(), tempRect.getHeight());
gc.strokeRect(tempRect.getX(), tempRect.getY(), tempRect.getWidth(), tempRect.getHeight());
undoHistory.push(new Rectangle(tempRect.getX(), tempRect.getY(), tempRect.getWidth(), tempRect.getHeight()));
}
else if(shape.getClass() == Circle.class) {
Circle tempCirc = (Circle) shape;
gc.fillOval(tempCirc.getCenterX(), tempCirc.getCenterY(), tempCirc.getRadius(), tempCirc.getRadius());
gc.strokeOval(tempCirc.getCenterX(), tempCirc.getCenterY(), tempCirc.getRadius(), tempCirc.getRadius());
undoHistory.push(new Circle(tempCirc.getCenterX(), tempCirc.getCenterY(), tempCirc.getRadius()));
}
else if(shape.getClass() == Ellipse.class) {
Ellipse tempElps = (Ellipse) shape;
gc.fillOval(tempElps.getCenterX(), tempElps.getCenterY(), tempElps.getRadiusX(), tempElps.getRadiusY());
gc.strokeOval(tempElps.getCenterX(), tempElps.getCenterY(), tempElps.getRadiusX(), tempElps.getRadiusY());
undoHistory.push(new Ellipse(tempElps.getCenterX(), tempElps.getCenterY(), tempElps.getRadiusX(), tempElps.getRadiusY()));
}
Shape lastUndo = undoHistory.lastElement();
lastUndo.setFill(gc.getFill());
lastUndo.setStroke(gc.getStroke());
lastUndo.setStrokeWidth(gc.getLineWidth());
} else {
System.out.println("there is no action to redo");
}
});
/*------- Save & Open ------*/
// Open
open.setOnAction((e)->{
FileChooser openFile = new FileChooser();
openFile.setTitle("Open File");
File file = openFile.showOpenDialog(primaryStage);
if (file != null) {
try {
InputStream io = new FileInputStream(file);
Image img = new Image(io);
gc.drawImage(img, 0, 0);
} catch (IOException ex) {
System.out.println("Error!");
}
}
});
// Save
save.setOnAction((e)->{
FileChooser savefile = new FileChooser();
savefile.setTitle("Save File");
File file = savefile.showSaveDialog(primaryStage);
if (file != null) {
try {
WritableImage writableImage = new WritableImage(1080, 790);
canvas.snapshot(null, writableImage);
RenderedImage renderedImage = SwingFXUtils.fromFXImage(writableImage, null);
ImageIO.write(renderedImage, "png", file);
} catch (IOException ex) {
System.out.println("Error!");
}
}
});
/* ----------STAGE & SCENE---------- */
BorderPane pane = new BorderPane();
pane.setLeft(btns);
pane.setCenter(canvas);
Scene scene = new Scene(pane, 1200, 800);
primaryStage.setTitle("Paint");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
@mrl1992

This comment has been minimized.

Copy link

mrl1992 commented Oct 21, 2019

hi

how can imake the rubber button larger? right now it only erases a small amaount of what i've drawn

really cool application btw

@abdelaziz321

This comment has been minimized.

Copy link
Owner Author

abdelaziz321 commented Oct 21, 2019

Hey @mrl1992,
you can achieve this by increasing the line width using the slider at line 275.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.