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 scene = new Scene(root, 1200, 768);
|
||||||
|
|
||||||
var tm = ThemeManager.getInstance();
|
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); }
|
if (IS_DEV_MODE) { startCssFX(scene); }
|
||||||
|
|
||||||
scene.getStylesheets().addAll(
|
scene.getStylesheets().addAll(
|
||||||
|
@ -62,7 +62,9 @@ public class ThemePage extends AbstractPage {
|
|||||||
selector.getItems().setAll(manager.getAvailableThemes());
|
selector.getItems().setAll(manager.getAvailableThemes());
|
||||||
selector.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
selector.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
||||||
if (val != null && getScene() != null) {
|
if (val != null && getScene() != null) {
|
||||||
ThemeManager.getInstance().setTheme(getScene(), val);
|
var tm = ThemeManager.getInstance();
|
||||||
|
tm.setTheme(val);
|
||||||
|
tm.reloadCustomCSS();
|
||||||
colorPalette.updateColorInfo(Duration.ofSeconds(1));
|
colorPalette.updateColorInfo(Duration.ofSeconds(1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -69,7 +69,9 @@ public class TypographyPage extends AbstractPage {
|
|||||||
|
|
||||||
spinner.valueProperty().addListener((obs, old, val) -> {
|
spinner.valueProperty().addListener((obs, old, val) -> {
|
||||||
if (val != null && getScene() != null) {
|
if (val != null && getScene() != null) {
|
||||||
ThemeManager.getInstance().setFontSize(getScene(), val);
|
var tm = ThemeManager.getInstance();
|
||||||
|
tm.setFontSize(val);
|
||||||
|
tm.reloadCustomCSS();
|
||||||
updateFontInfo(Duration.ofMillis(1000));
|
updateFontInfo(Duration.ofMillis(1000));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -122,10 +124,10 @@ public class TypographyPage extends AbstractPage {
|
|||||||
|
|
||||||
private SampleBlock fontWeightSample() {
|
private SampleBlock fontWeightSample() {
|
||||||
var box = new HBox(10,
|
var box = new HBox(10,
|
||||||
text("Bold", TEXT_BOLD),
|
text("Bold", TEXT_BOLD),
|
||||||
text("Bolder", TEXT_BOLDER),
|
text("Bolder", TEXT_BOLDER),
|
||||||
text("Normal", TEXT_NORMAL),
|
text("Normal", TEXT_NORMAL),
|
||||||
text("Lighter", TEXT_LIGHTER)
|
text("Lighter", TEXT_LIGHTER)
|
||||||
);
|
);
|
||||||
box.setAlignment(Pos.BASELINE_LEFT);
|
box.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
@ -134,10 +136,10 @@ public class TypographyPage extends AbstractPage {
|
|||||||
|
|
||||||
private SampleBlock fontStyleSample() {
|
private SampleBlock fontStyleSample() {
|
||||||
var box = new HBox(10,
|
var box = new HBox(10,
|
||||||
text("Italic", TEXT_ITALIC),
|
text("Italic", TEXT_ITALIC),
|
||||||
text("Oblique", TEXT_OBLIQUE),
|
text("Oblique", TEXT_OBLIQUE),
|
||||||
text("Underlined", TEXT_UNDERLINED),
|
text("Underlined", TEXT_UNDERLINED),
|
||||||
text("Strikethrough", TEXT_STRIKETHROUGH)
|
text("Strikethrough", TEXT_STRIKETHROUGH)
|
||||||
);
|
);
|
||||||
box.setAlignment(Pos.BASELINE_LEFT);
|
box.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
@ -146,10 +148,10 @@ public class TypographyPage extends AbstractPage {
|
|||||||
|
|
||||||
private SampleBlock textColorSample() {
|
private SampleBlock textColorSample() {
|
||||||
var box = new HBox(10,
|
var box = new HBox(10,
|
||||||
text("Accent", TEXT, ACCENT),
|
text("Accent", TEXT, ACCENT),
|
||||||
text("Success", TEXT, SUCCESS),
|
text("Success", TEXT, SUCCESS),
|
||||||
text("Warning", TEXT, WARNING),
|
text("Warning", TEXT, WARNING),
|
||||||
text("Danger", TEXT, DANGER)
|
text("Danger", TEXT, DANGER)
|
||||||
);
|
);
|
||||||
box.setAlignment(Pos.BASELINE_LEFT);
|
box.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
@ -164,9 +166,9 @@ public class TypographyPage extends AbstractPage {
|
|||||||
linkVisited.setMnemonicParsing(true);
|
linkVisited.setMnemonicParsing(true);
|
||||||
|
|
||||||
var box = new HBox(10,
|
var box = new HBox(10,
|
||||||
linkNormal,
|
linkNormal,
|
||||||
linkVisited,
|
linkVisited,
|
||||||
hyperlink("Disabled", false, true)
|
hyperlink("Disabled", false, true)
|
||||||
);
|
);
|
||||||
box.setAlignment(Pos.BASELINE_LEFT);
|
box.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import atlantafx.base.theme.Theme;
|
|||||||
import atlantafx.sampler.Launcher;
|
import atlantafx.sampler.Launcher;
|
||||||
import atlantafx.sampler.Resources;
|
import atlantafx.sampler.Resources;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@ -17,9 +18,25 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||||||
public final class ThemeManager {
|
public final class ThemeManager {
|
||||||
|
|
||||||
private static final String DUMMY_STYLESHEET = Resources.getResource("assets/styles/empty.css").toString();
|
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 Theme currentTheme = null;
|
||||||
private int currentFontSize = 14;
|
private int currentFontSize = 14;
|
||||||
|
private Scene scene;
|
||||||
|
|
||||||
|
public Scene getScene() {
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScene(Scene scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
public Theme getTheme() {
|
public Theme getTheme() {
|
||||||
return currentTheme;
|
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
|
* 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()}.
|
* 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);
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
Application.setUserAgentStylesheet(Objects.requireNonNull(theme.getUserAgentStylesheet()));
|
Application.setUserAgentStylesheet(Objects.requireNonNull(theme.getUserAgentStylesheet()));
|
||||||
@ -81,17 +99,59 @@ public final class ThemeManager {
|
|||||||
return currentFontSize;
|
return currentFontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFontSize(Scene scene, int fontSize) {
|
public void setFontSize(int fontSize) {
|
||||||
String css = String.format(".root { -fx-font-size: %dpx; } .ikonli-font-icon { -fx-icon-size: %dpx; }",
|
setCustomDeclaration("-fx-font-size", fontSize + "px");
|
||||||
fontSize,
|
setCustomRule(".ikonli-font-icon", String.format("-fx-icon-size: %dpx;", fontSize + 2));
|
||||||
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().removeIf(uri -> uri.startsWith("data:text/css"));
|
||||||
scene.getStylesheets().add(
|
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) {
|
public HighlightJSTheme getMatchingHighlightJSTheme(Theme theme) {
|
||||||
|
Loading…
Reference in New Issue
Block a user