From 903f55a24f9685bcd6e784ae7fe3ccacea4cca0c Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Tue, 22 Jun 2021 17:15:37 +0200 Subject: [PATCH] improve health check gui: * convert milliseconds to hours, minutes and seconds * spelling * adjust to dark theme --- .../ui/health/CheckDetailController.java | 35 ++++++++--- .../cryptomator/ui/health/CheckListCell.java | 61 +++++++++++++++++-- .../ui/health/CheckListController.java | 14 +---- src/main/resources/i18n/strings.properties | 7 ++- 4 files changed, 91 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java index 281e404cc..68a74c4a0 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java +++ b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java @@ -15,6 +15,8 @@ import javafx.collections.FXCollections; import javafx.concurrent.Worker; import javafx.fxml.FXML; import javafx.scene.control.ListView; +import java.time.Duration; +import java.util.ResourceBundle; import java.util.function.Function; import java.util.stream.Stream; @@ -24,8 +26,7 @@ public class CheckDetailController implements FxController { private final EasyObservableList results; private final OptionalBinding taskState; private final Binding taskName; - private final Binding taskDuration; - private final ResultListCellFactory resultListCellFactory; + private final Binding taskDuration; private final Binding taskRunning; private final Binding taskScheduled; private final Binding taskFinished; @@ -35,17 +36,20 @@ public class CheckDetailController implements FxController { private final Binding taskCancelled; private final Binding countOfWarnSeverity; private final Binding countOfCritSeverity; + private final ResultListCellFactory resultListCellFactory; + private final ResourceBundle resourceBundle; public ListView resultsListView; private Subscription resultSubscription; @Inject - public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory) { + public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory, ResourceBundle resourceBundle) { + this.resultListCellFactory = resultListCellFactory; + this.resourceBundle = resourceBundle; this.results = EasyBind.wrapList(FXCollections.observableArrayList()); this.taskState = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::stateProperty); this.taskName = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::getTitle).orElse(""); - this.taskDuration = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::durationInMillisProperty).orElse(-1L); - this.resultListCellFactory = resultListCellFactory; + this.taskDuration = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::durationInMillisProperty).orElse(-1L).map(this::millisToReadAbleDuration); this.taskRunning = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::runningProperty).orElse(false); //TODO: DOES NOT WORK this.taskScheduled = taskState.map(Worker.State.SCHEDULED::equals).orElse(false); this.taskNotStarted = taskState.map(Worker.State.READY::equals).orElse(false); @@ -87,11 +91,11 @@ public class CheckDetailController implements FxController { return taskName; } - public Number getTaskDuration() { + public String getTaskDuration() { return taskDuration.getValue(); } - public Binding taskDurationProperty() { + public Binding taskDurationProperty() { return taskDuration; } @@ -167,4 +171,21 @@ public class CheckDetailController implements FxController { return taskCancelled; } + private String millisToReadAbleDuration(Number millis) { + Duration tmp = Duration.ofMillis(millis.longValue()); + long hours = tmp.toHoursPart(); + long minutes = tmp.toMinutesPart(); + long seconds = tmp.toSecondsPart(); + if (hours != 0) { + String hms_format = resourceBundle.getString("health.check.detail.hmsFormat"); + return String.format(hms_format, hours, minutes, seconds); + } else if (minutes != 0) { + String ms_format = resourceBundle.getString("health.check.detail.msFormat"); + return String.format(ms_format, minutes, seconds); + } else { + String s_format = resourceBundle.getString("health.check.detail.sFormat"); + return String.format(s_format, seconds); + } + } + } diff --git a/src/main/java/org/cryptomator/ui/health/CheckListCell.java b/src/main/java/org/cryptomator/ui/health/CheckListCell.java index 78f8b1b33..63d9d8d6b 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckListCell.java +++ b/src/main/java/org/cryptomator/ui/health/CheckListCell.java @@ -4,35 +4,81 @@ import org.cryptomator.ui.controls.FontAwesome5Icon; import org.cryptomator.ui.controls.FontAwesome5IconView; import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; +import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; import javafx.scene.control.ListCell; +import javafx.util.Callback; class CheckListCell extends ListCell { private final FontAwesome5IconView stateIcon = new FontAwesome5IconView(); + private final Callback selectedGetter; + private final ObjectProperty stateProperty; - CheckListCell() { + private CheckBox checkBox = new CheckBox(); + private BooleanProperty selectedProperty; + + CheckListCell(Callback selectedGetter, ObservableValue switchIndicator) { + this.selectedGetter = selectedGetter; + this.stateProperty = new SimpleObjectProperty<>(State.SELECTION); + switchIndicator.addListener(this::changeState); setPadding(new Insets(6)); setAlignment(Pos.CENTER_LEFT); setContentDisplay(ContentDisplay.LEFT); + getStyleClass().add("label"); + } + + private void changeState(ObservableValue 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) { - textProperty().bind(item.titleProperty()); + 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())); + graphicProperty().bind(Bindings.createObjectBinding(() -> graphicForState(item.getState()), item.stateProperty())); stateIcon.setGlyph(glyphForState(item.getState())); } else { - textProperty().unbind(); graphicProperty().unbind(); setGraphic(null); setText(null); @@ -61,4 +107,9 @@ class CheckListCell extends ListCell { case SUCCEEDED -> FontAwesome5Icon.CHECK; }; } + + private enum State { + SELECTION, + RUN; + } } diff --git a/src/main/java/org/cryptomator/ui/health/CheckListController.java b/src/main/java/org/cryptomator/ui/health/CheckListController.java index ccb41d56b..f72aa8481 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckListController.java +++ b/src/main/java/org/cryptomator/ui/health/CheckListController.java @@ -84,17 +84,7 @@ public class CheckListController implements FxController { @FXML public void initialize() { checksListView.setItems(tasks); - checksListView.setCellFactory(CheckBoxListCell.forListView(listPickIndicators::get, new StringConverter() { - @Override - public String toString(HealthCheckTask object) { - return object.getTitle(); - } - - @Override - public HealthCheckTask fromString(String string) { - return null; - } - })); + checksListView.setCellFactory(view -> new CheckListCell(listPickIndicators::get, showResultScreen)); selectedTask.bind(checksListView.getSelectionModel().selectedItemProperty()); } @@ -115,7 +105,7 @@ public class CheckListController implements FxController { runningTask.set(batchService); showResultScreen.set(true); checksListView.getSelectionModel().select(batch.get(0)); - checksListView.setCellFactory(view -> new CheckListCell()); + checksListView.refresh(); window.sizeToScene(); } diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index f0f550769..23e86c55a 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -153,19 +153,22 @@ health.start.configValid=Reading and parsing vault configuration file was succes health.start.configInvalid=Error while reading and parsing the vault configuration file. health.checkList.header=Available Health Checks health.checkList.selectAllBox=Select All -health.check.runBatchBtn=Run selected Checks +health.check.runBatchBtn=Run Selected Checks ## Detail view health.check.detail.noSelectedCheck=For results select a finished health check in the left list. health.check.detail.header=Results of %s health.check.detail.taskNotStarted=The check was not selected to run. health.check.detail.taskScheduled=The check is scheduled. health.check.detail.taskRunning=The check is currently running… -health.check.detail.taskSucceeded=The check finished successfully after %d milliseconds. +health.check.detail.taskSucceeded=The check finished successfully after %s. health.check.detail.taskFailed=The check exited due to an error. health.check.detail.taskCancelled=The check was cancelled. health.check.detail.problemCount=Found %d problems and %d unfixable errors. health.check.exportBtn=Export Report health.check.fixBtn=Fix +health.check.detail.hmsFormat= %d hours, %2d minutes and %2d seconds +health.check.detail.msFormat= %d minutes and %2d seconds +health.check.detail.sFormat= %d seconds ## Checks health.org.cryptomator.cryptofs.health.dirid.DirIdCheck=Directory Check