From 7d73f57f3668d9aa563c34d8e078861b405eafc5 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Fri, 14 May 2021 21:21:37 +0200 Subject: [PATCH] Add time to "stop recording at" --- CHANGELOG.md | 1 + .../ctbrec/ui/action/SetStopDateAction.java | 14 +-- .../ctbrec/ui/controls/DateTimePicker.java | 115 ++++++++++++++++++ 3 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 client/src/main/java/ctbrec/ui/controls/DateTimePicker.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 66d4c775..cb1d1815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Added new post-processing variables modelGroupName and modelGroupId * Added possibility to define a default value for post-processing variables For example: ``${modelGroupName?${modelSanitizedName}}`` +* Added time to "stop recording at" 4.2.1 ======================== diff --git a/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java b/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java index daa4a4e5..a30ae45b 100644 --- a/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java +++ b/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; -import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; import java.util.concurrent.CompletableFuture; @@ -17,11 +17,11 @@ import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.SubsequentAction; import ctbrec.recorder.Recorder; +import ctbrec.ui.controls.DateTimePicker; import ctbrec.ui.controls.Dialogs; import javafx.geometry.Insets; import javafx.scene.Cursor; import javafx.scene.Node; -import javafx.scene.control.DatePicker; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.ToggleGroup; @@ -44,7 +44,7 @@ public class SetStopDateAction { public CompletableFuture execute() { source.setCursor(Cursor.WAIT); - var datePicker = new DatePicker(); + var datePicker = new DateTimePicker(); var gridPane = new GridPane(); gridPane.setHgap(10); gridPane.setVgap(10); @@ -65,15 +65,15 @@ public class SetStopDateAction { HBox.setMargin(removeButton, new Insets(5)); gridPane.add(row, 1, 1); if (model.isRecordingTimeLimited()) { - var localDate = LocalDate.ofInstant(model.getRecordUntil(), ZoneId.systemDefault()); - datePicker.setValue(localDate); + var localDate = LocalDateTime.ofInstant(model.getRecordUntil(), ZoneId.systemDefault()); + datePicker.setDateTimeValue(localDate); } boolean userClickedOk = Dialogs.showCustomInput(source.getScene(), "Stop Recording at", gridPane); return CompletableFuture.supplyAsync(() -> { if (userClickedOk) { SubsequentAction action = pauseButton.isSelected() ? PAUSE : REMOVE; - LOG.info("Stop at {} and {}", datePicker.getValue(), action); - var stopAt = Instant.from(datePicker.getValue().atStartOfDay().atZone(ZoneId.systemDefault())); + LOG.info("Stop at {} and {}", datePicker.getDateTimeValue(), action); + var stopAt = Instant.from(datePicker.getDateTimeValue().atZone(ZoneId.systemDefault())); model.setRecordUntil(stopAt); model.setRecordUntilSubsequentAction(action); try { diff --git a/client/src/main/java/ctbrec/ui/controls/DateTimePicker.java b/client/src/main/java/ctbrec/ui/controls/DateTimePicker.java new file mode 100644 index 00000000..5814a516 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/controls/DateTimePicker.java @@ -0,0 +1,115 @@ + +package ctbrec.ui.controls; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.control.DatePicker; +import javafx.util.StringConverter; + +/** + * A DateTimePicker with configurable datetime format where both date and time can be changed + * via the text field and the date can additionally be changed via the JavaFX default date picker. + * + * @author Edvin Syse + * @author Rene Fischer + * + * copyright TornadoFX + * license Apache 2.0 + * + */ +public class DateTimePicker extends DatePicker { + private static final Logger LOG = LoggerFactory.getLogger(DateTimePicker.class); + public static final String DefaultFormat = "yyyy-MM-dd HH:mm"; + + private DateTimeFormatter formatter; + private ObjectProperty dateTimeValue = new SimpleObjectProperty<>(LocalDateTime.now()); + private ObjectProperty format = new SimpleObjectProperty() { + @Override + public void set(String newValue) { + super.set(newValue); + formatter = DateTimeFormatter.ofPattern(newValue); + } + }; + + public DateTimePicker() { + getStyleClass().add("datetime-picker"); + setFormat(DefaultFormat); + setConverter(new InternalConverter()); + + // Syncronize changes to the underlying date value back to the dateTimeValue + valueProperty().addListener((observable, oldValue, newValue) -> { + if (newValue == null) { + dateTimeValue.set(null); + } else { + if (dateTimeValue.get() == null) { + dateTimeValue.set(LocalDateTime.of(newValue, LocalTime.now())); + } else { + var time = dateTimeValue.get().toLocalTime(); + dateTimeValue.set(LocalDateTime.of(newValue, time)); + } + } + LOG.debug("{} {}", newValue, dateTimeValue); + }); + + editorProperty().get().textProperty().addListener((obs, ov, nv) -> { + try { + getConverter().fromString(nv); + } catch (Exception e) { + } + }); + + // Syncronize changes to dateTimeValue back to the underlying date value + dateTimeValue.addListener((observable, oldValue, newValue) -> setValue(newValue == null ? null : newValue.toLocalDate())); + } + + public LocalDateTime getDateTimeValue() { + return dateTimeValue.get(); + } + + public void setDateTimeValue(LocalDateTime dateTimeValue) { + this.dateTimeValue.set(dateTimeValue); + } + + public ObjectProperty dateTimeValueProperty() { + return dateTimeValue; + } + + public String getFormat() { + return format.get(); + } + + public ObjectProperty formatProperty() { + return format; + } + + public void setFormat(String format) { + this.format.set(format); + } + + class InternalConverter extends StringConverter { + @Override + public String toString(LocalDate object) { + LocalDateTime value = getDateTimeValue(); + return (value != null) ? value.format(formatter) : ""; + } + + @Override + public LocalDate fromString(String value) { + if (value == null) { + dateTimeValue.set(null); + return null; + } + + dateTimeValue.set(LocalDateTime.parse(value, formatter)); + return dateTimeValue.get().toLocalDate(); + } + } +}