diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java index 24dbed679..46615091d 100644 --- a/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/src/main/java/org/cryptomator/common/settings/Settings.java @@ -67,8 +67,8 @@ public class Settings { public final StringProperty quickAccessService; public final ObjectProperty userInterfaceOrientation; public final StringProperty licenseKey; - public final BooleanProperty showMinimizeButton; public final BooleanProperty showTrayIcon; + public final BooleanProperty compactMode; public final IntegerProperty windowXPosition; public final IntegerProperty windowYPosition; public final IntegerProperty windowWidth; @@ -105,8 +105,8 @@ public class Settings { this.keychainProvider = new SimpleStringProperty(this, "keychainProvider", json.keychainProvider); this.userInterfaceOrientation = new SimpleObjectProperty<>(this, "userInterfaceOrientation", parseEnum(json.uiOrientation, NodeOrientation.class, NodeOrientation.LEFT_TO_RIGHT)); this.licenseKey = new SimpleStringProperty(this, "licenseKey", json.licenseKey); - this.showMinimizeButton = new SimpleBooleanProperty(this, "showMinimizeButton", json.showMinimizeButton); this.showTrayIcon = new SimpleBooleanProperty(this, "showTrayIcon", json.showTrayIcon); + this.compactMode = new SimpleBooleanProperty(this, "compactMode", json.compactMode); this.windowXPosition = new SimpleIntegerProperty(this, "windowXPosition", json.windowXPosition); this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition); this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth); @@ -134,8 +134,8 @@ public class Settings { keychainProvider.addListener(this::somethingChanged); userInterfaceOrientation.addListener(this::somethingChanged); licenseKey.addListener(this::somethingChanged); - showMinimizeButton.addListener(this::somethingChanged); showTrayIcon.addListener(this::somethingChanged); + compactMode.addListener(this::somethingChanged); windowXPosition.addListener(this::somethingChanged); windowYPosition.addListener(this::somethingChanged); windowWidth.addListener(this::somethingChanged); @@ -190,8 +190,8 @@ public class Settings { json.keychainProvider = keychainProvider.get(); json.uiOrientation = userInterfaceOrientation.get().name(); json.licenseKey = licenseKey.get(); - json.showMinimizeButton = showMinimizeButton.get(); json.showTrayIcon = showTrayIcon.get(); + json.compactMode = compactMode.get(); json.windowXPosition = windowXPosition.get(); json.windowYPosition = windowYPosition.get(); json.windowWidth = windowWidth.get(); diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJson.java b/src/main/java/org/cryptomator/common/settings/SettingsJson.java index 7996dcb39..5a98ca91c 100644 --- a/src/main/java/org/cryptomator/common/settings/SettingsJson.java +++ b/src/main/java/org/cryptomator/common/settings/SettingsJson.java @@ -51,12 +51,12 @@ class SettingsJson { @JsonProperty("port") int port = Settings.DEFAULT_PORT; - @JsonProperty("showMinimizeButton") - boolean showMinimizeButton = Settings.DEFAULT_SHOW_MINIMIZE_BUTTON; - @JsonProperty("showTrayIcon") boolean showTrayIcon; + @JsonProperty("compactMode") + boolean compactMode; + @JsonProperty("startHidden") boolean startHidden = Settings.DEFAULT_START_HIDDEN; diff --git a/src/main/java/org/cryptomator/ui/controls/NotificationBar.java b/src/main/java/org/cryptomator/ui/controls/NotificationBar.java new file mode 100644 index 000000000..64a08f220 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/controls/NotificationBar.java @@ -0,0 +1,96 @@ +package org.cryptomator.ui.controls; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.fxml.FXML; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; + +public class NotificationBar extends HBox { + + @FXML + private Label notificationLabel; + + private final BooleanProperty dismissable = new SimpleBooleanProperty(); + private final BooleanProperty notify = new SimpleBooleanProperty(); + + + public NotificationBar() { + setAlignment(Pos.CENTER); + setStyle("-fx-alignment: center;"); + + Region spacer = new Region(); + spacer.setMinWidth(40); + + Region leftRegion = new Region(); + HBox.setHgrow(leftRegion, javafx.scene.layout.Priority.ALWAYS); + + Region rightRegion = new Region(); + HBox.setHgrow(rightRegion, javafx.scene.layout.Priority.ALWAYS); + + VBox vbox = new VBox(); + vbox.setAlignment(Pos.CENTER); + HBox.setHgrow(vbox, javafx.scene.layout.Priority.ALWAYS); + + notificationLabel = new Label(); + notificationLabel.getStyleClass().add("notification-label"); + notificationLabel.setStyle("-fx-alignment: center;"); + vbox.getChildren().add(notificationLabel); + + Button closeButton = new Button("X"); + closeButton.setMinWidth(40); + closeButton.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-weight: bold;"); + closeButton.visibleProperty().bind(dismissable); + + closeButton.setOnAction(_ -> { + visibleProperty().unbind(); + managedProperty().unbind(); + visibleProperty().set(false); + managedProperty().set(false); + }); + closeButton.visibleProperty().bind(dismissable); + + getChildren().addAll(spacer, leftRegion, vbox, rightRegion, closeButton); + + visibleProperty().bind(notifyProperty()); + managedProperty().bind(notifyProperty()); + } + + public String getText() { + return notificationLabel.getText(); + } + + public void setText(String text) { + notificationLabel.setText(text); + } + + public void setStyleClass(String styleClass) { + getStyleClass().clear(); + getStyleClass().add(styleClass); + } + + public boolean isDismissable() { + return dismissable.get(); + } + + public void setDismissable(boolean value) { + dismissable.set(value); + } + + public boolean getNotify() { + return notify.get(); + } + + public void setNotify(boolean value) { + notify.set(value); + } + + public BooleanProperty notifyProperty() { + return notify; + } + +} diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java index b2c912834..999ff7882 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java @@ -1,19 +1,26 @@ package org.cryptomator.ui.mainwindow; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.common.vaults.VaultListManager; -import org.cryptomator.ui.common.FxController; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; import javafx.beans.Observable; +import javafx.beans.binding.BooleanBinding; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.fxml.FXML; import javafx.scene.layout.StackPane; import javafx.stage.Stage; +import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.LicenseHolder; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultListManager; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.fxapp.FxApplicationWindows; +import org.cryptomator.ui.fxapp.UpdateChecker; +import org.cryptomator.ui.preferences.SelectedPreferencesTab; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; @MainWindowScoped public class MainWindowController implements FxController { @@ -22,22 +29,61 @@ public class MainWindowController implements FxController { private final Stage window; private final ReadOnlyObjectProperty selectedVault; + private final Settings settings; + private final FxApplicationWindows appWindows; + private final BooleanBinding updateAvailable; + private final LicenseHolder licenseHolder; - public StackPane root; + @FXML + private StackPane root; @Inject - public MainWindowController(@MainWindow Stage window, ObjectProperty selectedVault) { + public MainWindowController(@MainWindow Stage window, // + ObjectProperty selectedVault, // + Settings settings, // + FxApplicationWindows appWindows, // + UpdateChecker updateChecker, // + LicenseHolder licenseHolder) { this.window = window; this.selectedVault = selectedVault; + this.settings = settings; + this.appWindows = appWindows; + this.updateAvailable = updateChecker.updateAvailableProperty(); + this.licenseHolder = licenseHolder; + updateChecker.automaticallyCheckForUpdatesIfEnabled(); + } @FXML public void initialize() { LOG.trace("init MainWindowController"); + if (SystemUtils.IS_OS_WINDOWS) { root.getStyleClass().add("os-windows"); } window.focusedProperty().addListener(this::mainWindowFocusChanged); + + if (!neverTouched()) { + window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight()); + window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth()); + window.setX(settings.windowXPosition.get()); + window.setY(settings.windowYPosition.get()); + } + window.widthProperty().addListener((_, _, _) -> savePositionalSettings()); + window.heightProperty().addListener((_, _, _) -> savePositionalSettings()); + window.xProperty().addListener((_, _, _) -> savePositionalSettings()); + window.yProperty().addListener((_, _, _) -> savePositionalSettings()); + } + + private boolean neverTouched() { + return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0); + } + + public void savePositionalSettings() { + settings.windowWidth.setValue(window.getWidth()); + settings.windowHeight.setValue(window.getHeight()); + settings.windowXPosition.setValue(window.getX()); + settings.windowYPosition.setValue(window.getY()); } private void mainWindowFocusChanged(Observable observable) { @@ -47,4 +93,43 @@ public class MainWindowController implements FxController { } } + @FXML + public void showGeneralPreferences() { + appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL); + } + + @FXML + public void showContributePreferences() { + appWindows.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE); + } + + @FXML + public void showUpdatePreferences() { + appWindows.showPreferencesWindow(SelectedPreferencesTab.UPDATES); + } + + public ReadOnlyBooleanProperty debugModeEnabledProperty() { + return settings.debugMode; + } + + public boolean getDebugModeEnabled() { + return debugModeEnabledProperty().get(); + } + + public BooleanBinding updateAvailableProperty() { + return updateAvailable; + } + + public boolean getUpdateAvailable() { + return updateAvailable.get(); + } + + public BooleanBinding licenseValidProperty(){ + return licenseHolder.validLicenseProperty(); + } + + public boolean getLicenseValid() { + return licenseHolder.isValidLicense(); + } + } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java index 0b403bb47..5fdaa6a8a 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java @@ -6,7 +6,6 @@ import dagger.Provides; import dagger.multibindings.IntoMap; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; -import org.cryptomator.ui.error.ErrorComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxControllerKey; import org.cryptomator.ui.common.FxmlFile; @@ -14,8 +13,8 @@ import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageInitializer; +import org.cryptomator.ui.error.ErrorComponent; import org.cryptomator.ui.fxapp.PrimaryStage; -import org.cryptomator.ui.health.HealthCheckComponent; import org.cryptomator.ui.migration.MigrationComponent; import org.cryptomator.ui.removevault.RemoveVaultComponent; import org.cryptomator.ui.stats.VaultStatisticsComponent; @@ -28,7 +27,6 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.scene.Scene; import javafx.stage.Modality; import javafx.stage.Stage; -import javafx.stage.StageStyle; import java.util.Map; import java.util.ResourceBundle; @@ -41,9 +39,8 @@ abstract class MainWindowModule { static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) { initializer.accept(stage); stage.setTitle("Cryptomator"); - stage.initStyle(StageStyle.UNDECORATED); stage.setMinWidth(650); - stage.setMinHeight(440); + stage.setMinHeight(498); return stage; } @@ -85,16 +82,6 @@ abstract class MainWindowModule { @FxControllerKey(MainWindowController.class) abstract FxController bindMainWindowController(MainWindowController controller); - @Binds - @IntoMap - @FxControllerKey(MainWindowTitleController.class) - abstract FxController bindMainWindowTitleController(MainWindowTitleController controller); - - @Binds - @IntoMap - @FxControllerKey(ResizeController.class) - abstract FxController bindResizeController(ResizeController controller); - @Binds @IntoMap @FxControllerKey(VaultListController.class) diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java index d78192186..fbc96513b 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowSceneFactory.java @@ -18,22 +18,20 @@ public class MainWindowSceneFactory extends DefaultSceneFactory { protected static final KeyCodeCombination SHORTCUT_N = new KeyCodeCombination(KeyCode.N, KeyCombination.SHORTCUT_DOWN); protected static final KeyCodeCombination SHORTCUT_O = new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN); - private final Lazy mainWindowTitleController; + private final Stage window; private final Lazy vaultListController; @Inject - public MainWindowSceneFactory(Settings settings, Lazy mainWindowTitleController, Lazy vaultListController) { + public MainWindowSceneFactory(Settings settings, @MainWindow Stage window, Lazy vaultListController) { super(settings); - this.mainWindowTitleController = mainWindowTitleController; + this.window = window; this.vaultListController = vaultListController; } @Override protected void setupDefaultAccelerators(Scene scene, Stage stage) { - if (SystemUtils.IS_OS_WINDOWS) { - scene.getAccelerators().put(ALT_F4, mainWindowTitleController.get()::close); - } else { - scene.getAccelerators().put(SHORTCUT_W, mainWindowTitleController.get()::close); + if (!SystemUtils.IS_OS_WINDOWS) { + scene.getAccelerators().put(SHORTCUT_W, window::close); } scene.getAccelerators().put(SHORTCUT_N, vaultListController.get()::didClickAddNewVault); scene.getAccelerators().put(SHORTCUT_O, vaultListController.get()::didClickAddExistingVault); diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java deleted file mode 100644 index f3c92790d..000000000 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java +++ /dev/null @@ -1,157 +0,0 @@ -package org.cryptomator.ui.mainwindow; - -import org.cryptomator.common.LicenseHolder; -import org.cryptomator.common.settings.Settings; -import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.fxapp.FxApplicationTerminator; -import org.cryptomator.ui.fxapp.FxApplicationWindows; -import org.cryptomator.ui.fxapp.UpdateChecker; -import org.cryptomator.ui.preferences.SelectedPreferencesTab; -import org.cryptomator.ui.traymenu.TrayMenuComponent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.fxml.FXML; -import javafx.scene.input.MouseButton; -import javafx.scene.layout.HBox; -import javafx.stage.Stage; - -@MainWindowScoped -public class MainWindowTitleController implements FxController { - - private static final Logger LOG = LoggerFactory.getLogger(MainWindowTitleController.class); - - private final Stage window; - private final FxApplicationTerminator terminator; - private final FxApplicationWindows appWindows; - private final boolean trayMenuInitialized; - private final UpdateChecker updateChecker; - private final BooleanBinding updateAvailable; - private final LicenseHolder licenseHolder; - private final Settings settings; - private final BooleanBinding showMinimizeButton; - - public HBox titleBar; - private double xOffset; - private double yOffset; - - @Inject - MainWindowTitleController(@MainWindow Stage window, FxApplicationTerminator terminator, FxApplicationWindows appWindows, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) { - this.window = window; - this.terminator = terminator; - this.appWindows = appWindows; - this.trayMenuInitialized = trayMenu.isInitialized(); - this.updateChecker = updateChecker; - this.updateAvailable = updateChecker.updateAvailableProperty(); - this.licenseHolder = licenseHolder; - this.settings = settings; - this.showMinimizeButton = Bindings.createBooleanBinding(this::isShowMinimizeButton, settings.showMinimizeButton, settings.showTrayIcon); - } - - @FXML - public void initialize() { - LOG.trace("init MainWindowTitleController"); - updateChecker.automaticallyCheckForUpdatesIfEnabled(); - titleBar.setOnMousePressed(event -> { - xOffset = event.getSceneX(); - yOffset = event.getSceneY(); - - }); - titleBar.setOnMouseClicked(event -> { - if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) { - window.setFullScreen(!window.isFullScreen()); - } - }); - titleBar.setOnMouseDragged(event -> { - if (window.isFullScreen()) return; - window.setX(event.getScreenX() - xOffset); - window.setY(event.getScreenY() - yOffset); - }); - titleBar.setOnDragDetected(mouseDragEvent -> { - titleBar.startFullDrag(); - }); - titleBar.setOnMouseDragReleased(mouseDragEvent -> { - saveWindowSettings(); - }); - - window.setOnCloseRequest(event -> { - close(); - event.consume(); - }); - } - - private void saveWindowSettings() { - settings.windowXPosition.setValue(window.getX()); - settings.windowYPosition.setValue(window.getY()); - settings.windowWidth.setValue(window.getWidth()); - settings.windowHeight.setValue(window.getHeight()); - } - - @FXML - public void close() { - if (trayMenuInitialized) { - window.close(); - } else { - terminator.terminate(); - } - } - - @FXML - public void minimize() { - window.setIconified(true); - } - - @FXML - public void showPreferences() { - appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY); - } - - @FXML - public void showGeneralPreferences() { - appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL); - } - - @FXML - public void showContributePreferences() { - appWindows.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE); - } - - /* Getter/Setter */ - - public LicenseHolder getLicenseHolder() { - return licenseHolder; - } - - public BooleanBinding updateAvailableProperty() { - return updateAvailable; - } - - public boolean isUpdateAvailable() { - return updateAvailable.get(); - } - - public boolean isTrayIconPresent() { - return trayMenuInitialized; - } - - public ReadOnlyBooleanProperty debugModeEnabledProperty() { - return settings.debugMode; - } - - public boolean isDebugModeEnabled() { - return debugModeEnabledProperty().get(); - } - - public BooleanBinding showMinimizeButtonProperty() { - return showMinimizeButton; - } - - public boolean isShowMinimizeButton() { - // always show the minimize button if no tray icon is present OR it is explicitly enabled - return !trayMenuInitialized || settings.showMinimizeButton.get(); - } -} diff --git a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java deleted file mode 100644 index 2c3838ea0..000000000 --- a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java +++ /dev/null @@ -1,194 +0,0 @@ -package org.cryptomator.ui.mainwindow; - -import org.cryptomator.common.settings.Settings; -import org.cryptomator.ui.common.FxController; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javafx.beans.binding.BooleanBinding; -import javafx.fxml.FXML; -import javafx.geometry.Rectangle2D; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Region; -import javafx.stage.Screen; -import javafx.stage.Stage; -import javafx.stage.WindowEvent; - -@MainWindow -public class ResizeController implements FxController { - - private static final Logger LOG = LoggerFactory.getLogger(ResizeController.class); - - private final Stage window; - - public Region tlResizer; - public Region trResizer; - public Region blResizer; - public Region brResizer; - public Region tResizer; - public Region rResizer; - public Region bResizer; - public Region lResizer; - public Region lDefaultRegion; - public Region tDefaultRegion; - public Region rDefaultRegion; - public Region bDefaultRegion; - - private double origX, origY, origW, origH; - - private final Settings settings; - - private final BooleanBinding showResizingArrows; - - @Inject - ResizeController(@MainWindow Stage window, Settings settings) { - this.window = window; - this.settings = settings; - this.showResizingArrows = window.fullScreenProperty().not(); - } - - @FXML - public void initialize() { - LOG.trace("init ResizeController"); - - if (!neverTouched()) { - window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight()); - window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth()); - window.setX(settings.windowXPosition.get()); - window.setY(settings.windowYPosition.get()); - } - - window.setOnShowing(this::checkDisplayBounds); - } - - private boolean neverTouched() { - return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0); - } - - private void checkDisplayBounds(WindowEvent evt) { - // Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000) - // See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453 - // If the position is (-32000, -32000), restore to the last saved position - if (window.getX() == -32000 && window.getY() == -32000) { - window.setX(settings.windowXPosition.get()); - window.setY(settings.windowYPosition.get()); - window.setWidth(settings.windowWidth.get()); - window.setHeight(settings.windowHeight.get()); - } - - if (isOutOfDisplayBounds()) { - // If the position is illegal, then the window appears on the main screen in the middle of the window. - LOG.debug("Resetting window position due to insufficient screen overlap"); - Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds(); - window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2); - window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2); - window.setWidth(window.getMinWidth()); - window.setHeight(window.getMinHeight()); - savePositionalSettings(); - } - } - - private boolean isOutOfDisplayBounds() { - // define a rect which is inset on all sides from the window's rect: - final double x = window.getX() + 20; // 20px left - final double y = window.getY() + 5; // 5px top - final double w = window.getWidth() - 40; // 20px left + 20px right - final double h = window.getHeight() - 25; // 5px top + 20px bottom - return isRectangleOutOfScreen(x, y, 0, h) // Left pixel column - || isRectangleOutOfScreen(x + w, y, 0, h) // Right pixel column - || isRectangleOutOfScreen(x, y, w, 0) // Top pixel row - || isRectangleOutOfScreen(x, y + h, w, 0); // Bottom pixel row - } - - private boolean isRectangleOutOfScreen(double x, double y, double width, double height) { - return Screen.getScreensForRectangle(x, y, width, height).isEmpty(); - } - - private void startResize(MouseEvent evt) { - origX = window.getX(); - origY = window.getY(); - origW = window.getWidth(); - origH = window.getHeight(); - } - - @FXML - private void resizeTopLeft(MouseEvent evt) { - resizeTop(evt); - resizeLeft(evt); - } - - @FXML - private void resizeTopRight(MouseEvent evt) { - resizeTop(evt); - resizeRight(evt); - } - - @FXML - private void resizeBottomLeft(MouseEvent evt) { - resizeBottom(evt); - resizeLeft(evt); - } - - @FXML - private void resizeBottomRight(MouseEvent evt) { - resizeBottom(evt); - resizeRight(evt); - } - - @FXML - private void resizeTop(MouseEvent evt) { - startResize(evt); - double newY = evt.getScreenY(); - double dy = newY - origY; - double newH = origH - dy; - if (newH < window.getMaxHeight() && newH > window.getMinHeight()) { - window.setY(newY); - window.setHeight(newH); - } - } - - @FXML - private void resizeLeft(MouseEvent evt) { - startResize(evt); - double newX = evt.getScreenX(); - double dx = newX - origX; - double newW = origW - dx; - if (newW < window.getMaxWidth() && newW > window.getMinWidth()) { - window.setX(newX); - window.setWidth(newW); - } - } - - @FXML - private void resizeBottom(MouseEvent evt) { - double newH = evt.getSceneY(); - if (newH < window.getMaxHeight() && newH > window.getMinHeight()) { - window.setHeight(newH); - } - } - - @FXML - private void resizeRight(MouseEvent evt) { - double newW = evt.getSceneX(); - if (newW < window.getMaxWidth() && newW > window.getMinWidth()) { - window.setWidth(newW); - } - } - - @FXML - public void savePositionalSettings() { - settings.windowWidth.setValue(window.getWidth()); - settings.windowHeight.setValue(window.getHeight()); - settings.windowXPosition.setValue(window.getX()); - settings.windowYPosition.setValue(window.getY()); - } - - public BooleanBinding showResizingArrowsProperty() { - return showResizingArrows; - } - - public boolean isShowResizingArrows() { - return showResizingArrows.get(); - } -} \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java index 6374d7ae3..38d7ed1c7 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.mainwindow; +import org.cryptomator.common.settings.Settings; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.ui.common.Animations; @@ -18,6 +19,7 @@ public class VaultListCellController implements FxController { private final ObjectProperty vault = new SimpleObjectProperty<>(); private final ObservableValue glyph; + private final ObservableValue compactMode; private AutoAnimator spinAnimation; @@ -25,8 +27,9 @@ public class VaultListCellController implements FxController { public FontAwesome5IconView vaultStateView; @Inject - VaultListCellController() { + VaultListCellController(Settings settings) { this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState); + this.compactMode = settings.compactMode; } public void initialize() { @@ -68,6 +71,14 @@ public class VaultListCellController implements FxController { return vault.get(); } + public ObservableValue compactModeProperty() { + return compactMode; + } + + public boolean getCompactMode() { + return compactMode.getValue(); + } + public void setVault(Vault value) { vault.set(value); } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java index e8ea21fe4..37b9841d0 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.mainwindow; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.settings.Settings; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.cryptofs.CryptoFileSystemProvider; @@ -9,6 +10,7 @@ import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.VaultService; import org.cryptomator.ui.fxapp.FxApplicationWindows; +import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.cryptomator.ui.removevault.RemoveVaultComponent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,7 +26,6 @@ import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.geometry.Side; -import javafx.scene.control.Button; import javafx.scene.control.ContextMenu; import javafx.scene.control.ListView; import javafx.scene.input.ContextMenuEvent; @@ -33,6 +34,7 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; +import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import java.io.File; @@ -69,10 +71,11 @@ public class VaultListController implements FxController { private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty(); private final ResourceBundle resourceBundle; private final FxApplicationWindows appWindows; - + private final ObservableValue cellSize; public ListView vaultList; public StackPane root; - public Button addVaultBtn; + @FXML + private HBox addVaultButton; @FXML private ContextMenu addVaultContextMenu; @@ -86,7 +89,8 @@ public class VaultListController implements FxController { RemoveVaultComponent.Builder removeVaultDialogue, // VaultListManager vaultListManager, // ResourceBundle resourceBundle, // - FxApplicationWindows appWindows) { + FxApplicationWindows appWindows, // + Settings settings) { this.mainWindow = mainWindow; this.vaults = vaults; this.selectedVault = selectedVault; @@ -101,11 +105,13 @@ public class VaultListController implements FxController { this.emptyVaultList = Bindings.isEmpty(vaults); selectedVault.addListener(this::selectedVaultDidChange); + cellSize = settings.compactMode.map(compact -> compact ? 30.0 : 60.0); } public void initialize() { vaultList.setItems(vaults); vaultList.setCellFactory(cellFactory); + selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty()); vaults.addListener((ListChangeListener.Change c) -> { while (c.next()) { @@ -171,7 +177,7 @@ public class VaultListController implements FxController { if (addVaultContextMenu.isShowing()) { addVaultContextMenu.hide(); } else { - addVaultContextMenu.show(addVaultBtn, Side.BOTTOM, 0.0, 0.0); + addVaultContextMenu.show(addVaultButton, Side.BOTTOM, 0.0, 0.0); } } @@ -247,6 +253,11 @@ public class VaultListController implements FxController { } } + @FXML + public void showPreferences() { + appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY); + } + // Getter and Setter public BooleanBinding emptyVaultListProperty() { @@ -265,5 +276,12 @@ public class VaultListController implements FxController { return draggingVaultOver.get(); } + public ObservableValue cellSizeProperty() { + return cellSize; + } + + public Double getCellSize() { + return cellSize.getValue(); + } } diff --git a/src/main/java/org/cryptomator/ui/preferences/InterfacePreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/InterfacePreferencesController.java index 40983c3f0..573bfc394 100644 --- a/src/main/java/org/cryptomator/ui/preferences/InterfacePreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/InterfacePreferencesController.java @@ -36,8 +36,8 @@ public class InterfacePreferencesController implements FxController { private final ResourceBundle resourceBundle; private final SupportedLanguages supportedLanguages; public ChoiceBox themeChoiceBox; - public CheckBox showMinimizeButtonCheckbox; public CheckBox showTrayIconCheckbox; + public CheckBox compactModeCheckbox; public ChoiceBox preferredLanguageChoiceBox; public ToggleGroup nodeOrientation; public RadioButton nodeOrientationLtr; @@ -63,9 +63,8 @@ public class InterfacePreferencesController implements FxController { themeChoiceBox.valueProperty().bindBidirectional(settings.theme); themeChoiceBox.setConverter(new UiThemeConverter(resourceBundle)); - showMinimizeButtonCheckbox.selectedProperty().bindBidirectional(settings.showMinimizeButton); - showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon); + compactModeCheckbox.selectedProperty().bindBidirectional(settings.compactMode); preferredLanguageChoiceBox.getItems().addAll(supportedLanguages.getLanguageTags()); preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.language); diff --git a/src/main/resources/css/dark_theme.css b/src/main/resources/css/dark_theme.css index 86d9aaa71..72af57751 100644 --- a/src/main/resources/css/dark_theme.css +++ b/src/main/resources/css/dark_theme.css @@ -181,31 +181,20 @@ -fx-border-width: 1px; } -.main-window .title { - -fx-background-color: CONTROL_BORDER_NORMAL, TITLE_BG; - -fx-background-insets: 0, 0 0 1px 0; +.main-window .button-bar { + -fx-background-color: MAIN_BG; + -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; + -fx-border-width: 1px 0 0 0; } -.main-window .title .button { - -fx-pref-height: 30px; - -fx-pref-width: 30px; - -fx-background-color: none; - -fx-padding: 0; +.main-window .button-left { + -fx-border-color: CONTROL_BORDER_NORMAL; + -fx-border-width: 0 1px 0 0; } -.main-window .title .button .glyph-icon { - -fx-fill: white; -} - -.main-window .title .button:armed .glyph-icon { - -fx-fill: GRAY_8; -} - -.main-window .update-indicator { - -fx-background-color: white, RED_5; - -fx-background-insets: 1px, 2px; - -fx-background-radius: 6px, 5px; - -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 2, 0, 0, 0); +.main-window .button-right { + -fx-border-color: CONTROL_BORDER_NORMAL; + -fx-border-width: 0 0 0 1px; } /******************************************************************************* @@ -322,23 +311,33 @@ -fx-fill: transparent; } -.button.toolbar-button { - -fx-min-height: 40px; - -fx-background-color: transparent; - -fx-background-insets: 0; - -fx-background-radius: 0; - -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; - -fx-border-width: 1px 0 0 0; - -fx-padding: 0; +/******************************************************************************* + * * + * NotificationBar * + * * + ******************************************************************************/ + +.notification-label { + -fx-text-fill: white; + -fx-font-weight: bold; } -.button.toolbar-button:focused { - -fx-background-color: CONTROL_BORDER_FOCUSED, MAIN_BG; - -fx-background-insets: 0, 2px 1px 1px 1px; +.notification-debug { + -fx-min-height:24px; + -fx-max-height:24px; + -fx-background-color: RED_5; } -.button.toolbar-button:armed { - -fx-background-color: CONTROL_BG_ARMED; +.notification-update { + -fx-min-height:24px; + -fx-max-height:24px; + -fx-background-color: YELLOW_5; +} + +.notification-support { + -fx-min-height:24px; + -fx-max-height:24px; + -fx-background-color: PRIMARY; } /******************************************************************************* @@ -394,16 +393,6 @@ -fx-background-color: MUTED_BG; } -/* Note: These values below are kinda random such that it looks ok. I'm pretty sure there is room for improvement. Additionally, fx-text-fill does not work*/ -.badge-debug { - -fx-font-family: 'Open Sans Bold'; - -fx-font-size: 1.0em; - -fx-background-radius: 8px; - -fx-padding: 0.3em 0.55em 0.3em 0.55em; - -fx-background-color: RED_5; - -fx-background-radius: 2em; -} - /******************************************************************************* * * * Password Strength Indicator * diff --git a/src/main/resources/css/light_theme.css b/src/main/resources/css/light_theme.css index 88cf6d970..84df05b10 100644 --- a/src/main/resources/css/light_theme.css +++ b/src/main/resources/css/light_theme.css @@ -177,34 +177,24 @@ /* windows needs an explicit border: */ .main-window.os-windows { - -fx-border-color: TITLE_BG; - -fx-border-width: 1px; + -fx-border-color: CONTROL_BORDER_NORMAL; + -fx-border-width: 1px 0 0 0; } -.main-window .title { - -fx-background-color: TITLE_BG; +.main-window .button-bar { + -fx-background-color: MAIN_BG; + -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; + -fx-border-width: 1px 0 0 0; } -.main-window .title .button { - -fx-pref-height: 30px; - -fx-pref-width: 30px; - -fx-background-color: none; - -fx-padding: 0; +.main-window .button-bar .button-left { + -fx-border-color: CONTROL_BORDER_NORMAL; + -fx-border-width: 0 1px 0 0; } -.main-window .title .button .glyph-icon { - -fx-fill: white; -} - -.main-window .title .button:armed .glyph-icon { - -fx-fill: GRAY_8; -} - -.main-window .update-indicator { - -fx-background-color: white, RED_5; - -fx-background-insets: 1px, 2px; - -fx-background-radius: 6px, 5px; - -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 2, 0, 0, 0); +.main-window .button-bar .button-right { + -fx-border-color: CONTROL_BORDER_NORMAL; + -fx-border-width: 0 0 0 1px; } /******************************************************************************* @@ -321,23 +311,33 @@ -fx-fill: transparent; } -.button.toolbar-button { - -fx-min-height: 40px; - -fx-background-color: transparent; - -fx-background-insets: 0; - -fx-background-radius: 0; - -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; - -fx-border-width: 1px 0 0 0; - -fx-padding: 0; +/******************************************************************************* + * * + * NotificationBar * + * * + ******************************************************************************/ + +.notification-label { + -fx-text-fill: white; + -fx-font-weight: bold; } -.button.toolbar-button:focused { - -fx-background-color: CONTROL_BORDER_FOCUSED, MAIN_BG; - -fx-background-insets: 0, 2px 1px 1px 1px; +.notification-debug { + -fx-min-height:24px; + -fx-max-height:24px; + -fx-background-color: RED_5; } -.button.toolbar-button:armed { - -fx-background-color: CONTROL_BG_ARMED; +.notification-update { + -fx-min-height:24px; + -fx-max-height:24px; + -fx-background-color: YELLOW_5; +} + +.notification-support { + -fx-min-height:24px; + -fx-max-height:24px; + -fx-background-color: PRIMARY; } /******************************************************************************* @@ -393,16 +393,6 @@ -fx-background-color: MUTED_BG; } -/* Note: These values below are kinda random such that it looks ok. I'm pretty sure there is room for improvement. Additionally, fx-text-fill does not work*/ -.badge-debug { - -fx-font-family: 'Open Sans Bold'; - -fx-font-size: 1.0em; - -fx-background-radius: 8px; - -fx-padding: 0.3em 0.55em 0.3em 0.55em; - -fx-background-color: RED_5; - -fx-background-radius: 2em; -} - /******************************************************************************* * * * Password Strength Indicator * diff --git a/src/main/resources/fxml/main_window.fxml b/src/main/resources/fxml/main_window.fxml index 2796455d3..327e2c470 100644 --- a/src/main/resources/fxml/main_window.fxml +++ b/src/main/resources/fxml/main_window.fxml @@ -3,17 +3,31 @@ + + - - + + + + - diff --git a/src/main/resources/fxml/main_window_resize.fxml b/src/main/resources/fxml/main_window_resize.fxml deleted file mode 100644 index 7d5fb9437..000000000 --- a/src/main/resources/fxml/main_window_resize.fxml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/fxml/main_window_title.fxml b/src/main/resources/fxml/main_window_title.fxml deleted file mode 100644 index bd60aa25d..000000000 --- a/src/main/resources/fxml/main_window_title.fxml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/fxml/preferences_interface.fxml b/src/main/resources/fxml/preferences_interface.fxml index 6cb9b7d73..36f58ebb3 100644 --- a/src/main/resources/fxml/preferences_interface.fxml +++ b/src/main/resources/fxml/preferences_interface.fxml @@ -37,9 +37,7 @@ - - - + diff --git a/src/main/resources/fxml/vault_list.fxml b/src/main/resources/fxml/vault_list.fxml index f9cb29258..ae0905d4e 100644 --- a/src/main/resources/fxml/vault_list.fxml +++ b/src/main/resources/fxml/vault_list.fxml @@ -1,15 +1,16 @@ - - + + + - + @@ -25,30 +26,42 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/vault_list_cell.fxml b/src/main/resources/fxml/vault_list_cell.fxml index aedb18b06..c6cc8cf24 100644 --- a/src/main/resources/fxml/vault_list_cell.fxml +++ b/src/main/resources/fxml/vault_list_cell.fxml @@ -23,7 +23,7 @@