diff --git a/sampler/src/main/java/atlantafx/sampler/Launcher.java b/sampler/src/main/java/atlantafx/sampler/Launcher.java index ab91fab..07387a3 100755 --- a/sampler/src/main/java/atlantafx/sampler/Launcher.java +++ b/sampler/src/main/java/atlantafx/sampler/Launcher.java @@ -51,7 +51,8 @@ public class Launcher extends Application { var scene = new Scene(root, 1200, 768); var tm = ThemeManager.getInstance(); - tm.setTheme(scene, tm.getAvailableThemes().get(0)); + tm.setScene(scene); + tm.setTheme(tm.getAvailableThemes().get(0)); if (IS_DEV_MODE) { startCssFX(scene); } scene.getStylesheets().addAll( diff --git a/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java b/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java index ff9765c..988c21d 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/general/ThemePage.java @@ -62,7 +62,9 @@ public class ThemePage extends AbstractPage { selector.getItems().setAll(manager.getAvailableThemes()); selector.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> { if (val != null && getScene() != null) { - ThemeManager.getInstance().setTheme(getScene(), val); + var tm = ThemeManager.getInstance(); + tm.setTheme(val); + tm.reloadCustomCSS(); colorPalette.updateColorInfo(Duration.ofSeconds(1)); } }); diff --git a/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java b/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java index 843d7ed..44c1275 100755 --- a/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/general/TypographyPage.java @@ -69,7 +69,9 @@ public class TypographyPage extends AbstractPage { spinner.valueProperty().addListener((obs, old, val) -> { if (val != null && getScene() != null) { - ThemeManager.getInstance().setFontSize(getScene(), val); + var tm = ThemeManager.getInstance(); + tm.setFontSize(val); + tm.reloadCustomCSS(); updateFontInfo(Duration.ofMillis(1000)); } }); @@ -122,10 +124,10 @@ public class TypographyPage extends AbstractPage { private SampleBlock fontWeightSample() { var box = new HBox(10, - text("Bold", TEXT_BOLD), - text("Bolder", TEXT_BOLDER), - text("Normal", TEXT_NORMAL), - text("Lighter", TEXT_LIGHTER) + text("Bold", TEXT_BOLD), + text("Bolder", TEXT_BOLDER), + text("Normal", TEXT_NORMAL), + text("Lighter", TEXT_LIGHTER) ); box.setAlignment(Pos.BASELINE_LEFT); @@ -134,10 +136,10 @@ public class TypographyPage extends AbstractPage { private SampleBlock fontStyleSample() { var box = new HBox(10, - text("Italic", TEXT_ITALIC), - text("Oblique", TEXT_OBLIQUE), - text("Underlined", TEXT_UNDERLINED), - text("Strikethrough", TEXT_STRIKETHROUGH) + text("Italic", TEXT_ITALIC), + text("Oblique", TEXT_OBLIQUE), + text("Underlined", TEXT_UNDERLINED), + text("Strikethrough", TEXT_STRIKETHROUGH) ); box.setAlignment(Pos.BASELINE_LEFT); @@ -146,10 +148,10 @@ public class TypographyPage extends AbstractPage { private SampleBlock textColorSample() { var box = new HBox(10, - text("Accent", TEXT, ACCENT), - text("Success", TEXT, SUCCESS), - text("Warning", TEXT, WARNING), - text("Danger", TEXT, DANGER) + text("Accent", TEXT, ACCENT), + text("Success", TEXT, SUCCESS), + text("Warning", TEXT, WARNING), + text("Danger", TEXT, DANGER) ); box.setAlignment(Pos.BASELINE_LEFT); @@ -164,9 +166,9 @@ public class TypographyPage extends AbstractPage { linkVisited.setMnemonicParsing(true); var box = new HBox(10, - linkNormal, - linkVisited, - hyperlink("Disabled", false, true) + linkNormal, + linkVisited, + hyperlink("Disabled", false, true) ); box.setAlignment(Pos.BASELINE_LEFT); diff --git a/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java b/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java index 934ad5a..81e61c9 100644 --- a/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java +++ b/sampler/src/main/java/atlantafx/sampler/theme/ThemeManager.java @@ -7,6 +7,7 @@ import atlantafx.base.theme.Theme; import atlantafx.sampler.Launcher; import atlantafx.sampler.Resources; import javafx.application.Application; +import javafx.css.PseudoClass; import javafx.scene.Scene; import java.net.URI; @@ -17,9 +18,25 @@ import static java.nio.charset.StandardCharsets.UTF_8; public final class ThemeManager { private static final String DUMMY_STYLESHEET = Resources.getResource("assets/styles/empty.css").toString(); + private static final PseudoClass USER_CUSTOM = PseudoClass.getPseudoClass("user-custom"); + + // KEY | VALUE + // -fx-property | value; + private final Map customCSSDeclarations = new LinkedHashMap<>(); + // .foo | -fx-property: value; + private final Map customCSSRules = new LinkedHashMap<>(); private Theme currentTheme = null; private int currentFontSize = 14; + private Scene scene; + + public Scene getScene() { + return scene; + } + + public void setScene(Scene scene) { + this.scene = scene; + } public Theme getTheme() { return currentTheme; @@ -33,7 +50,8 @@ public final class ThemeManager { * E.g. JavaFX ignores Ikonli -fx-icon-color and -fx-icon-size properties, but for an unknown * reason they won't be ignored when exactly the same stylesheet is set via {@link Scene#getStylesheets()}. */ - public void setTheme(Scene scene, Theme theme) { + public void setTheme(Theme theme) { + Objects.requireNonNull(scene); Objects.requireNonNull(theme); Application.setUserAgentStylesheet(Objects.requireNonNull(theme.getUserAgentStylesheet())); @@ -81,17 +99,59 @@ public final class ThemeManager { return currentFontSize; } - public void setFontSize(Scene scene, int fontSize) { - String css = String.format(".root { -fx-font-size: %dpx; } .ikonli-font-icon { -fx-icon-size: %dpx; }", - fontSize, - fontSize + 2 - ); + public void setFontSize(int fontSize) { + setCustomDeclaration("-fx-font-size", fontSize + "px"); + setCustomRule(".ikonli-font-icon", String.format("-fx-icon-size: %dpx;", fontSize + 2)); + currentFontSize = fontSize; + } + + private void setCustomDeclaration(String property, String value) { + customCSSDeclarations.put(property, value); + } + + private void setCustomRule(String selector, String rule) { + customCSSRules.put(selector, rule); + } + + public void reloadCustomCSS() { + Objects.requireNonNull(scene); + StringBuilder css = new StringBuilder(); + + css.append(".root:"); + css.append(USER_CUSTOM.getPseudoClassName()); + css.append(" {\n"); + customCSSDeclarations.forEach((k, v) -> { + css.append("\t"); + css.append(k); + css.append(":\s"); + css.append(v); + css.append(";\n"); + }); + css.append("}\n"); + + customCSSRules.forEach((k, v) -> { + css.append(".root:"); + css.append(USER_CUSTOM.getPseudoClassName()); + css.append(" "); + css.append(k); + css.append(" {"); + css.append(v); + css.append("}\n"); + }); + + System.out.println(css); + scene.getStylesheets().removeIf(uri -> uri.startsWith("data:text/css")); scene.getStylesheets().add( - "data:text/css;base64," + Base64.getEncoder().encodeToString(css.getBytes(UTF_8)) + "data:text/css;base64," + Base64.getEncoder().encodeToString(css.toString().getBytes(UTF_8)) ); + scene.getRoot().pseudoClassStateChanged(USER_CUSTOM, true); + } - currentFontSize = fontSize; + public void resetCustomCSS() { + customCSSDeclarations.clear(); + customCSSRules.clear(); + scene.getRoot().pseudoClassStateChanged(USER_CUSTOM, false); } public HighlightJSTheme getMatchingHighlightJSTheme(Theme theme) {