Refactor theme manager to support arbitrary CSS modification at runtime
This commit is contained in:
parent
8c60ae7c07
commit
cd16046340
@ -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(
|
||||
|
@ -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));
|
||||
}
|
||||
});
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<String, String> customCSSDeclarations = new LinkedHashMap<>();
|
||||
// .foo | -fx-property: value;
|
||||
private final Map<String, String> 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) {
|
||||
|
Loading…
Reference in New Issue
Block a user