Implement automatic theme detection

This commit is contained in:
crschnick 2023-05-15 18:50:31 +00:00
parent 06cb31bf55
commit 441226855f
10 changed files with 144 additions and 50 deletions

View file

@ -24,6 +24,7 @@ apply from: "$rootDir/gradle/gradle_scripts/lombok.gradle"
apply from: "$projectDir/gradle_scripts/github-api.gradle" apply from: "$projectDir/gradle_scripts/github-api.gradle"
apply from: "$projectDir/gradle_scripts/flexmark.gradle" apply from: "$projectDir/gradle_scripts/flexmark.gradle"
apply from: "$rootDir/gradle/gradle_scripts/picocli.gradle" apply from: "$rootDir/gradle/gradle_scripts/picocli.gradle"
apply from: "$rootDir/gradle/gradle_scripts/versioncompare.gradle"
configurations { configurations {
implementation.extendsFrom(dep) implementation.extendsFrom(dep)
@ -38,8 +39,8 @@ dependencies {
compileOnly 'org.junit.jupiter:junit-jupiter-api:5.9.0' compileOnly 'org.junit.jupiter:junit-jupiter-api:5.9.0'
compileOnly 'org.junit.jupiter:junit-jupiter-params:5.9.0' compileOnly 'org.junit.jupiter:junit-jupiter-params:5.9.0'
implementation 'net.java.dev.jna:jna-jpms:5.12.1' implementation 'net.java.dev.jna:jna-jpms:5.13.0'
implementation 'net.java.dev.jna:jna-platform-jpms:5.12.1' implementation 'net.java.dev.jna:jna-platform-jpms:5.13.0'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "2.13.0" implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "2.13.0"
implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-parameter-names', version: "2.13.0" implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-parameter-names', version: "2.13.0"
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: "2.13.0" implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: "2.13.0"
@ -56,7 +57,14 @@ dependencies {
implementation 'com.jfoenix:jfoenix:9.0.10' implementation 'com.jfoenix:jfoenix:9.0.10'
implementation 'org.controlsfx:controlsfx:11.1.1' implementation 'org.controlsfx:controlsfx:11.1.1'
implementation 'net.synedra:validatorfx:0.3.1' implementation 'net.synedra:validatorfx:0.3.1'
implementation 'io.github.mkpaz:atlantafx-base:1.2.0' implementation name: 'atlantafx-base-1.2.1'
implementation name: 'atlantafx-styles-1.2.1'
implementation name: 'jSystemThemeDetector-3.8'
implementation group: 'com.github.oshi', name: 'oshi-core-java11', version: '6.4.2'
implementation 'org.jetbrains:annotations:24.0.1'
implementation ('de.jangassen:jfa:1.2.0') {
exclude group: 'net.java.dev.jna', module: 'jna'
}
} }
apply from: "$rootDir/gradle/gradle_scripts/junit.gradle" apply from: "$rootDir/gradle/gradle_scripts/junit.gradle"

View file

@ -1,17 +1,9 @@
package io.xpipe.app.core; package io.xpipe.app.core;
import atlantafx.base.theme.NordDark;
import atlantafx.base.theme.NordLight;
import atlantafx.base.theme.PrimerDark;
import atlantafx.base.theme.PrimerLight;
import io.xpipe.app.ext.PrefsChoiceValue;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import javafx.application.Application;
import javafx.scene.Scene; import javafx.scene.Scene;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
@ -36,9 +28,6 @@ public class AppStyle {
loadStylesheets(); loadStylesheets();
if (AppPrefs.get() != null) { if (AppPrefs.get() != null) {
AppPrefs.get().theme.addListener((c, o, n) -> {
changeTheme(o, n);
});
AppPrefs.get().useSystemFont.addListener((c, o, n) -> { AppPrefs.get().useSystemFont.addListener((c, o, n) -> {
changeFontUsage(n); changeFontUsage(n);
}); });
@ -78,12 +67,6 @@ public class AppStyle {
} }
} }
private static void changeTheme(Theme oldTheme, Theme newTheme) {
scenes.forEach(scene -> {
Application.setUserAgentStylesheet(newTheme.getTheme().getUserAgentStylesheet());
});
}
private static void changeFontUsage(boolean use) { private static void changeFontUsage(boolean use) {
if (!use) { if (!use) {
scenes.forEach(scene -> { scenes.forEach(scene -> {
@ -106,10 +89,6 @@ public class AppStyle {
} }
public static void addStylesheets(Scene scene) { public static void addStylesheets(Scene scene) {
var t = AppPrefs.get() != null ? AppPrefs.get().theme.getValue() : Theme.LIGHT;
Application.setUserAgentStylesheet(t.getTheme().getUserAgentStylesheet());
TrackEvent.debug("Set theme " + t.getId() + " for scene");
if (AppPrefs.get() != null && !AppPrefs.get().useSystemFont.get()) { if (AppPrefs.get() != null && !AppPrefs.get().useSystemFont.get()) {
scene.getStylesheets().add(FONT_CONTENTS); scene.getStylesheets().add(FONT_CONTENTS);
} }
@ -122,21 +101,4 @@ public class AppStyle {
scenes.add(scene); scenes.add(scene);
} }
@AllArgsConstructor
@Getter
public enum Theme implements PrefsChoiceValue {
LIGHT("light", new PrimerLight()),
DARK("dark", new PrimerDark()),
NORD_LIGHT("nordLight", new NordLight()),
NORD_DARK("nordDark", new NordDark());
// DARK("dark");
private final String id;
private final atlantafx.base.theme.Theme theme;
@Override
public String toTranslatedString() {
return theme.getName();
}
}
} }

View file

@ -0,0 +1,103 @@
package io.xpipe.app.core;
import atlantafx.base.theme.*;
import com.jthemedetecor.OsThemeDetector;
import io.xpipe.app.ext.PrefsChoiceValue;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.process.OsType;
import javafx.application.Application;
import lombok.AllArgsConstructor;
import lombok.Getter;
public class AppTheme {
public static void init() {
if (AppPrefs.get() == null) {
return;
}
OsThemeDetector detector = OsThemeDetector.getDetector();
if (AppPrefs.get().theme.getValue() == null) {
try {
setDefault(detector.isDark());
} catch (Throwable ex) {
ErrorEvent.fromThrowable(ex).omit().handle();
setDefault(false);
}
}
var t = AppPrefs.get().theme.getValue();
Application.setUserAgentStylesheet(t.getTheme().getUserAgentStylesheet());
TrackEvent.debug("Set theme " + t.getId() + " for scene");
detector.registerListener(dark -> {
PlatformThread.runLaterIfNeeded(() -> {
if (dark && !AppPrefs.get().theme.getValue().getTheme().isDarkMode()) {
AppPrefs.get().theme.setValue(Theme.getDefaultDarkTheme());
}
if (!dark && AppPrefs.get().theme.getValue().getTheme().isDarkMode()) {
AppPrefs.get().theme.setValue(Theme.getDefaultLightTheme());
}
});
});
AppPrefs.get().theme.addListener((c, o, n) -> {
changeTheme(n);
});
}
private static void setDefault(boolean dark) {
if (dark) {
AppPrefs.get().theme.setValue(Theme.getDefaultDarkTheme());
} else {
AppPrefs.get().theme.setValue(Theme.getDefaultLightTheme());
}
}
private static void changeTheme(Theme newTheme) {
PlatformThread.runLaterIfNeeded(() -> {
Application.setUserAgentStylesheet(newTheme.getTheme().getUserAgentStylesheet());
TrackEvent.debug("Set theme " + newTheme.getId() + " for scene");
});
}
@AllArgsConstructor
@Getter
public enum Theme implements PrefsChoiceValue {
PRIMER_LIGHT("light", new PrimerLight()),
PRIMER_DARK("dark", new PrimerDark()),
NORD_LIGHT("nordLight", new NordLight()),
NORD_DARK("nordDark", new NordDark()),
CUPERTINO_LIGHT("cupertinoLight", new CupertinoLight()),
CUPERTINO_DARK("cupertinoDark", new CupertinoDark()),
DRACULA("dracula", new Dracula());
static Theme getDefaultLightTheme() {
return switch (OsType.getLocal()) {
case OsType.Windows windows -> PRIMER_LIGHT;
case OsType.Linux linux -> NORD_LIGHT;
case OsType.MacOs macOs -> CUPERTINO_LIGHT;
};
}
static Theme getDefaultDarkTheme() {
return switch (OsType.getLocal()) {
case OsType.Windows windows -> PRIMER_DARK;
case OsType.Linux linux -> NORD_DARK;
case OsType.MacOs macOs -> CUPERTINO_DARK;
};
}
private final String id;
private final atlantafx.base.theme.Theme theme;
@Override
public String toTranslatedString() {
return theme.getName();
}
}
}

View file

@ -60,6 +60,7 @@ public abstract class PlatformMode extends OperationMode {
TrackEvent.info("mode", "Platform mode initial setup"); TrackEvent.info("mode", "Platform mode initial setup");
AppI18n.init(); AppI18n.init();
AppFont.loadFonts(); AppFont.loadFonts();
AppTheme.init();
AppStyle.init(); AppStyle.init();
AppImages.init(); AppImages.init();
TrackEvent.info("mode", "Finished essential component initialization before platform"); TrackEvent.info("mode", "Finished essential component initialization before platform");

View file

@ -11,7 +11,7 @@ import com.dlsc.preferencesfx.util.VisibilityProperty;
import io.xpipe.app.comp.base.ButtonComp; import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppProperties; import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.AppStyle; import io.xpipe.app.core.AppTheme;
import io.xpipe.app.ext.PrefsChoiceValue; import io.xpipe.app.ext.PrefsChoiceValue;
import io.xpipe.app.ext.PrefsHandler; import io.xpipe.app.ext.PrefsHandler;
import io.xpipe.app.ext.PrefsProvider; import io.xpipe.app.ext.PrefsProvider;
@ -68,8 +68,8 @@ public class AppPrefs {
private static AppPrefs INSTANCE; private static AppPrefs INSTANCE;
private final SimpleListProperty<SupportedLocale> languageList = private final SimpleListProperty<SupportedLocale> languageList =
new SimpleListProperty<>(FXCollections.observableArrayList(Arrays.asList(SupportedLocale.values()))); new SimpleListProperty<>(FXCollections.observableArrayList(Arrays.asList(SupportedLocale.values())));
private final SimpleListProperty<AppStyle.Theme> themeList = private final SimpleListProperty<AppTheme.Theme> themeList =
new SimpleListProperty<>(FXCollections.observableArrayList(Arrays.asList(AppStyle.Theme.values()))); new SimpleListProperty<>(FXCollections.observableArrayList(Arrays.asList(AppTheme.Theme.values())));
private final SimpleListProperty<CloseBehaviour> closeBehaviourList = new SimpleListProperty<>( private final SimpleListProperty<CloseBehaviour> closeBehaviourList = new SimpleListProperty<>(
FXCollections.observableArrayList(PrefsChoiceValue.getSupported(CloseBehaviour.class))); FXCollections.observableArrayList(PrefsChoiceValue.getSupported(CloseBehaviour.class)));
private final SimpleListProperty<ExternalEditorType> externalEditorList = new SimpleListProperty<>( private final SimpleListProperty<ExternalEditorType> externalEditorList = new SimpleListProperty<>(
@ -90,11 +90,10 @@ public class AppPrefs {
languageList, languageInternal) languageList, languageInternal)
.render(() -> new TranslatableComboBoxControl<>()); .render(() -> new TranslatableComboBoxControl<>());
private final ObjectProperty<AppStyle.Theme> themeInternal = public final ObjectProperty<AppTheme.Theme> theme =
typed(new SimpleObjectProperty<>(AppStyle.Theme.LIGHT), AppStyle.Theme.class); typed(new SimpleObjectProperty<>(), AppTheme.Theme.class);
public final ReadOnlyProperty<AppStyle.Theme> theme = themeInternal; private final SingleSelectionField<AppTheme.Theme> themeControl =
private final SingleSelectionField<AppStyle.Theme> themeControl = Field.ofSingleSelectionType(themeList, theme).render(() -> new TranslatableComboBoxControl<>());
Field.ofSingleSelectionType(themeList, themeInternal).render(() -> new TranslatableComboBoxControl<>());
private final BooleanProperty useSystemFontInternal = typed(new SimpleBooleanProperty(true), Boolean.class); private final BooleanProperty useSystemFontInternal = typed(new SimpleBooleanProperty(true), Boolean.class);
public final ReadOnlyBooleanProperty useSystemFont = useSystemFontInternal; public final ReadOnlyBooleanProperty useSystemFont = useSystemFontInternal;
private final IntegerProperty tooltipDelayInternal = typed(new SimpleIntegerProperty(1000), Integer.class); private final IntegerProperty tooltipDelayInternal = typed(new SimpleIntegerProperty(1000), Integer.class);
@ -512,7 +511,7 @@ public class AppPrefs {
Group.of( Group.of(
"uiOptions", "uiOptions",
Setting.of("language", languageControl, languageInternal), Setting.of("language", languageControl, languageInternal),
Setting.of("theme", themeControl, themeInternal), Setting.of("theme", themeControl, theme),
Setting.of("useSystemFont", useSystemFontInternal), Setting.of("useSystemFont", useSystemFontInternal),
Setting.of("tooltipDelay", tooltipDelayInternal, tooltipDelayMin, tooltipDelayMax)), Setting.of("tooltipDelay", tooltipDelayInternal, tooltipDelayMin, tooltipDelayMax)),
Group.of("windowOptions", Setting.of("saveWindowLocation", saveWindowLocationInternal))), Group.of("windowOptions", Setting.of("saveWindowLocation", saveWindowLocationInternal))),

View file

@ -83,6 +83,8 @@ open module io.xpipe.app {
requires java.management; requires java.management;
requires jdk.management; requires jdk.management;
requires jdk.management.agent; requires jdk.management.agent;
requires com.jthemedetector;
requires versioncompare;
// Required by extensions // Required by extensions
requires commons.math3; requires commons.math3;

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,19 @@
dependencies {
implementation files("$buildDir/generated-modules/versioncompare-1.5.0.jar")
}
addDependenciesModuleInfo {
overwriteExistingFiles = true
jdepsExtraArgs = ['-q']
outputDirectory = file("$buildDir/generated-modules")
modules {
module {
artifact "io.github.g00fy2:versioncompare:1.5.0"
moduleInfoSource = '''
module versioncompare {
exports io.github.g00fy2.versioncompare;
}
'''
}
}
}