From fdfe0741dc5884a56ab98b1f545b5d37a8f49dfa Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Thu, 7 May 2020 16:38:11 +0200 Subject: [PATCH 01/41] First draft of a task progress dialog Implemented a task progress dialog using a TaskProgressView. Tasks show up, but without info. Neither the progress nor title and message are shown. --- src/main/java/org/jabref/gui/JabRefFrame.java | 8 +++ .../java/org/jabref/gui/StateManager.java | 10 +++ .../gui/fieldeditors/LinkedFileViewModel.java | 2 + .../TaskProgressDialog.css | 15 +++++ .../TaskProgressDialog.fxml | 13 ++++ .../TaskProgressDialog.java | 49 ++++++++++++++ .../taskprogressmanager/TaskViewModel.java | 67 +++++++++++++++++++ .../org/jabref/gui/util/BackgroundTask.java | 5 ++ .../jabref/gui/util/DefaultTaskExecutor.java | 9 ++- 9 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css create mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml create mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java create mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 3dcb7450849..d8a6343baa2 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -40,6 +40,7 @@ import javafx.scene.layout.VBox; import javafx.stage.Stage; +import javafx.stage.Window; import org.jabref.Globals; import org.jabref.JabRefExecutorService; import org.jabref.gui.actions.ActionFactory; @@ -107,6 +108,7 @@ import org.jabref.gui.shared.ConnectToSharedDatabaseCommand; import org.jabref.gui.shared.PullChangesFromSharedAction; import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory; +import org.jabref.gui.taskprogressmanager.TaskProgressDialog; import org.jabref.gui.texparser.ParseLatexAction; import org.jabref.gui.undo.CountingUndoManager; import org.jabref.gui.undo.UndoRedoAction; @@ -1074,6 +1076,12 @@ private void addImportedEntries(final BasePanel panel, final ParserResult parser ImportEntriesDialog dialog = new ImportEntriesDialog(panel.getBibDatabaseContext(), task); dialog.setTitle(Localization.lang("Import")); dialog.showAndWait(); + + TaskProgressDialog taskProgressDialog = new TaskProgressDialog(); + taskProgressDialog.setTitle("Task progress"); + Window dialogWindow = taskProgressDialog.getDialogPane().getScene().getWindow(); + dialogWindow.setOnCloseRequest(event -> dialogWindow.hide()); + taskProgressDialog.showAndWait(); } public FileHistoryMenu getFileHistory() { diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 05a9364f65d..9b6665ad33a 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -13,6 +13,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; +import javafx.concurrent.Task; import javafx.scene.Node; import org.jabref.gui.util.CustomLocalDragboard; @@ -41,6 +42,7 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); + private final ObservableList backgroundTasks = FXCollections.observableArrayList(); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); @@ -112,4 +114,12 @@ public void setSearchQuery(SearchQuery searchQuery) { public OptionalObjectProperty focusOwnerProperty() { return focusOwner; } public Optional getFocusOwner() { return focusOwner.get(); } + + public ObservableList getBackgroundTasks() { + return backgroundTasks; + } + + public void addBackgroundTask(Task backgroundTask) { + this.backgroundTasks.add(backgroundTask); + } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index 352cff0eaa3..27bbf44d76f 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -417,6 +417,8 @@ public void download() { entry.addFile(0, newLinkedFile); }); downloadProgress.bind(downloadTask.workDonePercentageProperty()); + downloadTask.titleProperty().set("Downloading"); + downloadTask.messageProperty().set(linkedFile.getLink()); taskExecutor.execute(downloadTask); } catch (MalformedURLException exception) { dialogService.showErrorDialogAndWait(Localization.lang("Invalid URL"), exception); diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css new file mode 100644 index 00000000000..81442716e85 --- /dev/null +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css @@ -0,0 +1,15 @@ +.addEntryButton { + -fx-font-size: 2em; +} + +.entry-container { + /*-fx-padding: 0.5em 0em 0.5em 0em;*/ +} + +.list-cell { + -fx-padding: 0.5em 0 1em 0.5em; +} + +.list-cell:entry-selected { + -fx-background-color: derive(-jr-selected, 60%); +} diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml new file mode 100644 index 00000000000..13ef28d7354 --- /dev/null +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java new file mode 100644 index 00000000000..d0875562142 --- /dev/null +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java @@ -0,0 +1,49 @@ +package org.jabref.gui.taskprogressmanager; + +import com.airhacks.afterburner.views.ViewLoader; +import javafx.collections.ListChangeListener; +import javafx.concurrent.Task; +import javafx.fxml.FXML; +import org.controlsfx.control.TaskProgressView; +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.util.*; + +import javax.inject.Inject; + +public class TaskProgressDialog extends BaseDialog { + + public TaskProgressView> taskProgressView; + private TaskViewModel viewModel; + @Inject private DialogService dialogService; + @Inject private StateManager stateManager; + + public TaskProgressDialog() { + ViewLoader.view(this) + .load() + .setAsDialogPane(this); + } + + @FXML + private void initialize() { + viewModel = new TaskViewModel(dialogService, stateManager); + taskProgressView.setRetainTasks(true); + + viewModel.getBackgroundTasks().addListener(new ListChangeListener() { + @Override + public void onChanged(Change c) { + while (c.next()) { + + for (Task t : c.getAddedSubList()) { + taskProgressView.getTasks().add(t); + System.out.println("MY LOG add TITLE \"" + t.getTitle() + "\""); + System.out.println("MY LOG add MESSAGE \"" + t.getMessage()+ "\""); + } + for (Task t : c.getRemoved()) { + taskProgressView.getTasks().remove(t); + } + } + } + }); + } +} diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java new file mode 100644 index 00000000000..4e1fef9e921 --- /dev/null +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java @@ -0,0 +1,67 @@ +package org.jabref.gui.taskprogressmanager; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import org.jabref.Globals; +import org.jabref.JabRefGUI; +import org.jabref.gui.AbstractViewModel; +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; +import org.jabref.gui.externalfiles.ImportHandler; +import org.jabref.gui.externalfiletype.ExternalFileTypes; +import org.jabref.gui.fieldeditors.LinkedFileViewModel; +import org.jabref.gui.groups.GroupTreeNodeViewModel; +import org.jabref.gui.groups.UndoableAddOrRemoveGroup; +import org.jabref.gui.undo.NamedCompound; +import org.jabref.gui.undo.UndoableInsertEntries; +import org.jabref.gui.undo.UndoableInsertString; +import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.bibtex.DuplicateCheck; +import org.jabref.logic.importer.ParserResult; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibtexString; +import org.jabref.model.entry.LinkedFile; +import org.jabref.model.groups.GroupTreeNode; +import org.jabref.model.metadata.MetaData; +import org.jabref.model.util.FileUpdateMonitor; +import org.jabref.preferences.PreferencesService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.undo.UndoManager; +import java.util.List; +import java.util.Optional; + +public class TaskViewModel extends AbstractViewModel { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskViewModel.class); + + private final StringProperty message; + private final DialogService dialogService; + private final StateManager stateManager; + private ObservableList tasks; + + public TaskViewModel(DialogService dialogService, StateManager stateManager) { + this.dialogService = dialogService; + this.stateManager = stateManager; + this.tasks = stateManager.getBackgroundTasks(); + this.message = new SimpleStringProperty(); + } + + public String getMessage() { + return message.get(); + } + + public ObservableList getBackgroundTasks() {return this.tasks;}; + + public StringProperty messageProperty() { + return message; + } +} diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index f3f2cd24d09..4d56ca11280 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -34,6 +34,7 @@ public abstract class BackgroundTask { private BooleanProperty isCanceled = new SimpleBooleanProperty(false); private ObjectProperty progress = new SimpleObjectProperty<>(new BackgroundProgress(0, 0)); private StringProperty message = new SimpleStringProperty(""); + private StringProperty title = new SimpleStringProperty(this.getClass().getSimpleName()); private DoubleProperty workDonePercentage = new SimpleDoubleProperty(0); public BackgroundTask() { @@ -90,6 +91,10 @@ public StringProperty messageProperty() { return message; } + public StringProperty titleProperty() { + return title; + } + public double getWorkDonePercentage() { return workDonePercentage.get(); } diff --git a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java index 91bf5da2180..ee860ebccfe 100644 --- a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java +++ b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java @@ -16,6 +16,8 @@ import javafx.application.Platform; import javafx.concurrent.Task; +import org.jabref.Globals; +import org.jabref.gui.StateManager; import org.jabref.logic.util.DelayTaskThrottler; import org.slf4j.Logger; @@ -96,7 +98,9 @@ public static void runInJavaFXThread(Runnable runnable) { @Override public Future execute(BackgroundTask task) { - return execute(getJavaFXTask(task)); + Task javafxTask = getJavaFXTask(task); + Globals.stateManager.addBackgroundTask(javafxTask); + return execute(javafxTask); } @Override @@ -128,8 +132,11 @@ private Task getJavaFXTask(BackgroundTask task) { Task javaTask = new Task() { { + this.updateMessage(task.messageProperty().get()); + this.updateTitle(task.titleProperty().get()); BindingsHelper.subscribeFuture(task.progressProperty(), progress -> updateProgress(progress.getWorkDone(), progress.getMax())); BindingsHelper.subscribeFuture(task.messageProperty(), this::updateMessage); + BindingsHelper.subscribeFuture(task.titleProperty(), this::updateTitle); BindingsHelper.subscribeFuture(task.isCanceledProperty(), cancelled -> { if (cancelled) { cancel(); From 6fd181130c8d6d4e7f848cb2aebddb4c41e9baf5 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Thu, 7 May 2020 22:03:34 +0200 Subject: [PATCH 02/41] Added progress indicator in JabRefFrame There now i a progress indicator at the rightmost postition of JabRefs toolbar. It shows the average progress of all running background tasks. Clicking it will show a TaskProgressDialog. still looks ugly and the binding to the average progress does not seem to be working. --- src/main/java/org/jabref/gui/JabRefFrame.java | 33 +++++++++++++++---- .../java/org/jabref/gui/StateManager.java | 16 +++++++-- .../TaskProgressDialog.java | 2 -- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index d8a6343baa2..daabfe6abf6 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -14,6 +14,9 @@ import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; +import javafx.collections.ListChangeListener; +import javafx.concurrent.Task; +import javafx.event.EventHandler; import javafx.geometry.Orientation; import javafx.scene.Node; import javafx.scene.control.Alert; @@ -23,6 +26,7 @@ import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; +import javafx.scene.control.ProgressIndicator; import javafx.scene.control.Separator; import javafx.scene.control.SeparatorMenuItem; import javafx.scene.control.SplitPane; @@ -32,6 +36,7 @@ import javafx.scene.control.Tooltip; import javafx.scene.control.skin.TabPaneSkin; import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; @@ -519,7 +524,9 @@ private Node createToolbar() { new Separator(Orientation.VERTICAL), factory.createIconButton(StandardActions.OPEN_GITHUB, new OpenBrowserAction("https://github.com/JabRef/jabref")), factory.createIconButton(StandardActions.OPEN_FACEBOOK, new OpenBrowserAction("https://www.facebook.com/JabRef/")), - factory.createIconButton(StandardActions.OPEN_TWITTER, new OpenBrowserAction("https://twitter.com/jabref_org")) + factory.createIconButton(StandardActions.OPEN_TWITTER, new OpenBrowserAction("https://twitter.com/jabref_org")), + new Separator(Orientation.VERTICAL), + createTaskIndicator() ); HBox.setHgrow(globalSearchBar, Priority.ALWAYS); @@ -923,6 +930,24 @@ private MenuBar createMenu() { return menu; } + private ProgressIndicator createTaskIndicator() { + ProgressIndicator indicator = new ProgressIndicator(1); + indicator.progressProperty().bind(stateManager.tasksProgressBinding); + + indicator.setOnMouseClicked(new EventHandler() { + @Override + public void handle(MouseEvent event) { + TaskProgressDialog taskProgressDialog = new TaskProgressDialog(); + // @TODO add localization + taskProgressDialog.setTitle("Task progress"); + Window dialogWindow = taskProgressDialog.getDialogPane().getScene().getWindow(); + dialogWindow.setOnCloseRequest(windowEvent -> dialogWindow.hide()); + taskProgressDialog.showAndWait(); + } + }); + return indicator; + } + public void addParserResult(ParserResult parserResult, boolean focusPanel) { if (parserResult.toOpenTab()) { // Add the entries to the open tab. @@ -1076,12 +1101,6 @@ private void addImportedEntries(final BasePanel panel, final ParserResult parser ImportEntriesDialog dialog = new ImportEntriesDialog(panel.getBibDatabaseContext(), task); dialog.setTitle(Localization.lang("Import")); dialog.showAndWait(); - - TaskProgressDialog taskProgressDialog = new TaskProgressDialog(); - taskProgressDialog.setTitle("Task progress"); - Window dialogWindow = taskProgressDialog.getDialogPane().getScene().getWindow(); - dialogWindow.setOnCloseRequest(event -> dialogWindow.hide()); - taskProgressDialog.showAndWait(); } public FileHistoryMenu getFileHistory() { diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 9b6665ad33a..68dcb740cf2 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -5,7 +5,10 @@ import java.util.Optional; import java.util.stream.Collectors; +import javafx.beans.binding.Binding; import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.binding.DoubleBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ReadOnlyListProperty; import javafx.beans.property.ReadOnlyListWrapper; @@ -18,6 +21,7 @@ import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.OptionalObjectProperty; +import org.jabref.gui.util.uithreadaware.UiThreadObservableList; import org.jabref.logic.search.SearchQuery; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -42,7 +46,7 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final ObservableList backgroundTasks = FXCollections.observableArrayList(); + private final UiThreadObservableList backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); @@ -115,11 +119,19 @@ public void setSearchQuery(SearchQuery searchQuery) { public Optional getFocusOwner() { return focusOwner.get(); } - public ObservableList getBackgroundTasks() { + public UiThreadObservableList getBackgroundTasks() { return backgroundTasks; } public void addBackgroundTask(Task backgroundTask) { this.backgroundTasks.add(backgroundTask); } + + public BooleanBinding anyTaskRunningBinding = Bindings.createBooleanBinding( + () -> backgroundTasks.stream().anyMatch(Task::isRunning) + ); + + public DoubleBinding tasksProgressBinding = Bindings.createDoubleBinding( + () -> backgroundTasks.stream().filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1) + ); } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java index d0875562142..964aa573ef1 100644 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java @@ -36,8 +36,6 @@ public void onChanged(Change c) { for (Task t : c.getAddedSubList()) { taskProgressView.getTasks().add(t); - System.out.println("MY LOG add TITLE \"" + t.getTitle() + "\""); - System.out.println("MY LOG add MESSAGE \"" + t.getMessage()+ "\""); } for (Task t : c.getRemoved()) { taskProgressView.getTasks().remove(t); From b8834914e5e735abaa7a710216888abe6bf54277 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 09:17:50 +0200 Subject: [PATCH 03/41] Beautified progressindicator and added localization --- src/main/java/org/jabref/gui/Base.css | 15 ++++++++ src/main/java/org/jabref/gui/JabRefFrame.java | 34 ++++++++++++------- src/main/resources/l10n/JabRef_en.properties | 2 ++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index c136404b276..cd542f800cf 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -387,6 +387,21 @@ -fx-padding: -0.1em 0.5em 0.5em 0.5em; } +.progress-indicator { + -fx-progress-color: -jr-theme; + -fx-border-width: 0px; + -fx-background-color: -jr-icon-background; + -fx-padding: 0.5em; +} + +.progress-indicator .percentage { + -fx-fill:null; +} + +.progress-indicator:hover { + -fx-background-color: -jr-icon-background-active; +} + .check-box { -fx-label-padding: 0.0em 0.0em 0.0em 0.75em; -fx-text-fill: -fx-text-background-color; diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index daabfe6abf6..dc363cb4a88 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -18,6 +18,7 @@ import javafx.concurrent.Task; import javafx.event.EventHandler; import javafx.geometry.Orientation; +import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.control.Alert; import javafx.scene.control.Button; @@ -43,6 +44,7 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import javafx.stage.Window; @@ -930,22 +932,30 @@ private MenuBar createMenu() { return menu; } - private ProgressIndicator createTaskIndicator() { - ProgressIndicator indicator = new ProgressIndicator(1); + private Group createTaskIndicator() { + ProgressIndicator indicator = new ProgressIndicator(); + indicator.getStyleClass().setAll("progress-indicator"); indicator.progressProperty().bind(stateManager.tasksProgressBinding); - indicator.setOnMouseClicked(new EventHandler() { - @Override - public void handle(MouseEvent event) { - TaskProgressDialog taskProgressDialog = new TaskProgressDialog(); - // @TODO add localization - taskProgressDialog.setTitle("Task progress"); - Window dialogWindow = taskProgressDialog.getDialogPane().getScene().getWindow(); - dialogWindow.setOnCloseRequest(windowEvent -> dialogWindow.hide()); - taskProgressDialog.showAndWait(); + /* + The label of the indicator cannot be removed with styling. Therefore, + hide it and clip it to a square of (width x width) each time width is updated. + */ + indicator.widthProperty().addListener((observable, oldValue, newValue) -> { + if(newValue.doubleValue()>0){ + Rectangle clip=new Rectangle(newValue.doubleValue(),newValue.doubleValue()); + indicator.setClip(clip); } }); - return indicator; + + indicator.setOnMouseClicked(event -> { + TaskProgressDialog taskProgressDialog = new TaskProgressDialog(); + taskProgressDialog.setTitle(Localization.lang("Background Tasks")); + Window dialogWindow = taskProgressDialog.getDialogPane().getScene().getWindow(); + dialogWindow.setOnCloseRequest(windowEvent -> dialogWindow.hide()); + taskProgressDialog.showAndWait(); + }); + return new Group(indicator); } public void addParserResult(ParserResult parserResult, boolean focusPanel) { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index b20516b6a12..fecf3681fc4 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -92,6 +92,8 @@ Available\ import\ formats=Available import formats %0\ source=%0 source +Background\ Tasks=Background Tasks + Browse=Browse by=by From 255a6e491c7ad7da26c5fa761395e9f448aa671b Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 11:15:56 +0200 Subject: [PATCH 04/41] Changed to Task in the Tasklist. This makes the view work with download tasks for example. Most other tasks are still shown without title, message (because none are set) and progress. Also, there are a lot of tasks somehow. The progress indicator in the main view still does not work as I can't get the bindings to work. --- src/main/java/org/jabref/gui/JabRefFrame.java | 2 +- .../java/org/jabref/gui/StateManager.java | 22 +++++++++++-------- .../TaskProgressDialog.java | 18 +++------------ .../taskprogressmanager/TaskViewModel.java | 4 ++-- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index dc363cb4a88..fd57232bb07 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -935,7 +935,7 @@ private MenuBar createMenu() { private Group createTaskIndicator() { ProgressIndicator indicator = new ProgressIndicator(); indicator.getStyleClass().setAll("progress-indicator"); - indicator.progressProperty().bind(stateManager.tasksProgressBinding); + //indicator.progressProperty().bind(stateManager.tasksProgressBinding); /* The label of the indicator cannot be removed with styling. Therefore, diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 68dcb740cf2..b8e437e1540 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -19,6 +19,8 @@ import javafx.concurrent.Task; import javafx.scene.Node; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.monadic.MonadicBinding; import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.gui.util.uithreadaware.UiThreadObservableList; @@ -46,7 +48,7 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final UiThreadObservableList backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); + private final UiThreadObservableList> backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); @@ -119,19 +121,21 @@ public void setSearchQuery(SearchQuery searchQuery) { public Optional getFocusOwner() { return focusOwner.get(); } - public UiThreadObservableList getBackgroundTasks() { + public UiThreadObservableList> getBackgroundTasks() { return backgroundTasks; } - public void addBackgroundTask(Task backgroundTask) { + public void addBackgroundTask(Task backgroundTask) { this.backgroundTasks.add(backgroundTask); } - - public BooleanBinding anyTaskRunningBinding = Bindings.createBooleanBinding( - () -> backgroundTasks.stream().anyMatch(Task::isRunning) +/* + public Binding anyTaskRunningBinding = EasyBind.combine( + backgroundTasks, + stream -> stream.anyMatch(Task::getProgress) ); - public DoubleBinding tasksProgressBinding = Bindings.createDoubleBinding( - () -> backgroundTasks.stream().filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1) - ); + public Binding tasksProgressBinding = (Binding) EasyBind.combine( + backgroundTasks, + stream -> stream.filter(Task::isRunning).mapToDouble(Task::getProgress) + .average().orElse(1));*/ } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java index 964aa573ef1..0d0b3aa9445 100644 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java @@ -5,6 +5,7 @@ import javafx.concurrent.Task; import javafx.fxml.FXML; import org.controlsfx.control.TaskProgressView; +import org.fxmisc.easybind.EasyBind; import org.jabref.gui.DialogService; import org.jabref.gui.StateManager; import org.jabref.gui.util.*; @@ -13,7 +14,7 @@ public class TaskProgressDialog extends BaseDialog { - public TaskProgressView> taskProgressView; + public TaskProgressView> taskProgressView; private TaskViewModel viewModel; @Inject private DialogService dialogService; @Inject private StateManager stateManager; @@ -29,19 +30,6 @@ private void initialize() { viewModel = new TaskViewModel(dialogService, stateManager); taskProgressView.setRetainTasks(true); - viewModel.getBackgroundTasks().addListener(new ListChangeListener() { - @Override - public void onChanged(Change c) { - while (c.next()) { - - for (Task t : c.getAddedSubList()) { - taskProgressView.getTasks().add(t); - } - for (Task t : c.getRemoved()) { - taskProgressView.getTasks().remove(t); - } - } - } - }); + EasyBind.listBind(taskProgressView.getTasks(), viewModel.getBackgroundTasks()); } } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java index 4e1fef9e921..d4f5d455a57 100644 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java @@ -46,7 +46,7 @@ public class TaskViewModel extends AbstractViewModel { private final StringProperty message; private final DialogService dialogService; private final StateManager stateManager; - private ObservableList tasks; + private ObservableList> tasks; public TaskViewModel(DialogService dialogService, StateManager stateManager) { this.dialogService = dialogService; @@ -59,7 +59,7 @@ public String getMessage() { return message.get(); } - public ObservableList getBackgroundTasks() {return this.tasks;}; + public ObservableList> getBackgroundTasks() {return this.tasks;}; public StringProperty messageProperty() { return message; From 38dd89dce28b72d195de18b848b011532ef1f868 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 13:14:39 +0200 Subject: [PATCH 05/41] Resolved typing issues for bindings The progress indicator is now successfully bound to the list of tasks. However, tasks do not show up in the dialogue any more. --- src/main/java/org/jabref/gui/JabRefFrame.java | 2 +- .../java/org/jabref/gui/StateManager.java | 22 ++++++++++--------- .../TaskProgressDialog.java | 15 ++++++++++++- .../taskprogressmanager/TaskViewModel.java | 6 +++-- .../jabref/gui/util/DefaultTaskExecutor.java | 3 ++- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index fd57232bb07..dc363cb4a88 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -935,7 +935,7 @@ private MenuBar createMenu() { private Group createTaskIndicator() { ProgressIndicator indicator = new ProgressIndicator(); indicator.getStyleClass().setAll("progress-indicator"); - //indicator.progressProperty().bind(stateManager.tasksProgressBinding); + indicator.progressProperty().bind(stateManager.tasksProgressBinding); /* The label of the indicator cannot be removed with styling. Therefore, diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index b8e437e1540..954b332bd42 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -10,6 +10,7 @@ import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.DoubleBinding; import javafx.beans.property.IntegerProperty; +import javafx.beans.property.Property; import javafx.beans.property.ReadOnlyListProperty; import javafx.beans.property.ReadOnlyListWrapper; import javafx.beans.property.SimpleIntegerProperty; @@ -20,7 +21,6 @@ import javafx.scene.Node; import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.monadic.MonadicBinding; import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.gui.util.uithreadaware.UiThreadObservableList; @@ -48,7 +48,7 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final UiThreadObservableList> backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); + private final UiThreadObservableList>> backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); @@ -121,21 +121,23 @@ public void setSearchQuery(SearchQuery searchQuery) { public Optional getFocusOwner() { return focusOwner.get(); } - public UiThreadObservableList> getBackgroundTasks() { + public UiThreadObservableList>> getBackgroundTasks() { return backgroundTasks; } - public void addBackgroundTask(Task backgroundTask) { + public void addBackgroundTask(Property> backgroundTask) { this.backgroundTasks.add(backgroundTask); } -/* - public Binding anyTaskRunningBinding = EasyBind.combine( + + Binding anyTaskRunningBinding = EasyBind., Boolean>combine( backgroundTasks, - stream -> stream.anyMatch(Task::getProgress) + tStream -> tStream.anyMatch(Task::isRunning) ); - public Binding tasksProgressBinding = (Binding) EasyBind.combine( + public Binding tasksProgressBinding = EasyBind., Double>combine( backgroundTasks, - stream -> stream.filter(Task::isRunning).mapToDouble(Task::getProgress) - .average().orElse(1));*/ + stream -> stream.>filter(Task::isRunning).mapToDouble(Task::getProgress) + .average().orElse(1) + ); + } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java index 0d0b3aa9445..03d3b1544f4 100644 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java @@ -1,6 +1,7 @@ package org.jabref.gui.taskprogressmanager; import com.airhacks.afterburner.views.ViewLoader; +import javafx.beans.property.Property; import javafx.collections.ListChangeListener; import javafx.concurrent.Task; import javafx.fxml.FXML; @@ -30,6 +31,18 @@ private void initialize() { viewModel = new TaskViewModel(dialogService, stateManager); taskProgressView.setRetainTasks(true); - EasyBind.listBind(taskProgressView.getTasks(), viewModel.getBackgroundTasks()); + viewModel.getBackgroundTasks().addListener(new ListChangeListener>>() { + @Override + public void onChanged(Change>> c) { + while(c.next()) { + for (Property> taskProperty : c.getAddedSubList()) { + taskProgressView.getTasks().add(taskProperty.getValue()); + } + for (Property> taskProperty : c.getRemoved()) { + taskProgressView.getTasks().remove(taskProperty.getValue()); + } + } + } + }); } } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java index d4f5d455a57..0ee1351f75f 100644 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.taskprogressmanager; +import javafx.beans.property.Property; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; @@ -21,6 +22,7 @@ import org.jabref.gui.undo.UndoableInsertString; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; +import org.jabref.gui.util.uithreadaware.UiThreadObservableList; import org.jabref.logic.bibtex.DuplicateCheck; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; @@ -46,7 +48,7 @@ public class TaskViewModel extends AbstractViewModel { private final StringProperty message; private final DialogService dialogService; private final StateManager stateManager; - private ObservableList> tasks; + private UiThreadObservableList>> tasks; public TaskViewModel(DialogService dialogService, StateManager stateManager) { this.dialogService = dialogService; @@ -59,7 +61,7 @@ public String getMessage() { return message.get(); } - public ObservableList> getBackgroundTasks() {return this.tasks;}; + public UiThreadObservableList>> getBackgroundTasks() {return this.tasks;}; public StringProperty messageProperty() { return message; diff --git a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java index ee860ebccfe..6540c78eccb 100644 --- a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java +++ b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java @@ -14,6 +14,7 @@ import java.util.function.Consumer; import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; import javafx.concurrent.Task; import org.jabref.Globals; @@ -99,7 +100,7 @@ public static void runInJavaFXThread(Runnable runnable) { @Override public Future execute(BackgroundTask task) { Task javafxTask = getJavaFXTask(task); - Globals.stateManager.addBackgroundTask(javafxTask); + Globals.stateManager.addBackgroundTask(new SimpleObjectProperty>(javafxTask)); return execute(javafxTask); } From 1b2aaa6cbe51571c83bc5b65e5a9c99047b03ba5 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 13:33:42 +0200 Subject: [PATCH 06/41] Converted list of Properties to tasks for listbind When using ObjectProperties in the list of tasks, we can use EasyBind to convert the list into a list of tasks which in turn can be bound to the list of tasks in the view. With this, the basic functionality works. There is a progress indicator in the toolbar that shows the average progress of all running background tasks. It is indeterminate if any task has indeterminate progress and shows 100% if no tasks are running. Clicking it opens an overview of all running tasks and their progress. Currently, there are many tasks running all the time. The only tasks that were adapted for this to be pretty are the download tasks, and they are also still missing an icon. --- .../java/org/jabref/gui/StateManager.java | 8 ++++---- .../TaskProgressDialog.java | 20 ++++++------------- .../taskprogressmanager/TaskViewModel.java | 6 +++--- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 954b332bd42..218fc758bae 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -10,7 +10,7 @@ import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.DoubleBinding; import javafx.beans.property.IntegerProperty; -import javafx.beans.property.Property; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyListProperty; import javafx.beans.property.ReadOnlyListWrapper; import javafx.beans.property.SimpleIntegerProperty; @@ -48,7 +48,7 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final UiThreadObservableList>> backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); + private final UiThreadObservableList>> backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); @@ -121,11 +121,11 @@ public void setSearchQuery(SearchQuery searchQuery) { public Optional getFocusOwner() { return focusOwner.get(); } - public UiThreadObservableList>> getBackgroundTasks() { + public UiThreadObservableList>> getBackgroundTasks() { return backgroundTasks; } - public void addBackgroundTask(Property> backgroundTask) { + public void addBackgroundTask(ObjectProperty> backgroundTask) { this.backgroundTasks.add(backgroundTask); } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java index 03d3b1544f4..d07e3bdad26 100644 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java @@ -1,8 +1,9 @@ package org.jabref.gui.taskprogressmanager; import com.airhacks.afterburner.views.ViewLoader; -import javafx.beans.property.Property; +import javafx.beans.property.ObjectProperty; import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.fxml.FXML; import org.controlsfx.control.TaskProgressView; @@ -20,6 +21,8 @@ public class TaskProgressDialog extends BaseDialog { @Inject private DialogService dialogService; @Inject private StateManager stateManager; + private ObservableList> tasks; + public TaskProgressDialog() { ViewLoader.view(this) .load() @@ -31,18 +34,7 @@ private void initialize() { viewModel = new TaskViewModel(dialogService, stateManager); taskProgressView.setRetainTasks(true); - viewModel.getBackgroundTasks().addListener(new ListChangeListener>>() { - @Override - public void onChanged(Change>> c) { - while(c.next()) { - for (Property> taskProperty : c.getAddedSubList()) { - taskProgressView.getTasks().add(taskProperty.getValue()); - } - for (Property> taskProperty : c.getRemoved()) { - taskProgressView.getTasks().remove(taskProperty.getValue()); - } - } - } - }); + tasks = EasyBind.map(viewModel.getBackgroundTasks(), ObjectProperty>::>get); + EasyBind.listBind(taskProgressView.getTasks(), tasks); } } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java index 0ee1351f75f..d22fdec0d10 100644 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java +++ b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java @@ -1,6 +1,6 @@ package org.jabref.gui.taskprogressmanager; -import javafx.beans.property.Property; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; @@ -48,7 +48,7 @@ public class TaskViewModel extends AbstractViewModel { private final StringProperty message; private final DialogService dialogService; private final StateManager stateManager; - private UiThreadObservableList>> tasks; + private UiThreadObservableList>> tasks; public TaskViewModel(DialogService dialogService, StateManager stateManager) { this.dialogService = dialogService; @@ -61,7 +61,7 @@ public String getMessage() { return message.get(); } - public UiThreadObservableList>> getBackgroundTasks() {return this.tasks;}; + public UiThreadObservableList>> getBackgroundTasks() {return this.tasks;}; public StringProperty messageProperty() { return message; From baf9bd0e7d55de96a74d1dbc12df604a4ef5c997 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 13:45:47 +0200 Subject: [PATCH 07/41] New Tasks are first in the list --- src/main/java/org/jabref/gui/StateManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 218fc758bae..3737e0f56ba 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -126,7 +126,7 @@ public UiThreadObservableList>> getBackgroundTasks() { } public void addBackgroundTask(ObjectProperty> backgroundTask) { - this.backgroundTasks.add(backgroundTask); + this.backgroundTasks.add(0, backgroundTask); } Binding anyTaskRunningBinding = EasyBind., Boolean>combine( From 40a8febeeff1e5f8aa760144ac7ec514dabe16f2 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 15:15:07 +0200 Subject: [PATCH 08/41] Use a PopOver instead of a dialog --- src/main/java/org/jabref/gui/JabRefFrame.java | 23 +++++-- .../TaskProgressDialog.css | 15 ---- .../TaskProgressDialog.fxml | 13 ---- .../TaskProgressDialog.java | 40 ----------- .../taskprogressmanager/TaskViewModel.java | 69 ------------------- 5 files changed, 17 insertions(+), 143 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css delete mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml delete mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java delete mode 100644 src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index dc363cb4a88..8d210bdd922 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -12,9 +12,11 @@ import java.util.TimerTask; import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.event.EventHandler; import javafx.geometry.Orientation; @@ -48,6 +50,8 @@ import javafx.stage.Stage; import javafx.stage.Window; +import org.controlsfx.control.PopOver; +import org.controlsfx.control.TaskProgressView; import org.jabref.Globals; import org.jabref.JabRefExecutorService; import org.jabref.gui.actions.ActionFactory; @@ -115,7 +119,6 @@ import org.jabref.gui.shared.ConnectToSharedDatabaseCommand; import org.jabref.gui.shared.PullChangesFromSharedAction; import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory; -import org.jabref.gui.taskprogressmanager.TaskProgressDialog; import org.jabref.gui.texparser.ParseLatexAction; import org.jabref.gui.undo.CountingUndoManager; import org.jabref.gui.undo.UndoRedoAction; @@ -948,13 +951,21 @@ hide it and clip it to a square of (width x width) each time width is updated. } }); + indicator.setOnMouseClicked(event -> { - TaskProgressDialog taskProgressDialog = new TaskProgressDialog(); - taskProgressDialog.setTitle(Localization.lang("Background Tasks")); - Window dialogWindow = taskProgressDialog.getDialogPane().getScene().getWindow(); - dialogWindow.setOnCloseRequest(windowEvent -> dialogWindow.hide()); - taskProgressDialog.showAndWait(); + + TaskProgressView taskProgressView = new TaskProgressView(); + ObservableList> tasks = EasyBind.map(stateManager.getBackgroundTasks(), ObjectProperty>::>get); + EasyBind.listBind(taskProgressView.getTasks(), tasks); + taskProgressView.setRetainTasks(true); + + PopOver progressViewPopOver = new PopOver(taskProgressView); + progressViewPopOver.setTitle(Localization.lang("Background Tasks")); + progressViewPopOver.setArrowLocation(PopOver.ArrowLocation.RIGHT_TOP); + + progressViewPopOver.show(indicator); }); + return new Group(indicator); } diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css deleted file mode 100644 index 81442716e85..00000000000 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.css +++ /dev/null @@ -1,15 +0,0 @@ -.addEntryButton { - -fx-font-size: 2em; -} - -.entry-container { - /*-fx-padding: 0.5em 0em 0.5em 0em;*/ -} - -.list-cell { - -fx-padding: 0.5em 0 1em 0.5em; -} - -.list-cell:entry-selected { - -fx-background-color: derive(-jr-selected, 60%); -} diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml deleted file mode 100644 index 13ef28d7354..00000000000 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.fxml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java deleted file mode 100644 index d07e3bdad26..00000000000 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskProgressDialog.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.jabref.gui.taskprogressmanager; - -import com.airhacks.afterburner.views.ViewLoader; -import javafx.beans.property.ObjectProperty; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; -import javafx.fxml.FXML; -import org.controlsfx.control.TaskProgressView; -import org.fxmisc.easybind.EasyBind; -import org.jabref.gui.DialogService; -import org.jabref.gui.StateManager; -import org.jabref.gui.util.*; - -import javax.inject.Inject; - -public class TaskProgressDialog extends BaseDialog { - - public TaskProgressView> taskProgressView; - private TaskViewModel viewModel; - @Inject private DialogService dialogService; - @Inject private StateManager stateManager; - - private ObservableList> tasks; - - public TaskProgressDialog() { - ViewLoader.view(this) - .load() - .setAsDialogPane(this); - } - - @FXML - private void initialize() { - viewModel = new TaskViewModel(dialogService, stateManager); - taskProgressView.setRetainTasks(true); - - tasks = EasyBind.map(viewModel.getBackgroundTasks(), ObjectProperty>::>get); - EasyBind.listBind(taskProgressView.getTasks(), tasks); - } -} diff --git a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java b/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java deleted file mode 100644 index d22fdec0d10..00000000000 --- a/src/main/java/org/jabref/gui/taskprogressmanager/TaskViewModel.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.jabref.gui.taskprogressmanager; - -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; -import org.jabref.Globals; -import org.jabref.JabRefGUI; -import org.jabref.gui.AbstractViewModel; -import org.jabref.gui.DialogService; -import org.jabref.gui.StateManager; -import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; -import org.jabref.gui.externalfiles.ImportHandler; -import org.jabref.gui.externalfiletype.ExternalFileTypes; -import org.jabref.gui.fieldeditors.LinkedFileViewModel; -import org.jabref.gui.groups.GroupTreeNodeViewModel; -import org.jabref.gui.groups.UndoableAddOrRemoveGroup; -import org.jabref.gui.undo.NamedCompound; -import org.jabref.gui.undo.UndoableInsertEntries; -import org.jabref.gui.undo.UndoableInsertString; -import org.jabref.gui.util.BackgroundTask; -import org.jabref.gui.util.TaskExecutor; -import org.jabref.gui.util.uithreadaware.UiThreadObservableList; -import org.jabref.logic.bibtex.DuplicateCheck; -import org.jabref.logic.importer.ParserResult; -import org.jabref.logic.l10n.Localization; -import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.BibtexString; -import org.jabref.model.entry.LinkedFile; -import org.jabref.model.groups.GroupTreeNode; -import org.jabref.model.metadata.MetaData; -import org.jabref.model.util.FileUpdateMonitor; -import org.jabref.preferences.PreferencesService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.undo.UndoManager; -import java.util.List; -import java.util.Optional; - -public class TaskViewModel extends AbstractViewModel { - - private static final Logger LOGGER = LoggerFactory.getLogger(TaskViewModel.class); - - private final StringProperty message; - private final DialogService dialogService; - private final StateManager stateManager; - private UiThreadObservableList>> tasks; - - public TaskViewModel(DialogService dialogService, StateManager stateManager) { - this.dialogService = dialogService; - this.stateManager = stateManager; - this.tasks = stateManager.getBackgroundTasks(); - this.message = new SimpleStringProperty(); - } - - public String getMessage() { - return message.get(); - } - - public UiThreadObservableList>> getBackgroundTasks() {return this.tasks;}; - - public StringProperty messageProperty() { - return message; - } -} From cac989b24af0df0e18f35063d9866a94fbe47623 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 16:08:14 +0200 Subject: [PATCH 09/41] Only show download tasks --- .../jabref/gui/externalfiles/DownloadFullTextAction.java | 1 + src/main/java/org/jabref/gui/util/BackgroundTask.java | 9 +++++++++ .../java/org/jabref/gui/util/DefaultTaskExecutor.java | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java index 52002cba4dd..5a3c4bb15bf 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java @@ -166,6 +166,7 @@ private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, B dialogService.notify(Localization.lang("Finished downloading full text document for entry %0.", entry.getCiteKeyOptional().orElse(Localization.lang("undefined")))); }); + downloadTask.showToUser(true); Globals.TASK_EXECUTOR.execute(downloadTask); } catch (MalformedURLException exception) { dialogService.showErrorDialogAndWait(Localization.lang("Invalid URL"), exception); diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index 4d56ca11280..765f4ad94b9 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -36,6 +36,7 @@ public abstract class BackgroundTask { private StringProperty message = new SimpleStringProperty(""); private StringProperty title = new SimpleStringProperty(this.getClass().getSimpleName()); private DoubleProperty workDonePercentage = new SimpleDoubleProperty(0); + private BooleanProperty showToUser = new SimpleBooleanProperty(false); public BackgroundTask() { workDonePercentage.bind(EasyBind.map(progress, BackgroundTask.BackgroundProgress::getWorkDonePercentage)); @@ -111,6 +112,14 @@ public ObjectProperty progressProperty() { return progress; } + public boolean showToUser() { + return showToUser.get(); + } + + public void showToUser(boolean show) { + showToUser.set(show); + } + /** * Sets the {@link Runnable} that is invoked after the task is started. */ diff --git a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java index 6540c78eccb..97de212036b 100644 --- a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java +++ b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java @@ -100,7 +100,9 @@ public static void runInJavaFXThread(Runnable runnable) { @Override public Future execute(BackgroundTask task) { Task javafxTask = getJavaFXTask(task); - Globals.stateManager.addBackgroundTask(new SimpleObjectProperty>(javafxTask)); + if (task.showToUser()) { + Globals.stateManager.addBackgroundTask(new SimpleObjectProperty>(javafxTask)); + } return execute(javafxTask); } From 8f525b8a2956070f1cb92ed2848df6ea776db342 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 16:13:04 +0200 Subject: [PATCH 10/41] Better messages for download tasks These are shorter and therefore the task progress view does not need a horizontal scroll bar. --- .../jabref/gui/externalfiles/DownloadFullTextAction.java | 3 +++ .../org/jabref/gui/fieldeditors/LinkedFileViewModel.java | 6 ++++-- src/main/resources/l10n/JabRef_en.properties | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java index 5a3c4bb15bf..ba6dbaa4b2e 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java @@ -166,6 +166,9 @@ private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, B dialogService.notify(Localization.lang("Finished downloading full text document for entry %0.", entry.getCiteKeyOptional().orElse(Localization.lang("undefined")))); }); + downloadTask.titleProperty().set(Localization.lang("Downloading")); + downloadTask.messageProperty().set( + Localization.lang("Fulltext for") + ": " + entry.getCiteKeyOptional().orElse(Localization.lang("New entry"))); downloadTask.showToUser(true); Globals.TASK_EXECUTOR.execute(downloadTask); } catch (MalformedURLException exception) { diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index 27bbf44d76f..3ea362ebb1c 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -417,8 +417,10 @@ public void download() { entry.addFile(0, newLinkedFile); }); downloadProgress.bind(downloadTask.workDonePercentageProperty()); - downloadTask.titleProperty().set("Downloading"); - downloadTask.messageProperty().set(linkedFile.getLink()); + downloadTask.titleProperty().set(Localization.lang("Downloading")); + downloadTask.messageProperty().set( + Localization.lang("Fulltext for") + ": " + entry.getCiteKeyOptional().orElse(Localization.lang("New entry"))); + downloadTask.showToUser(true); taskExecutor.execute(downloadTask); } catch (MalformedURLException exception) { dialogService.showErrorDialogAndWait(Localization.lang("Invalid URL"), exception); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index fecf3681fc4..474c7d3a024 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -211,6 +211,8 @@ Default\ encoding=Default encoding Default\ grouping\ field=Default grouping field +Downloading=Downloading + Execute\ default\ action\ in\ dialog=Execute default action in dialog Delete=Delete @@ -370,6 +372,8 @@ Formatter\ name=Formatter name found\ in\ AUX\ file=found in AUX file +Fulltext\ for=Fulltext for + Further\ information\ about\ Mr.\ DLib\ for\ JabRef\ users.=Further information about Mr. DLib for JabRef users. General=General From c877431d429a2436316912568ef154e10dad65cb Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Fri, 8 May 2020 16:36:34 +0200 Subject: [PATCH 11/41] Type Witnesses are not needed any more. --- src/main/java/org/jabref/gui/StateManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 3737e0f56ba..571e06e1bce 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -129,14 +129,14 @@ public void addBackgroundTask(ObjectProperty> backgroundTask) { this.backgroundTasks.add(0, backgroundTask); } - Binding anyTaskRunningBinding = EasyBind., Boolean>combine( + Binding anyTaskRunningBinding = EasyBind.combine( backgroundTasks, tStream -> tStream.anyMatch(Task::isRunning) ); - public Binding tasksProgressBinding = EasyBind., Double>combine( + public Binding tasksProgressBinding = EasyBind.combine( backgroundTasks, - stream -> stream.>filter(Task::isRunning).mapToDouble(Task::getProgress) + stream -> stream.filter(Task::isRunning).mapToDouble(Task::getProgress) .average().orElse(1) ); From 1ad95986b8e081369939e920e1a219eb1d76627b Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sat, 9 May 2020 19:42:16 +0200 Subject: [PATCH 12/41] Added extractor to task list --- src/main/java/org/jabref/gui/JabRefFrame.java | 4 ---- src/main/java/org/jabref/gui/StateManager.java | 13 ++++++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 8d210bdd922..e6668d8e58a 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -15,10 +15,8 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.concurrent.Task; -import javafx.event.EventHandler; import javafx.geometry.Orientation; import javafx.scene.Group; import javafx.scene.Node; @@ -39,7 +37,6 @@ import javafx.scene.control.Tooltip; import javafx.scene.control.skin.TabPaneSkin; import javafx.scene.input.KeyEvent; -import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; @@ -49,7 +46,6 @@ import javafx.scene.shape.Rectangle; import javafx.stage.Stage; -import javafx.stage.Window; import org.controlsfx.control.PopOver; import org.controlsfx.control.TaskProgressView; import org.jabref.Globals; diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 571e06e1bce..8f68065a18f 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -5,10 +5,9 @@ import java.util.Optional; import java.util.stream.Collectors; +import javafx.beans.Observable; import javafx.beans.binding.Binding; import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.binding.DoubleBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyListProperty; @@ -23,7 +22,6 @@ import org.fxmisc.easybind.EasyBind; import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.OptionalObjectProperty; -import org.jabref.gui.util.uithreadaware.UiThreadObservableList; import org.jabref.logic.search.SearchQuery; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; @@ -48,7 +46,9 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final UiThreadObservableList>> backgroundTasks = new UiThreadObservableList(FXCollections.observableArrayList()); + private final ObservableList>> backgroundTasks = FXCollections.observableArrayList(taskProperty -> { + return new Observable[] { taskProperty.get().progressProperty()}; + }); public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); @@ -121,7 +121,7 @@ public void setSearchQuery(SearchQuery searchQuery) { public Optional getFocusOwner() { return focusOwner.get(); } - public UiThreadObservableList>> getBackgroundTasks() { + public ObservableList>> getBackgroundTasks() { return backgroundTasks; } @@ -136,8 +136,7 @@ public void addBackgroundTask(ObjectProperty> backgroundTask) { public Binding tasksProgressBinding = EasyBind.combine( backgroundTasks, - stream -> stream.filter(Task::isRunning).mapToDouble(Task::getProgress) - .average().orElse(1) + stream -> stream.filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1) ); } From cd4e38e8b41e90960cac721da22a18b4ce942c3a Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sat, 9 May 2020 20:10:13 +0200 Subject: [PATCH 13/41] Made anyTaskRunningBinding public For consistency with other variables --- .../java/org/jabref/gui/StateManager.java | 2 +- .../WaitForBackgroundtasksFinishedDialog.java | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 8f68065a18f..fadce1a70a5 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -129,7 +129,7 @@ public void addBackgroundTask(ObjectProperty> backgroundTask) { this.backgroundTasks.add(0, backgroundTask); } - Binding anyTaskRunningBinding = EasyBind.combine( + public Binding anyTaskRunningBinding = EasyBind.combine( backgroundTasks, tStream -> tStream.anyMatch(Task::isRunning) ); diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java new file mode 100644 index 00000000000..f8758ab8e1e --- /dev/null +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -0,0 +1,43 @@ +package org.jabref.gui; + +import java.util.List; + +import javafx.concurrent.Task; + +import org.jabref.logic.l10n.Localization; + +/** + * Dialog shown when closing of application needs to wait for a save operation to finish. + */ +public class WaitForSaveFinishedDialog { + + private final DialogService dialogService; + + public WaitForSaveFinishedDialog(DialogService dialogService) { + this.dialogService = dialogService; + } + + public void showAndWait(List basePanels) { + if (basePanels.stream().anyMatch(BasePanel::isSaving)) { + Task waitForSaveFinished = new Task() { + @Override + protected Void call() throws Exception { + while (basePanels.stream().anyMatch(BasePanel::isSaving)) { + if (isCancelled()) { + return null; + } else { + Thread.sleep(100); + } + } + return null; + } + }; + + dialogService.showProgressDialogAndWait( + Localization.lang("Please wait..."), + Localization.lang("Waiting for save operation to finish") + "...", + waitForSaveFinished + ); + } + } +} From 23f8cf053f8203a9785b8beecde015d51c6b98aa Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sat, 9 May 2020 21:06:16 +0200 Subject: [PATCH 14/41] Removed ObjectProperty wrapping --- src/main/java/org/jabref/gui/JabRefFrame.java | 3 +-- .../java/org/jabref/gui/StateManager.java | 23 ++++++++----------- .../jabref/gui/util/DefaultTaskExecutor.java | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index e6668d8e58a..6c94b787598 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -951,8 +951,7 @@ hide it and clip it to a square of (width x width) each time width is updated. indicator.setOnMouseClicked(event -> { TaskProgressView taskProgressView = new TaskProgressView(); - ObservableList> tasks = EasyBind.map(stateManager.getBackgroundTasks(), ObjectProperty>::>get); - EasyBind.listBind(taskProgressView.getTasks(), tasks); + EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); taskProgressView.setRetainTasks(true); PopOver progressViewPopOver = new PopOver(taskProgressView); diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index fadce1a70a5..0f42ade454d 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -6,10 +6,10 @@ import java.util.stream.Collectors; import javafx.beans.Observable; -import javafx.beans.binding.Binding; import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.binding.DoubleBinding; import javafx.beans.property.IntegerProperty; -import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyListProperty; import javafx.beans.property.ReadOnlyListWrapper; import javafx.beans.property.SimpleIntegerProperty; @@ -19,7 +19,6 @@ import javafx.concurrent.Task; import javafx.scene.Node; -import org.fxmisc.easybind.EasyBind; import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.OptionalObjectProperty; import org.jabref.logic.search.SearchQuery; @@ -46,8 +45,8 @@ public class StateManager { private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final ObservableList>> backgroundTasks = FXCollections.observableArrayList(taskProperty -> { - return new Observable[] { taskProperty.get().progressProperty()}; + private final ObservableList> backgroundTasks = FXCollections.observableArrayList(taskProperty -> { + return new Observable[] { taskProperty.progressProperty()}; }); public StateManager() { @@ -121,22 +120,20 @@ public void setSearchQuery(SearchQuery searchQuery) { public Optional getFocusOwner() { return focusOwner.get(); } - public ObservableList>> getBackgroundTasks() { + public ObservableList> getBackgroundTasks() { return backgroundTasks; } - public void addBackgroundTask(ObjectProperty> backgroundTask) { + public void addBackgroundTask(Task backgroundTask) { this.backgroundTasks.add(0, backgroundTask); } - public Binding anyTaskRunningBinding = EasyBind.combine( - backgroundTasks, - tStream -> tStream.anyMatch(Task::isRunning) + public BooleanBinding anyTaskRunningBinding = Bindings.createBooleanBinding( + () -> backgroundTasks.stream().anyMatch(Task::isRunning), backgroundTasks ); - public Binding tasksProgressBinding = EasyBind.combine( - backgroundTasks, - stream -> stream.filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1) + public DoubleBinding tasksProgressBinding = Bindings.createDoubleBinding( + () -> backgroundTasks.stream().filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1), backgroundTasks ); } diff --git a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java index 97de212036b..74ceab20238 100644 --- a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java +++ b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java @@ -101,7 +101,7 @@ public static void runInJavaFXThread(Runnable runnable) { public Future execute(BackgroundTask task) { Task javafxTask = getJavaFXTask(task); if (task.showToUser()) { - Globals.stateManager.addBackgroundTask(new SimpleObjectProperty>(javafxTask)); + Globals.stateManager.addBackgroundTask(javafxTask); } return execute(javafxTask); } From 4628c3dcb0b234025046ecfad6acd3d4aceddd2b Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sat, 9 May 2020 21:10:33 +0200 Subject: [PATCH 15/41] NOT WORKING: quit dialogue --- src/main/java/org/jabref/gui/JabRefFrame.java | 8 ++++- .../WaitForBackgroundtasksFinishedDialog.java | 35 ++++++++++++------- src/main/resources/l10n/JabRef_en.properties | 1 + 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 6c94b787598..331779ba915 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -405,7 +405,13 @@ private void tearDownJabRef(List filenames) { * @return true if the user chose to quit; false otherwise */ public boolean quit() { - // First ask if the user really wants to close, if the library has not been saved since last save. + // First ask if the user really wants to close, if there are still background tasks running + if (stateManager.anyTaskRunningBinding.getValue()) { + WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); + waitForBackgroundtasksFinishedDialog.showAndWait(stateManager); + } + + // Then ask if the user really wants to close, if the library has not been saved since last save. List filenames = new ArrayList<>(); for (int i = 0; i < tabbedPane.getTabs().size(); i++) { BasePanel panel = getBasePanelAt(i); diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java index f8758ab8e1e..8c2ed39b276 100644 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -1,28 +1,30 @@ package org.jabref.gui; -import java.util.List; - import javafx.concurrent.Task; - import org.jabref.logic.l10n.Localization; +import java.util.List; + /** - * Dialog shown when closing of application needs to wait for a save operation to finish. + * Dialog shown when closing of application needs to wait for some background tasks. */ -public class WaitForSaveFinishedDialog { +public class WaitForBackgroundtasksFinishedDialog { private final DialogService dialogService; - public WaitForSaveFinishedDialog(DialogService dialogService) { + public WaitForBackgroundtasksFinishedDialog(DialogService dialogService) { this.dialogService = dialogService; } - public void showAndWait(List basePanels) { - if (basePanels.stream().anyMatch(BasePanel::isSaving)) { - Task waitForSaveFinished = new Task() { + public void showAndWait(StateManager stateManager) { + if (stateManager.anyTaskRunningBinding.getValue()) { + Task waitForBackgroundtasksFinished = new Task() { @Override protected Void call() throws Exception { - while (basePanels.stream().anyMatch(BasePanel::isSaving)) { + System.out.println("THREAD STARTED"); + while (stateManager.anyTaskRunningBinding.getValue()) { + System.out.println("updated value to " + stateManager.tasksProgressBinding.getValue()); + updateProgress(stateManager.tasksProgressBinding.getValue(), 1); if (isCancelled()) { return null; } else { @@ -33,11 +35,20 @@ protected Void call() throws Exception { } }; + Thread th = new Thread(waitForBackgroundtasksFinished); + th.setDaemon(true); + th.start(); + dialogService.showProgressDialogAndWait( Localization.lang("Please wait..."), - Localization.lang("Waiting for save operation to finish") + "...", - waitForSaveFinished + Localization.lang("Waiting for background tasks to finish") + "...", + waitForBackgroundtasksFinished ); + try { + Thread.sleep(60000); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 474c7d3a024..e61fc97587f 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1005,6 +1005,7 @@ search\ expression=search expression Optional\ fields\ 2=Optional fields 2 Waiting\ for\ save\ operation\ to\ finish=Waiting for save operation to finish +Waiting\ for\ background\ tasks\ to\ finish=Waiting for background tasks to finish Find\ and\ remove\ duplicate\ BibTeX\ keys=Find and remove duplicate BibTeX keys Expected\ syntax\ for\ --fetch\='\:'=Expected syntax for --fetch=':' From 90ff19e47b4a7182e7a159471f097d4fc19d0350 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sat, 9 May 2020 21:11:57 +0200 Subject: [PATCH 16/41] Cleanup --- .../org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java index 8c2ed39b276..ccb0d40f6a6 100644 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -44,11 +44,6 @@ protected Void call() throws Exception { Localization.lang("Waiting for background tasks to finish") + "...", waitForBackgroundtasksFinished ); - try { - Thread.sleep(60000); - } catch (InterruptedException e) { - e.printStackTrace(); - } } } } From 62d7c142724573bdc5e65b1187c72f37ee401d7d Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sat, 9 May 2020 21:16:38 +0200 Subject: [PATCH 17/41] Fix in dialog service Make showProgressDialogAndWait actually not only show but also wait. --- src/main/java/org/jabref/gui/DialogService.java | 6 +++--- src/main/java/org/jabref/gui/JabRefDialogService.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/DialogService.java b/src/main/java/org/jabref/gui/DialogService.java index 2ca281876b0..4644665c183 100644 --- a/src/main/java/org/jabref/gui/DialogService.java +++ b/src/main/java/org/jabref/gui/DialogService.java @@ -188,12 +188,12 @@ Optional showCustomButtonDialogAndWait(Alert.AlertType type, String /** * Constructs and shows a canceable {@link ProgressDialog}. Clicking cancel will cancel the underlying service and close the dialog - * - * @param title title of the dialog + * @param title title of the dialog * @param content message to show above the progress bar * @param task The {@link Task} which executes the work and for which to show the dialog + * @return */ - void showProgressDialogAndWait(String title, String content, Task task); + Optional showProgressDialogAndWait(String title, String content, Task task); /** * Notify the user in an non-blocking way (i.e., in form of toast in a snackbar). diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index 6c0ccf37611..59809903bd6 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -267,7 +267,7 @@ public Optional showCustomDialogAndWait(Dialog dialog) { } @Override - public void showProgressDialogAndWait(String title, String content, Task task) { + public Optional showProgressDialogAndWait(String title, String content, Task task) { ProgressDialog progressDialog = new ProgressDialog(task); progressDialog.setHeaderText(null); progressDialog.setTitle(title); @@ -283,7 +283,7 @@ public void showProgressDialogAndWait(String title, String content, Task progressDialog.close(); }); themeLoader.installCss(progressDialog.getDialogPane().getScene(), preferences); - progressDialog.show(); + return progressDialog.showAndWait(); } @Override From 008d55e652d04a4d73b40df19098d66abfebd4b5 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 13:08:52 +0200 Subject: [PATCH 18/41] Add extractor for isRunning --- src/main/java/org/jabref/gui/StateManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 0f42ade454d..0a7583e1214 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -46,7 +46,7 @@ public class StateManager { private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); private final ObservableList> backgroundTasks = FXCollections.observableArrayList(taskProperty -> { - return new Observable[] { taskProperty.progressProperty()}; + return new Observable[] { taskProperty.progressProperty(), taskProperty.runningProperty()}; }); public StateManager() { From a9f4493ac5ff446be297570667ed88f18b0562b0 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 13:31:36 +0200 Subject: [PATCH 19/41] More informative (and working) quit dialog The dialog that is shown when the user quits JabRef during ongoing background tasks now shows which background tasks are still running. When all of them complete or the user chooses to quit anyway, JabRef quits. The user can also cancel the dialog. In that case, the dialog closes and JobRef remains running. --- src/main/java/org/jabref/gui/JabRefFrame.java | 4 +- .../WaitForBackgroundtasksFinishedDialog.java | 67 ++++++++++--------- src/main/resources/l10n/JabRef_en.properties | 2 +- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 331779ba915..8846a83bdec 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -408,7 +408,9 @@ public boolean quit() { // First ask if the user really wants to close, if there are still background tasks running if (stateManager.anyTaskRunningBinding.getValue()) { WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); - waitForBackgroundtasksFinishedDialog.showAndWait(stateManager); + if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager)) { + return false; + } } // Then ask if the user really wants to close, if the library has not been saved since last save. diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java index ccb0d40f6a6..77524f2cb85 100644 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -1,9 +1,13 @@ package org.jabref.gui; -import javafx.concurrent.Task; +import javafx.scene.control.*; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import org.controlsfx.control.TaskProgressView; +import org.fxmisc.easybind.EasyBind; import org.jabref.logic.l10n.Localization; -import java.util.List; +import java.util.Optional; /** * Dialog shown when closing of application needs to wait for some background tasks. @@ -16,34 +20,35 @@ public WaitForBackgroundtasksFinishedDialog(DialogService dialogService) { this.dialogService = dialogService; } - public void showAndWait(StateManager stateManager) { - if (stateManager.anyTaskRunningBinding.getValue()) { - Task waitForBackgroundtasksFinished = new Task() { - @Override - protected Void call() throws Exception { - System.out.println("THREAD STARTED"); - while (stateManager.anyTaskRunningBinding.getValue()) { - System.out.println("updated value to " + stateManager.tasksProgressBinding.getValue()); - updateProgress(stateManager.tasksProgressBinding.getValue(), 1); - if (isCancelled()) { - return null; - } else { - Thread.sleep(100); - } - } - return null; - } - }; - - Thread th = new Thread(waitForBackgroundtasksFinished); - th.setDaemon(true); - th.start(); - - dialogService.showProgressDialogAndWait( - Localization.lang("Please wait..."), - Localization.lang("Waiting for background tasks to finish") + "...", - waitForBackgroundtasksFinished - ); - } + public boolean showAndWait(StateManager stateManager) { + TaskProgressView taskProgressView = new TaskProgressView(); + EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); + taskProgressView.setRetainTasks(false); + + Label message = new Label(Localization.lang("Waiting for background tasks to finish. Quit anyway?")); + + VBox box = new VBox(taskProgressView, message); + + DialogPane contentPane = new DialogPane(); + contentPane.setContent(box); + + FXDialog alert = new FXDialog(Alert.AlertType.NONE, Localization.lang("Please wait...")); + alert.setDialogPane(contentPane); + alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.CANCEL); + alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + alert.setResizable(true); + + stateManager.anyTaskRunningBinding.addListener((observable, oldValue, newValue) -> { + if (!newValue) { + alert.setResult(ButtonType.YES); + alert.close(); + } + }); + + Dialog dialog = () -> alert.showAndWait(); + + Optional pressedButton =dialogService.showCustomDialogAndWait(dialog); + + return pressedButton.isPresent() && pressedButton.get() == ButtonType.YES; } } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index e61fc97587f..fbd9c8a4a4e 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1005,7 +1005,7 @@ search\ expression=search expression Optional\ fields\ 2=Optional fields 2 Waiting\ for\ save\ operation\ to\ finish=Waiting for save operation to finish -Waiting\ for\ background\ tasks\ to\ finish=Waiting for background tasks to finish +Waiting\ for\ background\ tasks\ to\ finish.\ Quit\ anyway?=Waiting for background tasks to finish. Quit anyway? Find\ and\ remove\ duplicate\ BibTeX\ keys=Find and remove duplicate BibTeX keys Expected\ syntax\ for\ --fetch\='\:'=Expected syntax for --fetch=':' From 66ac31675dc099ba793ec7450f7d6dc85b25068d Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 14:08:06 +0200 Subject: [PATCH 20/41] Added graphics callback --- src/main/java/org/jabref/gui/JabRefFrame.java | 1 + .../WaitForBackgroundtasksFinishedDialog.java | 2 ++ .../org/jabref/gui/util/BackgroundTask.java | 17 +++++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 8846a83bdec..121edb241d0 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -961,6 +961,7 @@ hide it and clip it to a square of (width x width) each time width is updated. TaskProgressView taskProgressView = new TaskProgressView(); EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); taskProgressView.setRetainTasks(true); + taskProgressView.setGraphicFactory(BackgroundTask.iconCallback); PopOver progressViewPopOver = new PopOver(taskProgressView); progressViewPopOver.setTitle(Localization.lang("Background Tasks")); diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java index 77524f2cb85..340b1962961 100644 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -5,6 +5,7 @@ import javafx.scene.layout.VBox; import org.controlsfx.control.TaskProgressView; import org.fxmisc.easybind.EasyBind; +import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.l10n.Localization; import java.util.Optional; @@ -24,6 +25,7 @@ public boolean showAndWait(StateManager stateManager) { TaskProgressView taskProgressView = new TaskProgressView(); EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); taskProgressView.setRetainTasks(false); + taskProgressView.setGraphicFactory(BackgroundTask.iconCallback); Label message = new Label(Localization.lang("Waiting for background tasks to finish. Quit anyway?")); diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index 765f4ad94b9..bb2a30fbaa1 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -6,6 +6,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import com.google.common.collect.ImmutableMap; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.ObjectProperty; @@ -16,7 +17,11 @@ import javafx.beans.property.StringProperty; import javafx.concurrent.Task; +import javafx.scene.Node; +import javafx.util.Callback; import org.fxmisc.easybind.EasyBind; +import org.jabref.gui.icon.IconTheme; +import org.jabref.logic.l10n.Localization; /** * This class is essentially a wrapper around {@link Task}. @@ -38,6 +43,18 @@ public abstract class BackgroundTask { private DoubleProperty workDonePercentage = new SimpleDoubleProperty(0); private BooleanProperty showToUser = new SimpleBooleanProperty(false); + public static ImmutableMap iconMap = ImmutableMap.of( + Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode() + ); + + public static Callback, Node> iconCallback = task -> { + if(BackgroundTask.iconMap.containsKey(task.getTitle())) { + return BackgroundTask.iconMap.get(task.getTitle()); + } else { + return null; + } + }; + public BackgroundTask() { workDonePercentage.bind(EasyBind.map(progress, BackgroundTask.BackgroundProgress::getWorkDonePercentage)); } From e9a717693827c6ceb8c9d2ad9f30cfdf8d107c44 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 15:44:52 +0200 Subject: [PATCH 21/41] Fixed some style issues There are still some ImportOrder errors though where I can see no issue. --- src/main/java/org/jabref/gui/JabRefFrame.java | 9 ++---- .../java/org/jabref/gui/StateManager.java | 18 +++++------ .../WaitForBackgroundtasksFinishedDialog.java | 12 +++++--- .../org/jabref/gui/util/BackgroundTask.java | 30 ++++++++++--------- .../jabref/gui/util/DefaultTaskExecutor.java | 2 -- 5 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 121edb241d0..b8372eec5e7 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -12,11 +12,8 @@ import java.util.TimerTask; import javafx.application.Platform; -import javafx.beans.property.ObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import javafx.collections.ObservableList; -import javafx.concurrent.Task; import javafx.geometry.Orientation; import javafx.scene.Group; import javafx.scene.Node; @@ -949,15 +946,13 @@ private Group createTaskIndicator() { hide it and clip it to a square of (width x width) each time width is updated. */ indicator.widthProperty().addListener((observable, oldValue, newValue) -> { - if(newValue.doubleValue()>0){ - Rectangle clip=new Rectangle(newValue.doubleValue(),newValue.doubleValue()); + if (newValue.doubleValue() > 0) { + Rectangle clip = new Rectangle(newValue.doubleValue(), newValue.doubleValue()); indicator.setClip(clip); } }); - indicator.setOnMouseClicked(event -> { - TaskProgressView taskProgressView = new TaskProgressView(); EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); taskProgressView.setRetainTasks(true); diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 0a7583e1214..b1df37d52bf 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -46,9 +46,17 @@ public class StateManager { private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); private final ObservableList> backgroundTasks = FXCollections.observableArrayList(taskProperty -> { - return new Observable[] { taskProperty.progressProperty(), taskProperty.runningProperty()}; + return new Observable[] {taskProperty.progressProperty(), taskProperty.runningProperty()}; }); + public BooleanBinding anyTaskRunningBinding = Bindings.createBooleanBinding( + () -> backgroundTasks.stream().anyMatch(Task::isRunning), backgroundTasks + ); + + public DoubleBinding tasksProgressBinding = Bindings.createDoubleBinding( + () -> backgroundTasks.stream().filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1), backgroundTasks + ); + public StateManager() { activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElse(null))); } @@ -128,12 +136,4 @@ public void addBackgroundTask(Task backgroundTask) { this.backgroundTasks.add(0, backgroundTask); } - public BooleanBinding anyTaskRunningBinding = Bindings.createBooleanBinding( - () -> backgroundTasks.stream().anyMatch(Task::isRunning), backgroundTasks - ); - - public DoubleBinding tasksProgressBinding = Bindings.createDoubleBinding( - () -> backgroundTasks.stream().filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1), backgroundTasks - ); - } diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java index 340b1962961..9ffa9e9605b 100644 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -1,15 +1,19 @@ package org.jabref.gui; -import javafx.scene.control.*; +import java.util.Optional; + +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.DialogPane; +import javafx.scene.control.Label; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; + import org.controlsfx.control.TaskProgressView; import org.fxmisc.easybind.EasyBind; import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.l10n.Localization; -import java.util.Optional; - /** * Dialog shown when closing of application needs to wait for some background tasks. */ @@ -49,7 +53,7 @@ public boolean showAndWait(StateManager stateManager) { Dialog dialog = () -> alert.showAndWait(); - Optional pressedButton =dialogService.showCustomDialogAndWait(dialog); + Optional pressedButton = dialogService.showCustomDialogAndWait(dialog); return pressedButton.isPresent() && pressedButton.get() == ButtonType.YES; } diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index bb2a30fbaa1..4d1ff24730d 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -1,12 +1,13 @@ package org.jabref.gui.util; +import com.google.common.collect.ImmutableMap; + import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; -import com.google.common.collect.ImmutableMap; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.ObjectProperty; @@ -16,9 +17,9 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.concurrent.Task; - import javafx.scene.Node; import javafx.util.Callback; + import org.fxmisc.easybind.EasyBind; import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; @@ -32,29 +33,30 @@ * @param type of the return value of the task */ public abstract class BackgroundTask { - private Runnable onRunning; - private Consumer onSuccess; - private Consumer onException; - private Runnable onFinished; - private BooleanProperty isCanceled = new SimpleBooleanProperty(false); - private ObjectProperty progress = new SimpleObjectProperty<>(new BackgroundProgress(0, 0)); - private StringProperty message = new SimpleStringProperty(""); - private StringProperty title = new SimpleStringProperty(this.getClass().getSimpleName()); - private DoubleProperty workDonePercentage = new SimpleDoubleProperty(0); - private BooleanProperty showToUser = new SimpleBooleanProperty(false); public static ImmutableMap iconMap = ImmutableMap.of( - Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode() + Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode() ); public static Callback, Node> iconCallback = task -> { - if(BackgroundTask.iconMap.containsKey(task.getTitle())) { + if (BackgroundTask.iconMap.containsKey(task.getTitle())) { return BackgroundTask.iconMap.get(task.getTitle()); } else { return null; } }; + private Runnable onRunning; + private Consumer onSuccess; + private Consumer onException; + private Runnable onFinished; + private BooleanProperty isCanceled = new SimpleBooleanProperty(false); + private ObjectProperty progress = new SimpleObjectProperty<>(new BackgroundProgress(0, 0)); + private StringProperty message = new SimpleStringProperty(""); + private StringProperty title = new SimpleStringProperty(this.getClass().getSimpleName()); + private DoubleProperty workDonePercentage = new SimpleDoubleProperty(0); + private BooleanProperty showToUser = new SimpleBooleanProperty(false); + public BackgroundTask() { workDonePercentage.bind(EasyBind.map(progress, BackgroundTask.BackgroundProgress::getWorkDonePercentage)); } diff --git a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java index 74ceab20238..ab734e748d5 100644 --- a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java +++ b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java @@ -14,11 +14,9 @@ import java.util.function.Consumer; import javafx.application.Platform; -import javafx.beans.property.SimpleObjectProperty; import javafx.concurrent.Task; import org.jabref.Globals; -import org.jabref.gui.StateManager; import org.jabref.logic.util.DelayTaskThrottler; import org.slf4j.Logger; From d7442cc2247772b6e54bcb68cea947a5a2a604bc Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 16:10:04 +0200 Subject: [PATCH 22/41] Registered the save task as background task This makes the dialog that pops up if background tasks are running wait for pending saves. --- src/main/java/org/jabref/gui/JabRefFrame.java | 41 +++++++++++++----- .../jabref/gui/WaitForSaveFinishedDialog.java | 43 ------------------- .../org/jabref/gui/util/BackgroundTask.java | 3 +- 3 files changed, 32 insertions(+), 55 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index b8372eec5e7..73f17011f54 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -14,6 +14,7 @@ import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; +import javafx.concurrent.Task; import javafx.geometry.Orientation; import javafx.scene.Group; import javafx.scene.Node; @@ -402,15 +403,6 @@ private void tearDownJabRef(List filenames) { * @return true if the user chose to quit; false otherwise */ public boolean quit() { - // First ask if the user really wants to close, if there are still background tasks running - if (stateManager.anyTaskRunningBinding.getValue()) { - WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); - if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager)) { - return false; - } - } - - // Then ask if the user really wants to close, if the library has not been saved since last save. List filenames = new ArrayList<>(); for (int i = 0; i < tabbedPane.getTabs().size(); i++) { BasePanel panel = getBasePanelAt(i); @@ -431,8 +423,35 @@ public boolean quit() { context.getDatabasePath().map(Path::toAbsolutePath).map(Path::toString).ifPresent(filenames::add); } - WaitForSaveFinishedDialog waitForSaveFinishedDialog = new WaitForSaveFinishedDialog(dialogService); - waitForSaveFinishedDialog.showAndWait(getBasePanelList()); + // Check if any tabs are saving. If so, create a task waiting for them + if (getBasePanelList().stream().anyMatch(BasePanel::isSaving)) { + Task waitForSaveFinished = new Task() { + @Override + protected Void call() throws Exception { + updateTitle("Saving"); + while (getBasePanelList().stream().anyMatch(BasePanel::isSaving)) { + if (isCancelled()) { + return null; + } else { + Thread.sleep(100); + } + } + return null; + } + }; + + stateManager.getBackgroundTasks().add(waitForSaveFinished); + Globals.TASK_EXECUTOR.execute(waitForSaveFinished); + + } + + // Ask if the user really wants to close, if there are background tasks running. + if (stateManager.anyTaskRunningBinding.getValue()) { + WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); + if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager)) { + return false; + } + } // Good bye! tearDownJabRef(filenames); diff --git a/src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java deleted file mode 100644 index f8758ab8e1e..00000000000 --- a/src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.jabref.gui; - -import java.util.List; - -import javafx.concurrent.Task; - -import org.jabref.logic.l10n.Localization; - -/** - * Dialog shown when closing of application needs to wait for a save operation to finish. - */ -public class WaitForSaveFinishedDialog { - - private final DialogService dialogService; - - public WaitForSaveFinishedDialog(DialogService dialogService) { - this.dialogService = dialogService; - } - - public void showAndWait(List basePanels) { - if (basePanels.stream().anyMatch(BasePanel::isSaving)) { - Task waitForSaveFinished = new Task() { - @Override - protected Void call() throws Exception { - while (basePanels.stream().anyMatch(BasePanel::isSaving)) { - if (isCancelled()) { - return null; - } else { - Thread.sleep(100); - } - } - return null; - } - }; - - dialogService.showProgressDialogAndWait( - Localization.lang("Please wait..."), - Localization.lang("Waiting for save operation to finish") + "...", - waitForSaveFinished - ); - } - } -} diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index 4d1ff24730d..85dbd38d718 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -35,7 +35,8 @@ public abstract class BackgroundTask { public static ImmutableMap iconMap = ImmutableMap.of( - Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode() + Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode(), + Localization.lang("Saving"), IconTheme.JabRefIcons.SAVE_ALL.getGraphicNode() ); public static Callback, Node> iconCallback = task -> { From 5defe3e467f83c2ce86407fd998c093f78a7a519 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 16:42:18 +0200 Subject: [PATCH 23/41] Revert "Registered the save task as background task" This reverts commit d7442cc2247772b6e54bcb68cea947a5a2a604bc. --- src/main/java/org/jabref/gui/JabRefFrame.java | 41 +++++------------- .../jabref/gui/WaitForSaveFinishedDialog.java | 43 +++++++++++++++++++ .../org/jabref/gui/util/BackgroundTask.java | 3 +- 3 files changed, 55 insertions(+), 32 deletions(-) create mode 100644 src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 73f17011f54..b8372eec5e7 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -14,7 +14,6 @@ import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import javafx.concurrent.Task; import javafx.geometry.Orientation; import javafx.scene.Group; import javafx.scene.Node; @@ -403,6 +402,15 @@ private void tearDownJabRef(List filenames) { * @return true if the user chose to quit; false otherwise */ public boolean quit() { + // First ask if the user really wants to close, if there are still background tasks running + if (stateManager.anyTaskRunningBinding.getValue()) { + WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); + if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager)) { + return false; + } + } + + // Then ask if the user really wants to close, if the library has not been saved since last save. List filenames = new ArrayList<>(); for (int i = 0; i < tabbedPane.getTabs().size(); i++) { BasePanel panel = getBasePanelAt(i); @@ -423,35 +431,8 @@ public boolean quit() { context.getDatabasePath().map(Path::toAbsolutePath).map(Path::toString).ifPresent(filenames::add); } - // Check if any tabs are saving. If so, create a task waiting for them - if (getBasePanelList().stream().anyMatch(BasePanel::isSaving)) { - Task waitForSaveFinished = new Task() { - @Override - protected Void call() throws Exception { - updateTitle("Saving"); - while (getBasePanelList().stream().anyMatch(BasePanel::isSaving)) { - if (isCancelled()) { - return null; - } else { - Thread.sleep(100); - } - } - return null; - } - }; - - stateManager.getBackgroundTasks().add(waitForSaveFinished); - Globals.TASK_EXECUTOR.execute(waitForSaveFinished); - - } - - // Ask if the user really wants to close, if there are background tasks running. - if (stateManager.anyTaskRunningBinding.getValue()) { - WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); - if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager)) { - return false; - } - } + WaitForSaveFinishedDialog waitForSaveFinishedDialog = new WaitForSaveFinishedDialog(dialogService); + waitForSaveFinishedDialog.showAndWait(getBasePanelList()); // Good bye! tearDownJabRef(filenames); diff --git a/src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java new file mode 100644 index 00000000000..f8758ab8e1e --- /dev/null +++ b/src/main/java/org/jabref/gui/WaitForSaveFinishedDialog.java @@ -0,0 +1,43 @@ +package org.jabref.gui; + +import java.util.List; + +import javafx.concurrent.Task; + +import org.jabref.logic.l10n.Localization; + +/** + * Dialog shown when closing of application needs to wait for a save operation to finish. + */ +public class WaitForSaveFinishedDialog { + + private final DialogService dialogService; + + public WaitForSaveFinishedDialog(DialogService dialogService) { + this.dialogService = dialogService; + } + + public void showAndWait(List basePanels) { + if (basePanels.stream().anyMatch(BasePanel::isSaving)) { + Task waitForSaveFinished = new Task() { + @Override + protected Void call() throws Exception { + while (basePanels.stream().anyMatch(BasePanel::isSaving)) { + if (isCancelled()) { + return null; + } else { + Thread.sleep(100); + } + } + return null; + } + }; + + dialogService.showProgressDialogAndWait( + Localization.lang("Please wait..."), + Localization.lang("Waiting for save operation to finish") + "...", + waitForSaveFinished + ); + } + } +} diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index 85dbd38d718..4d1ff24730d 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -35,8 +35,7 @@ public abstract class BackgroundTask { public static ImmutableMap iconMap = ImmutableMap.of( - Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode(), - Localization.lang("Saving"), IconTheme.JabRefIcons.SAVE_ALL.getGraphicNode() + Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode() ); public static Callback, Node> iconCallback = task -> { From fba9c70bec3813d8684e79ae21c29b770625a734 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 16:46:30 +0200 Subject: [PATCH 24/41] Added note on dialog-order upon close --- src/main/java/org/jabref/gui/JabRefFrame.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index b8372eec5e7..58ba4202439 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -403,6 +403,10 @@ private void tearDownJabRef(List filenames) { */ public boolean quit() { // First ask if the user really wants to close, if there are still background tasks running + /* + It is important to wait for unfinished background tasks before checking if a save-operation is needed, because + the background tasks may make changes themselves that need saving. + */ if (stateManager.anyTaskRunningBinding.getValue()) { WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager)) { From 6a62e6fea391b2845943b976152c634f3cbc5750 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 16:55:17 +0200 Subject: [PATCH 25/41] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f4e34895c..24c9aafa976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We now show the number of items found and selected to import in the online search dialog. [#6248](https://github.com/JabRef/jabref/pull/6248) - We created a new install screen for macOS. [#5759](https://github.com/JabRef/jabref/issues/5759) - We implemented an option to download fulltext files while importing. [#6381](https://github.com/JabRef/jabref/pull/6381) +- We added a progress-indicator showing the average progress of background tasks to the toolbar. Clicking it reveals a pop-over with a list of running background tasks. [6443](https://github.com/JabRef/jabref/pull/6443) ### Changed From a97af13c93b3cd32fef6242af5622332765a8727 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Sun, 10 May 2020 17:54:13 +0200 Subject: [PATCH 26/41] Fixed style --- src/main/java/org/jabref/gui/JabRefFrame.java | 4 ++-- .../jabref/gui/WaitForBackgroundtasksFinishedDialog.java | 5 +++-- src/main/java/org/jabref/gui/util/BackgroundTask.java | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 58ba4202439..e0165bb433e 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -43,8 +43,6 @@ import javafx.scene.shape.Rectangle; import javafx.stage.Stage; -import org.controlsfx.control.PopOver; -import org.controlsfx.control.TaskProgressView; import org.jabref.Globals; import org.jabref.JabRefExecutorService; import org.jabref.gui.actions.ActionFactory; @@ -140,6 +138,8 @@ import org.jabref.preferences.LastFocusedTabPreferences; import com.google.common.eventbus.Subscribe; +import org.controlsfx.control.PopOver; +import org.controlsfx.control.TaskProgressView; import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java index 9ffa9e9605b..c2841c72cce 100644 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -9,11 +9,12 @@ import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import org.controlsfx.control.TaskProgressView; -import org.fxmisc.easybind.EasyBind; import org.jabref.gui.util.BackgroundTask; import org.jabref.logic.l10n.Localization; +import org.controlsfx.control.TaskProgressView; +import org.fxmisc.easybind.EasyBind; + /** * Dialog shown when closing of application needs to wait for some background tasks. */ diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index 4d1ff24730d..eb7864003f2 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -1,7 +1,5 @@ package org.jabref.gui.util; -import com.google.common.collect.ImmutableMap; - import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -20,10 +18,12 @@ import javafx.scene.Node; import javafx.util.Callback; -import org.fxmisc.easybind.EasyBind; import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; +import com.google.common.collect.ImmutableMap; +import org.fxmisc.easybind.EasyBind; + /** * This class is essentially a wrapper around {@link Task}. * We cannot use {@link Task} directly since it runs certain update notifications on the JavaFX thread, From 44d9ca8f0fe2c322d30352a98676851b83649857 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 09:48:06 +0200 Subject: [PATCH 27/41] Quickfix for resizing indicator when indeterminate Set the pref-width when the indicator is determinate to keep the indeterminate state from making the indicator wider. --- src/main/java/org/jabref/gui/JabRefFrame.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index e0165bb433e..f01853f422c 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -950,6 +950,13 @@ private Group createTaskIndicator() { hide it and clip it to a square of (width x width) each time width is updated. */ indicator.widthProperty().addListener((observable, oldValue, newValue) -> { + /* + The indeterminate spinner is wider than the determinate spinner. + We must make sure they are the same width for the clipping to result in a square of the same size always. + */ + if (!indicator.isIndeterminate()) { + indicator.setPrefWidth(newValue.doubleValue()); + } if (newValue.doubleValue() > 0) { Rectangle clip = new Rectangle(newValue.doubleValue(), newValue.doubleValue()); indicator.setClip(clip); From bd2eefd21e6e83b168d324635179b2cca4f18405 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 09:56:53 +0200 Subject: [PATCH 28/41] Styled dialog waiting for background tasks --- src/main/java/org/jabref/gui/JabRefFrame.java | 2 +- .../org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index f01853f422c..339168eb2ba 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -409,7 +409,7 @@ public boolean quit() { */ if (stateManager.anyTaskRunningBinding.getValue()) { WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); - if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager)) { + if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager, themeLoader, prefs)) { return false; } } diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java index c2841c72cce..06360803eed 100644 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java @@ -10,7 +10,9 @@ import javafx.scene.layout.VBox; import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.ThemeLoader; import org.jabref.logic.l10n.Localization; +import org.jabref.preferences.JabRefPreferences; import org.controlsfx.control.TaskProgressView; import org.fxmisc.easybind.EasyBind; @@ -26,7 +28,7 @@ public WaitForBackgroundtasksFinishedDialog(DialogService dialogService) { this.dialogService = dialogService; } - public boolean showAndWait(StateManager stateManager) { + public boolean showAndWait(StateManager stateManager, ThemeLoader themeLoader, JabRefPreferences preferences) { TaskProgressView taskProgressView = new TaskProgressView(); EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); taskProgressView.setRetainTasks(false); @@ -44,6 +46,7 @@ public boolean showAndWait(StateManager stateManager) { alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.CANCEL); alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); alert.setResizable(true); + themeLoader.installCss(alert.getDialogPane().getScene(), preferences); stateManager.anyTaskRunningBinding.addListener((observable, oldValue, newValue) -> { if (!newValue) { From 41efc047ee0cb7bb8bdd3389ff384f5d53ca6c8f Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 10:37:01 +0200 Subject: [PATCH 29/41] Minor style fix --- src/main/java/org/jabref/gui/DialogService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/DialogService.java b/src/main/java/org/jabref/gui/DialogService.java index 4644665c183..4c8d0a0130b 100644 --- a/src/main/java/org/jabref/gui/DialogService.java +++ b/src/main/java/org/jabref/gui/DialogService.java @@ -188,7 +188,8 @@ Optional showCustomButtonDialogAndWait(Alert.AlertType type, String /** * Constructs and shows a canceable {@link ProgressDialog}. Clicking cancel will cancel the underlying service and close the dialog - * @param title title of the dialog + * + * @param title title of the dialog * @param content message to show above the progress bar * @param task The {@link Task} which executes the work and for which to show the dialog * @return From 2c9ccea66fef2bcc6e68d0fadfdf407b1a0b6262 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 14:15:20 +0200 Subject: [PATCH 30/41] Removed Globals from DefaultTaskExecutor Addresses https://github.com/JabRef/jabref/pull/6443#discussion_r422981410 --- src/main/java/org/jabref/Globals.java | 12 ++++++------ .../org/jabref/gui/util/DefaultTaskExecutor.java | 11 +++++++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jabref/Globals.java b/src/main/java/org/jabref/Globals.java index f89bc583772..4ee99f58804 100644 --- a/src/main/java/org/jabref/Globals.java +++ b/src/main/java/org/jabref/Globals.java @@ -44,8 +44,13 @@ public class Globals { // Remote listener public static final RemoteListenerServerLifecycle REMOTE_LISTENER = new RemoteListenerServerLifecycle(); + /** + * Manager for the state of the GUI. + */ + public static StateManager stateManager = new StateManager(); + public static final ImportFormatReader IMPORT_FORMAT_READER = new ImportFormatReader(); - public static final TaskExecutor TASK_EXECUTOR = new DefaultTaskExecutor(); + public static final TaskExecutor TASK_EXECUTOR = new DefaultTaskExecutor(stateManager); /** * Each test case initializes this field if required @@ -64,11 +69,6 @@ public class Globals { */ public static ProtectedTermsLoader protectedTermsLoader; - /** - * Manager for the state of the GUI. - */ - public static StateManager stateManager = new StateManager(); - public static ExporterFactory exportFactory; public static CountingUndoManager undoManager = new CountingUndoManager(); public static BibEntryTypesManager entryTypesManager = new BibEntryTypesManager(); diff --git a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java index ab734e748d5..6cbb6b27515 100644 --- a/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java +++ b/src/main/java/org/jabref/gui/util/DefaultTaskExecutor.java @@ -16,7 +16,7 @@ import javafx.application.Platform; import javafx.concurrent.Task; -import org.jabref.Globals; +import org.jabref.gui.StateManager; import org.jabref.logic.util.DelayTaskThrottler; import org.slf4j.Logger; @@ -34,6 +34,13 @@ public class DefaultTaskExecutor implements TaskExecutor { private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(2); private final WeakHashMap throttlers = new WeakHashMap<>(); + private final StateManager stateManager; + + public DefaultTaskExecutor(StateManager stateManager) { + super(); + this.stateManager = stateManager; + } + /** * */ @@ -99,7 +106,7 @@ public static void runInJavaFXThread(Runnable runnable) { public Future execute(BackgroundTask task) { Task javafxTask = getJavaFXTask(task); if (task.showToUser()) { - Globals.stateManager.addBackgroundTask(javafxTask); + stateManager.addBackgroundTask(javafxTask); } return execute(javafxTask); } From ff9ce0068d6b80e131826a9138aab3b1f981b00e Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 14:51:08 +0200 Subject: [PATCH 31/41] Removed WaitForBackgroundtasksFinishedDialog More in line with the other JabRef dialogs Addresses https://github.com/JabRef/jabref/pull/6443#discussion_r422977336 --- .../java/org/jabref/gui/DialogService.java | 12 ++++ .../org/jabref/gui/JabRefDialogService.java | 40 ++++++++++++ src/main/java/org/jabref/gui/JabRefFrame.java | 7 +- .../WaitForBackgroundtasksFinishedDialog.java | 64 ------------------- 4 files changed, 57 insertions(+), 66 deletions(-) delete mode 100644 src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java diff --git a/src/main/java/org/jabref/gui/DialogService.java b/src/main/java/org/jabref/gui/DialogService.java index 4c8d0a0130b..87da6a3b517 100644 --- a/src/main/java/org/jabref/gui/DialogService.java +++ b/src/main/java/org/jabref/gui/DialogService.java @@ -196,6 +196,18 @@ Optional showCustomButtonDialogAndWait(Alert.AlertType type, String */ Optional showProgressDialogAndWait(String title, String content, Task task); + /** + * Constructs and shows a dialog showing the progress of running background tasks. + * Clicking cancel will cancel the underlying service and close the dialog. + * The dialog will exit as soon as none of the background tasks are running + * + * @param title title of the dialog + * @param content message to show below the list of background tasks + * @param stateManager The {@link StateManager} which contains the background tasks + * @return + */ + Optional showBackgroundProgressDialogAndWait(String title, String content, StateManager stateManager); + /** * Notify the user in an non-blocking way (i.e., in form of toast in a snackbar). * diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index 59809903bd6..2048ab8cfb1 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -12,6 +12,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +import javafx.beans.binding.Bindings; +import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.print.PrinterJob; import javafx.scene.Group; @@ -23,9 +25,11 @@ import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceDialog; import javafx.scene.control.DialogPane; +import javafx.scene.control.Label; import javafx.scene.control.TextInputDialog; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -33,6 +37,7 @@ import javafx.util.Duration; import org.jabref.gui.icon.IconTheme; +import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DirectoryDialogConfiguration; import org.jabref.gui.util.FileDialogConfiguration; import org.jabref.gui.util.ThemeLoader; @@ -43,8 +48,10 @@ import com.jfoenix.controls.JFXSnackbar; import com.jfoenix.controls.JFXSnackbar.SnackbarEvent; import com.jfoenix.controls.JFXSnackbarLayout; +import org.controlsfx.control.TaskProgressView; import org.controlsfx.dialog.ExceptionDialog; import org.controlsfx.dialog.ProgressDialog; +import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -286,6 +293,39 @@ public Optional showProgressDialogAndWait(String title, String content return progressDialog.showAndWait(); } + @Override + public Optional showBackgroundProgressDialogAndWait(String title, String content, StateManager stateManager) { + TaskProgressView taskProgressView = new TaskProgressView(); + EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); + taskProgressView.setRetainTasks(false); + taskProgressView.setGraphicFactory(BackgroundTask.iconCallback); + + Label message = new Label(content); + + VBox box = new VBox(taskProgressView, message); + + DialogPane contentPane = new DialogPane(); + contentPane.setContent(box); + + FXDialog alert = new FXDialog(AlertType.WARNING, title); + alert.setDialogPane(contentPane); + alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.CANCEL); + alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + alert.setResizable(true); + themeLoader.installCss(alert.getDialogPane().getScene(), preferences); + + stateManager.anyTaskRunningBinding.addListener((observable, oldValue, newValue) -> { + if (!newValue) { + alert.setResult(ButtonType.YES); + alert.close(); + } + }); + + Dialog dialog = () -> alert.showAndWait(); + + return showCustomDialogAndWait(dialog); + } + @Override public void notify(String message) { LOGGER.info(message); diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 339168eb2ba..ef824d75c0f 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -408,8 +408,11 @@ public boolean quit() { the background tasks may make changes themselves that need saving. */ if (stateManager.anyTaskRunningBinding.getValue()) { - WaitForBackgroundtasksFinishedDialog waitForBackgroundtasksFinishedDialog = new WaitForBackgroundtasksFinishedDialog(dialogService); - if (!waitForBackgroundtasksFinishedDialog.showAndWait(stateManager, themeLoader, prefs)) { + if (!(dialogService.showBackgroundProgressDialogAndWait( + Localization.lang("Please wait..."), + Localization.lang("Waiting for background tasks to finish. Quit anyway?"), + stateManager + ).orElse(ButtonType.CANCEL) == ButtonType.YES)) { return false; } } diff --git a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java b/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java deleted file mode 100644 index 06360803eed..00000000000 --- a/src/main/java/org/jabref/gui/WaitForBackgroundtasksFinishedDialog.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.jabref.gui; - -import java.util.Optional; - -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonType; -import javafx.scene.control.DialogPane; -import javafx.scene.control.Label; -import javafx.scene.layout.Region; -import javafx.scene.layout.VBox; - -import org.jabref.gui.util.BackgroundTask; -import org.jabref.gui.util.ThemeLoader; -import org.jabref.logic.l10n.Localization; -import org.jabref.preferences.JabRefPreferences; - -import org.controlsfx.control.TaskProgressView; -import org.fxmisc.easybind.EasyBind; - -/** - * Dialog shown when closing of application needs to wait for some background tasks. - */ -public class WaitForBackgroundtasksFinishedDialog { - - private final DialogService dialogService; - - public WaitForBackgroundtasksFinishedDialog(DialogService dialogService) { - this.dialogService = dialogService; - } - - public boolean showAndWait(StateManager stateManager, ThemeLoader themeLoader, JabRefPreferences preferences) { - TaskProgressView taskProgressView = new TaskProgressView(); - EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); - taskProgressView.setRetainTasks(false); - taskProgressView.setGraphicFactory(BackgroundTask.iconCallback); - - Label message = new Label(Localization.lang("Waiting for background tasks to finish. Quit anyway?")); - - VBox box = new VBox(taskProgressView, message); - - DialogPane contentPane = new DialogPane(); - contentPane.setContent(box); - - FXDialog alert = new FXDialog(Alert.AlertType.NONE, Localization.lang("Please wait...")); - alert.setDialogPane(contentPane); - alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.CANCEL); - alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); - alert.setResizable(true); - themeLoader.installCss(alert.getDialogPane().getScene(), preferences); - - stateManager.anyTaskRunningBinding.addListener((observable, oldValue, newValue) -> { - if (!newValue) { - alert.setResult(ButtonType.YES); - alert.close(); - } - }); - - Dialog dialog = () -> alert.showAndWait(); - - Optional pressedButton = dialogService.showCustomDialogAndWait(dialog); - - return pressedButton.isPresent() && pressedButton.get() == ButtonType.YES; - } -} From d56138bbb629dcd5ffdad9d29d9c7b87d80ba7c2 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 15:24:57 +0200 Subject: [PATCH 32/41] Made Bindings in StateManager private Addresses https://github.com/JabRef/jabref/pull/6443#discussion_r422975104 --- src/main/java/org/jabref/gui/JabRefDialogService.java | 4 +--- src/main/java/org/jabref/gui/JabRefFrame.java | 4 ++-- src/main/java/org/jabref/gui/StateManager.java | 11 +++++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index 2048ab8cfb1..84438edd735 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -12,8 +12,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import javafx.beans.binding.Bindings; -import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.print.PrinterJob; import javafx.scene.Group; @@ -314,7 +312,7 @@ public Optional showBackgroundProgressDialogAndWait(String title alert.setResizable(true); themeLoader.installCss(alert.getDialogPane().getScene(), preferences); - stateManager.anyTaskRunningBinding.addListener((observable, oldValue, newValue) -> { + stateManager.getAnyTaskRunning().addListener((observable, oldValue, newValue) -> { if (!newValue) { alert.setResult(ButtonType.YES); alert.close(); diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index ef824d75c0f..58d061bb9fe 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -407,7 +407,7 @@ public boolean quit() { It is important to wait for unfinished background tasks before checking if a save-operation is needed, because the background tasks may make changes themselves that need saving. */ - if (stateManager.anyTaskRunningBinding.getValue()) { + if (stateManager.getAnyTaskRunning().getValue()) { if (!(dialogService.showBackgroundProgressDialogAndWait( Localization.lang("Please wait..."), Localization.lang("Waiting for background tasks to finish. Quit anyway?"), @@ -946,7 +946,7 @@ private MenuBar createMenu() { private Group createTaskIndicator() { ProgressIndicator indicator = new ProgressIndicator(); indicator.getStyleClass().setAll("progress-indicator"); - indicator.progressProperty().bind(stateManager.tasksProgressBinding); + indicator.progressProperty().bind(stateManager.getTasksProgress()); /* The label of the indicator cannot be removed with styling. Therefore, diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index b1df37d52bf..235a49ec4de 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -49,11 +49,11 @@ public class StateManager { return new Observable[] {taskProperty.progressProperty(), taskProperty.runningProperty()}; }); - public BooleanBinding anyTaskRunningBinding = Bindings.createBooleanBinding( + private BooleanBinding anyTaskRunning = Bindings.createBooleanBinding( () -> backgroundTasks.stream().anyMatch(Task::isRunning), backgroundTasks ); - public DoubleBinding tasksProgressBinding = Bindings.createDoubleBinding( + private DoubleBinding tasksProgress = Bindings.createDoubleBinding( () -> backgroundTasks.stream().filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1), backgroundTasks ); @@ -136,4 +136,11 @@ public void addBackgroundTask(Task backgroundTask) { this.backgroundTasks.add(0, backgroundTask); } + public BooleanBinding getAnyTaskRunning() { + return anyTaskRunning; + } + + public DoubleBinding getTasksProgress() { + return tasksProgress; + } } From cf10859542c790e170866698f57a6fc8a704f7df Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 15:32:26 +0200 Subject: [PATCH 33/41] Added tooltip to progress indicator Addresses https://github.com/JabRef/jabref/pull/6443#discussion_r422973356 --- src/main/java/org/jabref/gui/JabRefFrame.java | 14 ++++++++++++++ src/main/resources/l10n/JabRef_en.properties | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 58d061bb9fe..793f1bc40ab 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -948,6 +948,20 @@ private Group createTaskIndicator() { indicator.getStyleClass().setAll("progress-indicator"); indicator.progressProperty().bind(stateManager.getTasksProgress()); + Tooltip someTasksRunning = new Tooltip(Localization.lang("Background Tasks are running")); + Tooltip noTasksRunning = new Tooltip(Localization.lang("Background Tasks are done")); + indicator.setTooltip(noTasksRunning); + stateManager.getAnyTaskRunning().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + if (newValue.booleanValue()) { + indicator.setTooltip(someTasksRunning); + } else { + indicator.setTooltip(noTasksRunning); + } + } + }); + /* The label of the indicator cannot be removed with styling. Therefore, hide it and clip it to a square of (width x width) each time width is updated. diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index f04f255d8a5..22c3c891ddf 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -94,6 +94,10 @@ Available\ import\ formats=Available import formats Background\ Tasks=Background Tasks +Background\ Tasks\ are\ running=Background Tasks are running + +Background\ Tasks\ are\ done=Background Tasks are done + Browse=Browse by=by From fcb1d0cc0ec3af744485b4d9321e3a3215c532f1 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 15:51:13 +0200 Subject: [PATCH 34/41] Not working: own styleclass for toolbar progress indicator Tries to address https://github.com/JabRef/jabref/pull/6443#discussion_r422971198 --- src/main/java/org/jabref/gui/Base.css | 9 +++++++-- src/main/java/org/jabref/gui/JabRefFrame.java | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index 64024ee72f1..db47c898732 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -387,18 +387,23 @@ -fx-padding: -0.1em 0.5em 0.5em 0.5em; } +.progress-indicatorToolbar, .progress-indicator { -fx-progress-color: -jr-theme; -fx-border-width: 0px; -fx-background-color: -jr-icon-background; +} + +.progress-indicatorToolbar { -fx-padding: 0.5em; } -.progress-indicator .percentage { +.progress-indicatorToolbar .percentage { -fx-fill:null; } -.progress-indicator:hover { +.progress-indicator:hover, +.progress-indicatorToolbar:hover { -fx-background-color: -jr-icon-background-active; } diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 793f1bc40ab..f1e3f48b68d 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -945,7 +945,7 @@ private MenuBar createMenu() { private Group createTaskIndicator() { ProgressIndicator indicator = new ProgressIndicator(); - indicator.getStyleClass().setAll("progress-indicator"); + indicator.getStyleClass().setAll("progress-indicatorToolbar"); indicator.progressProperty().bind(stateManager.getTasksProgress()); Tooltip someTasksRunning = new Tooltip(Localization.lang("Background Tasks are running")); From 396411a68cf3a2583cbe367b716d6195f0773cf2 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Mon, 11 May 2020 15:59:53 +0200 Subject: [PATCH 35/41] Changed callback to method in BackgroundTask Addresses https://github.com/JabRef/jabref/pull/6443#discussion_r422980468 --- .../java/org/jabref/gui/JabRefDialogService.java | 2 +- src/main/java/org/jabref/gui/JabRefFrame.java | 2 +- .../java/org/jabref/gui/util/BackgroundTask.java | 16 +++++++--------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefDialogService.java b/src/main/java/org/jabref/gui/JabRefDialogService.java index 84438edd735..d1a190c4979 100644 --- a/src/main/java/org/jabref/gui/JabRefDialogService.java +++ b/src/main/java/org/jabref/gui/JabRefDialogService.java @@ -296,7 +296,7 @@ public Optional showBackgroundProgressDialogAndWait(String title TaskProgressView taskProgressView = new TaskProgressView(); EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); taskProgressView.setRetainTasks(false); - taskProgressView.setGraphicFactory(BackgroundTask.iconCallback); + taskProgressView.setGraphicFactory(BackgroundTask::getIcon); Label message = new Label(content); diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index f1e3f48b68d..bb7b9395b4e 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -984,7 +984,7 @@ hide it and clip it to a square of (width x width) each time width is updated. TaskProgressView taskProgressView = new TaskProgressView(); EasyBind.listBind(taskProgressView.getTasks(), stateManager.getBackgroundTasks()); taskProgressView.setRetainTasks(true); - taskProgressView.setGraphicFactory(BackgroundTask.iconCallback); + taskProgressView.setGraphicFactory(BackgroundTask::getIcon); PopOver progressViewPopOver = new PopOver(taskProgressView); progressViewPopOver.setTitle(Localization.lang("Background Tasks")); diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index eb7864003f2..2b9e7b65d2a 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -16,7 +16,6 @@ import javafx.beans.property.StringProperty; import javafx.concurrent.Task; import javafx.scene.Node; -import javafx.util.Callback; import org.jabref.gui.icon.IconTheme; import org.jabref.logic.l10n.Localization; @@ -38,14 +37,6 @@ public abstract class BackgroundTask { Localization.lang("Downloading"), IconTheme.JabRefIcons.DOWNLOAD.getGraphicNode() ); - public static Callback, Node> iconCallback = task -> { - if (BackgroundTask.iconMap.containsKey(task.getTitle())) { - return BackgroundTask.iconMap.get(task.getTitle()); - } else { - return null; - } - }; - private Runnable onRunning; private Consumer onSuccess; private Consumer onException; @@ -266,6 +257,13 @@ public BackgroundTask withInitialMessage(String message) { return this; } + public static Node getIcon(Object task) { + if (task instanceof Task) { + return BackgroundTask.iconMap.getOrDefault(((Task) task).getTitle(), null); + } + return null; + } + static class BackgroundProgress { private final double workDone; From e24c1418564a9304b9c5172573e9edc9d1036a54 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Tue, 12 May 2020 08:17:26 +0200 Subject: [PATCH 36/41] Fixed progress-indicator styling --- src/main/java/org/jabref/gui/Base.css | 10 ++++------ src/main/java/org/jabref/gui/JabRefFrame.java | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/jabref/gui/Base.css b/src/main/java/org/jabref/gui/Base.css index db47c898732..9d8f0a00ee8 100644 --- a/src/main/java/org/jabref/gui/Base.css +++ b/src/main/java/org/jabref/gui/Base.css @@ -387,13 +387,16 @@ -fx-padding: -0.1em 0.5em 0.5em 0.5em; } -.progress-indicatorToolbar, .progress-indicator { -fx-progress-color: -jr-theme; -fx-border-width: 0px; -fx-background-color: -jr-icon-background; } +.progress-indicator:hover { + -fx-background-color: -jr-icon-background-active; +} + .progress-indicatorToolbar { -fx-padding: 0.5em; } @@ -402,11 +405,6 @@ -fx-fill:null; } -.progress-indicator:hover, -.progress-indicatorToolbar:hover { - -fx-background-color: -jr-icon-background-active; -} - .check-box { -fx-label-padding: 0.0em 0.0em 0.0em 0.75em; -fx-text-fill: -fx-text-background-color; diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index bb7b9395b4e..4d79d4b19a8 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -945,7 +945,7 @@ private MenuBar createMenu() { private Group createTaskIndicator() { ProgressIndicator indicator = new ProgressIndicator(); - indicator.getStyleClass().setAll("progress-indicatorToolbar"); + indicator.getStyleClass().add("progress-indicatorToolbar"); indicator.progressProperty().bind(stateManager.getTasksProgress()); Tooltip someTasksRunning = new Tooltip(Localization.lang("Background Tasks are running")); From 478ee059e5606251361d663b939284f19bc834e4 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 12 May 2020 09:38:03 +0200 Subject: [PATCH 37/41] Improve getIcon method --- src/main/java/org/jabref/gui/util/BackgroundTask.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index 2b9e7b65d2a..a6db2797cf1 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -257,11 +257,8 @@ public BackgroundTask withInitialMessage(String message) { return this; } - public static Node getIcon(Object task) { - if (task instanceof Task) { - return BackgroundTask.iconMap.getOrDefault(((Task) task).getTitle(), null); - } - return null; + public static Node getIcon(Task task) { + return BackgroundTask.iconMap.getOrDefault(task).getTitle(), null); } static class BackgroundProgress { From 0557c678a657e6315dcb2c2c1c98d90b45a76e08 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 12 May 2020 09:41:04 +0200 Subject: [PATCH 38/41] Well, I said hopefully ;-) --- src/main/java/org/jabref/gui/util/BackgroundTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index a6db2797cf1..7dac72734ea 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -258,7 +258,7 @@ public BackgroundTask withInitialMessage(String message) { } public static Node getIcon(Task task) { - return BackgroundTask.iconMap.getOrDefault(task).getTitle(), null); + return BackgroundTask.iconMap.getOrDefault((task).getTitle(), null); } static class BackgroundProgress { From 23b7e6947549c1e44488e72c6a55429f55d63032 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Tue, 12 May 2020 10:03:21 +0200 Subject: [PATCH 39/41] Revert "Well, I said hopefully ;-)" This reverts commit 0557c678a657e6315dcb2c2c1c98d90b45a76e08. --- src/main/java/org/jabref/gui/util/BackgroundTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index 7dac72734ea..a6db2797cf1 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -258,7 +258,7 @@ public BackgroundTask withInitialMessage(String message) { } public static Node getIcon(Task task) { - return BackgroundTask.iconMap.getOrDefault((task).getTitle(), null); + return BackgroundTask.iconMap.getOrDefault(task).getTitle(), null); } static class BackgroundProgress { From e3ae7963d447e5fad4dc5af0b27384e63d2b82d1 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Tue, 12 May 2020 10:03:37 +0200 Subject: [PATCH 40/41] Revert "Improve getIcon method" This reverts commit 478ee059e5606251361d663b939284f19bc834e4. --- src/main/java/org/jabref/gui/util/BackgroundTask.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/util/BackgroundTask.java b/src/main/java/org/jabref/gui/util/BackgroundTask.java index a6db2797cf1..2b9e7b65d2a 100644 --- a/src/main/java/org/jabref/gui/util/BackgroundTask.java +++ b/src/main/java/org/jabref/gui/util/BackgroundTask.java @@ -257,8 +257,11 @@ public BackgroundTask withInitialMessage(String message) { return this; } - public static Node getIcon(Task task) { - return BackgroundTask.iconMap.getOrDefault(task).getTitle(), null); + public static Node getIcon(Object task) { + if (task instanceof Task) { + return BackgroundTask.iconMap.getOrDefault(((Task) task).getTitle(), null); + } + return null; } static class BackgroundProgress { From 3db399741b0ce098cd930005870bc745cd719c70 Mon Sep 17 00:00:00 2001 From: Benedikt Tutzer Date: Tue, 12 May 2020 13:24:25 +0200 Subject: [PATCH 41/41] Improved readability in JabRefFrame --- src/main/java/org/jabref/gui/JabRefFrame.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 4d79d4b19a8..67d97243523 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -408,11 +408,12 @@ public boolean quit() { the background tasks may make changes themselves that need saving. */ if (stateManager.getAnyTaskRunning().getValue()) { - if (!(dialogService.showBackgroundProgressDialogAndWait( + Optional shouldClose = dialogService.showBackgroundProgressDialogAndWait( Localization.lang("Please wait..."), Localization.lang("Waiting for background tasks to finish. Quit anyway?"), stateManager - ).orElse(ButtonType.CANCEL) == ButtonType.YES)) { + ); + if (!(shouldClose.isPresent() && shouldClose.get() == ButtonType.YES)) { return false; } }