diff --git a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 66b12a93e..a8f1213d1 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -3,11 +3,16 @@ package org.cryptomator.ui.fxapp; import dagger.Lazy; import javafx.application.Application; import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableSet; import javafx.stage.Stage; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.UiTheme; import org.cryptomator.common.vaults.Vault; +import org.cryptomator.jni.MacApplicationUiState; import org.cryptomator.jni.MacFunctions; import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.preferences.PreferencesComponent; @@ -31,6 +36,8 @@ public class FxApplication extends Application { private final UnlockComponent.Builder unlockWindowBuilder; private final QuitComponent.Builder quitWindowBuilder; private final Optional macFunctions; + private final ObservableSet visibleStages = FXCollections.observableSet(); + private final BooleanBinding hasVisibleStages = Bindings.isNotEmpty(visibleStages); @Inject FxApplication(Settings settings, Lazy mainWindow, Lazy preferencesWindow, UnlockComponent.Builder unlockWindowBuilder, QuitComponent.Builder quitWindowBuilder, Optional macFunctions) { @@ -46,6 +53,8 @@ public class FxApplication extends Application { LOG.trace("FxApplication.start()"); Platform.setImplicitExit(false); + hasVisibleStages.addListener(this::hasVisibleStagesChanged); + settings.theme().addListener(this::themeChanged); loadSelectedStyleSheet(settings.theme().get()); } @@ -55,30 +64,47 @@ public class FxApplication extends Application { throw new UnsupportedOperationException("Use start() instead."); } + private void addVisibleStage(Stage stage) { + visibleStages.add(stage); + stage.setOnHidden(evt -> visibleStages.remove(stage)); + } + + private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) { + if (newValue) { + macFunctions.map(MacFunctions::uiState).ifPresent(MacApplicationUiState::transformToForegroundApplication); + } else { + macFunctions.map(MacFunctions::uiState).ifPresent(MacApplicationUiState::transformToAgentApplication); + } + } + public void showPreferencesWindow() { Platform.runLater(() -> { - preferencesWindow.get().showPreferencesWindow(); + Stage stage = preferencesWindow.get().showPreferencesWindow(); + addVisibleStage(stage); LOG.debug("Showing Preferences"); }); } public void showMainWindow() { Platform.runLater(() -> { - mainWindow.get().showMainWindow(); + Stage stage = mainWindow.get().showMainWindow(); + addVisibleStage(stage); LOG.debug("Showing MainWindow"); }); } public void showUnlockWindow(Vault vault) { Platform.runLater(() -> { - unlockWindowBuilder.vault(vault).build().showUnlockWindow(); + Stage stage = unlockWindowBuilder.vault(vault).build().showUnlockWindow(); + addVisibleStage(stage); LOG.debug("Showing UnlockWindow for {}", vault.getDisplayableName()); }); } public void showQuitWindow(QuitResponse response) { Platform.runLater(() -> { - quitWindowBuilder.quitResponse(response).build().showQuitWindow(); + Stage stage = quitWindowBuilder.quitResponse(response).build().showQuitWindow(); + addVisibleStage(stage); LOG.debug("Showing QuitWindow"); }); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java index 8f505ae4d..65b06555d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowComponent.java @@ -22,11 +22,12 @@ public interface MainWindowComponent { @FxmlScene(FxmlFile.MAIN_WINDOW) Lazy scene(); - default void showMainWindow() { + default Stage showMainWindow() { Stage stage = window(); stage.setScene(scene().get()); stage.show(); stage.requestFocus(); + return stage; } @Subcomponent.Builder diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesComponent.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesComponent.java index 6e470a827..49408d639 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesComponent.java @@ -23,11 +23,12 @@ public interface PreferencesComponent { @FxmlScene(FxmlFile.PREFERENCES) Lazy scene(); - default void showPreferencesWindow() { + default Stage showPreferencesWindow() { Stage stage = window(); stage.setScene(scene().get()); stage.show(); stage.requestFocus(); + return stage; } @Subcomponent.Builder diff --git a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitComponent.java b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitComponent.java index c548b7c41..0b8f9a729 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitComponent.java @@ -25,11 +25,12 @@ public interface QuitComponent { @FxmlScene(FxmlFile.QUIT) Lazy scene(); - default void showQuitWindow() { + default Stage showQuitWindow() { Stage stage = window(); stage.setScene(scene().get()); stage.show(); stage.requestFocus(); + return stage; } @Subcomponent.Builder diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java index 0da5af2aa..a958ab532 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayIconController.java @@ -3,6 +3,9 @@ package org.cryptomator.ui.traymenu; import javafx.beans.Observable; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.settings.Settings; +import org.cryptomator.jni.JniException; +import org.cryptomator.jni.MacApplicationUiState; +import org.cryptomator.jni.MacFunctions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,6 +13,7 @@ import javax.inject.Inject; import java.awt.AWTException; import java.awt.SystemTray; import java.awt.TrayIcon; +import java.util.Optional; @TrayMenuScoped public class TrayIconController { @@ -20,15 +24,15 @@ public class TrayIconController { private final TrayImageFactory imageFactory; private final TrayMenuController trayMenuController; private final TrayIcon trayIcon; - // private final Optional macFunctions; + private final Optional macFunctions; @Inject - TrayIconController(Settings settings, TrayImageFactory imageFactory, TrayMenuController trayMenuController) { + TrayIconController(Settings settings, TrayImageFactory imageFactory, TrayMenuController trayMenuController, Optional macFunctions) { this.settings = settings; this.trayMenuController = trayMenuController; this.imageFactory = imageFactory; this.trayIcon = new TrayIcon(imageFactory.loadImage(), "Cryptomator", trayMenuController.getMenu()); -// this.macFunctions = macFunctions; + this.macFunctions = macFunctions; } public void initializeTrayIcon() { @@ -38,6 +42,8 @@ public class TrayIconController { if (SystemUtils.IS_OS_WINDOWS) { // TODO: test on windows: is this a double click? trayIcon.addActionListener(trayMenuController::showMainWindow); + } else if (SystemUtils.IS_OS_MAC) { + macFunctions.map(MacFunctions::uiState).ifPresent(JniException.ignore(MacApplicationUiState::transformToAgentApplication)); } try { diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java index 81ce7b4c5..7c0d9f704 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockComponent.java @@ -24,10 +24,11 @@ public interface UnlockComponent { @FxmlScene(FxmlFile.UNLOCK) Lazy scene(); - default void showUnlockWindow() { + default Stage showUnlockWindow() { Stage stage = window(); stage.setScene(scene().get()); stage.show(); + return stage; } @Subcomponent.Builder