diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java b/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java index 3960cf1f6..ff5613f1e 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java @@ -17,6 +17,7 @@ import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.geometry.NodeOrientation; import java.util.function.Consumer; @@ -30,9 +31,10 @@ public class Settings { public static final int DEFAULT_PORT = 42427; public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV; - public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT; public static final boolean DEFAULT_DEBUG_MODE = false; public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = System.getProperty("os.name").toLowerCase().contains("windows") ? VolumeImpl.DOKANY : VolumeImpl.FUSE; + public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT; + public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT; private final ObservableList directories = FXCollections.observableArrayList(VaultSettings::observables); private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK); @@ -44,6 +46,7 @@ public class Settings { private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE); private final ObjectProperty preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL); private final ObjectProperty theme = new SimpleObjectProperty<>(DEFAULT_THEME); + private final ObjectProperty userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION); private Consumer saveCmd; @@ -61,6 +64,7 @@ public class Settings { debugMode.addListener(this::somethingChanged); preferredVolumeImpl.addListener(this::somethingChanged); theme.addListener(this::somethingChanged); + userInterfaceOrientation.addListener(this::somethingChanged); } void setSaveCmd(Consumer saveCmd) { @@ -118,4 +122,8 @@ public class Settings { public ObjectProperty theme() { return theme; } + + public ObjectProperty userInterfaceOrientation() { + return userInterfaceOrientation; + } } diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java index 0929ed050..874994cf8 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java @@ -9,6 +9,7 @@ import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; +import javafx.geometry.NodeOrientation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +37,7 @@ public class SettingsJsonAdapter extends TypeAdapter { out.name("debugMode").value(value.debugMode().get()); out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name()); out.name("theme").value(value.theme().get().name()); + out.name("uiOrientation").value(value.userInterfaceOrientation().get().name()); out.endObject(); } @@ -85,6 +87,9 @@ public class SettingsJsonAdapter extends TypeAdapter { case "theme": settings.theme().set(parseUiTheme(in.nextString())); break; + case "uiOrientation": + settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString())); + break; default: LOG.warn("Unsupported vault setting found in JSON: " + name); in.skipValue(); @@ -109,7 +114,7 @@ public class SettingsJsonAdapter extends TypeAdapter { try { return WebDavUrlScheme.valueOf(webDavUrlSchemeName.toUpperCase()); } catch (IllegalArgumentException e) { - LOG.warn("Invalid volume type {}. Defaulting to {}.", webDavUrlSchemeName, Settings.DEFAULT_GVFS_SCHEME); + LOG.warn("Invalid WebDAV url scheme {}. Defaulting to {}.", webDavUrlSchemeName, Settings.DEFAULT_GVFS_SCHEME); return Settings.DEFAULT_GVFS_SCHEME; } } @@ -118,11 +123,20 @@ public class SettingsJsonAdapter extends TypeAdapter { try { return UiTheme.valueOf(uiThemeName.toUpperCase()); } catch (IllegalArgumentException e) { - LOG.warn("Invalid volume type {}. Defaulting to {}.", uiThemeName, Settings.DEFAULT_THEME); + LOG.warn("Invalid ui theme {}. Defaulting to {}.", uiThemeName, Settings.DEFAULT_THEME); return Settings.DEFAULT_THEME; } } + private NodeOrientation parseUiOrientation(String uiOrientationName) { + try { + return NodeOrientation.valueOf(uiOrientationName.toUpperCase()); + } catch (IllegalArgumentException e) { + LOG.warn("Invalid ui orientation {}. Defaulting to {}.", uiOrientationName, Settings.DEFAULT_USER_INTERFACE_ORIENTATION); + return Settings.DEFAULT_USER_INTERFACE_ORIENTATION; + } + } + private List readVaultSettingsArray(JsonReader in) throws IOException { List result = new ArrayList<>(); in.beginArray(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java b/main/ui/src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java index 14efe8f27..a2757d7b7 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java @@ -8,6 +8,7 @@ import javafx.scene.input.KeyCombination; import javafx.stage.Stage; import javafx.stage.Window; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.fxapp.FxApplicationScoped; import javax.inject.Inject; @@ -19,16 +20,25 @@ public class DefaultSceneFactory implements Function { protected static final KeyCodeCombination ALT_F4 = new KeyCodeCombination(KeyCode.F4, KeyCombination.ALT_DOWN); protected static final KeyCodeCombination SHORTCUT_W = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN); + protected final Settings settings; + @Inject - public DefaultSceneFactory() {} + public DefaultSceneFactory(Settings settings) { + this.settings = settings; + } @Override public Scene apply(Parent root) { Scene scene = new Scene(root); + configureRoot(root); configureScene(scene); return scene; } + protected void configureRoot(Parent root) { + root.nodeOrientationProperty().bind(settings.userInterfaceOrientation()); + } + protected void configureScene(Scene scene) { scene.windowProperty().addListener(observable -> { Window window = scene.getWindow(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java index 8c6486dc6..b86a56e5f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java @@ -7,6 +7,7 @@ import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.stage.Stage; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.common.DefaultSceneFactory; import javax.inject.Inject; @@ -20,7 +21,8 @@ public class MainWindowSceneFactory extends DefaultSceneFactory { private final Lazy vaultListController; @Inject - public MainWindowSceneFactory(Lazy mainWindowController, Lazy vaultListController) { + public MainWindowSceneFactory(Settings settings, Lazy mainWindowController, Lazy vaultListController) { + super(settings); this.mainWindowController = mainWindowController; this.vaultListController = vaultListController; } diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index d03832358..bbccb0ad7 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java @@ -1,21 +1,33 @@ package org.cryptomator.ui.preferences; +import javafx.beans.value.ObservableValue; +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 org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.UiTheme; import org.cryptomator.ui.common.FxController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; @PreferencesScoped public class GeneralPreferencesController implements FxController { + + private static final Logger LOG = LoggerFactory.getLogger(GeneralPreferencesController.class); private final Settings settings; public ChoiceBox themeChoiceBox; public CheckBox startHiddenCheckbox; public CheckBox debugModeCheckbox; + public ToggleGroup nodeOrientation; + public RadioButton nodeOrientationLtr; + public RadioButton nodeOrientationRtl; @Inject GeneralPreferencesController(Settings settings) { @@ -30,6 +42,20 @@ public class GeneralPreferencesController implements FxController { startHiddenCheckbox.selectedProperty().bindBidirectional(settings.startHidden()); debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode()); + + nodeOrientation.selectedToggleProperty().addListener(this::toggleNodeOrientation); + nodeOrientationLtr.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.LEFT_TO_RIGHT); + nodeOrientationRtl.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.RIGHT_TO_LEFT); + } + + private void toggleNodeOrientation(@SuppressWarnings("unused") ObservableValue 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); + } } /* Helper classes */ diff --git a/main/ui/src/main/resources/fxml/preferences_general.fxml b/main/ui/src/main/resources/fxml/preferences_general.fxml index 3546ebe65..1f05151f8 100644 --- a/main/ui/src/main/resources/fxml/preferences_general.fxml +++ b/main/ui/src/main/resources/fxml/preferences_general.fxml @@ -6,10 +6,15 @@ + + + + + @@ -18,6 +23,12 @@