mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 18:21:26 +00:00
Merge pull request #1595 from cryptomator/feature/MCGA
Feature: Make Context menu Great Again * adding additional entries * fixes #1478 * only appears on a selected vault
This commit is contained in:
@@ -86,6 +86,11 @@ abstract class MainWindowModule {
|
||||
@FxControllerKey(VaultListController.class)
|
||||
abstract FxController bindVaultListController(VaultListController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(VaultListContextMenuController.class)
|
||||
abstract FxController bindVaultListContextMenuController(VaultListContextMenuController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(VaultDetailController.class)
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import com.tobiasdiez.easybind.optional.ObservableOptionalValue;
|
||||
import com.tobiasdiez.easybind.optional.OptionalBinding;
|
||||
import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.ERROR;
|
||||
import static org.cryptomator.common.vaults.VaultState.LOCKED;
|
||||
import static org.cryptomator.common.vaults.VaultState.MISSING;
|
||||
import static org.cryptomator.common.vaults.VaultState.NEEDS_MIGRATION;
|
||||
import static org.cryptomator.common.vaults.VaultState.UNLOCKED;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultListContextMenuController implements FxController {
|
||||
|
||||
private final ObservableOptionalValue<Vault> selectedVault;
|
||||
private final Stage mainWindow;
|
||||
private final FxApplication application;
|
||||
private final KeychainManager keychain;
|
||||
private final RemoveVaultComponent.Builder removeVault;
|
||||
private final VaultOptionsComponent.Builder vaultOptionsWindow;
|
||||
private final OptionalBinding<VaultState> selectedVaultState;
|
||||
private final Binding<Boolean> selectedVaultPassphraseStored;
|
||||
private final Binding<Boolean> selectedVaultRemovable;
|
||||
private final Binding<Boolean> selectedVaultUnlockable;
|
||||
private final Binding<Boolean> selectedVaultLockable;
|
||||
|
||||
@Inject
|
||||
VaultListContextMenuController(ObjectProperty<Vault> selectedVault, @MainWindow Stage mainWindow, FxApplication application, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Builder vaultOptionsWindow) {
|
||||
this.selectedVault = EasyBind.wrapNullable(selectedVault);
|
||||
this.mainWindow = mainWindow;
|
||||
this.application = application;
|
||||
this.keychain = keychain;
|
||||
this.removeVault = removeVault;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
|
||||
this.selectedVaultState = this.selectedVault.mapObservable(Vault::stateProperty);
|
||||
this.selectedVaultPassphraseStored = this.selectedVault.map(this::isPasswordStored).orElse(false);
|
||||
this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION)::contains).orElse(false);
|
||||
this.selectedVaultUnlockable = selectedVaultState.map(LOCKED::equals).orElse(false);
|
||||
this.selectedVaultLockable = selectedVaultState.map(UNLOCKED::equals).orElse(false);
|
||||
|
||||
}
|
||||
|
||||
private boolean isPasswordStored(Vault vault) {
|
||||
return keychain.getPassphraseStoredProperty(vault.getId()).get();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickRemoveVault() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
removeVault.vault(v).build().showRemoveVault();
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickShowVaultOptions() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
vaultOptionsWindow.vault(v).build().showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickUnlockVault() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
application.startUnlockWorkflow(v, Optional.of(mainWindow));
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickLockVault() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
application.startLockWorkflow(v, Optional.of(mainWindow));
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickRevealVault() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
application.getVaultService().reveal(v);
|
||||
});
|
||||
}
|
||||
|
||||
// Getter and Setter
|
||||
|
||||
public Binding<Boolean> selectedVaultUnlockableProperty() {
|
||||
return selectedVaultUnlockable;
|
||||
}
|
||||
|
||||
public boolean isSelectedVaultUnlockable() {
|
||||
return selectedVaultUnlockable.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> selectedVaultLockableProperty() {
|
||||
return selectedVaultLockable;
|
||||
}
|
||||
|
||||
public boolean isSelectedVaultLockable() {
|
||||
return selectedVaultLockable.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> selectedVaultRemovableProperty() {
|
||||
return selectedVaultRemovable;
|
||||
}
|
||||
|
||||
public boolean isSelectedVaultRemovable() {
|
||||
return selectedVaultRemovable.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> selectedVaultPassphraseStoredProperty() {
|
||||
return selectedVaultPassphraseStored;
|
||||
}
|
||||
|
||||
public boolean isSelectedVaultPassphraseStored() {
|
||||
return selectedVaultPassphraseStored.getValue();
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,6 @@ import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Bindings;
|
||||
@@ -17,30 +14,30 @@ import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.input.ContextMenuEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultListController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultListController.class);
|
||||
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final ObjectProperty<Vault> selectedVault;
|
||||
private final VaultListCellFactory cellFactory;
|
||||
private final AddVaultWizardComponent.Builder addVaultWizard;
|
||||
private final RemoveVaultComponent.Builder removeVault;
|
||||
private final BooleanBinding noVaultSelected;
|
||||
private final BooleanBinding emptyVaultList;
|
||||
|
||||
public ListView<Vault> vaultList;
|
||||
|
||||
@Inject
|
||||
VaultListController(ObservableList<Vault> vaults, ObjectProperty<Vault> selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard, RemoveVaultComponent.Builder removeVault) {
|
||||
VaultListController(ObservableList<Vault> vaults, ObjectProperty<Vault> selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard) {
|
||||
this.vaults = vaults;
|
||||
this.selectedVault = selectedVault;
|
||||
this.cellFactory = cellFactory;
|
||||
this.addVaultWizard = addVaultWizard;
|
||||
this.removeVault = removeVault;
|
||||
this.noVaultSelected = selectedVault.isNull();
|
||||
|
||||
this.emptyVaultList = Bindings.isEmpty(vaults);
|
||||
|
||||
selectedVault.addListener(this::selectedVaultDidChange);
|
||||
}
|
||||
|
||||
@@ -56,6 +53,19 @@ public class VaultListController implements FxController {
|
||||
}
|
||||
}
|
||||
});
|
||||
vaultList.addEventFilter(MouseEvent.MOUSE_RELEASED, this::deselect);
|
||||
vaultList.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, request -> {
|
||||
if (selectedVault.get() == null) {
|
||||
request.consume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deselect(MouseEvent released) {
|
||||
if (released.getY() > (vaultList.getItems().size() * vaultList.fixedCellSizeProperty().get())) {
|
||||
vaultList.getSelectionModel().clearSelection();
|
||||
released.consume();
|
||||
}
|
||||
}
|
||||
|
||||
private void selectedVaultDidChange(@SuppressWarnings("unused") ObservableValue<? extends Vault> observableValue, @SuppressWarnings("unused") Vault oldValue, Vault newValue) {
|
||||
@@ -70,16 +80,6 @@ public class VaultListController implements FxController {
|
||||
addVaultWizard.build().showAddVaultWizard();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickRemoveVault() {
|
||||
Vault v = selectedVault.get();
|
||||
if (v != null) {
|
||||
removeVault.vault(v).build().showRemoveVault();
|
||||
} else {
|
||||
LOG.debug("Cannot remove a vault if none is selected.");
|
||||
}
|
||||
}
|
||||
|
||||
// Getter and Setter
|
||||
|
||||
public BooleanBinding emptyVaultListProperty() {
|
||||
@@ -90,11 +90,4 @@ public class VaultListController implements FxController {
|
||||
return emptyVaultList.get();
|
||||
}
|
||||
|
||||
public BooleanBinding noVaultSelectedProperty() {
|
||||
return noVaultSelected;
|
||||
}
|
||||
|
||||
public boolean isNoVaultSelected() {
|
||||
return noVaultSelected.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ContextMenu?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
@@ -15,13 +13,9 @@
|
||||
fx:controller="org.cryptomator.ui.mainwindow.VaultListController"
|
||||
minWidth="206">
|
||||
<StackPane VBox.vgrow="ALWAYS">
|
||||
<ListView fx:id="vaultList" editable="true">
|
||||
<ListView fx:id="vaultList" editable="true" fixedCellSize="60">
|
||||
<contextMenu>
|
||||
<ContextMenu>
|
||||
<items>
|
||||
<MenuItem text="%main.vaultlist.contextMenu.remove" onAction="#didClickRemoveVault" disable="${controller.noVaultSelected}"/>
|
||||
</items>
|
||||
</ContextMenu>
|
||||
<fx:include source="vault_list_contextmenu.fxml"/>
|
||||
</contextMenu>
|
||||
</ListView>
|
||||
<VBox visible="${controller.emptyVaultList}" spacing="6" alignment="CENTER">
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
prefWidth="200"
|
||||
spacing="12"
|
||||
alignment="CENTER_LEFT">
|
||||
<!-- Remark Check the containing list view for a fixed cell size before editing height properties -->
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
|
||||
16
main/ui/src/main/resources/fxml/vault_list_contextmenu.fxml
Normal file
16
main/ui/src/main/resources/fxml/vault_list_contextmenu.fxml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.ContextMenu?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<ContextMenu xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.mainwindow.VaultListContextMenuController">
|
||||
<items>
|
||||
<MenuItem fx:id="revealEntry" text="%main.vaultlist.contextMenu.reveal" onAction="#didClickRevealVault" visible="${controller.selectedVaultLockable}"/>
|
||||
<MenuItem fx:id="lockEntry" text="%main.vaultlist.contextMenu.lock" onAction="#didClickLockVault" visible="${controller.selectedVaultLockable}"/>
|
||||
<MenuItem fx:id="unlockEntry" text="%main.vaultlist.contextMenu.unlock" onAction="#didClickUnlockVault" visible="${controller.selectedVaultUnlockable && !controller.selectedVaultPassphraseStored}"/>
|
||||
<MenuItem fx:id="unlockNowEntry" text="%main.vaultlist.contextMenu.unlockNow" onAction="#didClickUnlockVault" visible="${controller.selectedVaultUnlockable && controller.selectedVaultPassphraseStored}"/>
|
||||
<MenuItem fx:id="optionsEntry" text="%main.vaultlist.contextMenu.vaultoptions" onAction="#didClickShowVaultOptions" disable="${!controller.selectedVaultUnlockable}"/>
|
||||
<MenuItem fx:id="removeEntry" text="%main.vaultlist.contextMenu.remove" onAction="#didClickRemoveVault" disable="${!controller.selectedVaultRemovable}"/>
|
||||
</items>
|
||||
</ContextMenu>
|
||||
@@ -224,7 +224,12 @@ 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…
|
||||
main.vaultlist.contextMenu.remove=Remove…
|
||||
main.vaultlist.contextMenu.lock=Lock
|
||||
main.vaultlist.contextMenu.unlock=Unlock…
|
||||
main.vaultlist.contextMenu.unlockNow=Unlock Now
|
||||
main.vaultlist.contextMenu.vaultoptions=Show Vault Options
|
||||
main.vaultlist.contextMenu.reveal=Reveal Drive
|
||||
main.vaultlist.addVaultBtn=Add Vault
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
|
||||
Reference in New Issue
Block a user