From 0052037e5831260b302ca5e09273353d98ebdb78 Mon Sep 17 00:00:00 2001 From: _N0x Date: Sun, 14 Feb 2021 15:29:59 +0100 Subject: [PATCH] Finalizing methods, fixing centering issues, added slideshow-time-slider, project cleanup --- src/de/thm/tlf/photoViewer/Controller.java | 299 ++++++++++++------ .../thm/tlf/photoViewer/PictureHandler.java | 38 ++- 2 files changed, 228 insertions(+), 109 deletions(-) diff --git a/src/de/thm/tlf/photoViewer/Controller.java b/src/de/thm/tlf/photoViewer/Controller.java index 4b038a5..06b5ff5 100644 --- a/src/de/thm/tlf/photoViewer/Controller.java +++ b/src/de/thm/tlf/photoViewer/Controller.java @@ -3,10 +3,9 @@ package de.thm.tlf.photoViewer; import de.thm.tlf.photoViewer.data.PicturePreview; import javafx.application.Application; import javafx.application.Platform; +import javafx.beans.binding.Bindings; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; -//import javafx.beans.value.ChangeListener; -//import javafx.beans.value.ObservableValue; import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.event.EventHandler; @@ -16,6 +15,9 @@ import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import javafx.scene.layout.*; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -28,38 +30,42 @@ import java.util.List; * Holds GUI elements as well as the handling of any user input. * * @author Tim Lukas Förster - * @version 0.1 + * @version 1.0 */ public class Controller extends Application { + //////////////////////////// //------ Attributes ------// + //////////////////////////// + // CENTER // - private final DoubleProperty zoomProperty = new SimpleDoubleProperty(200); private final ScrollPane currentViewSP = new ScrollPane(); private final ImageView centerImageView = new ImageView(); - // LEFT // - private final VBox leftPane = new VBox(); - private final VBox selectionPane = new VBox(); - private final ScrollPane pictureSelector = new ScrollPane(); - // TOP // private final VBox menu = new VBox(); private final MenuBar menuBar = new MenuBar(); private final Menu fileMenu = new Menu("File"); private final Menu aboutMenu = new Menu("About"); private final MenuItem openFiles = new MenuItem("Open"); + private final MenuItem clearViewer = new Menu("Close all"); private final MenuItem startSlideShow = new MenuItem("Start Slide Show"); private final MenuItem exitViewer = new Menu("Exit"); private final MenuItem showInfo = new Menu("Information"); // BOTTOM // - private final BorderPane bottomPanel = new BorderPane(); + private final VBox bottomPanel = new VBox(); + + private final HBox selectionPane = new HBox(); + private final ScrollPane pictureSelector = new ScrollPane(); + + private final BorderPane bottomLowerPanel = new BorderPane(); private final HBox bottomLeft = new HBox(); private final HBox bottomMid = new HBox(); private final HBox bottomRight = new HBox(); private final Button openFilesButton = new Button("Open Pictures"); private Slider zoomSlider; + private Slider slideShowSpeedSlider; private final Button prevPicBtn = new Button("<-"); private final Button nextPicBtn = new Button("->"); @@ -73,51 +79,123 @@ public class Controller extends Application { // Etc. // private boolean bIsFullScreen = false; private boolean bSlideShowActive = false; - //------ End Attributes ------// + private final DoubleProperty slideShowSpeed = new SimpleDoubleProperty(4); + private final DoubleProperty zoomProperty = new SimpleDoubleProperty(200); + private final KeyCombination keyCrtlQ = new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_ANY); + //////////////////////////////// + //------ End Attributes ------// + //////////////////////////////// + + + ///////////////////////// //------ Methods ------// + ///////////////////////// + /** + * Entrypoint to program + * @param args String-Array supplied when starting the application + */ public static void main(String[] args) { launch(args); } + /** + * Method initializing GUI + * @param primaryStage Default stage supplied when GUI is created + */ @Override public void start(Stage primaryStage) { picHandler = PictureHandler.getInstance(); BorderPane root = new BorderPane(); - root.setLeft(createLeft()); root.setCenter(createCenter()); root.setTop(createTop()); root.setBottom(createBottom()); Scene mainScene = new Scene(root, 1200, 800); + // Create all Actions // + createSliderActions(); + createMenuActions(primaryStage); + createButtonActions(primaryStage); + // Keypress Actions // mainScene.setOnKeyPressed(event -> { - switch (event.getCode()) { - case RIGHT: - try{ - centerImageView.setImage(picHandler.getPrevPicture().getImage()); - } - catch (NoPicturesLoadedException npl) { showNoPicturesLoadedWarning(); } - break; - case LEFT: - try{ - centerImageView.setImage(picHandler.getNextPicture().getImage()); - } - catch (NoPicturesLoadedException npl) { showNoPicturesLoadedWarning(); } - break; + + if (keyCrtlQ.match(event)) { + Platform.exit(); + } + else{ + switch (event.getCode()) { + case RIGHT: + case A: + try { + centerImageView.setImage(picHandler.getPrevPicture().getImage()); + } catch (NoPicturesLoadedException npl) { + showNoPicturesLoadedWarning(); + } + break; + case LEFT: + case D: + try { + centerImageView.setImage(picHandler.getNextPicture().getImage()); + } catch (NoPicturesLoadedException npl) { + showNoPicturesLoadedWarning(); + } + break; + } } }); - // End Keypress Actions // - // Center Zoom Action // + primaryStage.setTitle("Photo Viewer"); + primaryStage.setScene(mainScene); + primaryStage.show(); + } + + + /** + * Task used to run the slideshow in separate Threat. + */ + @SuppressWarnings("rawtypes") + Task slideShowTask = new Task(){ + @Override + @SuppressWarnings("BusyWait") + protected Void call() throws Exception { + while(bSlideShowActive) { + Thread.sleep((long)(slideShowSpeed.get()*1000)); + centerImageView.setImage(picHandler.getNextPicture().getImage()); + } + return null; + } + }; + + /** + * Even wrapper for openFileDialogEvent + * @param stage the primary stage, used to display the file-open-dialog + * @return EventHandler for dialog handling + */ + private EventHandler openFileDialogEvent(Stage stage) { + return e -> openFileDialog(stage); + } + + /** + * Event wrapper for clear function + * @return EventHandle executing the clear function + */ + private EventHandler clearPreviewViewEvent(){ + return e -> clearPreviewView(); + } + + //////////////////////////////// + //------ Helper-Methods ------// + //////////////////////////////// + /** + * Helper method for handling sliders + */ + private void createSliderActions() { + // Zoom Action // zoomProperty.addListener(observable -> { - centerImageView.setFitWidth(zoomProperty.get()); - centerImageView.setFitHeight(zoomProperty.get()); - /* Previously used values centerImageView.setFitWidth(zoomProperty.get() * 4); centerImageView.setFitHeight(zoomProperty.get() * 3); - */ }); zoomSlider.valueProperty().addListener((observableValue, oldVal, newVal) -> { @@ -129,26 +207,27 @@ public class Controller extends Application { } }); - /* Currently not used. A method that allow scrolling with the mouse wheel when inside the center - currentViewSP.addEventFilter(ScrollEvent.ANY, scrollEvent -> { + // SlideShowSpeed Action // + slideShowSpeed.addListener(observable -> {}); + slideShowSpeedSlider.valueProperty().addListener((observableValue, oldVal, newVal) -> slideShowSpeed.set((double)newVal)); + } - if (scrollEvent.getDeltaY() > 0) { - zoomProperty.set(zoomProperty.get() * 1.1); - } else if (scrollEvent.getDeltaY() < 0) { - zoomProperty.set(zoomProperty.get() / 1.1); - } - }); - /**/ - // End Center Zoom Action // - - // Menu Actions // - openFiles.setOnAction(openFileDialog(primaryStage)); + /** + * Helper method for handling menus and -entries + * @param stage the primary stage, used to display the file-open-dialog + */ + private void createMenuActions(Stage stage) { + openFiles.setOnAction(openFileDialogEvent(stage)); exitViewer.setOnAction( e -> Platform.exit()); - // End Menu Actions // + clearViewer.setOnAction(clearPreviewViewEvent()); + } - - // Button Actions // - openFilesButton.setOnAction(openFileDialog(primaryStage)); + /** + * Helper method for handling button interaction + * @param stage the primary stage, used to display the file-open-dialog and control fullscreen functions + */ + private void createButtonActions(Stage stage) { + openFilesButton.setOnAction(openFileDialogEvent(stage)); prevPicBtn.setOnAction(e -> { try{ @@ -165,7 +244,7 @@ public class Controller extends Application { }); fullScreenBtn.setOnAction(e -> { - primaryStage.setFullScreen(!bIsFullScreen); + stage.setFullScreen(!bIsFullScreen); bIsFullScreen ^= true; }); @@ -175,10 +254,9 @@ public class Controller extends Application { @Override public void handle(ActionEvent actionEvent) { try { - // This statement is to catch any errors regarding no images loaded - centerImageView.setImage(picHandler.getNextPicture().getImage()); - if (!bSlideShowActive) { + // This statement is to catch any errors regarding no images loaded + centerImageView.setImage(picHandler.getNextPicture().getImage()); bSlideShowActive = true; slideShowBtn.setText("Stop Slide Show"); th = new Thread(slideShowTask); @@ -193,55 +271,36 @@ public class Controller extends Application { } } }); - // End Button Actions // - - primaryStage.setTitle("Photo Viewer"); - primaryStage.setScene(mainScene); - primaryStage.show(); } - @SuppressWarnings("rawtypes") - Task slideShowTask = new Task(){ - @Override - @SuppressWarnings("BusyWait") - protected Void call() throws Exception { - while(bSlideShowActive) { - Thread.sleep(3000); - centerImageView.setImage(picHandler.getNextPicture().getImage()); - } - return null; - } - }; - - //------ Helper-Methods ------// /** * Open file-dialog allowing to select multiple pictures (via filter), * adds them to the PictureHandler and updates the picture preview * @param stage the primary stage, used to display the file-open-dialog - * @return EventHandler for dialog handling */ - private EventHandler openFileDialog(Stage stage) { - return e -> { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Open Pictures"); - fileChooser.getExtensionFilters().addAll( - new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg", "*.gif")); - List selectedPictures = fileChooser.showOpenMultipleDialog(stage); - if (selectedPictures != null) { - try{ - picHandler.loadPictures(selectedPictures); - Controller.this.updatePreviewView(); - centerImageView.setImage(picHandler.getNextPicture().getImage()); - } - catch (NoPicturesLoadedException npl){ showNoPicturesLoadedWarning();} + private void openFileDialog(Stage stage) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Open Pictures"); + fileChooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg", "*.gif")); + List selectedPictures = fileChooser.showOpenMultipleDialog(stage); + if (selectedPictures != null) { + try { + picHandler.loadPictures(selectedPictures); + Controller.this.updatePreviewView(); + centerImageView.setImage(picHandler.getNextPicture().getImage()); + } catch (NoPicturesLoadedException npl) { + showNoPicturesLoadedWarning(); } - }; + } } /** * Method that updates the preview Pane with all PreviewPictures provided by the Picture Handler */ private void updatePreviewView(){ + clearPreviewView(); + selectionPane.setPadding(new Insets(5,5,5,5)); for(PicturePreview pp: picHandler.getPreviews()){ ImageView iv = new ImageView(pp.getImage()); iv.setFitWidth(150); @@ -252,15 +311,12 @@ public class Controller extends Application { } /** - * Method that handles the settings and layout of the Left Panel - * @return Node, set up for displaying on the left of a GridPane + * Used to clear the preview and center panel when the viewer is cleared */ - private Node createLeft(){ - selectionPane.setPadding(new Insets(5,5,5,5)); - selectionPane.setSpacing(5); - pictureSelector.setContent(selectionPane); - leftPane.getChildren().addAll(pictureSelector); - return leftPane; + private void clearPreviewView(){ + selectionPane.getChildren().clear(); + selectionPane.setPadding(new Insets(5,155,5,5)); + centerImageView.setImage(null); } /** @@ -269,11 +325,11 @@ public class Controller extends Application { */ private Node createTop(){ SeparatorMenuItem sep = new SeparatorMenuItem(); - fileMenu.getItems().addAll(openFiles, sep, startSlideShow, exitViewer); + // File Menu + fileMenu.getItems().addAll(openFiles, clearViewer, sep, startSlideShow, exitViewer); // About Menu - aboutMenu.getItems().addAll(showInfo); - // Menu bare + // Menu bar menuBar.getMenus().addAll(fileMenu, aboutMenu); // Adding Menus to Top Panel menu.getChildren().addAll(menuBar); @@ -285,10 +341,22 @@ public class Controller extends Application { * @return Node, set up for displaying on the center of a GridPane */ private Node createCenter(){ + // Use stack pane and magic to center the image on screen + StackPane imageHolder = new StackPane(centerImageView); + GridPane grid = new GridPane(); + currentViewSP.setContent(imageHolder); + + imageHolder.minWidthProperty().bind(Bindings.createDoubleBinding(() -> + currentViewSP.getViewportBounds().getWidth(), currentViewSP.viewportBoundsProperty())); + + imageHolder.minHeightProperty().bind(Bindings.createDoubleBinding(() -> + currentViewSP.getViewportBounds().getHeight(), currentViewSP.viewportBoundsProperty())); + + grid.getChildren().add(imageHolder); + centerImageView.setX(10); centerImageView.setY(10); centerImageView.setPreserveRatio(true); - currentViewSP.setContent(centerImageView); return currentViewSP; } @@ -297,23 +365,39 @@ public class Controller extends Application { * @return Node, set up for displaying on the bottom of a GridPane */ private Node createBottom() { - bottomPanel.setPadding(new Insets(5, 5, 5, 5)); + bottomLowerPanel.setPadding(new Insets(5, 5, 5, 5)); // Left Bottom Part zoomSlider = new Slider(0, 100, 25); zoomSlider.setShowTickMarks(true); - bottomLeft.getChildren().addAll(openFilesButton, zoomSlider); - bottomPanel.setLeft(bottomLeft); + Label zoomLabel = new Label("Zoom:"); + bottomLeft.getChildren().addAll(openFilesButton, zoomLabel, zoomSlider); + //bottomLeft.getChildren().addAll(zoomLabel, zoomSlider); + bottomLeft.setSpacing(5); + bottomLowerPanel.setLeft(bottomLeft); // Middle Bottom Part bottomMid.setAlignment(Pos.CENTER); bottomMid.setSpacing(5); bottomMid.getChildren().addAll(prevPicBtn, slideShowBtn, nextPicBtn); - bottomPanel.setCenter(bottomMid); + bottomLowerPanel.setCenter(bottomMid); // Right Bottom Part - bottomRight.getChildren().add(fullScreenBtn); - bottomPanel.setRight(bottomRight); + slideShowSpeedSlider = new Slider(0.5, 15, 4); + slideShowSpeedSlider.setShowTickMarks(true); + slideShowSpeedSlider.setShowTickLabels(true); + Label sssLabel = new Label("Speed:"); + bottomRight.getChildren().addAll(sssLabel,slideShowSpeedSlider, fullScreenBtn); + bottomLowerPanel.setRight(bottomRight); + + // Bottom upper Part -> Picture preview + selectionPane.setPadding(new Insets(5,5,155,5)); + selectionPane.autosize(); + selectionPane.setSpacing(5); + pictureSelector.setContent(selectionPane); + + bottomPanel.getChildren().addAll(pictureSelector, bottomLowerPanel); + return (bottomPanel); } @@ -329,6 +413,11 @@ public class Controller extends Application { alert.showAndWait().ifPresent(rs -> { }); } + //////////////////////////////////// //------ End Helper-Methods ------// + //////////////////////////////////// + + ///////////////////////////// //------ End Methods ------// + ///////////////////////////// } diff --git a/src/de/thm/tlf/photoViewer/PictureHandler.java b/src/de/thm/tlf/photoViewer/PictureHandler.java index 7d6c1a0..fbf0653 100644 --- a/src/de/thm/tlf/photoViewer/PictureHandler.java +++ b/src/de/thm/tlf/photoViewer/PictureHandler.java @@ -12,35 +12,60 @@ import java.util.*; * Implemented using singleton pattern to avoid multiple instances */ public final class PictureHandler { + //////////////////////////// //------ Attributes ------// + //////////////////////////// private final ArrayList pictures = new ArrayList<>(); private final ArrayList previews = new ArrayList<>(); private int currentPictureID = -1; private static final PictureHandler INSTANCE = new PictureHandler(); - //------ End Attributes ------// - + ////////////////////////////// //------ Constructors ------// + ////////////////////////////// + + /** + * Prevent creation of new instances + */ private PictureHandler() {} + /** + * Singleton-Pattern method to acquire the only instance + * @return PictureHandler instance + */ public static PictureHandler getInstance() {return INSTANCE;} - //------ End Constructors ------// - + ///////////////////////// //------ Methods ------// + ///////////////////////// + + /** + * @return Arraylist of the PicturePreview + */ public ArrayList getPreviews(){ return previews; } + /** + * Method to load a list of Files into Picture objects and store them locally + * @param pictureFiles List of files that will be converted to Pictures + */ public void loadPictures(List pictureFiles){ + pictures.clear(); + previews.clear(); for(File picFile : pictureFiles){ pictures.add(new Picture(picFile.getPath())); previews.add(new PicturePreview(picFile.getPath())); } } + /** + * Determines the next picture that should be displayed + * @return Picture object that should be displayed next from the ArrayList pictures + * @throws NoPicturesLoadedException Exception for when no pictures are loaded yet but tried to access them + */ public Picture getNextPicture() throws NoPicturesLoadedException { if(pictures.size() <= 0){ throw new NoPicturesLoadedException("No pictures have been loaded"); @@ -51,6 +76,11 @@ public final class PictureHandler { } } + /** + * Determines the previous picture that should be displayed + * @return Picture object that should be displayed next from the ArrayList pictures + * @throws NoPicturesLoadedException Exception for when no pictures are loaded yet but tried to access them + */ public Picture getPrevPicture() throws NoPicturesLoadedException{ if(pictures.size() <= 0){ throw new NoPicturesLoadedException("No pictures have been loaded");