reworked drag'n'drop l&f

This commit is contained in:
Sebastian Stenzel
2019-11-20 15:03:03 +01:00
parent 687f11596e
commit 1ddfcc3219
5 changed files with 97 additions and 40 deletions

View File

@@ -1,14 +1,15 @@
package org.cryptomator.ui.mainwindow;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.input.DragEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplication;
@@ -20,11 +21,11 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@MainWindowScoped
public class MainWindowController implements FxController {
@@ -39,9 +40,10 @@ public class MainWindowController implements FxController {
private final BooleanBinding updateAvailable;
private final VaultListManager vaultListManager;
private final WrongFileAlertComponent.Builder wrongFileAlert;
private final BooleanProperty draggingOver = new SimpleBooleanProperty();
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
public HBox titleBar;
public VBox root;
public Pane dragAndDropIndicator;
public Region resizer;
private double xOffset;
private double yOffset;
@@ -74,40 +76,53 @@ public class MainWindowController implements FxController {
window.setHeight(event.getSceneY());
});
updateChecker.automaticallyCheckForUpdatesIfEnabled();
dragAndDropIndicator.setVisible(false);
root.setOnDragOver(event -> {
if (event.getGestureSource() != root && event.getDragboard().hasFiles()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
dragAndDropIndicator.setVisible(true);
}
event.consume();
});
root.setOnDragExited(event -> dragAndDropIndicator.setVisible(false));
root.setOnDragDropped(event -> {
if (event.getGestureSource() != root && event.getDragboard().hasFiles()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
Collection<Vault> vaultPaths = event.getDragboard().getFiles().stream().map(File::toPath).flatMap(this::addVault).collect(Collectors.toSet());
if (vaultPaths.isEmpty()) {
wrongFileAlert.build().showWrongFileAlertWindow();
}
}
event.consume();
});
root.setOnDragEntered(this::handleDragEvent);
root.setOnDragOver(this::handleDragEvent);
root.setOnDragDropped(this::handleDragEvent);
root.setOnDragExited(this::handleDragEvent);
}
private Stream<Vault> addVault(Path pathToVault) {
private void handleDragEvent(DragEvent event) {
if (DragEvent.DRAG_ENTERED.equals(event.getEventType()) && event.getGestureSource() == null) {
draggingOver.set(true);
} else if (DragEvent.DRAG_OVER.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
event.acceptTransferModes(TransferMode.ANY);
draggingVaultOver.set(event.getDragboard().getFiles().stream().map(File::toPath).anyMatch(this::containsVault));
} else if (DragEvent.DRAG_DROPPED.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
Set<Path> vaultPaths = event.getDragboard().getFiles().stream().map(File::toPath).filter(this::containsVault).collect(Collectors.toSet());
if (vaultPaths.isEmpty()) {
wrongFileAlert.build().showWrongFileAlertWindow();
} else {
vaultPaths.forEach(this::addVault);
}
event.setDropCompleted(!vaultPaths.isEmpty());
event.consume();
} else if (DragEvent.DRAG_EXITED.equals(event.getEventType())) {
draggingOver.set(false);
draggingVaultOver.set(false);
}
}
private boolean containsVault(Path path) {
if (path.getFileName().toString().equals(MASTERKEY_FILENAME)) {
return true;
} else if (Files.isDirectory(path) && Files.exists(path.resolve(MASTERKEY_FILENAME))) {
return true;
} else {
return false;
}
}
private void addVault(Path pathToVault) {
try {
if (pathToVault.getFileName().toString().equals(MASTERKEY_FILENAME)) {
return Stream.of(vaultListManager.add(pathToVault.getParent()));
vaultListManager.add(pathToVault.getParent());
} else {
return Stream.of(vaultListManager.add(pathToVault));
vaultListManager.add(pathToVault);
}
} catch (NoSuchFileException e) {
LOG.debug("Not a vault: {}", pathToVault);
}
return Stream.empty();
}
@FXML
@@ -133,4 +148,20 @@ public class MainWindowController implements FxController {
public boolean isUpdateAvailable() {
return updateAvailable.get();
}
public BooleanProperty draggingOverProperty() {
return draggingOver;
}
public boolean isDraggingOver() {
return draggingOver.get();
}
public BooleanProperty draggingVaultOverProperty() {
return draggingVaultOver;
}
public boolean isDraggingVaultOver() {
return draggingVaultOver.get();
}
}

View File

@@ -201,6 +201,16 @@
-fx-translate-y: 1px;
}
.main-window .drag-n-drop-indicator {
-fx-border-color: INDICATOR_BG;
-fx-border-width: 3px;
}
.main-window .drag-n-drop-indicator .drag-n-drop-header {
-fx-background-color: INDICATOR_BG;
-fx-padding: 3px;
}
/*******************************************************************************
* *
* TabPane *

View File

@@ -201,6 +201,16 @@
-fx-translate-y: 1px;
}
.main-window .drag-n-drop-indicator {
-fx-border-color: INDICATOR_BG;
-fx-border-width: 3px;
}
.main-window .drag-n-drop-indicator .drag-n-drop-header {
-fx-background-color: INDICATOR_BG;
-fx-padding: 3px;
}
/*******************************************************************************
* *
* TabPane *

View File

@@ -9,9 +9,7 @@
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Rectangle?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.layout.Pane?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:id="root"
@@ -50,14 +48,19 @@
<fx:include source="/fxml/vault_list.fxml" SplitPane.resizableWithParent="false"/>
<fx:include source="/fxml/vault_detail.fxml" SplitPane.resizableWithParent="true"/>
</SplitPane>
<Region styleClass="resizer" StackPane.alignment="BOTTOM_RIGHT" fx:id="resizer" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity"/>
<!-- TODO: use css instead of adding a Rectangle: -->
<Pane fx:id="dragAndDropIndicator">
<StackPane.margin>
<Insets topRightBottomLeft="24"/>
</StackPane.margin>
<Rectangle arcHeight="4" arcWidth="4" fill="gainsboro" strokeType="CENTERED" strokeWidth="3" strokeDashArray="20, 20" strokeLineJoin="ROUND" stroke="black" height="${dragAndDropIndicator.height}"
width="${dragAndDropIndicator.width}"/>
</Pane>
<VBox styleClass="drag-n-drop-indicator" visible="${controller.draggingOver}" alignment="TOP_CENTER">
<HBox visible="${!controller.draggingVaultOver}" managed="${!controller.draggingVaultOver}" spacing="6" styleClass="drag-n-drop-header" alignment="CENTER" VBox.vgrow="NEVER">
<FontAwesome5IconView glyph="EXCLAMATION_TRIANGLE"/>
<Label text="%main.dropZone.unknownDragboardContent"/>
</HBox>
<HBox visible="${controller.draggingVaultOver}" managed="${controller.draggingVaultOver}" spacing="6" styleClass="drag-n-drop-header" alignment="CENTER" VBox.vgrow="NEVER">
<FontAwesome5IconView glyph="CHECK"/>
<Label text="%main.dropZone.dropVault"/>
</HBox>
<Region VBox.vgrow="ALWAYS"/>
</VBox>
</StackPane>
</VBox>

View File

@@ -132,6 +132,9 @@ preferences.donationKey.getDonationKey=Get a donation key
# Main Window
main.closeBtn.tooltip=Close
main.preferencesBtn.tooltip=Preferences
## Drag 'n' Drop
main.dropZone.dropVault=Add this vault
main.dropZone.unknownDragboardContent=If you want to add a vault, drag it to this window
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Click here to add a vault
main.vaultlist.contextMenu.remove=Remove Vault