diff --git a/PhotoViewer.iml b/PhotoViewer.iml index b8db988..c769145 100644 --- a/PhotoViewer.iml +++ b/PhotoViewer.iml @@ -7,6 +7,6 @@ - + \ No newline at end of file diff --git a/pic/image1.jpg b/demoPics/image1.jpg similarity index 100% rename from pic/image1.jpg rename to demoPics/image1.jpg diff --git a/pic/image2.jpg b/demoPics/image2.jpg similarity index 100% rename from pic/image2.jpg rename to demoPics/image2.jpg diff --git a/pic/image3.jpg b/demoPics/image3.jpg similarity index 100% rename from pic/image3.jpg rename to demoPics/image3.jpg diff --git a/icons/left-arrow.png b/icons/left-arrow.png new file mode 100644 index 0000000..d931587 Binary files /dev/null and b/icons/left-arrow.png differ diff --git a/icons/right-arrow.png b/icons/right-arrow.png new file mode 100644 index 0000000..54443e7 Binary files /dev/null and b/icons/right-arrow.png differ diff --git a/src/de/thm/tlf/photoViewer/Controller.java b/src/de/thm/tlf/photoViewer/PVController.java similarity index 67% rename from src/de/thm/tlf/photoViewer/Controller.java rename to src/de/thm/tlf/photoViewer/PVController.java index 06b5ff5..8177f87 100644 --- a/src/de/thm/tlf/photoViewer/Controller.java +++ b/src/de/thm/tlf/photoViewer/PVController.java @@ -10,10 +10,12 @@ import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; +import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.*; +import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; @@ -23,6 +25,8 @@ import javafx.stage.FileChooser; import javafx.stage.Stage; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.util.List; /** @@ -31,12 +35,17 @@ import java.util.List; * * @author Tim Lukas Förster * @version 1.0 + * repository: https://gitlab.com/n0x_io/PhotoViewer */ -public class Controller extends Application { +public class PVController extends Application { //////////////////////////// //------ Attributes ------// //////////////////////////// + // Constants // + private static final String ARROWPREV = "icons/left-arrow.png"; + private static final String ARROWNEXT = "icons/right-arrow.png"; + // CENTER // private final ScrollPane currentViewSP = new ScrollPane(); private final ImageView centerImageView = new ImageView(); @@ -46,17 +55,18 @@ public class Controller extends Application { 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"); + private final MenuItem menuItemOpenFiles = new MenuItem("Open"); + private final MenuItem menuItemClearViewer = new Menu("Close all"); + private final MenuItem menuItemStartSlideShow = new MenuItem("Start Slide Show"); + private final MenuItem menuItemExitViewer = new Menu("Exit"); + private final MenuItem menuItemShowInfo = new Menu("Information"); // BOTTOM // private final VBox bottomPanel = new VBox(); - private final HBox selectionPane = new HBox(); - private final ScrollPane pictureSelector = new ScrollPane(); + //private final HBox selectionPane = new HBox(); + private final ListView previewPane = new ListView<>(); + private final VBox pictureSelector = new VBox(); private final BorderPane bottomLowerPanel = new BorderPane(); private final HBox bottomLeft = new HBox(); @@ -77,11 +87,13 @@ public class Controller extends Application { private PictureHandler picHandler; // Etc. // + private Thread slideShowThread; private boolean bIsFullScreen = false; private boolean bSlideShowActive = false; private final DoubleProperty slideShowSpeed = new SimpleDoubleProperty(4); private final DoubleProperty zoomProperty = new SimpleDoubleProperty(200); + // Key Combinations & Shortcuts // private final KeyCombination keyCrtlQ = new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_ANY); //////////////////////////////// //------ End Attributes ------// @@ -116,7 +128,7 @@ public class Controller extends Application { // Create all Actions // createSliderActions(); createMenuActions(primaryStage); - createButtonActions(primaryStage); + createClickActions(primaryStage); // Keypress Actions // mainScene.setOnKeyPressed(event -> { @@ -151,12 +163,11 @@ public class Controller extends Application { primaryStage.show(); } - /** * Task used to run the slideshow in separate Threat. */ @SuppressWarnings("rawtypes") - Task slideShowTask = new Task(){ + private final Task slideShowTask = new Task(){ @Override @SuppressWarnings("BusyWait") protected Void call() throws Exception { @@ -179,10 +190,26 @@ public class Controller extends Application { /** * Event wrapper for clear function - * @return EventHandle executing the clear function + * @return EventHandle executing the clearPreviewView function */ private EventHandler clearPreviewViewEvent(){ - return e -> clearPreviewView(); + return e -> clearViewer(); + } + + /** + * Event wrapper for showAboutDialog function + * @return EventHandle executing the showAboutDialog function + */ + private EventHandler showAboutDialogEvent(){ + return e -> showAboutDialog(); + } + + /** + * Event wrapper for showAboutDialog function + * @return EventHandle executing the showAboutDialog function + */ + private EventHandler toggleSlidesHowEvent(){ + return e -> toggleSlideShow(); } //////////////////////////////// @@ -192,6 +219,7 @@ public class Controller extends Application { * Helper method for handling sliders */ private void createSliderActions() { + //TODO: Zoom kinda wonky... should be redone. // Zoom Action // zoomProperty.addListener(observable -> { centerImageView.setFitWidth(zoomProperty.get() * 4); @@ -217,18 +245,22 @@ public class Controller extends Application { * @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()); - clearViewer.setOnAction(clearPreviewViewEvent()); + menuItemOpenFiles.setOnAction(openFileDialogEvent(stage)); + menuItemExitViewer.setOnAction(e -> Platform.exit()); + menuItemClearViewer.setOnAction(clearPreviewViewEvent()); + menuItemStartSlideShow.setOnAction(toggleSlidesHowEvent()); + menuItemShowInfo.setOnAction(showAboutDialogEvent()); } /** * 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) { + private void createClickActions(Stage stage) { + // Opens the File dialog openFilesButton.setOnAction(openFileDialogEvent(stage)); + // Displays the previous Picture prevPicBtn.setOnAction(e -> { try{ centerImageView.setImage(picHandler.getPrevPicture().getImage()); @@ -236,6 +268,7 @@ public class Controller extends Application { catch (NoPicturesLoadedException npl){ showNoPicturesLoadedWarning();} }); + // Displays the next picture nextPicBtn.setOnAction(e -> { try{ centerImageView.setImage(picHandler.getNextPicture().getImage()); @@ -243,36 +276,54 @@ public class Controller extends Application { catch (NoPicturesLoadedException npl){ showNoPicturesLoadedWarning();} }); + // Sets the PictureViewer to fullscreen fullScreenBtn.setOnAction(e -> { stage.setFullScreen(!bIsFullScreen); bIsFullScreen ^= true; }); - slideShowBtn.setOnAction(new EventHandler<>() { - Thread th; + //slideShowBtn.setOnAction( }); + + // Starts the slideshow on first click, stops it on the next one + slideShowBtn.setOnAction(toggleSlidesHowEvent()); + + // Clicking on any picture in the Previews opens said picture in the main view + previewPane.setOnMouseClicked(e -> { + try { + centerImageView.setImage(picHandler.getPictureByID(previewPane.getSelectionModel().getSelectedIndex()).getImage()); + } catch (NoPicturesLoadedException ignored) { - @Override - public void handle(ActionEvent actionEvent) { - try { - 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); - th.start(); - } else { - bSlideShowActive = false; - slideShowBtn.setText("Slide Show"); - th.interrupt(); - } - } catch (NoPicturesLoadedException npl){ - showNoPicturesLoadedWarning(); - } } }); } + /** + * Method that will start and stop (toggle) the Slide show and set the corresponding texts to all buttons etc. + */ + private void toggleSlideShow(){ + try { + if (!bSlideShowActive) { + // This statement is to catch any errors regarding no images loaded + centerImageView.setImage(picHandler.getNextPicture().getImage()); + bSlideShowActive = true; + slideShowThread = new Thread(slideShowTask); + slideShowThread.start(); + // Change text of slideshow switches + slideShowBtn.setText("Stop Slide Show"); + menuItemStartSlideShow.setText("Stop Slide Show"); + } else { + bSlideShowActive = false; + // Change text of slideshow switches + slideShowBtn.setText("Slide Show"); + menuItemStartSlideShow.setText("Slide Show"); + assert slideShowThread != null; + slideShowThread.interrupt(); + } + } catch (NoPicturesLoadedException npl){ + showNoPicturesLoadedWarning(); + } + } + /** * Open file-dialog allowing to select multiple pictures (via filter), * adds them to the PictureHandler and updates the picture preview @@ -287,7 +338,7 @@ public class Controller extends Application { if (selectedPictures != null) { try { picHandler.loadPictures(selectedPictures); - Controller.this.updatePreviewView(); + PVController.this.updatePreviewView(); centerImageView.setImage(picHandler.getNextPicture().getImage()); } catch (NoPicturesLoadedException npl) { showNoPicturesLoadedWarning(); @@ -299,26 +350,55 @@ public class Controller extends Application { * 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)); + clearViewer(); + //previewPane.setPadding(new Insets(5,5,5,5)); for(PicturePreview pp: picHandler.getPreviews()){ ImageView iv = new ImageView(pp.getImage()); iv.setFitWidth(150); iv.setSmooth(true); iv.setPreserveRatio(true); - selectionPane.getChildren().add(iv); + previewPane.getItems().add(iv); } } /** - * Used to clear the preview and center panel when the viewer is cleared + * Used to clear the preview and center panel */ - private void clearPreviewView(){ - selectionPane.getChildren().clear(); - selectionPane.setPadding(new Insets(5,155,5,5)); + private void clearViewer(){ + previewPane.getItems().clear(); + //previewPane.setPadding(new Insets(5,5,5,5)); centerImageView.setImage(null); } + /** + * Show a dialogue displaying copyright information about the program. + */ + private void showAboutDialog(){ + Dialog aboutDialog = new Dialog<>(); + aboutDialog.setTitle("About this program"); + ButtonType btnType = new ButtonType("Ok", ButtonBar.ButtonData.OK_DONE); + aboutDialog.setContentText(""" + Created by Tim Lukas Förster + Icons made by www.flaticon.com/authors/roundicons for www.flaticon.com"""); + + + //Adding buttons to the aboutDialog pane + aboutDialog.getDialogPane().getButtonTypes().add(btnType); + } + + /** + * Displays a warning dialogue informing the user that no pictures has been loaded yet + * and the executed action is not possible. + */ + private void showNoPicturesLoadedWarning(){ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("No pictures loaded"); + alert.setContentText("No pictures have been loaded" + + "\nPlease select pictures via the menu or via the open button to view them."); + alert.showAndWait().ifPresent(rs -> { + }); + } + /** * Method that handles the settings and layout of the top Panel * @return Node, set up for displaying on the top of a GridPane @@ -326,9 +406,9 @@ public class Controller extends Application { private Node createTop(){ SeparatorMenuItem sep = new SeparatorMenuItem(); // File Menu - fileMenu.getItems().addAll(openFiles, clearViewer, sep, startSlideShow, exitViewer); + fileMenu.getItems().addAll(menuItemOpenFiles, menuItemClearViewer, sep, menuItemStartSlideShow, menuItemExitViewer); // About Menu - aboutMenu.getItems().addAll(showInfo); + aboutMenu.getItems().addAll(menuItemShowInfo); // Menu bar menuBar.getMenus().addAll(fileMenu, aboutMenu); // Adding Menus to Top Panel @@ -372,11 +452,31 @@ public class Controller extends Application { zoomSlider.setShowTickMarks(true); 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 + try { + // Set pictures as prev/next buttons + ImageView prevPic = new ImageView(new Image(new FileInputStream(ARROWPREV))); + prevPic.setFitHeight(10); + prevPic.setPreserveRatio(true); + prevPicBtn.setText(null); + prevPicBtn.setGraphic(prevPic); + + ImageView nextPic = new ImageView(new Image(new FileInputStream(ARROWNEXT))); + nextPic.setFitHeight(10); + nextPic.setPreserveRatio(true); + nextPicBtn.setText(null); + nextPicBtn.setGraphic(nextPic); + } catch (FileNotFoundException ignored){ + // fallback to text + prevPicBtn.setGraphic(null); + nextPicBtn.setGraphic(null); + + prevPicBtn.setText("<-"); + nextPicBtn.setText("->"); + } bottomMid.setAlignment(Pos.CENTER); bottomMid.setSpacing(5); bottomMid.getChildren().addAll(prevPicBtn, slideShowBtn, nextPicBtn); @@ -391,28 +491,16 @@ public class Controller extends Application { bottomLowerPanel.setRight(bottomRight); // Bottom upper Part -> Picture preview - selectionPane.setPadding(new Insets(5,5,155,5)); - selectionPane.autosize(); - selectionPane.setSpacing(5); - pictureSelector.setContent(selectionPane); + pictureSelector.setPadding(new Insets(2,2,2,2)); + previewPane.setOrientation(Orientation.HORIZONTAL); + previewPane.setMaxHeight(150); + //selectionPane.setSpacing(5); + pictureSelector.getChildren().add(previewPane); bottomPanel.getChildren().addAll(pictureSelector, bottomLowerPanel); return (bottomPanel); } - - /** - * Displays a warning dialogue informing the user that no pictures has been loaded yet - * and the executed action is not possible. - */ - private void showNoPicturesLoadedWarning(){ - Alert alert = new Alert(Alert.AlertType.WARNING); - alert.setTitle("No pictures loaded"); - alert.setContentText("No pictures have been loaded" + - "\nPlease select pictures via the menu or via the open button to view them."); - alert.showAndWait().ifPresent(rs -> { - }); - } //////////////////////////////////// //------ End Helper-Methods ------// //////////////////////////////////// diff --git a/src/de/thm/tlf/photoViewer/PictureHandler.java b/src/de/thm/tlf/photoViewer/PictureHandler.java index fbf0653..51bac9c 100644 --- a/src/de/thm/tlf/photoViewer/PictureHandler.java +++ b/src/de/thm/tlf/photoViewer/PictureHandler.java @@ -62,7 +62,8 @@ public final class PictureHandler { } /** - * Determines the next picture that should be displayed + * Determines the next picture that should be displayed. + * Wraps around to the beginning of the list if picture after the last one is requested. * @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 */ @@ -77,7 +78,8 @@ public final class PictureHandler { } /** - * Determines the previous picture that should be displayed + * Determines the previous picture that should be displayed. + * Wraps around to the end of the list if picture before the first one is requested. * @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 */ @@ -90,6 +92,21 @@ public final class PictureHandler { return pictures.get(currentPictureID); } } + + /** + * Method to acquire picture by the list-ID of a picture + * @param pictureID the ID of the Picture that is being requested + * @return Picture from the pictures ArrayList on position of the provided ID + * @throws NoPicturesLoadedException Exception for when no pictures are loaded yet but tried to access them + */ + public Picture getPictureByID(int pictureID) throws NoPicturesLoadedException{ + if(pictures.size() <= 0){ + throw new NoPicturesLoadedException("No pictures have been loaded"); + } + else { + return pictures.get(pictureID); + } + } //------ End Methods ------// }