Add font setting to settings tab
This commit is contained in:
parent
ffb1997e24
commit
b1f3390a69
|
@ -255,6 +255,7 @@ public class CamrecApplication extends Application {
|
|||
loadStyleSheet(primaryStage, "color.css");
|
||||
}
|
||||
loadStyleSheet(primaryStage, "style.css");
|
||||
loadStyleSheet(primaryStage, "font.css");
|
||||
primaryStage.getScene().getStylesheets().add("/ctbrec/ui/settings/ColorSettingsPane.css");
|
||||
primaryStage.getScene().getStylesheets().add("/ctbrec/ui/controls/SearchBox.css");
|
||||
primaryStage.getScene().getStylesheets().add("/ctbrec/ui/controls/Popover.css");
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package ctbrec.ui.settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.ui.controls.Dialogs;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.text.Font;
|
||||
|
||||
public class FontSettingsPane extends HBox {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FontSettingsPane.class);
|
||||
|
||||
private ComboBox<String> fontFaceCombo;
|
||||
private ComboBox<Integer> fontSizeCombo;
|
||||
|
||||
private SettingsTab settingsTab;
|
||||
private Config config;
|
||||
|
||||
public FontSettingsPane(SettingsTab settingsTab, Config config) {
|
||||
this.settingsTab = settingsTab;
|
||||
this.config = config;
|
||||
setSpacing(5);
|
||||
getChildren().addAll(buildFontFaceCombo(), buildFontSizeCombo(), buildFontResetButton());
|
||||
}
|
||||
|
||||
private ComboBox<String> buildFontFaceCombo() {
|
||||
ObservableList<String> lst = FXCollections.observableList(javafx.scene.text.Font.getFamilies());
|
||||
fontFaceCombo = new ComboBox<>(lst);
|
||||
fontFaceCombo.getSelectionModel().select(0);
|
||||
fontFaceCombo.setCellFactory((listview -> new ListCell<String>() {
|
||||
@Override
|
||||
protected void updateItem(String family, boolean empty) {
|
||||
super.updateItem(family, empty);
|
||||
if (empty) {
|
||||
setText(null);
|
||||
} else {
|
||||
setFont(Font.font(family));
|
||||
setText(family);
|
||||
}
|
||||
}
|
||||
}));
|
||||
fontFaceCombo.getSelectionModel().select(config.getSettings().fontFamily);
|
||||
fontFaceCombo.setOnAction(evt -> saveFontConfig());
|
||||
return fontFaceCombo;
|
||||
}
|
||||
|
||||
private ComboBox<Integer> buildFontSizeCombo() {
|
||||
ObservableList<Integer> lst = FXCollections
|
||||
.observableList(List.of(4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 32, 48, 64, 72, 80, 96, 128));
|
||||
fontSizeCombo = new ComboBox<>(lst);
|
||||
fontSizeCombo.setOnAction(evt -> saveFontConfig());
|
||||
int size = config.getSettings().fontSize;
|
||||
int selectedIndex = Math.max(0, lst.indexOf(size));
|
||||
fontSizeCombo.getSelectionModel().select(selectedIndex);
|
||||
return fontSizeCombo;
|
||||
}
|
||||
|
||||
private void saveFontConfig() {
|
||||
Font font = Font.font(fontFaceCombo.getSelectionModel().getSelectedItem());
|
||||
int size = fontSizeCombo.getSelectionModel().getSelectedItem();
|
||||
String css = ".root {\n -fx-font: " + size + " '" + font.getFamily() + "';\n}";
|
||||
config.getSettings().fontFamily = font.getFamily();
|
||||
config.getSettings().fontSize = size;
|
||||
try {
|
||||
config.save();
|
||||
Files.writeString(getFontCssFile().toPath(), css);
|
||||
settingsTab.showRestartRequired();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Couldn't write font file", e);
|
||||
Dialogs.showError(getScene(), "Error saving configuration", "The font stylesheet file couldn't be written", e);
|
||||
}
|
||||
}
|
||||
|
||||
private File getFontCssFile() {
|
||||
return new File(Config.getInstance().getConfigDir(), "font.css");
|
||||
}
|
||||
|
||||
private Button buildFontResetButton() {
|
||||
var button = new Button("Reset");
|
||||
button.setOnAction(evt -> {
|
||||
try {
|
||||
Files.delete(getFontCssFile().toPath());
|
||||
settingsTab.showRestartRequired();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Couldn't delete font file", e);
|
||||
Dialogs.showError(getScene(), "Error resetting font configuration", "The font stylesheet file couldn't be deleted", e);
|
||||
}
|
||||
});
|
||||
return button;
|
||||
}
|
||||
}
|
|
@ -50,14 +50,11 @@ import javafx.beans.property.SimpleLongProperty;
|
|||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import javafx.scene.layout.Background;
|
||||
|
@ -71,7 +68,6 @@ import javafx.scene.layout.GridPane;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.util.Duration;
|
||||
|
||||
public class SettingsTab extends Tab implements TabSelectionListener {
|
||||
|
@ -172,13 +168,16 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
proxyUser = new SimpleStringProperty(null, "proxyUser", settings.proxyUser);
|
||||
proxyPassword = new SimpleStringProperty(null, "proxyPassword", settings.proxyPassword);
|
||||
recordingsDir = new SimpleDirectoryProperty(null, "recordingsDir", settings.recordingsDir);
|
||||
directoryStructure = new SimpleListProperty<>(null, "recordingsDirStructure", FXCollections.observableList(List.of(FLAT, ONE_PER_MODEL, ONE_PER_RECORDING)));
|
||||
directoryStructure = new SimpleListProperty<>(null, "recordingsDirStructure",
|
||||
FXCollections.observableList(List.of(FLAT, ONE_PER_MODEL, ONE_PER_RECORDING)));
|
||||
splitAfter = new SimpleListProperty<>(null, "splitRecordingsAfterSecs", FXCollections.observableList(getSplitAfterSecsOptions()));
|
||||
splitBiggerThan = new SimpleListProperty<>(null, "splitRecordingsBiggerThanBytes", FXCollections.observableList(getSplitBiggerThanOptions()));
|
||||
resolutionRange = new SimpleRangeProperty<>(rangeValues, "minimumResolution", "maximumResolution", settings.minimumResolution, settings.maximumResolution);
|
||||
resolutionRange = new SimpleRangeProperty<>(rangeValues, "minimumResolution", "maximumResolution", settings.minimumResolution,
|
||||
settings.maximumResolution);
|
||||
concurrentRecordings = new SimpleIntegerProperty(null, "concurrentRecordings", settings.concurrentRecordings);
|
||||
onlineCheckIntervalInSecs = new SimpleIntegerProperty(null, "onlineCheckIntervalInSecs", settings.onlineCheckIntervalInSecs);
|
||||
leaveSpaceOnDevice = new SimpleLongProperty(null, "minimumSpaceLeftInBytes", (long) new GigabytesConverter().convertTo(settings.minimumSpaceLeftInBytes));
|
||||
leaveSpaceOnDevice = new SimpleLongProperty(null, "minimumSpaceLeftInBytes",
|
||||
(long) new GigabytesConverter().convertTo(settings.minimumSpaceLeftInBytes));
|
||||
ffmpegParameters = new SimpleStringProperty(null, "ffmpegMergedDownloadArgs", settings.ffmpegMergedDownloadArgs);
|
||||
logFFmpegOutput = new SimpleBooleanProperty(null, "logFFmpegOutput", settings.logFFmpegOutput);
|
||||
loghlsdlOutput = new SimpleBooleanProperty(null, "loghlsdlOutput", settings.loghlsdlOutput);
|
||||
|
@ -215,94 +214,62 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
var storage = new CtbrecPreferencesStorage(config);
|
||||
var prefs = Preferences.of(storage,
|
||||
Category.of("General",
|
||||
Group.of("General",
|
||||
Setting.of("User-Agent", httpUserAgent),
|
||||
Setting.of("User-Agent mobile", httpUserAgentMobile),
|
||||
Setting.of("Update overview interval (seconds)", overviewUpdateIntervalInSecs, "Update the thumbnail overviews every x seconds").needsRestart(),
|
||||
Setting.of("Update thumbnails", updateThumbnails, "The overviews will still be updated, but the thumbnails won't be changed. This is useful for less powerful systems."),
|
||||
Setting.of("Display stream resolution in overview", determineResolution),
|
||||
Setting.of("Manually select stream quality", chooseStreamQuality, "Opens a dialog to select the video resolution before recording"),
|
||||
Setting.of("Enable live previews (experimental)", livePreviews),
|
||||
Setting.of("Enable recently watched tab", recentlyWatched).needsRestart(),
|
||||
Setting.of("Add models from clipboard", monitorClipboard, "Monitor clipboard for model URLs and automatically add them to the recorder").needsRestart(),
|
||||
Setting.of("Minimize to tray", minimizeToTray, "Removes the app from the task bar, if minimized"),
|
||||
Setting.of("Fast scroll speed", fastScrollSpeed, "Makes the thumbnail overviews scroll faster with the mouse wheel").needsRestart(),
|
||||
Setting.of("Show confirmation dialogs", confirmationDialogs, "Show confirmation dialogs for irreversible actions"),
|
||||
Setting.of("Total model count in title", totalModelCountInTitle, "Show the total number of models in the title bar"),
|
||||
Setting.of("Start Tab", startTab),
|
||||
Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance())).needsRestart()
|
||||
//,
|
||||
//Setting.of("Font", buildFontFaceCombo()).needsRestart()
|
||||
),
|
||||
Group.of("Player",
|
||||
Setting.of("Player", mediaPlayer),
|
||||
Setting.of("Start parameters", mediaPlayerParams),
|
||||
Setting.of("Maximum resolution (0 = unlimited)", maximumResolutionPlayer, "video height, e.g. 720 or 1080"),
|
||||
Setting.of("Show \"Player Starting\" Message", showPlayerStarting),
|
||||
Setting.of("Start only one player at a time", singlePlayer)
|
||||
)
|
||||
),
|
||||
var prefs = Preferences.of(storage, Category.of("General", Group.of("General", Setting.of("User-Agent", httpUserAgent),
|
||||
Setting.of("User-Agent mobile", httpUserAgentMobile),
|
||||
Setting.of("Update overview interval (seconds)", overviewUpdateIntervalInSecs, "Update the thumbnail overviews every x seconds").needsRestart(),
|
||||
Setting.of("Update thumbnails", updateThumbnails,
|
||||
"The overviews will still be updated, but the thumbnails won't be changed. This is useful for less powerful systems."),
|
||||
Setting.of("Display stream resolution in overview", determineResolution),
|
||||
Setting.of("Manually select stream quality", chooseStreamQuality, "Opens a dialog to select the video resolution before recording"),
|
||||
Setting.of("Enable live previews (experimental)", livePreviews), Setting.of("Enable recently watched tab", recentlyWatched).needsRestart(),
|
||||
Setting.of("Add models from clipboard", monitorClipboard, "Monitor clipboard for model URLs and automatically add them to the recorder")
|
||||
.needsRestart(),
|
||||
Setting.of("Minimize to tray", minimizeToTray, "Removes the app from the task bar, if minimized"),
|
||||
Setting.of("Fast scroll speed", fastScrollSpeed, "Makes the thumbnail overviews scroll faster with the mouse wheel").needsRestart(),
|
||||
Setting.of("Show confirmation dialogs", confirmationDialogs, "Show confirmation dialogs for irreversible actions"),
|
||||
Setting.of("Total model count in title", totalModelCountInTitle, "Show the total number of models in the title bar"),
|
||||
Setting.of("Start Tab", startTab), Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance())).needsRestart(),
|
||||
Setting.of("Font", new FontSettingsPane(this, config)).needsRestart()),
|
||||
Group.of("Player", Setting.of("Player", mediaPlayer), Setting.of("Start parameters", mediaPlayerParams),
|
||||
Setting.of("Maximum resolution (0 = unlimited)", maximumResolutionPlayer, "video height, e.g. 720 or 1080"),
|
||||
Setting.of("Show \"Player Starting\" Message", showPlayerStarting), Setting.of("Start only one player at a time", singlePlayer))),
|
||||
Category.of("Recorder",
|
||||
Group.of("Settings",
|
||||
Setting.of("Recordings Directory", recordingsDir),
|
||||
Setting.of("Directory Structure", directoryStructure),
|
||||
Group.of("Settings", Setting.of("Recordings Directory", recordingsDir), Setting.of("Directory Structure", directoryStructure),
|
||||
Setting.of("Split recordings after", splitAfter).converter(SplitAfterOption.converter()).onChange(this::splitValuesChanged),
|
||||
Setting.of("Split recordings bigger than", splitBiggerThan).converter(SplitBiggerThanOption.converter()).onChange(this::splitValuesChanged),
|
||||
Setting.of("Split recordings bigger than", splitBiggerThan).converter(SplitBiggerThanOption.converter())
|
||||
.onChange(this::splitValuesChanged),
|
||||
Setting.of("Restrict Resolution", resolutionRange, "Only record streams with resolution within the given range"),
|
||||
Setting.of("Concurrent Recordings (0 = unlimited)", concurrentRecordings),
|
||||
Setting.of("Leave space on device (GiB)", leaveSpaceOnDevice, "Stop recording, if the free space on the device gets below this threshold").converter(new GigabytesConverter()),
|
||||
Setting.of("Leave space on device (GiB)", leaveSpaceOnDevice,
|
||||
"Stop recording, if the free space on the device gets below this threshold").converter(new GigabytesConverter()),
|
||||
Setting.of("FFmpeg parameters", ffmpegParameters, "FFmpeg parameters to use when merging stream segments"),
|
||||
Setting.of("File Extension", fileExtension, "File extension to use for recordings"),
|
||||
Setting.of("Check online state every (seconds)", onlineCheckIntervalInSecs, "Check every x seconds, if a model came online"),
|
||||
Setting.of("Skip online check for paused models", onlineCheckSkipsPausedModels, "Skip online check for paused models")
|
||||
),
|
||||
Group.of("Location",
|
||||
Setting.of("Record Location", recordLocal).needsRestart(),
|
||||
Setting.of("Server", server),
|
||||
Setting.of("Port", port),
|
||||
Setting.of("Skip online check for paused models", onlineCheckSkipsPausedModels, "Skip online check for paused models")),
|
||||
Group.of("Location", Setting.of("Record Location", recordLocal).needsRestart(), Setting.of("Server", server), Setting.of("Port", port),
|
||||
Setting.of("Path", path, "Leave empty, if you didn't change the servletContext in the server config"),
|
||||
Setting.of("Download Filename", downloadFilename, "File name pattern for downloads"),
|
||||
Setting.of("", variablesHelpButton),
|
||||
Setting.of("Download Filename", downloadFilename, "File name pattern for downloads"), Setting.of("", variablesHelpButton),
|
||||
Setting.of("Require authentication", requireAuthentication),
|
||||
Setting.of("Use Secure Communication (TLS)", transportLayerSecurity)
|
||||
)
|
||||
),
|
||||
Setting.of("Use Secure Communication (TLS)", transportLayerSecurity))),
|
||||
Category.of("Post-Processing",
|
||||
Group.of("Post-Processing",
|
||||
Setting.of("Threads", postProcessingThreads),
|
||||
Setting.of("Steps", postProcessingStepPanel),
|
||||
Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md"))
|
||||
)
|
||||
),
|
||||
Category.of("Events & Actions", new ActionSettingsPanel(recorder)),
|
||||
Category.of("Ignore List", ignoreList),
|
||||
Group.of("Post-Processing", Setting.of("Threads", postProcessingThreads), Setting.of("Steps", postProcessingStepPanel),
|
||||
Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md")))),
|
||||
Category.of("Events & Actions", new ActionSettingsPanel(recorder)), Category.of("Ignore List", ignoreList),
|
||||
Category.of("Sites", siteCategories.toArray(new Category[0])),
|
||||
Category.of("Proxy",
|
||||
Group.of("Proxy",
|
||||
Setting.of("Type", proxyType).needsRestart(),
|
||||
Setting.of("Host", proxyHost).needsRestart(),
|
||||
Setting.of("Port", proxyPort).needsRestart(),
|
||||
Setting.of("Username", proxyUser).needsRestart(),
|
||||
Setting.of("Password", proxyPassword).needsRestart()
|
||||
)
|
||||
),
|
||||
Group.of("Proxy", Setting.of("Type", proxyType).needsRestart(), Setting.of("Host", proxyHost).needsRestart(),
|
||||
Setting.of("Port", proxyPort).needsRestart(), Setting.of("Username", proxyUser).needsRestart(),
|
||||
Setting.of("Password", proxyPassword).needsRestart())),
|
||||
Category.of("Advanced / Devtools",
|
||||
Group.of("Networking",
|
||||
Setting.of("Playlist request timeout (ms)", playlistRequestTimeout, "Timeout in ms for playlist requests")
|
||||
),
|
||||
Group.of("Logging",
|
||||
Setting.of("Log FFmpeg output", logFFmpegOutput, "Log FFmpeg output to files in the system's temp directory"),
|
||||
Setting.of("Log missed segments", logMissedSegments, "Write a log files in the system's temp directory to analyze missed segments")
|
||||
),
|
||||
Group.of("Networking", Setting.of("Playlist request timeout (ms)", playlistRequestTimeout, "Timeout in ms for playlist requests")),
|
||||
Group.of("Logging", Setting.of("Log FFmpeg output", logFFmpegOutput, "Log FFmpeg output to files in the system's temp directory"),
|
||||
Setting.of("Log missed segments", logMissedSegments,
|
||||
"Write a log files in the system's temp directory to analyze missed segments")),
|
||||
Group.of("hlsdl (experimental)",
|
||||
Setting.of("Use hlsdl (if possible)", useHlsdl, "Use hlsdl to record the live streams. Some features might not work correctly."),
|
||||
Setting.of("Use hlsdl (if possible)", useHlsdl,
|
||||
"Use hlsdl to record the live streams. Some features might not work correctly."),
|
||||
Setting.of("hlsdl executable", hlsdlExecutable, "Path to the hlsdl executable"),
|
||||
Setting.of("Log hlsdl output", loghlsdlOutput, "Log hlsdl output to files in the system's temp directory")
|
||||
)
|
||||
)
|
||||
);
|
||||
Setting.of("Log hlsdl output", loghlsdlOutput, "Log hlsdl output to files in the system's temp directory"))));
|
||||
Region preferencesView = prefs.getView();
|
||||
prefs.onRestartRequired(this::showRestartRequired);
|
||||
storage.setPreferences(prefs);
|
||||
|
@ -313,7 +280,8 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
restartNotification.setVisible(false);
|
||||
restartNotification.setOpacity(0);
|
||||
restartNotification.setStyle("-fx-font-size: 28; -fx-padding: .3em");
|
||||
restartNotification.setBorder(new Border(new BorderStroke(Color.web(settings.colorAccent), BorderStrokeStyle.SOLID, new CornerRadii(5), new BorderWidths(2))));
|
||||
restartNotification
|
||||
.setBorder(new Border(new BorderStroke(Color.web(settings.colorAccent), BorderStrokeStyle.SOLID, new CornerRadii(5), new BorderWidths(2))));
|
||||
restartNotification.setBackground(new Background(new BackgroundFill(Color.web(settings.colorBase), new CornerRadii(5), Insets.EMPTY)));
|
||||
stackPane.getChildren().add(restartNotification);
|
||||
StackPane.setAlignment(restartNotification, Pos.TOP_RIGHT);
|
||||
|
@ -387,9 +355,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
private List<String> getTabNames() {
|
||||
return getTabPane().getTabs().stream()
|
||||
.map(Tab::getText)
|
||||
.collect(Collectors.toList());
|
||||
return getTabPane().getTabs().stream().map(Tab::getText).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SplitAfterOption> getSplitAfterSecsOptions() {
|
||||
|
@ -503,6 +469,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
private static final Duration ANIMATION_DURATION = new Duration(500);
|
||||
|
||||
private Transition changeOpacity(Node node, double opacity) {
|
||||
var transition = new FadeTransition(ANIMATION_DURATION, node);
|
||||
transition.setFromValue(node.getOpacity());
|
||||
|
@ -618,28 +585,5 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
}
|
||||
|
||||
private ComboBox<String> buildFontFaceCombo() {
|
||||
ObservableList<String> lst = FXCollections.observableList(javafx.scene.text.Font.getFamilies());
|
||||
ComboBox<String> cb = new ComboBox<>(lst);
|
||||
cb.getSelectionModel().select(0);
|
||||
cb.setCellFactory((listview -> new ListCell<String>() {
|
||||
@Override
|
||||
protected void updateItem(String family, boolean empty) {
|
||||
super.updateItem(family, empty);
|
||||
if (empty) {
|
||||
setText(null);
|
||||
} else {
|
||||
setFont(Font.font(family));
|
||||
setText(family);
|
||||
}
|
||||
}
|
||||
}));
|
||||
cb.setOnAction(evt -> {
|
||||
// TODO write a css file with the font setting
|
||||
// System.out.println(evt);
|
||||
// Font font = Font.font(cb.getSelectionModel().getSelectedItem());
|
||||
// SettingsTab.this.getTabPane().getScene().getStylesheets().add(".root {-fx-font: 24 '" + font.getFamily() + "';}");
|
||||
});
|
||||
return cb;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ public class Settings {
|
|||
public String ffmpegFileSuffix = "ts";
|
||||
public String flirt4freePassword;
|
||||
public String flirt4freeUsername;
|
||||
public String fontFamily = "Sans-Serif";
|
||||
public int fontSize = 14;
|
||||
public boolean generatePlaylist = true;
|
||||
public String hlsdlExecutable = "hlsdl";
|
||||
public int httpPort = 8080;
|
||||
|
|
Loading…
Reference in New Issue