Browse Source

allow changing of user interface orientation via settings

Sebastian Stenzel 5 năm trước cách đây
mục cha
commit
aa61ab2b6e

+ 9 - 1
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<VaultSettings> 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<VolumeImpl> preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL);
 	private final ObjectProperty<UiTheme> theme = new SimpleObjectProperty<>(DEFAULT_THEME);
+	private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
 
 	private Consumer<Settings> 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<Settings> saveCmd) {
@@ -118,4 +122,8 @@ public class Settings {
 	public ObjectProperty<UiTheme> theme() {
 		return theme;
 	}
+
+	public ObjectProperty<NodeOrientation> userInterfaceOrientation() {
+		return userInterfaceOrientation;
+	}
 }

+ 16 - 2
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<Settings> {
 		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<Settings> {
 				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<Settings> {
 		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<Settings> {
 		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<VaultSettings> readVaultSettingsArray(JsonReader in) throws IOException {
 		List<VaultSettings> result = new ArrayList<>();
 		in.beginArray();

+ 11 - 1
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<Parent, Scene> {
 	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();

+ 3 - 1
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> vaultListController;
 
 	@Inject
-	public MainWindowSceneFactory(Lazy<MainWindowController> mainWindowController, Lazy<VaultListController> vaultListController) {
+	public MainWindowSceneFactory(Settings settings, Lazy<MainWindowController> mainWindowController, Lazy<VaultListController> vaultListController) {
+		super(settings);
 		this.mainWindowController = mainWindowController;
 		this.vaultListController = vaultListController;
 	}

+ 26 - 0
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<UiTheme> 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<? 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);
+		}
 	}
 
 	/* Helper classes */

+ 11 - 0
main/ui/src/main/resources/fxml/preferences_general.fxml

@@ -6,10 +6,15 @@
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.VBox?>
+<?import javafx.scene.control.RadioButton?>
+<?import javafx.scene.control.ToggleGroup?>
 <VBox xmlns="http://javafx.com/javafx"
 	  xmlns:fx="http://javafx.com/fxml"
 	  fx:controller="org.cryptomator.ui.preferences.GeneralPreferencesController"
 	  spacing="6">
+	<fx:define>
+		<ToggleGroup fx:id="nodeOrientation"/>
+	</fx:define>
 	<padding>
 		<Insets topRightBottomLeft="12"/>
 	</padding>
@@ -18,6 +23,12 @@
 			<Label text="%preferences.general.theme"/>
 			<ChoiceBox fx:id="themeChoiceBox"/>
 		</HBox>
+		
+		<HBox spacing="6" alignment="CENTER_LEFT">
+			<Label text="Interface Orientation" HBox.hgrow="NEVER"/>
+			<RadioButton fx:id="nodeOrientationLtr" text="Left to Right" alignment="CENTER_LEFT" toggleGroup="${nodeOrientation}"/>
+			<RadioButton fx:id="nodeOrientationRtl" text="Right to Left" alignment="CENTER_RIGHT" toggleGroup="${nodeOrientation}"/>
+		</HBox>
 
 		<CheckBox fx:id="startHiddenCheckbox" text="%preferences.general.startHidden"/>