Finalizing methods, fixing centering issues, added slideshow-time-slider, project cleanup

This commit is contained in:
_N0x 2021-02-14 15:29:59 +01:00
parent b277fadb84
commit 0052037e58
2 changed files with 228 additions and 109 deletions

View File

@ -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<Void>(){
@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<ActionEvent> openFileDialogEvent(Stage stage) {
return e -> openFileDialog(stage);
}
/**
* Event wrapper for clear function
* @return EventHandle executing the clear function
*/
private EventHandler<ActionEvent> 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<Void>(){
@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<ActionEvent> 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<File> 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<File> 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 ------//
/////////////////////////////
}

View File

@ -12,35 +12,60 @@ import java.util.*;
* Implemented using singleton pattern to avoid multiple instances
*/
public final class PictureHandler {
////////////////////////////
//------ Attributes ------//
////////////////////////////
private final ArrayList<Picture> pictures = new ArrayList<>();
private final ArrayList<PicturePreview> 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<PicturePreview> 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<File> 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");