Add setting to customize the date time format in the GUI

This commit is contained in:
0xb00bface 2021-12-24 15:38:29 +01:00
parent a6e3fcc6e3
commit 38b898f405
11 changed files with 122 additions and 71 deletions

View File

@ -252,7 +252,7 @@ public class CamrecApplication extends Application {
tabPane.getTabs().add(new RecentlyWatchedTab(recorder, sites));
}
tabPane.getTabs().add(new SettingsTab(sites, recorder));
tabPane.getTabs().add(new NewsTab());
tabPane.getTabs().add(new NewsTab(config));
tabPane.getTabs().add(new DonateTabFx());
tabPane.getTabs().add(new HelpTab());
tabPane.getTabs().add(new LoggingTab());

View File

@ -1,16 +1,22 @@
package ctbrec.ui.controls;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class DateTimeCellFactory<T> implements Callback<TableColumn<T, Instant>, TableCell<T, Instant>> {
private final DateTimeFormatter formatter;
public DateTimeCellFactory (DateTimeFormatter formatter) {
this.formatter = formatter;
}
@Override
public TableCell<T, Instant> call(TableColumn<T, Instant> param) {
return new TableCell<T, Instant>() {
@ -20,7 +26,6 @@ public class DateTimeCellFactory<T> implements Callback<TableColumn<T, Instant>,
setText("");
} else {
var dateTime = LocalDateTime.ofInstant(item, ZoneId.systemDefault());
var formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
String formattedDateTime = formatter.format(dateTime);
setText(item.equals(Instant.EPOCH) ? "" : formattedDateTime);
}

View File

@ -5,6 +5,7 @@ import static ctbrec.io.HttpConstants.*;
import java.io.IOException;
import java.util.Objects;
import ctbrec.Config;
import org.json.JSONObject;
import com.squareup.moshi.JsonAdapter;
@ -26,10 +27,11 @@ import okhttp3.Request;
public class NewsTab extends Tab implements TabSelectionListener {
private static final String ACCESS_TOKEN = "a2804d73a89951a22e0f8483a6fcec8943afd88b7ba17c459c095aa9e6f94fd0";
private static final String URL = "https://mastodon.cloud/api/v1/accounts/480960/statuses?limit=20&exclude_replies=true";
private final Config config;
private final VBox layout = new VBox();
private VBox layout = new VBox();
public NewsTab() {
public NewsTab(Config config) {
this.config = config;
setText("News");
layout.setMaxWidth(800);
layout.setAlignment(Pos.CENTER);
@ -46,11 +48,11 @@ public class NewsTab extends Tab implements TabSelectionListener {
var request = new Request.Builder()
.url(URL)
.header("Authorization", "Bearer " + ACCESS_TOKEN)
.header(USER_AGENT, "ctbrec " + CamrecApplication.getVersion().toString())
.header(USER_AGENT, "ctbrec " + CamrecApplication.getVersion())
.build();
try (var response = CamrecApplication.httpClient.execute(request)) {
if (response.isSuccessful()) {
var body = response.body().string();
var body = Objects.requireNonNull(response.body(), "HTTP response body is null").string();
if (body.startsWith("[")) {
onSuccess(body);
} else if (body.startsWith("{")) {
@ -79,12 +81,12 @@ public class NewsTab extends Tab implements TabSelectionListener {
private void onSuccess(String body) throws IOException {
var moshi = new Moshi.Builder().build();
JsonAdapter<Status[]> statusListAdapter = moshi.adapter(Status[].class);
Status[] statusArray = statusListAdapter.fromJson(body);
Status[] statusArray = Objects.requireNonNull(statusListAdapter.fromJson(body));
Platform.runLater(() -> {
layout.getChildren().clear();
for (Status status : statusArray) {
if (status.getInReplyToId() == null && !Objects.equals("direct", status.getVisibility())) {
var stp = new StatusPane(status);
var stp = new StatusPane(status, config.getDateTimeFormatter());
layout.getChildren().add(stp);
VBox.setMargin(stp, new Insets(10));
}

View File

@ -1,10 +1,5 @@
package ctbrec.ui.news;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Set;
import ctbrec.io.HtmlParser;
import ctbrec.ui.DesktopIntegration;
import javafx.collections.ObservableList;
@ -12,20 +7,20 @@ import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Set;
public class StatusPane extends StackPane {
TextArea content;
Button reply;
public StatusPane(Status status) {
public StatusPane(Status status, DateTimeFormatter formatter) {
String text = HtmlParser.getText("<div>" + status.getContent() + "</div>", "div");
content = new TextArea(text);
@ -35,7 +30,7 @@ public class StatusPane extends StackPane {
getChildren().add(content);
ZonedDateTime createdAt = status.getCreationTime();
String creationTime = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT).format(createdAt);
String creationTime = formatter.format(createdAt);
var time = new Label(creationTime);
time.setStyle("-fx-background-color: -fx-base");
time.setOpacity(.7);
@ -63,18 +58,14 @@ public class StatusPane extends StackPane {
protected void layoutChildren() {
ObservableList<Node> childrenUnmodifiable = content.getChildrenUnmodifiable();
for (Node node : childrenUnmodifiable) {
if (node instanceof ScrollPane) {
var scrollPane = (ScrollPane) node;
if (node instanceof ScrollPane scrollPane) {
Set<Node> nodes = scrollPane.lookupAll(".scroll-bar");
for (final Node child : nodes) {
if (child instanceof ScrollBar) {
ScrollBar sb = (ScrollBar) child;
if (sb.getOrientation() == Orientation.VERTICAL) {
if (sb.isVisible()) {
StackPane.setMargin(reply, new Insets(5, 22, 5, 5));
} else {
StackPane.setMargin(reply, new Insets(5, 5, 5, 5));
}
if (child instanceof ScrollBar sb && sb.getOrientation() == Orientation.VERTICAL) {
if (sb.isVisible()) {
StackPane.setMargin(reply, new Insets(5, 22, 5, 5));
} else {
StackPane.setMargin(reply, new Insets(5, 5, 5, 5));
}
}
}

View File

@ -119,6 +119,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private LocalTimeProperty timeoutRecordingStartingAt;
private LocalTimeProperty timeoutRecordingEndingAt;
private SimpleLongProperty recordUntilDefaultDurationInMinutes;
private SimpleStringProperty dateTimeFormat;
public SettingsTab(List<Site> sites, Recorder recorder) {
this.sites = sites;
@ -189,6 +190,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
timeoutRecordingStartingAt = new LocalTimeProperty(null, "timeoutRecordingStartingAt", settings.timeoutRecordingStartingAt);
timeoutRecordingEndingAt = new LocalTimeProperty(null, "timeoutRecordingEndingAt", settings.timeoutRecordingEndingAt);
recordUntilDefaultDurationInMinutes = new SimpleLongProperty(null, "recordUntilDefaultDurationInMinutes", settings.recordUntilDefaultDurationInMinutes);
dateTimeFormat = new SimpleStringProperty(null, "dateTimeFormat", settings.dateTimeFormat);
}
private void createGui() {
@ -229,6 +231,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Group.of("Look & Feel",
Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance())).needsRestart(),
Setting.of("Font", new FontSettingsPane(this, config)).needsRestart(),
Setting.of("Date format (empty = system default)", dateTimeFormat, DATE_FORMATTER_TOOLTIP).needsRestart(),
Setting.of("Display stream resolution in overview", determineResolution),
Setting.of("Total model count in title", totalModelCountInTitle, "Show the total number of models in the title bar"),
Setting.of("Show grid lines in tables", showGridLinesInTables, "Show grid lines in tables").needsRestart(),
@ -588,5 +591,54 @@ public class SettingsTab extends Tab implements TabSelectionListener {
}
}
private static final String DATE_FORMATTER_TOOLTIP = """
Leave empty for system default
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
""";
}

View File

@ -147,8 +147,8 @@ public class RecentlyWatchedTab extends Tab implements ShutdownListener {
TableColumn<PlayerStartedEvent, Instant> timestamp = createTableColumn("Timestamp", 150, idx);
timestamp.setId("timestamp");
timestamp.setCellValueFactory(cdf -> new SimpleObjectProperty<Instant>(cdf.getValue().getTimestamp()));
timestamp.setCellFactory(new DateTimeCellFactory<>());
timestamp.setCellValueFactory(cdf -> new SimpleObjectProperty<>(cdf.getValue().getTimestamp()));
timestamp.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter()));
timestamp.setEditable(false);
timestamp.setSortType(SortType.DESCENDING);
table.getColumns().add(timestamp);

View File

@ -128,7 +128,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener, Shutdown
var instant = cdf.getValue().getStartDate();
return new SimpleObjectProperty<>(instant);
});
date.setCellFactory(new DateTimeCellFactory<>());
date.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter()));
date.setPrefWidth(200);
TableColumn<JavaFxRecording, String> status = new TableColumn<>("Status");
status.setId("status");

View File

@ -315,7 +315,7 @@ public abstract class AbstractRecordedModelsTab extends Tab implements TabSelect
protected void addAddedTimestampColumn(int columnIdx) {
TableColumn<JavaFxModel, Instant> tc = addTableColumn("addedTimestamp", "added at", columnIdx, 400);
tc.setCellFactory(new DateTimeCellFactory<>());
tc.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter()));
tc.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getAddedTimestamp()));
tc.setPrefWidth(150);
tc.setEditable(false);

View File

@ -124,7 +124,7 @@ public class RecordedModelsTab extends AbstractRecordedModelsTab implements TabS
table.getColumns().add(priority);
TableColumn<JavaFxModel, Instant> lastSeen = new TableColumn<>("last seen");
lastSeen.setCellValueFactory(cdf -> cdf.getValue().lastSeenProperty());
lastSeen.setCellFactory(new DateTimeCellFactory<>());
lastSeen.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter()));
lastSeen.setPrefWidth(150);
lastSeen.setEditable(false);
lastSeen.setId("lastSeen");
@ -134,7 +134,7 @@ public class RecordedModelsTab extends AbstractRecordedModelsTab implements TabS
table.getColumns().add(lastSeen);
TableColumn<JavaFxModel, Instant> lastRecorded = new TableColumn<>("last recorded");
lastRecorded.setCellValueFactory(cdf -> cdf.getValue().lastRecordedProperty());
lastRecorded.setCellFactory(new DateTimeCellFactory<>());
lastRecorded.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter()));
lastRecorded.setPrefWidth(150);
lastRecorded.setEditable(false);
lastRecorded.setId("lastRecorded");

View File

@ -1,7 +1,18 @@
package ctbrec;
import static java.nio.charset.StandardCharsets.*;
import static java.nio.file.StandardOpenOption.*;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import ctbrec.Settings.SplitStrategy;
import ctbrec.io.*;
import ctbrec.recorder.postprocessing.DeleteTooShort;
import ctbrec.recorder.postprocessing.PostProcessor;
import ctbrec.recorder.postprocessing.RemoveKeepFile;
import ctbrec.recorder.postprocessing.Script;
import ctbrec.sites.Site;
import ctbrec.sites.cam4.Cam4Model;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
@ -13,34 +24,13 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.time.format.FormatStyle;
import java.util.*;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import ctbrec.Settings.SplitStrategy;
import ctbrec.io.FileJsonAdapter;
import ctbrec.io.LocalTimeJsonAdapter;
import ctbrec.io.ModelJsonAdapter;
import ctbrec.io.PostProcessorJsonAdapter;
import ctbrec.io.UuidJSonAdapter;
import ctbrec.recorder.postprocessing.DeleteTooShort;
import ctbrec.recorder.postprocessing.PostProcessor;
import ctbrec.recorder.postprocessing.RemoveKeepFile;
import ctbrec.recorder.postprocessing.Script;
import ctbrec.sites.Site;
import ctbrec.sites.cam4.Cam4Model;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.*;
// TODO don't use singleton pattern
public class Config {
@ -305,6 +295,15 @@ public class Config {
return context;
}
public DateTimeFormatter getDateTimeFormatter() {
var dtf = getSettings().dateTimeFormat;
if (StringUtil.isNotBlank(dtf)) {
return DateTimeFormatter.ofPattern(dtf);
} else {
return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
}
}
public String getModelNotes(Model m) {
return Config.getInstance().getSettings().modelNotes.getOrDefault(m.getUrl(), "");
}

View File

@ -2,6 +2,7 @@ package ctbrec;
import java.io.File;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -66,6 +67,7 @@ public class Settings {
public int concurrentRecordings = 0;
public boolean confirmationForDangerousActions = false;
public String contactsheetTimestampLook = "font=sans-serif:fontcolor=white:fontsize=60:box=1:boxcolor=black@0.5:boxborderw=5";
public String dateTimeFormat = "";
public int defaultPriority = 50;
public boolean determineResolution = false;
public List<String> disabledSites = new ArrayList<>();