added tray icon, refactored dagger component graph:

singleton -> tray -> fxapplication -> mainwindow
This commit is contained in:
Sebastian Stenzel
2019-07-29 12:38:24 +02:00
parent 4931e6707a
commit 10e842e457
35 changed files with 340 additions and 56 deletions

View File

@@ -5,11 +5,10 @@
*******************************************************************************/
package org.cryptomator.launcher;
import javafx.application.Platform;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.logging.LoggerConfiguration;
import org.cryptomator.ui.FxApplication;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,15 +33,17 @@ public class Cryptomator {
private final Optional<String> applicationVersion;
private final CountDownLatch shutdownLatch;
private final CleanShutdownPerformer shutdownPerformer;
private final TrayMenuComponent.Builder trayComponent;
@Inject
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional<String> applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, CleanShutdownPerformer shutdownPerformer) {
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional<String> applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, CleanShutdownPerformer shutdownPerformer, TrayMenuComponent.Builder trayComponent) {
this.logConfig = logConfig;
this.debugMode = debugMode;
this.ipcFactory = ipcFactory;
this.applicationVersion = applicationVersion;
this.shutdownLatch = shutdownLatch;
this.shutdownPerformer = shutdownPerformer;
this.trayComponent = trayComponent;
}
public static void main(String[] args) {
@@ -89,7 +90,7 @@ public class Cryptomator {
private int runGuiApplication() {
try {
shutdownPerformer.registerShutdownHook();
CRYPTOMATOR_COMPONENT.fxApplicationComponent().start();
trayComponent.build().addIconToSystemTray();
shutdownLatch.await();
LOG.info("UI shut down");
return 0;

View File

@@ -3,7 +3,7 @@ package org.cryptomator.launcher;
import dagger.Component;
import org.cryptomator.common.CommonsModule;
import org.cryptomator.logging.LoggerModule;
import org.cryptomator.ui.FxApplicationComponent;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Singleton;
@@ -13,6 +13,4 @@ public interface CryptomatorComponent {
Cryptomator application();
FxApplicationComponent fxApplicationComponent();
}

View File

@@ -4,8 +4,10 @@ import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.SettingsProvider;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.UiModule;
import org.cryptomator.ui.model.AppLaunchEvent;
import org.cryptomator.ui.model.VaultComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -15,7 +17,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
@Module
@Module(includes = {UiModule.class}, subcomponents = {VaultComponent.class, TrayMenuComponent.class})
class CryptomatorModule {
@Provides

View File

@@ -16,6 +16,7 @@ import org.cryptomator.common.settings.Settings;
import org.cryptomator.jni.JniException;
import org.cryptomator.jni.MacApplicationUiState;
import org.cryptomator.jni.MacFunctions;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.ui.l10n.Localization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -16,10 +16,11 @@ import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.frontend.webdav.WebDavServer;
import org.cryptomator.keychain.KeychainModule;
import org.cryptomator.ui.model.VaultComponent;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.fxmisc.easybind.EasyBind;
import javax.inject.Named;
import javax.inject.Singleton;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -27,13 +28,15 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@Module(includes = {KeychainModule.class}, subcomponents = {VaultComponent.class})
// TODO move to common...
@Deprecated(forRemoval = true, since = "1.5.0")
@Module(includes = {KeychainModule.class})
public class UiModule {
private static final int NUM_SCHEDULER_THREADS = 4;
@Provides
@FxApplicationScoped
@Singleton
ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
final AtomicInteger threadNumber = new AtomicInteger(1);
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
@@ -48,7 +51,7 @@ public class UiModule {
// TODO @Binds abstract ExecutorService bindExecutorService(ScheduledExecutorService executor); ?
@Provides
@FxApplicationScoped
@Singleton
ExecutorService provideExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
final AtomicInteger threadNumber = new AtomicInteger(1);
ExecutorService executorService = Executors.newCachedThreadPool(r -> {
@@ -62,7 +65,7 @@ public class UiModule {
}
@Provides
@FxApplicationScoped
@Singleton
Binding<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
return Bindings.createObjectBinding(() -> {
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
@@ -71,7 +74,7 @@ public class UiModule {
}
@Provides
@FxApplicationScoped
@Singleton
WebDavServer provideWebDavServer(Binding<InetSocketAddress> serverSocketAddressBinding) {
WebDavServer server = WebDavServer.create();
// no need to unsubscribe eventually, because server is a singleton

View File

@@ -44,7 +44,7 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.ui.ExitUtil;
import org.cryptomator.ui.controls.DirectoryListCell;

View File

@@ -8,7 +8,7 @@ package org.cryptomator.ui.controllers;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.layout.VBox;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import javax.inject.Inject;

View File

@@ -24,7 +24,7 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.util.StringConverter;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.ui.l10n.Localization;

View File

@@ -6,7 +6,7 @@
package org.cryptomator.ui.controllers;
import javafx.fxml.FXMLLoader;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.ui.l10n.Localization;
import javax.inject.Inject;

View File

@@ -23,7 +23,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.VBox;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.l10n.Localization;
import org.cryptomator.ui.common.Tasks;

View File

@@ -1,4 +1,4 @@
package org.cryptomator.ui;
package org.cryptomator.ui.fxapp;
import javafx.application.Application;
import javafx.application.Platform;
@@ -38,6 +38,7 @@ public class FxApplication extends Application {
public void start() {
LOG.trace("FxApplication.start()");
Platform.setImplicitExit(false);
settings.theme().addListener(this::themeChanged);
loadSelectedStyleSheet(settings.theme().get());
@@ -45,8 +46,6 @@ public class FxApplication extends Application {
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_PREFERENCES)) {
Desktop.getDesktop().setPreferencesHandler(this::handlePreferences);
}
mainWindow.build().showMainWindow();
}
@Override
@@ -55,11 +54,19 @@ public class FxApplication extends Application {
}
private void handlePreferences(PreferencesEvent preferencesEvent) {
Platform.runLater(this::showPreferencesWindow);
showPreferencesWindow();
}
public void showPreferencesWindow() {
preferencesWindow.build().showPreferencesWindow();
Platform.runLater(() -> {
preferencesWindow.build().showPreferencesWindow();
});
}
public void showMainWindow() {
Platform.runLater(() -> {
mainWindow.build().showMainWindow();
});
}
private void themeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {

View File

@@ -3,22 +3,20 @@
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.ui;
package org.cryptomator.ui.fxapp;
import dagger.Subcomponent;
import javafx.application.Platform;
@FxApplicationScoped
@Subcomponent(modules = FxApplicationModule.class)
public interface FxApplicationComponent {
FxApplication application();
default void start() {
Platform.startup(() -> {
assert Platform.isFxApplicationThread();
application().start();
});
@Subcomponent.Builder
interface Builder {
FxApplicationComponent build();
}
}

View File

@@ -3,7 +3,7 @@
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.ui;
package org.cryptomator.ui.fxapp;
import dagger.Binds;
import dagger.Module;
@@ -12,6 +12,7 @@ import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import org.cryptomator.ui.UiModule;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.model.VaultList;
@@ -19,7 +20,7 @@ import org.cryptomator.ui.preferences.PreferencesComponent;
import java.util.ResourceBundle;
@Module(includes = {UiModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class})
@Module(subcomponents = {MainWindowComponent.class, PreferencesComponent.class})
abstract class FxApplicationModule {
@Binds

View File

@@ -1,4 +1,4 @@
package org.cryptomator.ui;
package org.cryptomator.ui.fxapp;
import javax.inject.Scope;
import java.lang.annotation.Documented;

View File

@@ -7,7 +7,7 @@ package org.cryptomator.ui.l10n;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -8,6 +8,8 @@ package org.cryptomator.ui.mainwindow;
import dagger.Subcomponent;
import javafx.stage.Stage;
import org.cryptomator.ui.common.FXMLLoaderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@MainWindowScoped
@Subcomponent(modules = {MainWindowModule.class})

View File

@@ -4,7 +4,7 @@ import javafx.fxml.FXML;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
import org.cryptomator.ui.FxApplication;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.common.FxController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -18,7 +18,6 @@ public class MainWindowController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(MainWindowController.class);
private final CountDownLatch shutdownLatch;
private final Stage window;
private final FxApplication application;
public HBox titleBar;
@@ -27,8 +26,7 @@ public class MainWindowController implements FxController {
private double yOffset;
@Inject
public MainWindowController(@Named("shutdownLatch") CountDownLatch shutdownLatch, @MainWindow Stage window, FxApplication application) {
this.shutdownLatch = shutdownLatch;
public MainWindowController(@MainWindow Stage window, FxApplication application) {
this.window = window;
this.application = application;
}
@@ -54,8 +52,6 @@ public class MainWindowController implements FxController {
@FXML
public void close() {
window.close();
LOG.info("closed...");
shutdownLatch.countDown();
}
@FXML

View File

@@ -5,7 +5,7 @@
*******************************************************************************/
package org.cryptomator.ui.model;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.keychain.KeychainAccess;
import org.slf4j.Logger;

View File

@@ -8,14 +8,14 @@
*******************************************************************************/
package org.cryptomator.ui.model;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.common.settings.VaultSettings;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@FxApplicationScoped
@Singleton
public class VaultFactory {
private final VaultComponent.Builder vaultComponentBuilder;

View File

@@ -9,15 +9,15 @@ import com.google.common.collect.Lists;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.TransformationList;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.stream.IntStream;
@FxApplicationScoped
@Singleton
public class VaultList extends TransformationList<Vault, VaultSettings> {
private final VaultFactory vaultFactory;

View File

@@ -6,11 +6,12 @@
package org.cryptomator.ui.model;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Set;
@@ -19,7 +20,7 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
@FxApplicationScoped
@Singleton
public final class WindowsDriveLetters {
private static final Logger LOG = LoggerFactory.getLogger(WindowsDriveLetters.class);

View File

@@ -5,7 +5,7 @@
*******************************************************************************/
package org.cryptomator.ui.model.upgrade;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.ui.model.Vault;
import javax.inject.Inject;

View File

@@ -7,7 +7,7 @@ package org.cryptomator.ui.model.upgrade;
import javafx.application.Platform;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.cryptolib.Cryptors;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.ui.l10n.Localization;

View File

@@ -7,7 +7,7 @@ package org.cryptomator.ui.model.upgrade;
import com.google.common.io.BaseEncoding;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.cryptolib.Cryptors;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.common.MessageDigestSupplier;

View File

@@ -5,7 +5,7 @@
*******************************************************************************/
package org.cryptomator.ui.model.upgrade;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.cryptolib.Cryptors;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.FileHeader;

View File

@@ -5,7 +5,7 @@
*******************************************************************************/
package org.cryptomator.ui.model.upgrade;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.cryptofs.migration.Migrators;
import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException;
import org.cryptomator.cryptolib.Cryptors;

View File

@@ -0,0 +1,46 @@
package org.cryptomator.ui.traymenu;
import javafx.application.Platform;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Inject;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@TrayMenuScoped
public class FxApplicationStarter {
private final CompletableFuture<FxApplication> future;
private final FxApplicationComponent.Builder fxAppComponent;
@Inject
public FxApplicationStarter(FxApplicationComponent.Builder fxAppComponent) {
this.fxAppComponent = fxAppComponent;
this.future = new CompletableFuture<>();
}
public synchronized FxApplication get() {
if (!future.isDone()) {
start();
}
try {
return future.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Interrupted while waiting for FxApplication startup.", e);
} catch (ExecutionException e) {
throw new IllegalStateException("FxApplication startup failed.", e);
}
}
private void start() {
Platform.startup(() -> {
assert Platform.isFxApplicationThread();
FxApplication app = fxAppComponent.build().application();
app.start();
future.complete(app);
});
}
}

View File

@@ -0,0 +1,49 @@
package org.cryptomator.ui.traymenu;
import javafx.beans.Observable;
import org.cryptomator.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.awt.AWTException;
import java.awt.SystemTray;
import java.awt.TrayIcon;
@TrayMenuScoped
public class TrayIconController {
private static final Logger LOG = LoggerFactory.getLogger(TrayIconController.class);
private final Settings settings;
private final TrayImageFactory imageFactory;
private final TrayMenuController trayMenuController;
private final TrayIcon trayIcon;
@Inject
TrayIconController(Settings settings, TrayImageFactory imageFactory, TrayMenuController trayMenuController) {
this.settings = settings;
this.trayMenuController = trayMenuController;
this.imageFactory = imageFactory;
this.trayIcon = new TrayIcon(imageFactory.loadImage(), "Cryptomator", trayMenuController.getMenu());
}
public void initializeTrayIcon() {
settings.theme().addListener(this::themeChanged);
trayMenuController.initTrayMenu();
try {
SystemTray.getSystemTray().add(trayIcon);
LOG.info("initialized tray icon");
} catch (AWTException e) {
LOG.error("Error adding tray icon", e);
}
}
private void themeChanged(@SuppressWarnings("unused") Observable observable) {
trayIcon.setImage(imageFactory.loadImage());
}
}

View File

@@ -0,0 +1,38 @@
package org.cryptomator.ui.traymenu;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.Settings;
import javax.inject.Inject;
import java.awt.Image;
import java.awt.Toolkit;
@TrayMenuScoped
class TrayImageFactory {
private final Settings settings;
@Inject
TrayImageFactory(Settings settings) {
this.settings = settings;
}
public Image loadImage() {
String resourceName = SystemUtils.IS_OS_MAC_OSX ? getMacResourceName() : getWinOrLinuxResourceName();
return Toolkit.getDefaultToolkit().getImage(getClass().getResource(resourceName));
}
private String getMacResourceName() {
switch (settings.theme().get()) {
case DARK:
return "/tray_icon_mac_white.png";
default:
return "/tray_icon_mac_black.png";
}
}
private String getWinOrLinuxResourceName() {
return "/tray_icon.png";
}
}

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.ui.traymenu;
import dagger.Subcomponent;
import java.awt.SystemTray;
@TrayMenuScoped
@Subcomponent(modules = {TrayMenuModule.class})
public interface TrayMenuComponent {
TrayIconController trayIconController();
default void addIconToSystemTray() {
if (SystemTray.isSupported()) {
trayIconController().initializeTrayIcon();
} else {
// TODO what?
}
}
@Subcomponent.Builder
interface Builder {
TrayMenuComponent build();
}
}

View File

@@ -0,0 +1,60 @@
package org.cryptomator.ui.traymenu;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.util.concurrent.CountDownLatch;
@TrayMenuScoped
public class TrayMenuController {
private static final Logger LOG = LoggerFactory.getLogger(TrayMenuController.class);
private final FxApplicationStarter fxApplicationStarter;
private final CountDownLatch shutdownLatch;
private final PopupMenu menu;
@Inject
TrayMenuController(FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch) {
this.fxApplicationStarter = fxApplicationStarter;
this.shutdownLatch = shutdownLatch;
this.menu = new PopupMenu();
}
public PopupMenu getMenu() {
return menu;
}
public void initTrayMenu() {
// TODO add listeners
rebuildMenu();
}
private void rebuildMenu() {
MenuItem showMainWindowItem = new MenuItem("TODO show");
showMainWindowItem.addActionListener(this::showMainWindow);
menu.add(showMainWindowItem);
menu.addSeparator();
// foreach vault: add submenu
menu.addSeparator();
MenuItem quitApplicationItem = new MenuItem("TODO quit");
quitApplicationItem.addActionListener(this::quitApplication);
menu.add(quitApplicationItem);
}
private void showMainWindow(ActionEvent actionEvent) {
fxApplicationStarter.get().showMainWindow();
}
private void quitApplication(ActionEvent actionEvent) {
shutdownLatch.countDown();
}
}

View File

@@ -0,0 +1,9 @@
package org.cryptomator.ui.traymenu;
import dagger.Module;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
@Module(subcomponents = {FxApplicationComponent.class})
abstract class TrayMenuModule {
}

View File

@@ -0,0 +1,13 @@
package org.cryptomator.ui.traymenu;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface TrayMenuScoped {
}

View File

@@ -15,7 +15,7 @@ import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.paint.Color;
import org.cryptomator.ui.FxApplicationScoped;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.cryptomator.ui.l10n.Localization;
import javax.inject.Inject;

View File

@@ -0,0 +1,27 @@
package org.cryptomator.ui.controls;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
public class Foo {
@Test
public void foo() {
StringProperty str = new SimpleStringProperty("foo");
CountDownLatch done = new CountDownLatch(1);
str.addListener(observable -> {
Assertions.assertEquals("bar", str.get());
done.countDown();
});
str.set("bar");
Assertions.assertTimeoutPreemptively(Duration.ofMillis(100), () -> {
done.await();
});
}
}