import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage; import javafx.scene.control.Button; import javafx.scene.control.ListView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Color; import javafx.geometry.Pos; import java.util.ArrayList; /** * A SillyStamper panel contains a List of icons, a canvas where * the user can "stamp" images of the icons, and a few control buttons. * The user clicks an icon in the list to select it, then clicks on the * canvas to place copies of the selected image. * * This program *requires* the icon image files from the stamper_icons directory. * These icons are 32-by-32 pixels. * (The images were taken from a KDE desktop icon collection.) */ public class SillyStamper extends Application { public static void main(String[] args) { launch(args); } //-------------------------------------------------------------- /** * An object of type IconInfo stores the information needed to draw * one icon image on the canvas. */ private static class IconInfo { int iconNumber; // an index into the iconImages array int x, y; // coords of the upper left corner of the image boolean big; // should icon be scaled up to a larger (48-by48) size } /** * Contains info for all the icons that have been placed on the * canvas. Might contain more than have actually been shown, * because of the Undo command. An icon that is removed from the * canvas by an undo is not removed from this list. */ private ArrayList icons = new ArrayList(); private int iconsShown; // Number of icons shown in the display area. private int iconsPlaced; // Number of icons that have been placed. Can be // greater than iconsShown, because of undo/redo. private ListView iconList; // The ListView from which the user selects icons. private Button undoButton; // A button for removing the most recently added image. private Button redoButton; // A button for restoring the most recently removed image. private Canvas canvas; // The canvas where the icons are displayed. private Image[] iconImages; // The little images that can be "stamped". public void start(Stage stage) { canvas = new Canvas(400,300); canvas.setOnMousePressed( this::mousePressed ); undoButton = new Button("Undo"); undoButton.setOnAction( e -> { if (iconsShown > 0) { // Decrement iconsShown, so one less icon will be drawn; // the icon is still in the array, for the Redo command. iconsShown--; redoButton.setDisable(false); redraw(); } }); undoButton.setDisable(true); redoButton = new Button("Redo"); redoButton.setOnAction( e -> { if (iconsShown < iconsPlaced) { // Increment iconsShown, so one more icon will be shown. iconsShown++; if (iconsShown == iconsPlaced) redoButton.setDisable(true); undoButton.setDisable(false); redraw(); } }); redoButton.setDisable(true); iconList = createIconList(); // Create the scrolling list of icons. iconList.setStyle("-fx-border-color:#009; -fx-border-width: 0 0 0 6px"); HBox bottom = new HBox(15,undoButton,redoButton); bottom.setStyle("-fx-padding:7px; -fx-border-color:#009; -fx-border-width:6px 0 0 0"); bottom.setAlignment(Pos.CENTER); BorderPane root = new BorderPane(canvas); root.setStyle("-fx-border-color:#009; -fx-border-width:6px"); root.setRight(iconList); root.setBottom(bottom); stage.setScene( new Scene(root) ); stage.setResizable(false); stage.setTitle("Silly Stamper -- Shift-click for big icon"); stage.show(); redraw(); } // end start() /** * Draw the entire content of the canvas, with a light blue background * and icons that the user has placed on the canvas. */ public void redraw() { GraphicsContext g = canvas.getGraphicsContext2D(); g.setFill(Color.color(0.93,0.93,1)); g.fillRect(0,0,canvas.getWidth(),canvas.getHeight()); for (int i = 0; i < iconsShown; i++) { IconInfo info = icons.get(i); if (info.big) g.drawImage(iconImages[info.iconNumber], info.x, info.y, 48, 48); else g.drawImage(iconImages[info.iconNumber], info.x, info.y); } } /** * When the user clicks the canvas, place a copy of the currently selected * icon image at the point where the user clicked. If the shift key is * down the icon is "big", i.e. drawn at 48 pixels instead of the usual 32. * Also, icons in the list that are no longer shown, because of "Undo" * commands, are effectively discarded. */ public void mousePressed(MouseEvent e) { IconInfo info = new IconInfo(); info.iconNumber = iconList.getSelectionModel().getSelectedIndex(); info.big = e.isShiftDown(); if (info.big) { // icon image will be 48-by-48 info.x = (int)(e.getX() - 24); // Offset coords, so center of icon is at info.y = (int)(e.getY() - 24); // the point that was clicked. } else { // icon image will be 32-by-32 info.x = (int)(e.getX() - 16); info.y = (int)(e.getY() - 16); } if (iconsShown == icons.size()) icons.add(info); else icons.set(iconsShown, info); iconsShown++; iconsPlaced = iconsShown; redoButton.setDisable(true); undoButton.setDisable(false); redraw(); } /** * Create a ListView that contains all of the available icon images. Initially, * the first icon is selected. The user can select a different icon by * clicking its image in the list. The items in the ListView are ImageView objects, * not Image objects, since a List will not display Image objects correctly. * This list is not editable and will not change after it is created. */ private ListView createIconList() { String[] iconNames = new String[] { // names of image resource file, in directory stamper_icons "icon5.png", "icon7.png", "icon8.png", "icon9.png", "icon10.png", "icon11.png", "icon24.png", "icon25.png", "icon26.png", "icon31.png", "icon33.png", "icon34.png" }; iconImages = new Image[iconNames.length]; ListView list = new ListView<>(); list.setPrefWidth(80); // The default pref width is much too wide, list.setPrefHeight(100); // The default pref height is 400, which is taller than the canvas, // which would force the height of the BorderPane to be too big. for (int i = 0; i < iconNames.length; i++) { Image icon = new Image("stamper_icons/" + iconNames[i]); iconImages[i] = icon; list.getItems().add( new ImageView(icon) ); } list.getSelectionModel().select(0); // The first item in the list is currently selected. return list; } } // end class SillyStamper