浏览代码

simplify task selection code

Sebastian Stenzel 3 年之前
父节点
当前提交
c7b9735f13

+ 4 - 47
src/main/java/org/cryptomator/ui/health/CheckListCell.java

@@ -20,68 +20,29 @@ import javafx.util.Callback;
 class CheckListCell extends ListCell<HealthCheckTask> {
 
 	private final FontAwesome5IconView stateIcon = new FontAwesome5IconView();
-	private final Callback<HealthCheckTask, BooleanProperty> selectedGetter;
-	private final ObjectProperty<State> stateProperty;
-
 	private CheckBox checkBox = new CheckBox();
-	private BooleanProperty selectedProperty;
 
-	CheckListCell(Callback<HealthCheckTask, BooleanProperty> selectedGetter, ObservableValue<Boolean> switchIndicator) {
-		this.selectedGetter = selectedGetter;
-		this.stateProperty = new SimpleObjectProperty<>(State.SELECTION);
-		switchIndicator.addListener(this::changeState);
+	CheckListCell() {
 		setPadding(new Insets(6));
 		setAlignment(Pos.CENTER_LEFT);
 		setContentDisplay(ContentDisplay.LEFT);
 		getStyleClass().add("label");
 	}
 
-	private void changeState(ObservableValue<? extends Boolean> observableValue, boolean oldValue, boolean newValue) {
-		if (newValue) {
-			stateProperty.set(State.RUN);
-		} else {
-			stateProperty.set(State.SELECTION);
-		}
-	}
-
 	@Override
 	protected void updateItem(HealthCheckTask item, boolean empty) {
 		super.updateItem(item, empty);
 		if (item != null) {
 			setText(item.getTitle());
-		}
-		switch (stateProperty.get()) {
-			case SELECTION -> updateItemSelection(item, empty);
-			case RUN -> updateItemRun(item, empty);
-		}
-	}
-
-	private void updateItemSelection(HealthCheckTask item, boolean empty) {
-		if (!empty) {
-			setGraphic(checkBox);
-
-			if (selectedProperty != null) {
-				checkBox.selectedProperty().unbindBidirectional(selectedProperty);
-			}
-			selectedProperty = selectedGetter.call(item);
-			if (selectedProperty != null) {
-				checkBox.selectedProperty().bindBidirectional(selectedProperty);
-			}
-		} else {
-			setGraphic(null);
-			setText(null);
-		}
-	}
-
-	private void updateItemRun(HealthCheckTask item, boolean empty) {
-		if (item != null) {
 			item.stateProperty().addListener(this::stateChanged);
 			graphicProperty().bind(Bindings.createObjectBinding(() -> graphicForState(item.getState()), item.stateProperty()));
 			stateIcon.setGlyph(glyphForState(item.getState()));
+			checkBox.selectedProperty().bindBidirectional(item.chosenForExecutionProperty());
 		} else {
 			graphicProperty().unbind();
 			setGraphic(null);
 			setText(null);
+			checkBox.selectedProperty().unbind();
 		}
 	}
 
@@ -92,7 +53,7 @@ class CheckListCell extends ListCell<HealthCheckTask> {
 
 	private Node graphicForState(Worker.State state) {
 		return switch (state) {
-			case READY -> null;
+			case READY -> checkBox;
 			case SCHEDULED, RUNNING, FAILED, CANCELLED, SUCCEEDED -> stateIcon;
 		};
 	}
@@ -108,8 +69,4 @@ class CheckListCell extends ListCell<HealthCheckTask> {
 		};
 	}
 
-	private enum State {
-		SELECTION,
-		RUN;
-	}
 }

+ 24 - 30
src/main/java/org/cryptomator/ui/health/CheckListController.java

@@ -1,6 +1,7 @@
 package org.cryptomator.ui.health;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
 import com.tobiasdiez.easybind.EasyBind;
 import dagger.Lazy;
 import org.cryptomator.ui.common.ErrorComponent;
@@ -10,28 +11,24 @@ import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
 import javafx.beans.binding.Binding;
+import javafx.beans.binding.Bindings;
 import javafx.beans.binding.BooleanBinding;
+import javafx.beans.binding.IntegerBinding;
 import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.IntegerProperty;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.property.SimpleIntegerProperty;
 import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
 import javafx.concurrent.Worker;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
 import javafx.scene.control.CheckBox;
 import javafx.scene.control.ListView;
-import javafx.scene.control.cell.CheckBoxListCell;
 import javafx.stage.Stage;
-import javafx.util.StringConverter;
 import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
@@ -43,6 +40,7 @@ public class CheckListController implements FxController {
 
 	private final Stage window;
 	private final ObservableList<HealthCheckTask> tasks;
+	private final FilteredList<HealthCheckTask> chosenTasks;
 	private final ReportWriter reportWriter;
 	private final ExecutorService executorService;
 	private final ObjectProperty<HealthCheckTask> selectedTask;
@@ -50,19 +48,18 @@ public class CheckListController implements FxController {
 	private final SimpleObjectProperty<Worker<?>> runningTask;
 	private final Binding<Boolean> running;
 	private final Binding<Boolean> finished;
-	private final Map<HealthCheckTask, BooleanProperty> listPickIndicators;
-	private final IntegerProperty numberOfPickedChecks;
+	private final IntegerBinding chosenTaskCount;
 	private final BooleanBinding anyCheckSelected;
 	private final BooleanProperty showResultScreen;
 
 	/* FXML */
 	public ListView<HealthCheckTask> checksListView;
 
-
 	@Inject
-	public CheckListController(@HealthCheckWindow Stage window, Lazy<Collection<HealthCheckTask>> tasks, ReportWriter reportWriteTask, ObjectProperty<HealthCheckTask> selectedTask, ExecutorService executorService, Lazy<ErrorComponent.Builder> errorComponentBuilder) {
+	public CheckListController(@HealthCheckWindow Stage window, Lazy<List<HealthCheckTask>> tasks, ReportWriter reportWriteTask, ObjectProperty<HealthCheckTask> selectedTask, ExecutorService executorService, Lazy<ErrorComponent.Builder> errorComponentBuilder) {
 		this.window = window;
-		this.tasks = FXCollections.observableArrayList(tasks.get());
+		this.tasks = FXCollections.observableList(tasks.get(), HealthCheckTask::observables);
+		this.chosenTasks = this.tasks.filtered(HealthCheckTask::isChosenForExecution);
 		this.reportWriter = reportWriteTask;
 		this.executorService = executorService;
 		this.selectedTask = selectedTask;
@@ -70,13 +67,7 @@ public class CheckListController implements FxController {
 		this.runningTask = new SimpleObjectProperty<>();
 		this.running = EasyBind.wrapNullable(runningTask).mapObservable(Worker::runningProperty).orElse(false);
 		this.finished = EasyBind.wrapNullable(runningTask).mapObservable(Worker::stateProperty).map(END_STATES::contains).orElse(false);
-		this.listPickIndicators = new HashMap<>();
-		this.numberOfPickedChecks = new SimpleIntegerProperty(0);
-		this.tasks.forEach(task -> {
-			var entrySelectedProp = new SimpleBooleanProperty(false);
-			entrySelectedProp.addListener((observable, oldValue, newValue) -> numberOfPickedChecks.set(numberOfPickedChecks.get() + (newValue ? 1 : -1)));
-			listPickIndicators.put(task, entrySelectedProp);
-		});
+		this.chosenTaskCount = Bindings.size(this.chosenTasks);
 		this.anyCheckSelected = selectedTask.isNotNull();
 		this.showResultScreen = new SimpleBooleanProperty(false);
 	}
@@ -84,27 +75,31 @@ public class CheckListController implements FxController {
 	@FXML
 	public void initialize() {
 		checksListView.setItems(tasks);
-		checksListView.setCellFactory(view -> new CheckListCell(listPickIndicators::get, showResultScreen));
+		checksListView.setCellFactory(view -> new CheckListCell());
 		selectedTask.bind(checksListView.getSelectionModel().selectedItemProperty());
 	}
 
 	@FXML
 	public void toggleSelectAll(ActionEvent event) {
 		if (event.getSource() instanceof CheckBox c) {
-			listPickIndicators.forEach( (task, pickProperty) -> pickProperty.set(c.isSelected()));
+			tasks.forEach(t -> t.chosenForExecutionProperty().set(c.isSelected()));
 		}
 	}
 
 	@FXML
 	public void runSelectedChecks() {
 		Preconditions.checkState(runningTask.get() == null);
-		var batch = checksListView.getItems().filtered(item -> listPickIndicators.get(item).get());
-		var batchService = new BatchService(batch);
+
+		// prevent further interaction by cancelling non-chosen tasks:
+		tasks.filtered(Predicates.not(chosenTasks::contains)).forEach(HealthCheckTask::cancel);
+
+		// run chosen tasks:
+		var batchService = new BatchService(chosenTasks);
 		batchService.setExecutor(executorService);
 		batchService.start();
 		runningTask.set(batchService);
 		showResultScreen.set(true);
-		checksListView.getSelectionModel().select(batch.get(0));
+		checksListView.getSelectionModel().select(chosenTasks.get(0));
 		checksListView.refresh();
 		window.sizeToScene();
 	}
@@ -158,13 +153,12 @@ public class CheckListController implements FxController {
 		return showResultScreen;
 	}
 
-	public int getNumberOfPickedChecks() {
-		return numberOfPickedChecks.get();
+	public int getChosenTaskCount() {
+		return chosenTaskCount.getValue();
 	}
 
-	public IntegerProperty numberOfPickedChecksProperty() {
-		return numberOfPickedChecks;
+	public IntegerBinding chosenTaskCountProperty() {
+		return chosenTaskCount;
 	}
 
-
 }

+ 2 - 3
src/main/java/org/cryptomator/ui/health/HealthCheckModule.java

@@ -19,8 +19,6 @@ import org.cryptomator.ui.keyloading.KeyLoadingComponent;
 import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
 import org.cryptomator.ui.mainwindow.MainWindow;
 
-import javax.annotation.Nullable;
-import javax.inject.Named;
 import javax.inject.Provider;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
@@ -30,6 +28,7 @@ import javafx.stage.Modality;
 import javafx.stage.Stage;
 import java.security.SecureRandom;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.ResourceBundle;
@@ -65,7 +64,7 @@ abstract class HealthCheckModule {
 	/* Only inject with Lazy-Wrapper!*/
 	@Provides
 	@HealthCheckScoped
-	static Collection<HealthCheckTask> provideAvailableHealthCheckTasks(Collection<HealthCheck> availableHealthChecks, @HealthCheckWindow Vault vault, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, SecureRandom csprng, ResourceBundle resourceBundle) {
+	static List<HealthCheckTask> provideAvailableHealthCheckTasks(Collection<HealthCheck> availableHealthChecks, @HealthCheckWindow Vault vault, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, SecureRandom csprng, ResourceBundle resourceBundle) {
 		return availableHealthChecks.stream().map(check -> new HealthCheckTask(vault.getPath(), vaultConfigRef.get(), masterkeyRef.get(), csprng, check, resourceBundle)).toList();
 	}
 

+ 17 - 12
src/main/java/org/cryptomator/ui/health/HealthCheckTask.java

@@ -9,7 +9,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javafx.application.Platform;
+import javafx.beans.Observable;
+import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.LongProperty;
+import javafx.beans.property.SimpleBooleanProperty;
 import javafx.beans.property.SimpleLongProperty;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
@@ -34,6 +37,7 @@ class HealthCheckTask extends Task<Void> {
 	private final HealthCheck check;
 	private final ObservableList<DiagnosticResult> results;
 	private final LongProperty durationInMillis;
+	private final BooleanProperty chosenForExecution;
 
 	public HealthCheckTask(Path vaultPath, VaultConfig vaultConfig, Masterkey masterkey, SecureRandom csprng, HealthCheck check, ResourceBundle resourceBundle) {
 		this.vaultPath = Objects.requireNonNull(vaultPath);
@@ -49,6 +53,7 @@ class HealthCheckTask extends Task<Void> {
 			updateTitle(check.identifier());
 		}
 		this.durationInMillis = new SimpleLongProperty(-1);
+		this.chosenForExecution = new SimpleBooleanProperty();
 	}
 
 	@Override
@@ -60,21 +65,10 @@ class HealthCheckTask extends Task<Void> {
 				if (isCancelled()) {
 					throw new CancellationException();
 				}
-				// FIXME: slowdown for demonstration purposes only:
-				try {
-					Thread.sleep(2000);
-				} catch (InterruptedException e) {
-					if (isCancelled()) {
-						return;
-					} else {
-						Thread.currentThread().interrupt();
-						throw new RuntimeException(e);
-					}
-				}
 				Platform.runLater(() -> results.add(result));
 			});
 		}
-		Platform.runLater(() ->durationInMillis.set(Duration.between(start, Instant.now()).toMillis()));
+		Platform.runLater(() -> durationInMillis.set(Duration.between(start, Instant.now()).toMillis()));
 		return null;
 	}
 
@@ -90,6 +84,10 @@ class HealthCheckTask extends Task<Void> {
 
 	/* Getter */
 
+	Observable[] observables() {
+		return new Observable[]{results, chosenForExecution};
+	}
+
 	public ObservableList<DiagnosticResult> results() {
 		return results;
 	}
@@ -106,4 +104,11 @@ class HealthCheckTask extends Task<Void> {
 		return durationInMillis.get();
 	}
 
+	public BooleanProperty chosenForExecutionProperty() {
+		return chosenForExecution;
+	}
+
+	public boolean isChosenForExecution() {
+		return chosenForExecution.get();
+	}
 }

+ 3 - 3
src/main/resources/fxml/health_check_list.fxml

@@ -3,13 +3,13 @@
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.ButtonBar?>
+<?import javafx.scene.control.CheckBox?>
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.control.ListView?>
 <?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.StackPane?>
 <?import javafx.scene.layout.VBox?>
 <?import java.lang.Integer?>
-<?import javafx.scene.layout.StackPane?>
-<?import javafx.scene.control.CheckBox?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.health.CheckListController"
@@ -39,7 +39,7 @@
 			<buttons>
 				<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#cancelCheck" disable="${!controller.running}" visible="${controller.showResultScreen}" managed="${controller.showResultScreen}" />
 				<Button text="%health.check.exportBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" disable="${!controller.finished}" visible="${controller.showResultScreen}" managed="${controller.showResultScreen}" onAction="#exportResults"/>
-				<Button text="%health.check.runBatchBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#runSelectedChecks" disable="${controller.numberOfPickedChecks == ZERO}" visible="${!controller.showResultScreen}" managed="${!controller.showResultScreen}"/>
+				<Button text="%health.check.runBatchBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#runSelectedChecks" disable="${controller.chosenTaskCount == ZERO}" visible="${!controller.showResultScreen}" managed="${!controller.showResultScreen}"/>
 			</buttons>
 		</ButtonBar>
 	</children>