Last active
June 18, 2023 00:11
-
-
Save abdelaziz321/e9932bd15e4b263c3dae08644c61600c to your computer and use it in GitHub Desktop.
simple paint App JavaFX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* =================================================== | |
* 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); | |
} | |
} |
Hey @mrl1992,
you can achieve this by increasing the line width using the slider at line 275.
noice bruh
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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