/** * Copyright [2015] [Christian Loehnert] * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.ks.gallery.ui.thumbnail; import de.ks.BaseController; import de.ks.gallery.GalleryItem; import de.ks.gallery.GalleryResource; import de.ks.gallery.ui.slideshow.Slideshow; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.geometry.HPos; import javafx.scene.Node; import javafx.scene.control.Control; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.ProgressIndicator; import javafx.scene.image.ImageView; import javafx.scene.layout.*; import javafx.util.Callback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.io.File; import java.net.URL; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; public class ThumbnailGallery extends BaseController<Object> { private static final Logger log = LoggerFactory.getLogger(ThumbnailGallery.class); @Inject protected GalleryResource resource; @Inject protected ThumbnailCache cache; @FXML protected StackPane root; @FXML protected ProgressIndicator loader; @FXML protected StackPane container; protected final Set<Thumbnail> allThumbNails = new HashSet<>(); protected Slideshow slideshow; protected BiFunction<Control, Thumbnail, Node> enhancer; @Override public void initialize(URL location, ResourceBundle resources) { slideshow = activityInitialization.loadAdditionalController(Slideshow.class).getController(); resource.setCallback(items -> { addItems(items); slideshow.getItems().addAll(items); }); root.widthProperty().addListener((p, o, n) -> { int itemsInRoot = Math.max(0, (int) ((Double) n / Thumbnail.DEFAULT_WIDTH)); relayoutThumbNails(itemsInRoot); }); showLoader(); } public void setFiles(Collection<File> files) { controller.getJavaFXExecutor().submit(this::showLoader); resource.setFiles(files); } public void showLoader() { loader.setProgress(-1); loader.setVisible(true); container.setVisible(false); } public void hideLoader() { loader.setVisible(false); container.setVisible(true); } public void setFolder(File folder, boolean recurse) { controller.getJavaFXExecutor().submit(this::showLoader); resource.setFolder(folder, recurse); } protected void addItems(List<GalleryItem> galleryItems) { log.info("Recreating gallery for {} items", galleryItems.size()); releaseThumbnails(); container.getChildren().clear(); allThumbNails.clear(); List<GalleryItem> items = new ArrayList<>(galleryItems); CompletableFuture<List<Thumbnail>> reserve = cache.reserve(items.size()); reserve.thenApplyAsync(thumbnails -> { int i = 0; hideLoader(); for (Thumbnail thumbnail : thumbnails) { GalleryItem item = items.get(i); thumbnail.setSlideshow(slideshow); thumbnail.setItem(item); i++; allThumbNails.add(thumbnail); } int itemsInRoot = Math.max(0, (int) root.getWidth() / Thumbnail.DEFAULT_WIDTH); relayoutThumbNails(itemsInRoot); return thumbnails; }, controller.getJavaFXExecutor()); } protected void relayoutThumbNails(int itemsInRoot) { container.getChildren().clear(); ObservableList<GridPane> items = FXCollections.observableArrayList(); Map<GridPane, List<Thumbnail>> box2Thumbnail = new HashMap<>(); GridPane gridPane = createPane(itemsInRoot, items); box2Thumbnail.put(gridPane, new ArrayList<>(5)); int i = 1; ArrayList<Thumbnail> thumbnails = new ArrayList<>(allThumbNails); Collections.sort(thumbnails, Comparator.comparing(t -> t.getItem())); for (Thumbnail thumbnail : thumbnails) { log.info("Processing thumbnail {}", thumbnail.getItem().getName()); Control root = thumbnail.getRoot(); box2Thumbnail.get(gridPane).add(thumbnail); Node enhance = enhance(root, thumbnail); gridPane.add(enhance, i - 1, 0); i++; if (i > itemsInRoot) { i = 1; gridPane = createPane(itemsInRoot, items); box2Thumbnail.put(gridPane, new ArrayList<>(5)); } } ListView<GridPane> listView = new ListView<>(); listView.setCellFactory(new Callback<ListView<GridPane>, ListCell<GridPane>>() { @Override public ListCell<GridPane> call(ListView<GridPane> param) { ListCell<GridPane> cell = new ListCell<GridPane>() { @Override protected void updateItem(GridPane item, boolean empty) { super.updateItem(item, empty); if (item != null) { box2Thumbnail.get(item).forEach(t -> { CompletableFuture.supplyAsync(() -> t.getItem().getThumbNail(), controller.getExecutorService())// .thenAcceptAsync(image -> { ImageView imageView = t.getImageView(); if (image != null) { imageView.setImage(image); imageView.setFitHeight(image.getHeight()); imageView.setFitWidth(image.getWidth()); } else { imageView.setImage(null); } }, controller.getJavaFXExecutor()); }); setGraphic(item); log.info("Updating item {}", items.indexOf(item)); } else { setGraphic(null); } } }; return cell; } }); listView.setItems(items); container.getChildren().add(listView); } private GridPane createPane(int itemsInRoot, ObservableList<GridPane> items) { GridPane gridPane = new GridPane(); for (int i = 0; i < itemsInRoot; i++) { gridPane.getColumnConstraints().add(new ColumnConstraints(Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE, Control.USE_COMPUTED_SIZE, Priority.SOMETIMES, HPos.CENTER, true)); } items.add(gridPane); return gridPane; } private Node enhance(Control root, Thumbnail thumbnail) { if (enhancer != null) { return enhancer.apply(root, thumbnail); } return root; } private void releaseThumbnails() { cache.release(allThumbNails); } public Pane getRoot() { return root; } public Set<Thumbnail> getAllThumbNails() { return allThumbNails; } public BiFunction<Control, Thumbnail, Node> getEnhancer() { return enhancer; } public void setEnhancer(BiFunction<Control, Thumbnail, Node> enhancer) { this.enhancer = enhancer; } public ProgressIndicator getLoader() { return loader; } public Slideshow getSlideshow() { return slideshow; } }