package com.twasyl.slideshowfx.controls.notification; import com.twasyl.slideshowfx.utils.PlatformHelper; import com.twasyl.slideshowfx.utils.concurrent.SlideshowFXTask; import javafx.animation.FadeTransition; import javafx.animation.PauseTransition; import javafx.animation.SequentialTransition; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlySetProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleSetProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableSet; import javafx.concurrent.Task; import javafx.geometry.Side; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; import javafx.scene.input.MouseButton; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.util.Duration; import java.util.HashSet; /** * A center that contains multiple {@link Notification}. The center displays a progress for the task that is considered * as currently running, as well as label for indicating the task's action. * Clicking on the progress will display all tasks that are currently registered to the center. Each task is wrapped * within a {@link Notification} and can be unregistered from the center. * * @author Thierry Wasylczenko * @version 1.0 * @since SlideshowFX 1.0 */ public class NotificationCenter extends StackPane { /* * Properties of the notification center */ private final ReadOnlySetProperty<SlideshowFXTask> registeredTasks = new SimpleSetProperty<>(FXCollections.observableSet(new HashSet<>())); private final ObjectProperty<SlideshowFXTask> currentTask = new SimpleObjectProperty<>(); /* * UI elements */ private final ProgressBar currentTaskProgress = new ProgressBar(0); private final Label currentTaskMessage = new Label(); private final SequentialTransition labelTransition = new SequentialTransition(); public NotificationCenter() { this.initAnimations(); this.initStatusListeners(); this.initProgressBar(); final HBox currentNotificationPane = new HBox(5); currentNotificationPane.getChildren().addAll(this.currentTaskProgress, this.currentTaskMessage); this.getChildren().add(currentNotificationPane); } /** * Initialize all animation used within the notification center. */ private final void initAnimations() { final PauseTransition pause = new PauseTransition(Duration.seconds(2)); final FadeTransition fadeOut = new FadeTransition(Duration.seconds(1), this.currentTaskMessage); fadeOut.setFromValue(1); fadeOut.setToValue(0); this.labelTransition.setNode(this.currentTaskMessage); this.labelTransition.getChildren().addAll(pause, fadeOut); } /** * Initialize listeners that update the UI when the current task changes or it's status. */ private final void initStatusListeners() { // Initialize the UI elements for responding to the current task status this.currentTaskMessage.textProperty().addListener((textValue, oldText, newText) -> { this.labelTransition.playFromStart(); }); // The listener to the task will update the label and the progress bar for the new task this.currentTask.addListener((taskValue, oldTask, newtask) -> { if (this.currentTaskProgress.progressProperty().isBound()) { this.currentTaskProgress.progressProperty().unbind(); } if (this.currentTaskMessage.textProperty().isBound()) { this.currentTaskMessage.textProperty().unbind(); } if (newtask != null) { this.currentTaskMessage.textProperty().bind(newtask.messageProperty()); this.currentTaskProgress.progressProperty().bind(newtask.progressProperty()); } }); } /** * Initialize the progress bar, like add listener when clicking on it for displaying all tasks. */ private final void initProgressBar() { this.currentTaskProgress.setPrefWidth(200); this.currentTaskProgress.setOnMouseClicked(event -> { if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) { final ContextMenu menu = new ContextMenu(); this.registeredTasks.stream().forEach(task -> { final Notification notification = new Notification(task); menu.getItems().add(notification); }); menu.getItems().addListener((ListChangeListener) (change) -> { if (change.next() && change.wasRemoved()) { change.getRemoved() . stream() .forEach(notification -> NotificationCenter.this.unregisterTask(((Notification) notification).getTask())); } change.reset(); menu.hide(); menu.show(this.currentTaskProgress, Side.TOP, 0, 0); }); menu.show(this.currentTaskProgress, Side.TOP, 0, 0); } }); } /** * Get the tasks currently registered to the notification center. tasks are present in the list until the user * decides to delete them from the UI. * @return The list containing the current tasks of the notification center. */ public ReadOnlySetProperty<SlideshowFXTask> registeredTasksProperty() { return registeredTasks; } /** * Get the tasks currently registered to the notification center. tasks are present in the list until the user * decides to delete them from the UI. * @return The list containing the current tasks of the notification center. */ public ObservableSet<SlideshowFXTask> getRegisteredTasks() { return registeredTasks.get(); } public String getText() { return this.currentTaskMessage.getText(); } public void setText(String text) { this.currentTaskMessage.setText(text); } public double getProgress() { return this.currentTaskProgress.getProgress(); } public void setProgress(double progress) { this.currentTaskProgress.setProgress(progress); } /** * Updates the NotificationCenter with the given progress and text. * @param progress The new progress * @param text The new text */ public void update(double progress, String text) { this.setProgress(progress); this.setText(text); PlatformHelper.run(() -> this.labelTransition.playFromStart()); } /** * Get the current task registered to the notification center. The current task is the one which informations is * displayed next to the progress bar. * @return The current task of the notification center. */ public ObjectProperty<SlideshowFXTask> currentTaskProperty() { return this.currentTask; } /** * Get the current task registered to the notification center. The current task is the one which informations is * displayed next to the progress currentTaskProgress. * @return The current task of the notification center. */ public Task getCurrentTask() { return currentTask.get(); } /** * Defines the given {@code task} as current task of this notification center. The task {@code task} is also * registered to the collection of tasks of this notification center. * This method adds listeners to the {@link javafx.concurrent.Task#messageProperty()} and * {@link javafx.concurrent.Task#progressProperty()} in order to reflect changes to the indicator. * * @param task The task to set to this indicator */ public void setCurrentTask(final SlideshowFXTask task) { this.currentTask.set(task); this.registerTask(task); } /** * Adds a task to the list of current tasks in the notification center. * @param task The task to add. * @throws NullPointerException If the provided task is {@code null}. */ public void registerTask(final Task task) throws NullPointerException { if(task == null) throw new NullPointerException("The task to add to the notification center can not be null"); ((SimpleSetProperty) this.registeredTasks).add(task); } /** * Unregister a task from the notification center. * @param task The task to remove from the notification center. * @throws NullPointerException If the given {@code task} is null. */ public void unregisterTask(final Task task) { if(task == null) throw new NullPointerException("The task to unregister from the notification center can not be null"); ((SimpleSetProperty) this.registeredTasks).remove(task); } }