diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 450cd4ca7..0ea231bdd 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -14,11 +14,13 @@ import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider; import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider; import org.cryptomator.integrations.tray.TrayMenuController; +import org.cryptomator.integrations.uiappearance.UiAppearanceProvider; import org.cryptomator.logging.LogbackConfiguratorFactory; import org.cryptomator.networking.SSLContextProvider; import org.cryptomator.networking.SSLContextWithMacKeychain; import org.cryptomator.networking.SSLContextWithPKCS12TrustStore; import org.cryptomator.networking.SSLContextWithWindowsCertStore; +import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider; import org.cryptomator.ui.traymenu.AwtTrayMenuController; open module org.cryptomator.desktop { @@ -61,6 +63,7 @@ open module org.cryptomator.desktop { uses SSLContextProvider; uses org.cryptomator.event.NotificationHandler; + provides UiAppearanceProvider with JfxUiAppearanceProvider; provides TrayMenuController with AwtTrayMenuController; provides Configurator with LogbackConfiguratorFactory; provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore; diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java index ee187fd65..76cb0671e 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.application.Application; +import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; @@ -36,6 +37,11 @@ public class FxApplicationStyle { } public void initialize() { + appearanceProvider.ifPresent(service -> { + if(service instanceof JfxUiAppearanceProvider fxService) { + fxService.setJavaFXPlatform(Platform.getPreferences()); + } + }); settings.theme.addListener(this::appThemeChanged); loadSelectedStyleSheet(settings.theme.get()); } diff --git a/src/main/java/org/cryptomator/ui/fxapp/JfxUiAppearanceProvider.java b/src/main/java/org/cryptomator/ui/fxapp/JfxUiAppearanceProvider.java new file mode 100644 index 000000000..bf1545b4d --- /dev/null +++ b/src/main/java/org/cryptomator/ui/fxapp/JfxUiAppearanceProvider.java @@ -0,0 +1,75 @@ +package org.cryptomator.ui.fxapp; + +import org.cryptomator.integrations.common.DisplayName; +import org.cryptomator.integrations.common.OperatingSystem; +import org.cryptomator.integrations.common.Priority; +import org.cryptomator.integrations.uiappearance.Theme; +import org.cryptomator.integrations.uiappearance.UiAppearanceException; +import org.cryptomator.integrations.uiappearance.UiAppearanceListener; +import org.cryptomator.integrations.uiappearance.UiAppearanceProvider; + +import javafx.application.ColorScheme; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; + +@DisplayName("JavaFX Color Scheme switcher") +@OperatingSystem(OperatingSystem.Value.LINUX) +@OperatingSystem(OperatingSystem.Value.WINDOWS) +@OperatingSystem(OperatingSystem.Value.MAC) +@Priority(1050) +public class JfxUiAppearanceProvider implements UiAppearanceProvider { + + final ConcurrentHashMap> uiAppearanceListeners = new ConcurrentHashMap<>(); + final AtomicReference fxPreferencesContainer = new AtomicReference<>(); + + public void setJavaFXPlatform(Platform.Preferences preferences) { + fxPreferencesContainer.set(preferences); + } + + @Override + public Theme getSystemTheme() { + var pref = fxPreferencesContainer.get(); + if (pref != null) { + return switch (pref.getColorScheme()) { + case DARK -> Theme.DARK; + case LIGHT -> Theme.LIGHT; + }; + } + return Theme.LIGHT; + } + + @Override + public void adjustToTheme(Theme theme) { + //no-op + } + + @Override + public void addListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException { + var pref = fxPreferencesContainer.get(); + if (pref != null) { + var fxChangeListener = (ChangeListener) (_, _, newScheme) -> { + var newTheme = switch (newScheme) { + case DARK -> Theme.DARK; + case LIGHT -> Theme.LIGHT; + }; + uiAppearanceListener.systemAppearanceChanged(newTheme); + }; + uiAppearanceListeners.compute(uiAppearanceListener, (k, v) -> { + pref.colorSchemeProperty().addListener(fxChangeListener); + return fxChangeListener; + }); + } + } + + @Override + public void removeListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException { + var pref = fxPreferencesContainer.get(); + var fxChangeListener = uiAppearanceListeners.remove(uiAppearanceListener); + if (pref != null && fxChangeListener != null) { + pref.colorSchemeProperty().removeListener(fxChangeListener); + } + } + +}