mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Implement automatic theme detection
This commit is contained in:
parent
06cb31bf55
commit
441226855f
10 changed files with 144 additions and 50 deletions
|
@ -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"
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
103
app/src/main/java/io/xpipe/app/core/AppTheme.java
Normal file
103
app/src/main/java/io/xpipe/app/core/AppTheme.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
|
|
@ -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))),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
BIN
gradle/gradle_scripts/atlantafx-base-1.2.1.jar
Normal file
BIN
gradle/gradle_scripts/atlantafx-base-1.2.1.jar
Normal file
Binary file not shown.
BIN
gradle/gradle_scripts/atlantafx-styles-1.2.1.jar
Normal file
BIN
gradle/gradle_scripts/atlantafx-styles-1.2.1.jar
Normal file
Binary file not shown.
BIN
gradle/gradle_scripts/jSystemThemeDetector-3.8.jar
Normal file
BIN
gradle/gradle_scripts/jSystemThemeDetector-3.8.jar
Normal file
Binary file not shown.
19
gradle/gradle_scripts/versioncompare.gradle
Normal file
19
gradle/gradle_scripts/versioncompare.gradle
Normal 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;
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue