mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-10-01 12:15:32 +00:00
Merge branch 'develop' into feature/hub
# Conflicts: # .github/workflows/appimage.yml # dist/linux/appimage/build.sh # dist/linux/debian/rules # dist/mac/dmg/build.sh # dist/win/build.ps1
This commit is contained in:
+4
-2
@@ -1,11 +1,13 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
# Number of days of inactivity before an issue becomes stale
|
||||||
daysUntilStale: 180
|
daysUntilStale: 365
|
||||||
# Number of days of inactivity before a stale issue is closed
|
# Number of days of inactivity before a stale issue is closed
|
||||||
daysUntilClose: 30
|
daysUntilClose: 90
|
||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- type:security-issue # never close automatically
|
- type:security-issue # never close automatically
|
||||||
- type:feature-request # never close automatically
|
- type:feature-request # never close automatically
|
||||||
|
- type:enhancement # never close automatically
|
||||||
|
- type:upstream-bug # never close automatically
|
||||||
- state:awaiting-response # handled by different bot
|
- state:awaiting-response # handled by different bot
|
||||||
- state:blocked
|
- state:blocked
|
||||||
- state:confirmed
|
- state:confirmed
|
||||||
|
|||||||
@@ -61,10 +61,16 @@ jobs:
|
|||||||
--output runtime
|
--output runtime
|
||||||
--module-path "${JAVA_HOME}/jmods"
|
--module-path "${JAVA_HOME}/jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||||
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress=1
|
--compress=1
|
||||||
|
- name: Prepare additional launcher
|
||||||
|
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
||||||
|
env:
|
||||||
|
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
|
||||||
|
REVISION_NUM: ${{ steps.versions.outputs.revNum }}
|
||||||
- name: Run jpackage
|
- name: Run jpackage
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -91,12 +97,12 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=false"
|
--java-options "-Dcryptomator.showTrayIcon=false"
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\""
|
||||||
|
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
|
||||||
--resource-dir dist/linux/resources
|
--resource-dir dist/linux/resources
|
||||||
- name: Patch Cryptomator.AppDir
|
- name: Patch Cryptomator.AppDir
|
||||||
run: |
|
run: |
|
||||||
mv appdir/Cryptomator Cryptomator.AppDir
|
mv appdir/Cryptomator Cryptomator.AppDir
|
||||||
cp -r dist/linux/appimage/resources/AppDir/* Cryptomator.AppDir/
|
cp -r dist/linux/appimage/resources/AppDir/* Cryptomator.AppDir/
|
||||||
envsubst '${REVISION_NO} ${SEMVER_STR}' < dist/linux/appimage/resources/AppDir/bin/cryptomator.sh > Cryptomator.AppDir/bin/cryptomator.sh
|
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
|
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
|
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
|
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
|
||||||
@@ -108,9 +114,6 @@ jobs:
|
|||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
||||||
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
|
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
|
||||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
env:
|
|
||||||
REVISION_NO: ${{ steps.versions.outputs.revNum }}
|
|
||||||
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
|
|
||||||
- name: Extract libjffi.so # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
|
- name: Extract libjffi.so # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
|
||||||
run: |
|
run: |
|
||||||
JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
|
JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
|
||||||
|
|||||||
Vendored
+3
@@ -20,12 +20,14 @@ ${JAVA_HOME}/bin/jlink \
|
|||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JAVA_HOME}/jmods" \
|
--module-path "${JAVA_HOME}/jmods" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||||
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
--strip-debug \
|
--strip-debug \
|
||||||
--compress=1
|
--compress=1
|
||||||
|
|
||||||
# create app dir
|
# create app dir
|
||||||
|
envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
||||||
${JAVA_HOME}/bin/jpackage \
|
${JAVA_HOME}/bin/jpackage \
|
||||||
--verbose \
|
--verbose \
|
||||||
--type app-image \
|
--type app-image \
|
||||||
@@ -48,6 +50,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
|
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
|
||||||
--java-options "-Dcryptomator.showTrayIcon=false" \
|
--java-options "-Dcryptomator.showTrayIcon=false" \
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
||||||
|
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
|
||||||
--resource-dir ../resources
|
--resource-dir ../resources
|
||||||
|
|
||||||
# transform AppDir
|
# transform AppDir
|
||||||
|
|||||||
+5
-20
@@ -15,26 +15,11 @@ elif command -v pacman &> /dev/null; then # don't forget arch
|
|||||||
GTK3_PRESENT=`pacman -Qi gtk3 &> /dev/null; echo $?`
|
GTK3_PRESENT=`pacman -Qi gtk3 &> /dev/null; echo $?`
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
|
|
||||||
GTK_FLAG="-Djdk.gtk.version=2"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
|
# workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
|
||||||
export LD_PRELOAD=lib/app/libjffi.so
|
export LD_PRELOAD=lib/app/libjffi.so
|
||||||
|
|
||||||
# start Cryptomator
|
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
|
||||||
./lib/runtime/bin/java \
|
bin/Cryptomator-gtk2
|
||||||
-p "lib/app/mods" \
|
else
|
||||||
-cp "lib/app/*" \
|
bin/Cryptomator
|
||||||
-Dfile.encoding="utf-8" \
|
fi
|
||||||
-Dcryptomator.logDir="~/.local/share/Cryptomator/logs" \
|
|
||||||
-Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" \
|
|
||||||
-Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" \
|
|
||||||
-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json" \
|
|
||||||
-Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" \
|
|
||||||
-Dcryptomator.buildNumber="appimage-${REVISION_NO}" \
|
|
||||||
-Dcryptomator.appVersion="${SEMVER_STR}" \
|
|
||||||
$GTK_FLAG \
|
|
||||||
-Xss5m \
|
|
||||||
-Xmx256m \
|
|
||||||
-m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
|
|
||||||
Vendored
+1
@@ -19,6 +19,7 @@ override_dh_auto_build:
|
|||||||
jlink \
|
jlink \
|
||||||
--output runtime \
|
--output runtime \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||||
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
--strip-debug \
|
--strip-debug \
|
||||||
|
|||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
java-options=-Xss5m \
|
||||||
|
-Xmx256m \
|
||||||
|
-Dfile.encoding=\"utf-8\" \
|
||||||
|
-Dcryptomator.appVersion=\"${SEMVER_STR}\" \
|
||||||
|
-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\" \
|
||||||
|
-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\" \
|
||||||
|
-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\" \
|
||||||
|
-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\" \
|
||||||
|
-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\" \
|
||||||
|
-Dcryptomator.showTrayIcon=false \
|
||||||
|
-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\" \
|
||||||
|
-Djdk.gtk.version=2
|
||||||
Vendored
+1
@@ -38,6 +38,7 @@ ${JAVA_HOME}/bin/jlink \
|
|||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JAVA_HOME}/jmods" \
|
--module-path "${JAVA_HOME}/jmods" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||||
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
--strip-debug \
|
--strip-debug \
|
||||||
|
|||||||
Vendored
+1
@@ -42,6 +42,7 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
|
|||||||
--output runtime `
|
--output runtime `
|
||||||
--module-path "$Env:JAVA_HOME/jmods" `
|
--module-path "$Env:JAVA_HOME/jmods" `
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr `
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr `
|
||||||
|
--strip-native-commands `
|
||||||
--no-header-files `
|
--no-header-files `
|
||||||
--no-man-pages `
|
--no-man-pages `
|
||||||
--strip-debug `
|
--strip-debug `
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package org.cryptomator.common.settings;
|
|||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
|
||||||
public enum UiTheme {
|
public enum UiTheme {
|
||||||
LIGHT("preferences.general.theme.light"), //
|
LIGHT("preferences.interface.theme.light"), //
|
||||||
DARK("preferences.general.theme.dark"), //
|
DARK("preferences.interface.theme.dark"), //
|
||||||
AUTOMATIC("preferences.general.theme.automatic");
|
AUTOMATIC("preferences.interface.theme.automatic");
|
||||||
|
|
||||||
public static UiTheme[] applicableValues() {
|
public static UiTheme[] applicableValues() {
|
||||||
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
|
||||||
|
|||||||
@@ -21,15 +21,18 @@ import javax.inject.Inject;
|
|||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Cryptomator {
|
public class Cryptomator {
|
||||||
|
|
||||||
|
private static final long STARTUP_TIME = System.currentTimeMillis();
|
||||||
// DaggerCryptomatorComponent gets generated by Dagger.
|
// DaggerCryptomatorComponent gets generated by Dagger.
|
||||||
// Run Maven and include target/generated-sources/annotations in your IDE.
|
// Run Maven and include target/generated-sources/annotations in your IDE.
|
||||||
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create();
|
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||||
|
|
||||||
private final LoggerConfiguration logConfig;
|
private final LoggerConfiguration logConfig;
|
||||||
@@ -50,6 +53,20 @@ public class Cryptomator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
var printVersion = Optional.ofNullable(args) //
|
||||||
|
.stream() //Streams either one element (the args-array) or zero elements
|
||||||
|
.flatMap(Arrays::stream) //
|
||||||
|
.anyMatch(arg -> "-v".equals(arg) || "--version".equals(arg));
|
||||||
|
|
||||||
|
if (printVersion) {
|
||||||
|
var appVer = System.getProperty("cryptomator.appVersion", "SNAPSHOT");
|
||||||
|
var buildNumber = System.getProperty("cryptomator.buildNumber", "SNAPSHOT");
|
||||||
|
|
||||||
|
//Reduce noise for parsers by using System.out directly
|
||||||
|
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
|
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
|
||||||
LOG.info("Exit {}", exitCode);
|
LOG.info("Exit {}", exitCode);
|
||||||
System.exit(exitCode); // end remaining non-daemon threads.
|
System.exit(exitCode); // end remaining non-daemon threads.
|
||||||
@@ -63,6 +80,7 @@ public class Cryptomator {
|
|||||||
*/
|
*/
|
||||||
private int run(String[] args) {
|
private int run(String[] args) {
|
||||||
logConfig.init();
|
logConfig.init();
|
||||||
|
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
|
||||||
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||||
debugMode.initialize();
|
debugMode.initialize();
|
||||||
supportedLanguages.applyPreferred();
|
supportedLanguages.applyPreferred();
|
||||||
@@ -112,7 +130,7 @@ public class Cryptomator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) {
|
public void start(Stage primaryStage) {
|
||||||
LOG.info("JavaFX application started.");
|
LOG.info("JavaFX runtime started after {}ms", System.currentTimeMillis() - STARTUP_TIME);
|
||||||
FxApplicationComponent component = CRYPTOMATOR_COMPONENT.fxAppComponentBuilder() //
|
FxApplicationComponent component = CRYPTOMATOR_COMPONENT.fxAppComponentBuilder() //
|
||||||
.fxApplication(this) //
|
.fxApplication(this) //
|
||||||
.primaryStage(primaryStage) //
|
.primaryStage(primaryStage) //
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import dagger.BindsInstance;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
import org.cryptomator.common.CommonsModule;
|
import org.cryptomator.common.CommonsModule;
|
||||||
import org.cryptomator.logging.LoggerModule;
|
import org.cryptomator.logging.LoggerModule;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -15,4 +17,9 @@ public interface CryptomatorComponent {
|
|||||||
|
|
||||||
FxApplicationComponent.Builder fxAppComponentBuilder();
|
FxApplicationComponent.Builder fxAppComponentBuilder();
|
||||||
|
|
||||||
|
@Component.Factory
|
||||||
|
interface Factory {
|
||||||
|
CryptomatorComponent create(@BindsInstance @Named("startupTime") long startupTime);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,20 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
import java.awt.SystemTray;
|
import java.awt.SystemTray;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
public class FxApplication {
|
public class FxApplication {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||||
|
|
||||||
|
private final long startupTime;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final AppLaunchEventHandler launchEventHandler;
|
private final AppLaunchEventHandler launchEventHandler;
|
||||||
private final Lazy<TrayMenuComponent> trayMenu;
|
private final Lazy<TrayMenuComponent> trayMenu;
|
||||||
@@ -26,7 +30,8 @@ public class FxApplication {
|
|||||||
private final AutoUnlocker autoUnlocker;
|
private final AutoUnlocker autoUnlocker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FxApplication(Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
||||||
|
this.startupTime = startupTime;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.launchEventHandler = launchEventHandler;
|
this.launchEventHandler = launchEventHandler;
|
||||||
this.trayMenu = trayMenu;
|
this.trayMenu = trayMenu;
|
||||||
@@ -61,6 +66,10 @@ public class FxApplication {
|
|||||||
stage.setIconified(true);
|
stage.setIconified(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LOG.debug("Main window initialized after {}ms", System.currentTimeMillis() - startupTime);
|
||||||
|
}).exceptionally(error -> {
|
||||||
|
LOG.error("Failed to show main window", error);
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
launchEventHandler.startHandlingLaunchEvents();
|
launchEventHandler.startHandlingLaunchEvents();
|
||||||
|
|||||||
@@ -1,37 +1,25 @@
|
|||||||
package org.cryptomator.ui.preferences;
|
package org.cryptomator.ui.preferences;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.LicenseHolder;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.UiTheme;
|
|
||||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||||
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
|
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
import org.cryptomator.launcher.SupportedLanguages;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.NodeOrientation;
|
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ChoiceBox;
|
import javafx.scene.control.ChoiceBox;
|
||||||
import javafx.scene.control.RadioButton;
|
|
||||||
import javafx.scene.control.Toggle;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@PreferencesScoped
|
@PreferencesScoped
|
||||||
@@ -41,39 +29,23 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final boolean trayMenuInitialized;
|
|
||||||
private final boolean trayMenuSupported;
|
|
||||||
private final Optional<AutoStartProvider> autoStartProvider;
|
private final Optional<AutoStartProvider> autoStartProvider;
|
||||||
private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty;
|
|
||||||
private final LicenseHolder licenseHolder;
|
|
||||||
private final ResourceBundle resourceBundle;
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
private final Set<KeychainAccessProvider> keychainAccessProviders;
|
private final Set<KeychainAccessProvider> keychainAccessProviders;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
public ChoiceBox<UiTheme> themeChoiceBox;
|
|
||||||
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
|
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
|
||||||
public CheckBox showMinimizeButtonCheckbox;
|
|
||||||
public CheckBox showTrayIconCheckbox;
|
|
||||||
public CheckBox startHiddenCheckbox;
|
public CheckBox startHiddenCheckbox;
|
||||||
public ChoiceBox<String> preferredLanguageChoiceBox;
|
|
||||||
public CheckBox debugModeCheckbox;
|
public CheckBox debugModeCheckbox;
|
||||||
public CheckBox autoStartCheckbox;
|
public CheckBox autoStartCheckbox;
|
||||||
public ToggleGroup nodeOrientation;
|
public ToggleGroup nodeOrientation;
|
||||||
public RadioButton nodeOrientationLtr;
|
|
||||||
public RadioButton nodeOrientationRtl;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional<AutoStartProvider> autoStartProvider, Set<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, FxApplicationWindows appWindows) {
|
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, Set<KeychainAccessProvider> keychainAccessProviders, Application application, Environment environment, FxApplicationWindows appWindows) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.trayMenuInitialized = trayMenu.isInitialized();
|
|
||||||
this.trayMenuSupported = trayMenu.isSupported();
|
|
||||||
this.autoStartProvider = autoStartProvider;
|
this.autoStartProvider = autoStartProvider;
|
||||||
this.keychainAccessProviders = keychainAccessProviders;
|
this.keychainAccessProviders = keychainAccessProviders;
|
||||||
this.selectedTabProperty = selectedTabProperty;
|
|
||||||
this.licenseHolder = licenseHolder;
|
|
||||||
this.resourceBundle = resourceBundle;
|
|
||||||
this.application = application;
|
this.application = application;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
@@ -81,32 +53,12 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
themeChoiceBox.getItems().addAll(UiTheme.applicableValues());
|
|
||||||
if (!themeChoiceBox.getItems().contains(settings.theme().get())) {
|
|
||||||
settings.theme().set(UiTheme.LIGHT);
|
|
||||||
}
|
|
||||||
themeChoiceBox.valueProperty().bindBidirectional(settings.theme());
|
|
||||||
themeChoiceBox.setConverter(new UiThemeConverter(resourceBundle));
|
|
||||||
|
|
||||||
showMinimizeButtonCheckbox.selectedProperty().bindBidirectional(settings.showMinimizeButton());
|
|
||||||
|
|
||||||
showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon());
|
|
||||||
|
|
||||||
startHiddenCheckbox.selectedProperty().bindBidirectional(settings.startHidden());
|
startHiddenCheckbox.selectedProperty().bindBidirectional(settings.startHidden());
|
||||||
|
|
||||||
preferredLanguageChoiceBox.getItems().add(null);
|
|
||||||
preferredLanguageChoiceBox.getItems().addAll(SupportedLanguages.LANGUAGAE_TAGS);
|
|
||||||
preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.languageProperty());
|
|
||||||
preferredLanguageChoiceBox.setConverter(new LanguageTagConverter(resourceBundle));
|
|
||||||
|
|
||||||
debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode());
|
debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode());
|
||||||
|
|
||||||
autoStartProvider.ifPresent(autoStart -> autoStartCheckbox.setSelected(autoStart.isEnabled()));
|
autoStartProvider.ifPresent(autoStart -> autoStartCheckbox.setSelected(autoStart.isEnabled()));
|
||||||
|
|
||||||
nodeOrientationLtr.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.LEFT_TO_RIGHT);
|
|
||||||
nodeOrientationRtl.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.RIGHT_TO_LEFT);
|
|
||||||
nodeOrientation.selectedToggleProperty().addListener(this::toggleNodeOrientation);
|
|
||||||
|
|
||||||
var keychainSettingsConverter = new KeychainProviderClassNameConverter(keychainAccessProviders);
|
var keychainSettingsConverter = new KeychainProviderClassNameConverter(keychainAccessProviders);
|
||||||
keychainBackendChoiceBox.getItems().addAll(keychainAccessProviders);
|
keychainBackendChoiceBox.getItems().addAll(keychainAccessProviders);
|
||||||
keychainBackendChoiceBox.setValue(keychainSettingsConverter.fromString(settings.keychainProvider().get()));
|
keychainBackendChoiceBox.setValue(keychainSettingsConverter.fromString(settings.keychainProvider().get()));
|
||||||
@@ -114,29 +66,10 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
Bindings.bindBidirectional(settings.keychainProvider(), keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
|
Bindings.bindBidirectional(settings.keychainProvider(), keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isTrayMenuInitialized() {
|
|
||||||
return trayMenuInitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTrayMenuSupported() {
|
|
||||||
return trayMenuSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAutoStartSupported() {
|
public boolean isAutoStartSupported() {
|
||||||
return autoStartProvider.isPresent();
|
return autoStartProvider.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleNodeOrientation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
|
||||||
if (nodeOrientationLtr.equals(newValue)) {
|
|
||||||
settings.userInterfaceOrientation().set(NodeOrientation.LEFT_TO_RIGHT);
|
|
||||||
} else if (nodeOrientationRtl.equals(newValue)) {
|
|
||||||
settings.userInterfaceOrientation().set(NodeOrientation.RIGHT_TO_LEFT);
|
|
||||||
} else {
|
|
||||||
LOG.warn("Unexpected toggle option {}", newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void toggleAutoStart() {
|
public void toggleAutoStart() {
|
||||||
autoStartProvider.ifPresent(autoStart -> {
|
autoStartProvider.ifPresent(autoStart -> {
|
||||||
@@ -155,16 +88,6 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public LicenseHolder getLicenseHolder() {
|
|
||||||
return licenseHolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void showContributeTab() {
|
|
||||||
selectedTabProperty.set(SelectedPreferencesTab.CONTRIBUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void showLogfileDirectory() {
|
public void showLogfileDirectory() {
|
||||||
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
||||||
@@ -172,53 +95,7 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
|
|
||||||
/* Helper classes */
|
/* Helper classes */
|
||||||
|
|
||||||
private static class UiThemeConverter extends StringConverter<UiTheme> {
|
private static class KeychainProviderDisplayNameConverter extends StringConverter<KeychainAccessProvider> {
|
||||||
|
|
||||||
private final ResourceBundle resourceBundle;
|
|
||||||
|
|
||||||
UiThemeConverter(ResourceBundle resourceBundle) {
|
|
||||||
this.resourceBundle = resourceBundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(UiTheme impl) {
|
|
||||||
return resourceBundle.getString(impl.getDisplayName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UiTheme fromString(String string) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class LanguageTagConverter extends StringConverter<String> {
|
|
||||||
|
|
||||||
private final ResourceBundle resourceBundle;
|
|
||||||
|
|
||||||
LanguageTagConverter(ResourceBundle resourceBundle) {
|
|
||||||
this.resourceBundle = resourceBundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(String tag) {
|
|
||||||
if (tag == null) {
|
|
||||||
return resourceBundle.getString("preferences.general.language.auto");
|
|
||||||
} else {
|
|
||||||
var locale = Locale.forLanguageTag(tag);
|
|
||||||
var lang = locale.getDisplayLanguage(locale);
|
|
||||||
var region = locale.getDisplayCountry(locale);
|
|
||||||
return lang + (Strings.isNullOrEmpty(region) ? "" : " (" + region + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String fromString(String displayLanguage) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class KeychainProviderDisplayNameConverter extends StringConverter<KeychainAccessProvider> {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(KeychainAccessProvider provider) {
|
public String toString(KeychainAccessProvider provider) {
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package org.cryptomator.ui.preferences;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import org.cryptomator.common.LicenseHolder;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.settings.UiTheme;
|
||||||
|
import org.cryptomator.launcher.SupportedLanguages;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.NodeOrientation;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
|
import javafx.scene.control.ChoiceBox;
|
||||||
|
import javafx.scene.control.RadioButton;
|
||||||
|
import javafx.scene.control.Toggle;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@PreferencesScoped
|
||||||
|
public class InterfacePreferencesController implements FxController {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(InterfacePreferencesController.class);
|
||||||
|
|
||||||
|
private final Settings settings;
|
||||||
|
private final boolean trayMenuInitialized;
|
||||||
|
private final boolean trayMenuSupported;
|
||||||
|
private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty;
|
||||||
|
private final LicenseHolder licenseHolder;
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
public ChoiceBox<UiTheme> themeChoiceBox;
|
||||||
|
public CheckBox showMinimizeButtonCheckbox;
|
||||||
|
public CheckBox showTrayIconCheckbox;
|
||||||
|
public ChoiceBox<String> preferredLanguageChoiceBox;
|
||||||
|
public ToggleGroup nodeOrientation;
|
||||||
|
public RadioButton nodeOrientationLtr;
|
||||||
|
public RadioButton nodeOrientationRtl;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
InterfacePreferencesController(Settings settings, TrayMenuComponent trayMenu, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle) {
|
||||||
|
this.settings = settings;
|
||||||
|
this.trayMenuInitialized = trayMenu.isInitialized();
|
||||||
|
this.trayMenuSupported = trayMenu.isSupported();
|
||||||
|
this.selectedTabProperty = selectedTabProperty;
|
||||||
|
this.licenseHolder = licenseHolder;
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
themeChoiceBox.getItems().addAll(UiTheme.applicableValues());
|
||||||
|
if (!themeChoiceBox.getItems().contains(settings.theme().get())) {
|
||||||
|
settings.theme().set(UiTheme.LIGHT);
|
||||||
|
}
|
||||||
|
themeChoiceBox.valueProperty().bindBidirectional(settings.theme());
|
||||||
|
themeChoiceBox.setConverter(new UiThemeConverter(resourceBundle));
|
||||||
|
|
||||||
|
showMinimizeButtonCheckbox.selectedProperty().bindBidirectional(settings.showMinimizeButton());
|
||||||
|
|
||||||
|
showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon());
|
||||||
|
|
||||||
|
preferredLanguageChoiceBox.getItems().add(null);
|
||||||
|
preferredLanguageChoiceBox.getItems().addAll(SupportedLanguages.LANGUAGAE_TAGS);
|
||||||
|
preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.languageProperty());
|
||||||
|
preferredLanguageChoiceBox.setConverter(new LanguageTagConverter(resourceBundle));
|
||||||
|
|
||||||
|
nodeOrientationLtr.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.LEFT_TO_RIGHT);
|
||||||
|
nodeOrientationRtl.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.RIGHT_TO_LEFT);
|
||||||
|
nodeOrientation.selectedToggleProperty().addListener(this::toggleNodeOrientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isTrayMenuInitialized() {
|
||||||
|
return trayMenuInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTrayMenuSupported() {
|
||||||
|
return trayMenuSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleNodeOrientation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
||||||
|
if (nodeOrientationLtr.equals(newValue)) {
|
||||||
|
settings.userInterfaceOrientation().set(NodeOrientation.LEFT_TO_RIGHT);
|
||||||
|
} else if (nodeOrientationRtl.equals(newValue)) {
|
||||||
|
settings.userInterfaceOrientation().set(NodeOrientation.RIGHT_TO_LEFT);
|
||||||
|
} else {
|
||||||
|
LOG.warn("Unexpected toggle option {}", newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LicenseHolder getLicenseHolder() {
|
||||||
|
return licenseHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showContributeTab() {
|
||||||
|
selectedTabProperty.set(SelectedPreferencesTab.CONTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper classes */
|
||||||
|
|
||||||
|
private static class UiThemeConverter extends StringConverter<UiTheme> {
|
||||||
|
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
|
||||||
|
UiThemeConverter(ResourceBundle resourceBundle) {
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(UiTheme impl) {
|
||||||
|
return resourceBundle.getString(impl.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UiTheme fromString(String string) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LanguageTagConverter extends StringConverter<String> {
|
||||||
|
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
|
||||||
|
LanguageTagConverter(ResourceBundle resourceBundle) {
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(String tag) {
|
||||||
|
if (tag == null) {
|
||||||
|
return resourceBundle.getString("preferences.interface.language.auto");
|
||||||
|
} else {
|
||||||
|
var locale = Locale.forLanguageTag(tag);
|
||||||
|
var lang = locale.getDisplayLanguage(locale);
|
||||||
|
var region = locale.getDisplayCountry(locale);
|
||||||
|
return lang + (Strings.isNullOrEmpty(region) ? "" : " (" + region + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fromString(String displayLanguage) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ public class PreferencesController implements FxController {
|
|||||||
private final BooleanBinding updateAvailable;
|
private final BooleanBinding updateAvailable;
|
||||||
public TabPane tabPane;
|
public TabPane tabPane;
|
||||||
public Tab generalTab;
|
public Tab generalTab;
|
||||||
|
public Tab interfaceTab;
|
||||||
public Tab volumeTab;
|
public Tab volumeTab;
|
||||||
public Tab updatesTab;
|
public Tab updatesTab;
|
||||||
public Tab contributeTab;
|
public Tab contributeTab;
|
||||||
@@ -50,10 +51,11 @@ public class PreferencesController implements FxController {
|
|||||||
|
|
||||||
private Tab getTabToSelect(SelectedPreferencesTab selectedTab) {
|
private Tab getTabToSelect(SelectedPreferencesTab selectedTab) {
|
||||||
return switch (selectedTab) {
|
return switch (selectedTab) {
|
||||||
case UPDATES -> updatesTab;
|
|
||||||
case VOLUME -> volumeTab;
|
|
||||||
case CONTRIBUTE -> contributeTab;
|
|
||||||
case GENERAL -> generalTab;
|
case GENERAL -> generalTab;
|
||||||
|
case INTERFACE -> interfaceTab;
|
||||||
|
case VOLUME -> volumeTab;
|
||||||
|
case UPDATES -> updatesTab;
|
||||||
|
case CONTRIBUTE -> contributeTab;
|
||||||
case ABOUT -> aboutTab;
|
case ABOUT -> aboutTab;
|
||||||
case ANY -> updateAvailable.get() ? updatesTab : generalTab;
|
case ANY -> updateAvailable.get() ? updatesTab : generalTab;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ abstract class PreferencesModule {
|
|||||||
@FxControllerKey(GeneralPreferencesController.class)
|
@FxControllerKey(GeneralPreferencesController.class)
|
||||||
abstract FxController bindGeneralPreferencesController(GeneralPreferencesController controller);
|
abstract FxController bindGeneralPreferencesController(GeneralPreferencesController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(InterfacePreferencesController.class)
|
||||||
|
abstract FxController bindInterfacePreferencesController(InterfacePreferencesController controller);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(UpdatesPreferencesController.class)
|
@FxControllerKey(UpdatesPreferencesController.class)
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ public enum SelectedPreferencesTab {
|
|||||||
*/
|
*/
|
||||||
GENERAL,
|
GENERAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show interface tab
|
||||||
|
*/
|
||||||
|
INTERFACE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show volume tab
|
* Show volume tab
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
fx:controller="org.cryptomator.ui.preferences.PreferencesController"
|
fx:controller="org.cryptomator.ui.preferences.PreferencesController"
|
||||||
minWidth="-Infinity"
|
minWidth="-Infinity"
|
||||||
maxWidth="-Infinity"
|
maxWidth="-Infinity"
|
||||||
prefWidth="500"
|
prefWidth="650"
|
||||||
tabMinWidth="60"
|
tabMinWidth="60"
|
||||||
tabClosingPolicy="UNAVAILABLE"
|
tabClosingPolicy="UNAVAILABLE"
|
||||||
tabDragPolicy="FIXED">
|
tabDragPolicy="FIXED">
|
||||||
@@ -22,6 +22,14 @@
|
|||||||
<fx:include source="preferences_general.fxml"/>
|
<fx:include source="preferences_general.fxml"/>
|
||||||
</content>
|
</content>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab fx:id="interfaceTab" id="INTERFACE" text="%preferences.interface">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="EYE"/>
|
||||||
|
</graphic>
|
||||||
|
<content>
|
||||||
|
<fx:include source="preferences_interface.fxml"/>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
<Tab fx:id="volumeTab" id="VOLUME" text="%preferences.volume">
|
<Tab fx:id="volumeTab" id="VOLUME" text="%preferences.volume">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="HDD"/>
|
<FontAwesome5IconView glyph="HDD"/>
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.preferences.AboutController"
|
fx:controller="org.cryptomator.ui.preferences.AboutController"
|
||||||
spacing="18">
|
spacing="24">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<HBox spacing="12" VBox.vgrow="NEVER">
|
<HBox spacing="12" VBox.vgrow="NEVER">
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.preferences.SupporterCertificateController"
|
fx:controller="org.cryptomator.ui.preferences.SupporterCertificateController"
|
||||||
spacing="18">
|
spacing="24">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<StackPane VBox.vgrow="NEVER" prefHeight="60">
|
<StackPane VBox.vgrow="NEVER" prefHeight="60">
|
||||||
|
|||||||
@@ -5,54 +5,35 @@
|
|||||||
<?import javafx.scene.control.ChoiceBox?>
|
<?import javafx.scene.control.ChoiceBox?>
|
||||||
<?import javafx.scene.control.Hyperlink?>
|
<?import javafx.scene.control.Hyperlink?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.RadioButton?>
|
|
||||||
<?import javafx.scene.control.ToggleGroup?>
|
<?import javafx.scene.control.ToggleGroup?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.preferences.GeneralPreferencesController"
|
fx:controller="org.cryptomator.ui.preferences.GeneralPreferencesController"
|
||||||
spacing="6">
|
spacing="12">
|
||||||
<fx:define>
|
<fx:define>
|
||||||
<ToggleGroup fx:id="nodeOrientation"/>
|
<ToggleGroup fx:id="nodeOrientation"/>
|
||||||
</fx:define>
|
</fx:define>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
<CheckBox fx:id="autoStartCheckbox" text="%preferences.general.autoStart" visible="${controller.autoStartSupported}" managed="${controller.autoStartSupported}" onAction="#toggleAutoStart"/>
|
||||||
<Label text="%preferences.general.theme"/>
|
|
||||||
<ChoiceBox fx:id="themeChoiceBox" disable="${!controller.licenseHolder.validLicense}"/>
|
|
||||||
<Hyperlink styleClass="hyperlink-underline,hyperlink-muted" text="%preferences.general.unlockThemes" onAction="#showContributeTab" visible="${!controller.licenseHolder.validLicense}" managed="${!controller.licenseHolder.validLicense}"/>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
|
||||||
<Label text="%preferences.general.interfaceOrientation" HBox.hgrow="NEVER"/>
|
|
||||||
<RadioButton fx:id="nodeOrientationLtr" text="%preferences.general.interfaceOrientation.ltr" alignment="CENTER_LEFT" toggleGroup="${nodeOrientation}"/>
|
|
||||||
<RadioButton fx:id="nodeOrientationRtl" text="%preferences.general.interfaceOrientation.rtl" alignment="CENTER_RIGHT" toggleGroup="${nodeOrientation}"/>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<CheckBox fx:id="showMinimizeButtonCheckbox" text="%preferences.general.showMinimizeButton" visible="${controller.trayMenuInitialized}" managed="${controller.trayMenuInitialized}"/>
|
|
||||||
|
|
||||||
<CheckBox fx:id="showTrayIconCheckbox" text="%preferences.general.showTrayIcon" visible="${controller.trayMenuSupported}" managed="${controller.trayMenuSupported}"/>
|
|
||||||
|
|
||||||
<CheckBox fx:id="startHiddenCheckbox" text="%preferences.general.startHidden" />
|
<CheckBox fx:id="startHiddenCheckbox" text="%preferences.general.startHidden" />
|
||||||
|
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
<Label text="%preferences.general.language"/>
|
|
||||||
<ChoiceBox fx:id="preferredLanguageChoiceBox"/>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
|
||||||
<CheckBox fx:id="debugModeCheckbox" text="%preferences.general.debugLogging"/>
|
|
||||||
<Hyperlink styleClass="hyperlink-underline" text="%preferences.general.debugDirectory" onAction="#showLogfileDirectory"/>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
|
||||||
<Label text="%preferences.general.keychainBackend"/>
|
<Label text="%preferences.general.keychainBackend"/>
|
||||||
<ChoiceBox fx:id="keychainBackendChoiceBox"/>
|
<ChoiceBox fx:id="keychainBackendChoiceBox"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<CheckBox fx:id="autoStartCheckbox" text="%preferences.general.autoStart" visible="${controller.autoStartSupported}" managed="${controller.autoStartSupported}" onAction="#toggleAutoStart"/>
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
|
<CheckBox fx:id="debugModeCheckbox" text="%preferences.general.debugLogging"/>
|
||||||
|
<Hyperlink styleClass="hyperlink-underline" text="%preferences.general.debugDirectory" onAction="#showLogfileDirectory"/>
|
||||||
|
</HBox>
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.ChoiceBox?>
|
||||||
|
<?import javafx.scene.control.Hyperlink?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.RadioButton?>
|
||||||
|
<?import javafx.scene.control.ToggleGroup?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
|
xmlns="http://javafx.com/javafx"
|
||||||
|
fx:controller="org.cryptomator.ui.preferences.InterfacePreferencesController"
|
||||||
|
spacing="12">
|
||||||
|
<fx:define>
|
||||||
|
<ToggleGroup fx:id="nodeOrientation"/>
|
||||||
|
</fx:define>
|
||||||
|
<padding>
|
||||||
|
<Insets topRightBottomLeft="24"/>
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
|
<Label text="%preferences.interface.theme"/>
|
||||||
|
<ChoiceBox fx:id="themeChoiceBox" disable="${!controller.licenseHolder.validLicense}"/>
|
||||||
|
<Hyperlink styleClass="hyperlink-underline,hyperlink-muted" text="%preferences.interface.unlockThemes" onAction="#showContributeTab" visible="${!controller.licenseHolder.validLicense}" managed="${!controller.licenseHolder.validLicense}"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
|
<Label text="%preferences.interface.language"/>
|
||||||
|
<ChoiceBox fx:id="preferredLanguageChoiceBox"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
|
<Label text="%preferences.interface.interfaceOrientation" HBox.hgrow="NEVER"/>
|
||||||
|
<RadioButton fx:id="nodeOrientationLtr" text="%preferences.interface.interfaceOrientation.ltr" alignment="CENTER_LEFT" toggleGroup="${nodeOrientation}"/>
|
||||||
|
<RadioButton fx:id="nodeOrientationRtl" text="%preferences.interface.interfaceOrientation.rtl" alignment="CENTER_RIGHT" toggleGroup="${nodeOrientation}"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
|
||||||
|
<CheckBox fx:id="showMinimizeButtonCheckbox" text="%preferences.interface.showMinimizeButton" visible="${controller.trayMenuInitialized}" managed="${controller.trayMenuInitialized}"/>
|
||||||
|
|
||||||
|
<CheckBox fx:id="showTrayIconCheckbox" text="%preferences.interface.showTrayIcon" visible="${controller.trayMenuSupported}" managed="${controller.trayMenuSupported}"/>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
@@ -11,19 +11,19 @@
|
|||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.preferences.UpdatesPreferencesController"
|
fx:controller="org.cryptomator.ui.preferences.UpdatesPreferencesController"
|
||||||
spacing="6">
|
spacing="12">
|
||||||
<fx:define>
|
<fx:define>
|
||||||
<FormattedString fx:id="linkLabel" format="%preferences.updates.updateAvailable" arg1="${controller.latestVersion}"/>
|
<FormattedString fx:id="linkLabel" format="%preferences.updates.updateAvailable" arg1="${controller.latestVersion}"/>
|
||||||
</fx:define>
|
</fx:define>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<FormattedLabel format="%preferences.updates.currentVersion" arg1="${controller.currentVersion}" textAlignment="CENTER" wrapText="true"/>
|
<FormattedLabel format="%preferences.updates.currentVersion" arg1="${controller.currentVersion}" textAlignment="CENTER" wrapText="true"/>
|
||||||
|
|
||||||
<CheckBox fx:id="checkForUpdatesCheckbox" text="%preferences.updates.autoUpdateCheck"/>
|
<CheckBox fx:id="checkForUpdatesCheckbox" text="%preferences.updates.autoUpdateCheck"/>
|
||||||
|
|
||||||
<VBox alignment="CENTER" spacing="6">
|
<VBox alignment="CENTER" spacing="12">
|
||||||
<Button text="%preferences.updates.checkNowBtn" defaultButton="true" onAction="#checkNow" contentDisplay="${controller.checkForUpdatesButtonState}">
|
<Button text="%preferences.updates.checkNowBtn" defaultButton="true" onAction="#checkNow" contentDisplay="${controller.checkForUpdatesButtonState}">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5Spinner fx:id="spinner" glyphSize="12"/>
|
<FontAwesome5Spinner fx:id="spinner" glyphSize="12"/>
|
||||||
|
|||||||
@@ -10,23 +10,23 @@
|
|||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.preferences.VolumePreferencesController"
|
fx:controller="org.cryptomator.ui.preferences.VolumePreferencesController"
|
||||||
spacing="6">
|
spacing="12">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
<Label text="%preferences.volume.type"/>
|
<Label text="%preferences.volume.type"/>
|
||||||
<ChoiceBox fx:id="volumeTypeChoiceBox"/>
|
<ChoiceBox fx:id="volumeTypeChoiceBox"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT" visible="${controller.showWebDavSettings}">
|
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.showWebDavSettings}">
|
||||||
<Label text="%preferences.volume.webdav.port"/>
|
<Label text="%preferences.volume.webdav.port"/>
|
||||||
<NumericTextField fx:id="webDavPortField"/>
|
<NumericTextField fx:id="webDavPortField"/>
|
||||||
<Button text="%generic.button.apply" fx:id="changeWebDavPortButton" onAction="#doChangeWebDavPort"/>
|
<Button text="%generic.button.apply" fx:id="changeWebDavPortButton" onAction="#doChangeWebDavPort"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT" visible="${controller.showWebDavScheme}">
|
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.showWebDavScheme}">
|
||||||
<Label text="%preferences.volume.webdav.scheme"/>
|
<Label text="%preferences.volume.webdav.scheme"/>
|
||||||
<ChoiceBox fx:id="webDavUrlSchemeChoiceBox" maxWidth="Infinity"/>
|
<ChoiceBox fx:id="webDavUrlSchemeChoiceBox" maxWidth="Infinity"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|||||||
@@ -191,23 +191,25 @@ health.fix.failTip=Fix failed, see log for details
|
|||||||
preferences.title=Preferences
|
preferences.title=Preferences
|
||||||
## General
|
## General
|
||||||
preferences.general=General
|
preferences.general=General
|
||||||
preferences.general.theme=Look & Feel
|
|
||||||
preferences.general.theme.automatic=Automatic
|
|
||||||
preferences.general.theme.light=Light
|
|
||||||
preferences.general.theme.dark=Dark
|
|
||||||
preferences.general.unlockThemes=Unlock dark mode
|
|
||||||
preferences.general.showMinimizeButton=Show minimize button
|
|
||||||
preferences.general.showTrayIcon=Show tray icon (requires restart)
|
|
||||||
preferences.general.startHidden=Hide window when starting Cryptomator
|
preferences.general.startHidden=Hide window when starting Cryptomator
|
||||||
preferences.general.language=Language (requires restart)
|
|
||||||
preferences.general.language.auto=System Default
|
|
||||||
preferences.general.debugLogging=Enable debug logging
|
preferences.general.debugLogging=Enable debug logging
|
||||||
preferences.general.debugDirectory=Reveal log files
|
preferences.general.debugDirectory=Reveal log files
|
||||||
preferences.general.autoStart=Launch Cryptomator on system start
|
preferences.general.autoStart=Launch Cryptomator on system start
|
||||||
preferences.general.keychainBackend=Store passwords with
|
preferences.general.keychainBackend=Store passwords with
|
||||||
preferences.general.interfaceOrientation=Interface Orientation
|
## Interface
|
||||||
preferences.general.interfaceOrientation.ltr=Left to Right
|
preferences.interface=Interface
|
||||||
preferences.general.interfaceOrientation.rtl=Right to Left
|
preferences.interface.theme=Look & Feel
|
||||||
|
preferences.interface.theme.automatic=Automatic
|
||||||
|
preferences.interface.theme.dark=Dark
|
||||||
|
preferences.interface.theme.light=Light
|
||||||
|
preferences.interface.unlockThemes=Unlock dark mode
|
||||||
|
preferences.interface.language=Language (requires restart)
|
||||||
|
preferences.interface.language.auto=System Default
|
||||||
|
preferences.interface.interfaceOrientation=Interface Orientation
|
||||||
|
preferences.interface.interfaceOrientation.ltr=Left to Right
|
||||||
|
preferences.interface.interfaceOrientation.rtl=Right to Left
|
||||||
|
preferences.interface.showMinimizeButton=Show minimize button
|
||||||
|
preferences.interface.showTrayIcon=Show tray icon (requires restart)
|
||||||
## Volume
|
## Volume
|
||||||
preferences.volume=Virtual Drive
|
preferences.volume=Virtual Drive
|
||||||
preferences.volume.type=Volume Type
|
preferences.volume.type=Volume Type
|
||||||
|
|||||||
Reference in New Issue
Block a user