Merge branch 'feature/new-ui' of https://github.com/cryptomator/cryptomator into feature/new-ui

This commit is contained in:
Armin Schrenk
2019-08-14 03:08:58 +02:00
12 changed files with 288 additions and 81 deletions

View File

@@ -2,44 +2,86 @@ package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ResourceBundle;
@AddVaultWizardScoped
public class CreateNewVaultLocationController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultLocationController.class);
private static final Path DEFAULT_CUSTOM_VAULT_PATH = Paths.get(System.getProperty("user.home"));
private final Stage window;
private final Lazy<Scene> previousScene;
private final Lazy<Scene> nextScene;
private final LocationPresets locationPresets;
private final ObjectProperty<Path> vaultPath;
private final BooleanBinding vaultPathIsNull;
private final StringProperty vaultName;
private final ResourceBundle resourceBundle;
private final BooleanProperty usePresetPath;
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
public ToggleGroup predefinedLocationToggler;
public RadioButton dropboxRadioButton;
public RadioButton gdriveRadioButton;
public RadioButton customRadioButton;
//TODO: add parameter for next window
@Inject
CreateNewVaultLocationController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> previousScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> nextScene, ObjectProperty<Path> vaultPath, StringProperty vaultName, ResourceBundle resourceBundle) {
CreateNewVaultLocationController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> previousScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> nextScene, LocationPresets locationPresets, ObjectProperty<Path> vaultPath, StringProperty vaultName, ResourceBundle resourceBundle) {
this.window = window;
this.previousScene = previousScene;
this.nextScene = nextScene;
this.locationPresets = locationPresets;
this.vaultPath = vaultPath;
this.vaultName = vaultName;
this.resourceBundle = resourceBundle;
this.vaultPathIsNull = vaultPath.isNull();
this.usePresetPath = new SimpleBooleanProperty();
}
@FXML
public void initialize() {
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
}
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
if (dropboxRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getDropboxLocation().resolve(vaultName.get()));
} else if (gdriveRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getGdriveLocation().resolve(vaultName.get()));
} else if (customRadioButton.equals(newValue)) {
vaultPath.set(customVaultPath.resolve(vaultName.get()));
}
}
@FXML
@@ -49,58 +91,38 @@ public class CreateNewVaultLocationController implements FxController {
@FXML
public void next() {
//TODO: what if there exists already a vault?
if (hasFullAccessToLocation()) {
window.setScene(nextScene.get());
} else {
//TODO error handling
}
}
private boolean hasFullAccessToLocation() {
try {
Path tmp = Files.createFile(vaultPath.get().resolve("tmp"));
Files.delete(tmp);
return true;
// check if we have write access AND the vaultPath doesn't already exist:
assert Files.isDirectory(vaultPath.get().getParent());
Path createdDir = Files.createDirectory(vaultPath.get());
Files.delete(createdDir); // assert: dir exists and is empty
window.setScene(nextScene.get());
} catch (FileAlreadyExistsException e) {
LOG.warn("Can not use already existing vault path: {}", vaultPath.get());
// TODO show specific error text "vault can not be created at this path because some object already exists"
} catch (NoSuchFileException | DirectoryNotEmptyException e) {
LOG.error("Failed to delete recently created directory.", e);
// TODO show generic error text for unexpected exception
} catch (IOException e) {
return false;
LOG.warn("Can not create vault at path: {}", vaultPath.get());
// TODO show generic error text for unexpected exception
}
}
@FXML
public void chooseDirectory() {
public void chooseCustomVaultPath() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle(resourceBundle.getString("addvaultwizard.new.directoryPickerTitle"));
setInitialDirectory(directoryChooser);
directoryChooser.setInitialDirectory(customVaultPath.toFile());
final File file = directoryChooser.showDialog(window);
if (file != null) {
vaultPath.setValue(file.toPath().toAbsolutePath());
customVaultPath = file.toPath().toAbsolutePath();
vaultPath.set(customVaultPath.resolve(vaultName.get()));
}
}
private void setInitialDirectory(DirectoryChooser chooser) {
File userHome;
try {
userHome = new File(System.getProperty("user.home"));
} catch (Exception e) {
userHome = null;
}
if (userHome != null) {
chooser.setInitialDirectory(userHome);
}
}
/* Getter/Setter */
public String getVaultName() {
return vaultName.get();
}
public StringProperty vaultNameProperty() {
return vaultName;
}
public Path getVaultPath() {
return vaultPath.get();
}
@@ -117,5 +139,15 @@ public class CreateNewVaultLocationController implements FxController {
return vaultPathIsNull;
}
public LocationPresets getLocationPresets() {
return locationPresets;
}
public BooleanProperty usePresetPathProperty() {
return usePresetPath;
}
public boolean getUsePresetPath() {
return usePresetPath.get();
}
}

View File

@@ -0,0 +1,84 @@
package org.cryptomator.ui.addvaultwizard;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javax.inject.Inject;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@AddVaultWizardScoped
public class LocationPresets {
private static final String USER_HOME = System.getProperty("user.home");
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive"};
private final ReadOnlyObjectProperty<Path> dropboxLocation;
private final ReadOnlyObjectProperty<Path> gdriveLocation;
private final BooleanBinding foundDropbox;
private final BooleanBinding foundGdrive;
@Inject
public LocationPresets() {
this.dropboxLocation = new SimpleObjectProperty<>(existingWritablePath(DROPBOX_LOCATIONS));
this.gdriveLocation = new SimpleObjectProperty<>(existingWritablePath(GDRIVE_LOCATIONS));
this.foundDropbox = dropboxLocation.isNotNull();
this.foundGdrive = gdriveLocation.isNotNull();
}
private static Path existingWritablePath(String... candidates) {
for (String candidate : candidates) {
Path path = Paths.get(resolveHomePath(candidate));
if (Files.isDirectory(path)) {
return path;
}
}
return null;
}
private static String resolveHomePath(String path) {
if (path.startsWith("~/")) {
return USER_HOME + path.substring(1);
} else {
return path;
}
}
/* Observables */
public ReadOnlyObjectProperty<Path> dropboxLocationProperty() {
return dropboxLocation;
}
public Path getDropboxLocation() {
return dropboxLocation.get();
}
public BooleanBinding foundDropboxProperty() {
return foundDropbox;
}
public boolean isFoundDropbox() {
return foundDropbox.get();
}
public ReadOnlyObjectProperty<Path> gdriveLocationProperty() {
return gdriveLocation;
}
public Path getGdriveLocation() {
return gdriveLocation.get();
}
public BooleanBinding froundGdriveProperty() {
return foundGdrive;
}
public boolean isFoundGdrive() {
return foundGdrive.get();
}
}

View File

@@ -5,16 +5,14 @@ import javafx.fxml.FXML;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.concurrent.CountDownLatch;
@MainWindowScoped
public class MainWindowController implements FxController {

View File

@@ -5,6 +5,9 @@ import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
@@ -46,8 +49,16 @@ abstract class MainWindowModule {
@Provides
@FxmlScene(FxmlFile.MAIN_WINDOW)
@MainWindowScoped
static Scene provideMainScene(@MainWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/main_window.fxml");
static Scene provideMainScene(@MainWindow FXMLLoaderFactory fxmlLoaders, MainWindowController mainWindowController, VaultListController vaultListController) {
Scene scene = fxmlLoaders.createScene("/fxml/main_window.fxml");
// still not perfect... cant't we have a global menubar via the AWT tray app?
KeyCombination cmdN = new KeyCodeCombination(KeyCode.N, KeyCombination.SHORTCUT_DOWN);
KeyCombination cmdW = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN);
scene.getAccelerators().put(cmdN, vaultListController::didClickAddVault);
scene.getAccelerators().put(cmdW, mainWindowController::close);
return scene;
}
// ------------------

View File

@@ -4,9 +4,6 @@ import javafx.beans.binding.BooleanBinding;
import javafx.fxml.FXML;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.cryptomator.ui.common.FxController;
@@ -34,13 +31,10 @@ public class PreferencesController implements FxController {
window.setOnShowing(this::windowWillAppear);
}
private void windowWillAppear(WindowEvent windowEvent) {
private void windowWillAppear(@SuppressWarnings("unused") WindowEvent windowEvent) {
if (updateAvailable.get()) {
tabPane.getSelectionModel().select(updatesTab);
}
KeyCombination cmdW = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN);
window.getScene().getAccelerators().put(cmdW, window::close);
}
}

View File

@@ -5,6 +5,9 @@ import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;
import org.cryptomator.ui.common.FXMLLoaderFactory;
import org.cryptomator.ui.common.FxController;
@@ -40,8 +43,13 @@ abstract class PreferencesModule {
@Provides
@FxmlScene(FxmlFile.PREFERENCES)
@PreferencesScoped
static Scene providePreferencesScene(@PreferencesWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/preferences.fxml");
static Scene providePreferencesScene(@PreferencesWindow FXMLLoaderFactory fxmlLoaders, @PreferencesWindow Stage window) {
Scene scene = fxmlLoaders.createScene("/fxml/preferences.fxml");
KeyCombination cmdW = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN);
scene.getAccelerators().put(cmdW, window::close);
return scene;
}
// ------------------

View File

@@ -1,11 +1,5 @@
package org.cryptomator.ui.vaultoptions;
import javafx.fxml.FXML;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
@@ -13,21 +7,7 @@ import javax.inject.Inject;
@VaultOptionsScoped
public class VaultOptionsController implements FxController {
private final Stage window;
@Inject
VaultOptionsController(@VaultOptionsWindow Stage window) {
this.window = window;
}
@FXML
public void initialize() {
window.setOnShowing(this::windowWillAppear);
}
private void windowWillAppear(WindowEvent windowEvent) {
KeyCombination cmdW = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN);
window.getScene().getAccelerators().put(cmdW, window::close);
}
VaultOptionsController() {}
}

View File

@@ -5,6 +5,9 @@ import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
@@ -48,8 +51,13 @@ abstract class VaultOptionsModule {
@Provides
@FxmlScene(FxmlFile.VAULT_OPTIONS)
@VaultOptionsScoped
static Scene provideVaultOptionsScene(@VaultOptionsWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/vault_options.fxml");
static Scene provideVaultOptionsScene(@VaultOptionsWindow FXMLLoaderFactory fxmlLoaders, @VaultOptionsWindow Stage window) {
Scene scene = fxmlLoaders.createScene("/fxml/vault_options.fxml");
KeyCombination cmdW = new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN);
scene.getAccelerators().put(cmdW, window::close);
return scene;
}
// ------------------

View File

@@ -364,6 +364,7 @@
.check-box {
-fx-text-fill: TEXT_FILL;
-fx-label-padding: 0 0 0 6px;
-fx-padding: 4px;
}
.check-box > .box {
@@ -387,6 +388,40 @@
-fx-background-color: TEXT_FILL;
}
/*******************************************************************************
* *
* RadioButton *
* *
******************************************************************************/
.radio-button {
-fx-text-fill: TEXT_FILL;
-fx-label-padding: 0 0 0 6px;
-fx-padding: 4px;
}
.radio-button > .radio {
-fx-border-color: CONTROL_BORDER_NORMAL;
-fx-border-radius: 1em; /* large value to make sure this remains circular */
-fx-background-color: CONTROL_BG_NORMAL;
-fx-background-radius: 1em;
-fx-padding: 4px; /* padding from outside edge to the inner black dot */
}
.radio-button:focused > .radio {
-fx-border-color: CONTROL_BORDER_FOCUSED;
}
.text-input:disabled > .radio {
-fx-border-color: CONTROL_BORDER_DISABLED;
-fx-background-color: CONTROL_BG_DISABLED;
}
.radio-button > .radio > .dot {
-fx-background-color: transparent;
-fx-background-radius: 1.0em; /* large value to make sure this remains circular */
-fx-padding: 3px; /* radius of the inner black dot when selected */
}
.radio-button:selected > .radio > .dot {
-fx-background-color: TEXT_FILL;
}
/*******************************************************************************
* *
* Dropdown *

View File

@@ -364,6 +364,7 @@
.check-box {
-fx-text-fill: TEXT_FILL;
-fx-label-padding: 0 0 0 6px;
-fx-padding: 4px;
}
.check-box > .box {
@@ -387,6 +388,40 @@
-fx-background-color: TEXT_FILL;
}
/*******************************************************************************
* *
* RadioButton *
* *
******************************************************************************/
.radio-button {
-fx-text-fill: TEXT_FILL;
-fx-label-padding: 0 0 0 6px;
-fx-padding: 4px;
}
.radio-button > .radio {
-fx-border-color: CONTROL_BORDER_NORMAL;
-fx-border-radius: 1em; /* large value to make sure this remains circular */
-fx-background-color: CONTROL_BG_NORMAL;
-fx-background-radius: 1em;
-fx-padding: 4px; /* padding from outside edge to the inner black dot */
}
.radio-button:focused > .radio {
-fx-border-color: CONTROL_BORDER_FOCUSED;
}
.text-input:disabled > .radio {
-fx-border-color: CONTROL_BORDER_DISABLED;
-fx-background-color: CONTROL_BG_DISABLED;
}
.radio-button > .radio > .dot {
-fx-background-color: transparent;
-fx-background-radius: 1.0em; /* large value to make sure this remains circular */
-fx-padding: 3px; /* radius of the inner black dot when selected */
}
.radio-button:selected > .radio > .dot {
-fx-background-color: TEXT_FILL;
}
/*******************************************************************************
* *
* Dropdown *

View File

@@ -4,26 +4,48 @@
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.addvaultwizard.CreateNewVaultLocationController"
prefHeight="400.0" prefWidth="600.0"
alignment="CENTER">
alignment="CENTER_LEFT"
spacing="6">
<fx:define>
<ToggleGroup fx:id="predefinedLocationToggler"/>
</fx:define>
<padding>
<Insets top="12" right="12" bottom="12" left="12"/>
</padding>
<children>
<Region VBox.vgrow="ALWAYS"/>
<Label text="%addvaultwizard.new.locationInstruction"/>
<HBox spacing="18">
<TextField promptText="%addvaultwizard.new.locationPrompt" text="${controller.vaultPath}" disable="true" HBox.hgrow="ALWAYS"/>
<Button text="%addvaultwizard.new.directoryPickerButton" onAction="#chooseDirectory" HBox.hgrow="NEVER" prefWidth="120"/>
<Label wrapText="true" text="%addvaultwizard.new.locationInstruction"/>
<Region prefHeight="24" VBox.vgrow="NEVER"/>
<RadioButton fx:id="dropboxRadioButton" toggleGroup="${predefinedLocationToggler}" text="TODO dropbox" visible="${controller.locationPresets.foundDropbox}"/>
<RadioButton fx:id="gdriveRadioButton" toggleGroup="${predefinedLocationToggler}" text="TODO google drive" visible="${controller.locationPresets.foundGdrive}"/>
<HBox spacing="12" alignment="CENTER_LEFT">
<RadioButton fx:id="customRadioButton" toggleGroup="${predefinedLocationToggler}" text="TODO custom location"/>
<Button contentDisplay="LEFT" text="%addvaultwizard.new.directoryPickerButton" onAction="#chooseCustomVaultPath" disable="${controller.usePresetPath}">
<graphic>
<FontAwesomeIconView styleClass="fa-icons" glyphName="FOLDER_OPEN"/>
</graphic>
</Button>
</HBox>
<Region prefHeight="24" VBox.vgrow="NEVER"/>
<TextField promptText="%addvaultwizard.new.locationPrompt" text="${controller.vaultPath}" disable="true" HBox.hgrow="ALWAYS"/>
<Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
<buttons>
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back"/>

View File

@@ -21,7 +21,7 @@ addvaultwizard.new.nameInstruction=Please enter a name for the vault:
addvaultwizard.new.namePrompt=TODO vault name
addvaultwizard.new.locationInstruction=Please pick a directory where your vault will be stored:
addvaultwizard.new.locationPrompt=TODO location
addvaultwizard.new.directoryPickerButton=TODO DirPicker
addvaultwizard.new.directoryPickerButton=Choose...
addvaultwizard.new.directoryPickerTitle=Select Directory
addvaultwizard.new.enterPassword=Please enter a Password for your vault:
addvaultwizard.new.reenterPassword=Please retype the password: