소스 검색

refine UI:
* align locate/decrypt buttons horizontally
* change to text icons
* dedup code

Armin Schrenk 1 개월 전
부모
커밋
24e31553fd

+ 97 - 67
src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java

@@ -6,14 +6,13 @@ import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.tobiasdiez.easybind.EasyBind;
 import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.common.Nullable;
 import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.integrations.mount.Mountpoint;
 import org.cryptomator.integrations.revealpath.RevealFailedException;
 import org.cryptomator.integrations.revealpath.RevealPathService;
 import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.common.StageFactory;
 import org.cryptomator.ui.common.VaultService;
-import org.cryptomator.ui.dialogs.SimpleDialog;
 import org.cryptomator.ui.fxapp.FxApplicationWindows;
 import org.cryptomator.ui.stats.VaultStatisticsComponent;
 import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
@@ -37,17 +36,19 @@ import javafx.scene.input.DataFormat;
 import javafx.scene.input.DragEvent;
 import javafx.scene.input.TransferMode;
 import javafx.stage.FileChooser;
-import javafx.stage.Popup;
 import javafx.stage.Stage;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.ResourceBundle;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 @MainWindowScoped
 public class VaultDetailUnlockedController implements FxController {
@@ -67,11 +68,14 @@ public class VaultDetailUnlockedController implements FxController {
 	private final ObservableValue<Boolean> accessibleViaPath;
 	private final ObservableValue<Boolean> accessibleViaUri;
 	private final ObservableValue<String> mountPoint;
-	private final BooleanProperty draggingOver = new SimpleBooleanProperty();
+	private final BooleanProperty draggingOverLocateEncrypted = new SimpleBooleanProperty();
+	private final BooleanProperty draggingOverDecryptName = new SimpleBooleanProperty();
 	private final BooleanProperty ciphertextPathsCopied = new SimpleBooleanProperty();
 
-	//FXML
-	public Button dropZone;
+	@FXML
+	public Button revealEncryptedDropZone;
+	@FXML
+	public Button decryptNameDropZone;
 
 	@Inject
 	public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, WrongFileAlertComponent.Builder wrongFileAlert, @MainWindow Stage mainWindow, Optional<RevealPathService> revealPathService, ResourceBundle resourceBundle) {
@@ -97,93 +101,92 @@ public class VaultDetailUnlockedController implements FxController {
 	}
 
 	public void initialize() {
-		dropZone.setOnDragEntered(this::handleDragEvent);
-		dropZone.setOnDragOver(this::handleDragEvent);
-		dropZone.setOnDragDropped(this::handleDragEvent);
-		dropZone.setOnDragExited(this::handleDragEvent);
+		revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted));
+		revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths));
+		revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false));
+
+		decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName));
+		decryptNameDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCleartextName, this::doSomethingWithFileNames));
+		decryptNameDropZone.setOnDragExited(_ -> draggingOverDecryptName.setValue(false));
 
-		EasyBind.includeWhen(dropZone.getStyleClass(), ACTIVE_CLASS, draggingOver);
+		EasyBind.includeWhen(revealEncryptedDropZone.getStyleClass(), ACTIVE_CLASS, draggingOverLocateEncrypted);
+		EasyBind.includeWhen(decryptNameDropZone.getStyleClass(), ACTIVE_CLASS, draggingOverDecryptName);
 	}
 
-	private void handleDragEvent(DragEvent event) {
-		if (DragEvent.DRAG_OVER.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
-			if(SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) {
+	private void handleDragOver(DragEvent event, BooleanProperty prop) {
+		if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
+			if (SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) {
 				event.acceptTransferModes(TransferMode.LINK);
 			} else {
 				event.acceptTransferModes(TransferMode.ANY);
 			}
-			draggingOver.set(true);
-		} else if (DragEvent.DRAG_DROPPED.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
-			List<Path> ciphertextPaths = event.getDragboard().getFiles().stream().map(File::toPath).map(this::getCiphertextPath).flatMap(Optional::stream).toList();
-			//TODO: differ between encrypted and decrypted files
-			if (ciphertextPaths.isEmpty()) {
+			prop.set(true);
+		}
+	}
+
+	//TODO: split in two dedicated Methods
+	private <T> void handleDragDropped(DragEvent event, Function<Path, T> computation, Consumer<List<T>> positiveAction) {
+		if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
+			List<T> objects = event.getDragboard().getFiles().stream().map(File::toPath).map(computation).filter(Objects::nonNull).toList();
+			if (objects.isEmpty()) {
 				wrongFileAlert.build().showWrongFileAlertWindow();
 			} else {
-				revealOrCopyPaths(ciphertextPaths);
+				positiveAction.accept(objects);
 			}
-			event.setDropCompleted(!ciphertextPaths.isEmpty());
+			event.setDropCompleted(!objects.isEmpty());
 			event.consume();
-		} else if (DragEvent.DRAG_EXITED.equals(event.getEventType())) {
-			draggingOver.set(false);
 		}
 	}
 
-	private VaultStatisticsComponent buildVaultStats(Vault vault) {
-		return vaultStatsBuilder.vault(vault).build();
-	}
-
-	@FXML
-	public void revealAccessLocation() {
-		vaultService.reveal(vault.get());
-	}
-
-	@FXML
-	public void copyMountUri() {
-		ClipboardContent clipboardContent = new ClipboardContent();
-		clipboardContent.putString(mountPoint.getValue());
-		Clipboard.getSystemClipboard().setContent(clipboardContent);
-	}
-
-	@FXML
-	public void lock() {
-		appWindows.startLockWorkflow(vault.get(), mainWindow);
-	}
-
 	@FXML
-	public void showVaultStatistics() {
-		vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
-	}
-
-	@FXML
-	public void chooseFileAndReveal() {
+	public void chooseDecryptedFileAndReveal() {
 		Preconditions.checkState(accessibleViaPath.getValue());
 		var fileChooser = new FileChooser();
-		fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.decryptedFilePickerTitle"));
+		fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.locateEncrypted.filePickerTitle"));
 		fileChooser.setInitialDirectory(Path.of(mountPoint.getValue()).toFile());
 		var cleartextFile = fileChooser.showOpenDialog(mainWindow);
 		if (cleartextFile != null) {
-			var ciphertextPaths = getCiphertextPath(cleartextFile.toPath()).stream().toList();
-			revealOrCopyPaths(ciphertextPaths);
+			var ciphertextPath = getCiphertextPath(cleartextFile.toPath());
+			if (ciphertextPath != null) {
+				revealOrCopyPaths(List.of(ciphertextPath));
+			}
 		}
 	}
 
 	@FXML
 	public void chooseEncryptedFileAndGetName() {
 		var fileChooser = new FileChooser();
-		fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.encryptedFilePickerTitle"));
+		fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.decryptName.filePickerTitle"));
 
 		fileChooser.setInitialDirectory(vault.getValue().getPath().toFile());
 		var ciphertextNode = fileChooser.showOpenDialog(mainWindow);
+		if (ciphertextNode != null) {
+			var nodeName = getCleartextName(ciphertextNode.toPath());
+			Alert dialog;
+			if (nodeName != null) {
+				dialog = new Alert(Alert.AlertType.INFORMATION, "The answer is: %s".formatted(nodeName), ButtonType.OK);
+			} else {
+				dialog = new Alert(Alert.AlertType.ERROR, "Unable to get Name", ButtonType.OK);
+			}
+			dialog.showAndWait();
+		}
+	}
+
+	private void doSomethingWithFileNames(List<String> names) {
+		Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, String.join(", ", names)));
+		ciphertextPathsCopied.setValue(true);
+		CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
+			ciphertextPathsCopied.set(false);
+		});
+	}
+
+	@Nullable
+	private String getCleartextName(Path ciphertextNode) {
 		try {
-			var nodeName = vault.get().getCleartextName(ciphertextNode.toPath());
-			var alert = new Alert(Alert.AlertType.INFORMATION, "The answer is: %s".formatted(nodeName), ButtonType.OK);
-			alert.showAndWait();
-			//.filter(response -> response == ButtonType.OK)
-			//.ifPresent(response -> formatSystem());
-		} catch (Exception e) {
+			return vault.get().getCleartextName(ciphertextNode);
+		} catch (IOException e) {
 			LOG.warn("Failed to decrypt filename for {}", ciphertextNode, e);
-			var alert = new Alert(Alert.AlertType.ERROR, "The exception is: %s".formatted(e.getClass()), ButtonType.OK);
-			alert.showAndWait();
+			return null;
 		}
 	}
 
@@ -191,16 +194,17 @@ public class VaultDetailUnlockedController implements FxController {
 		return path.startsWith(Path.of(mountPoint.getValue()));
 	}
 
-	private Optional<Path> getCiphertextPath(Path path) {
+	@Nullable
+	private Path getCiphertextPath(Path path) {
 		if (!startsWithVaultAccessPoint(path)) {
-			LOG.debug("Path does not start with access point of selected vault: {}", path);
-			return Optional.empty();
+			LOG.debug("Path does not start with mount point of selected vault: {}", path);
+			return null;
 		}
 		try {
-			return Optional.of(vault.get().getCiphertextPath(path));
+			return vault.get().getCiphertextPath(path);
 		} catch (IOException e) {
 			LOG.warn("Unable to get ciphertext path from path: {}", path, e);
-			return Optional.empty();
+			return null;
 		}
 	}
 
@@ -232,6 +236,32 @@ public class VaultDetailUnlockedController implements FxController {
 		});
 	}
 
+	private VaultStatisticsComponent buildVaultStats(Vault vault) {
+		return vaultStatsBuilder.vault(vault).build();
+	}
+
+	@FXML
+	public void revealAccessLocation() {
+		vaultService.reveal(vault.get());
+	}
+
+	@FXML
+	public void copyMountUri() {
+		ClipboardContent clipboardContent = new ClipboardContent();
+		clipboardContent.putString(mountPoint.getValue());
+		Clipboard.getSystemClipboard().setContent(clipboardContent);
+	}
+
+	@FXML
+	public void lock() {
+		appWindows.startLockWorkflow(vault.get(), mainWindow);
+	}
+
+	@FXML
+	public void showVaultStatistics() {
+		vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
+	}
+
 	/* Getter/Setter */
 
 	public ReadOnlyObjectProperty<Vault> vaultProperty() {

BIN
src/main/resources/css/courierprime_regular.ttf


BIN
src/main/resources/css/firacode_regular.ttf


+ 9 - 8
src/main/resources/css/light_theme.css

@@ -17,7 +17,8 @@
 }
 
 @font-face {
-	src: url('courierprime_regular.ttf');
+	src: url('firacode_regular.ttf');
+}
 }
 
 /*******************************************************************************
@@ -128,6 +129,13 @@
 	-fx-fill: TEXT_FILL;
 }
 
+.cryptic-text {
+	-fx-background-color: MAIN_BG;
+	-fx-text-fill: TEXT_FILL;
+	-fx-font-family: 'Fira Code';
+	-fx-font-size: 1.1em;
+}
+
 /*******************************************************************************
  *                                                                             *
  * Glyph Icons                                                                 *
@@ -929,13 +937,6 @@
 	-fx-border-width: 1px;
 }
 
-.cryptic-text {
-	-fx-background-color: MAIN_BG;
-	-fx-text-fill: TEXT_FILL;
-	-fx-font-family: 'Courier Prime';
-	-fx-font-size: 1.1em;
-}
-
 .button.drag-n-drop:focused {
 	-fx-border-color: CONTROL_BORDER_FOCUSED;
 }

+ 7 - 15
src/main/resources/fxml/vault_detail_unlocked.fxml

@@ -50,19 +50,15 @@
 			<padding>
 				<Insets topRightBottomLeft="0"/>
 			</padding>
-			<Button fx:id="dropZone" styleClass="drag-n-drop" text="%main.vaultDetail.locateEncryptedFileBtn" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseFileAndReveal" contentDisplay="TOP" visible="${!controller.ciphertextPathsCopied}" managed="${!controller.ciphertextPathsCopied}">
+			<Button fx:id="revealEncryptedDropZone" styleClass="drag-n-drop" text="%main.vaultDetail.locateEncryptedFileBtn" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseDecryptedFileAndReveal" contentDisplay="TOP" visible="${!controller.ciphertextPathsCopied}" managed="${!controller.ciphertextPathsCopied}">
 				<graphic>
-					<HBox spacing="4">
-						<Text styleClass="cryptic-text" text="abc"/>
-						<FontAwesome5IconView glyph="ARROW_RIGH" glyphSize="10"/>
-						<Text styleClass="cryptic-text" text="101010"/>
-					</HBox>
+					<Text styleClass="cryptic-text" text="abc → 101010"/>
 				</graphic>
 				<tooltip>
 					<Tooltip text="%main.vaultDetail.locateEncryptedFileBtn.tooltip"/>
 				</tooltip>
 			</Button>
-			<Button styleClass="drag-n-drop" text="%main.vaultDetail.encryptedPathsCopied" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseFileAndReveal" contentDisplay="TOP" visible="${controller.ciphertextPathsCopied}" managed="${controller.ciphertextPathsCopied}">
+			<Button styleClass="drag-n-drop" text="%main.vaultDetail.encryptedPathsCopied" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseDecryptedFileAndReveal" contentDisplay="TOP" visible="${controller.ciphertextPathsCopied}" managed="${controller.ciphertextPathsCopied}">
 				<graphic>
 					<FontAwesome5IconView glyph="CHECK" glyphSize="15"/>
 				</graphic>
@@ -73,19 +69,15 @@
 			<padding>
 				<Insets topRightBottomLeft="0"/>
 			</padding>
-			<Button fx:id="dropZone2" styleClass="drag-n-drop" text="Decrypt file name" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseEncryptedFileAndGetName" contentDisplay="TOP" visible="${!controller.ciphertextPathsCopied}" managed="${!controller.ciphertextPathsCopied}">
+			<Button fx:id="decryptNameDropZone" styleClass="drag-n-drop" text="%main.vaultDetail.decryptName.buttonLabel" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseEncryptedFileAndGetName" contentDisplay="TOP" visible="${!controller.ciphertextPathsCopied}" managed="${!controller.ciphertextPathsCopied}">
 				<graphic>
-					<HBox spacing="4">
-						<Text styleClass="cryptic-text" text="101010"/>
-						<FontAwesome5IconView glyph="LONG_ARROW_ALT_RIGHT" glyphSize="10"/>
-						<Text styleClass="cryptic-text" text="abc"/>
-					</HBox>
+					<Text styleClass="cryptic-text" text="101010 → abc"/>
 				</graphic>
 				<tooltip>
-					<Tooltip text="%main.vaultDetail.locateEncryptedFileBtn.tooltip"/>
+					<Tooltip text="%main.vaultDetail.decryptName.tooltip"/>
 				</tooltip>
 			</Button>
-			<Button styleClass="drag-n-drop" text="%main.vaultDetail.encryptedPathsCopied" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseEncryptedFileAndGetName" contentDisplay="TOP" visible="${controller.ciphertextPathsCopied}" managed="${controller.ciphertextPathsCopied}">
+			<Button styleClass="drag-n-drop" text="%main.vaultDetail.decryptName.copied" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseEncryptedFileAndGetName" contentDisplay="TOP" visible="${controller.ciphertextPathsCopied}" managed="${controller.ciphertextPathsCopied}">
 				<graphic>
 					<FontAwesome5IconView glyph="CHECK" glyphSize="15"/>
 				</graphic>

+ 5 - 1
src/main/resources/i18n/strings.properties

@@ -423,7 +423,11 @@ main.vaultDetail.stats=Vault Statistics
 main.vaultDetail.locateEncryptedFileBtn=Locate Encrypted File
 main.vaultDetail.locateEncryptedFileBtn.tooltip=Choose a file from your vault to locate its encrypted counterpart
 main.vaultDetail.encryptedPathsCopied=Paths Copied to Clipboard!
-main.vaultDetail.decryptedFilePickerTitle=Select File Inside Vault
+main.vaultDetail.locateEncrypted.filePickerTitle=Select File Inside Vault
+main.vaultDetail.decryptName.buttonLabel=Decrypt File Name
+main.vaultDetail.decryptName.filePickerTitle=Select encrypted file
+main.vaultDetail.decryptName.tooltip=Choose an encrypted vault file to decrypt its name
+main.vaultDetail.decryptName.copied=Names copied to clipboard!
 ### Missing
 main.vaultDetail.missing.info=Cryptomator could not find a vault at this path.
 main.vaultDetail.missing.recheck=Recheck