Implement changing font family at runtime

This commit is contained in:
mkpaz 2022-08-25 16:03:55 +04:00
parent cd16046340
commit c2e03d2193
10 changed files with 63 additions and 56 deletions

@ -55,10 +55,7 @@ public class Launcher extends Application {
tm.setTheme(tm.getAvailableThemes().get(0)); tm.setTheme(tm.getAvailableThemes().get(0));
if (IS_DEV_MODE) { startCssFX(scene); } if (IS_DEV_MODE) { startCssFX(scene); }
scene.getStylesheets().addAll( scene.getStylesheets().addAll(Resources.resolve("assets/styles/index.css"));
Resources.resolve("assets/fonts/index.css"),
Resources.resolve("assets/styles/index.css")
);
stage.setScene(scene); stage.setScene(scene);
stage.setTitle(System.getProperty("app.name")); stage.setTitle(System.getProperty("app.name"));

@ -5,13 +5,16 @@ import atlantafx.sampler.page.AbstractPage;
import atlantafx.sampler.page.SampleBlock; import atlantafx.sampler.page.SampleBlock;
import atlantafx.sampler.theme.ThemeManager; import atlantafx.sampler.theme.ThemeManager;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Hyperlink; import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.Spinner; import javafx.scene.control.Spinner;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.scene.text.TextFlow; import javafx.scene.text.TextFlow;
@ -23,6 +26,8 @@ import static atlantafx.base.theme.Styles.*;
public class TypographyPage extends AbstractPage { public class TypographyPage extends AbstractPage {
private static final double CONTROL_WIDTH = 200;
public static final String NAME = "Typography"; public static final String NAME = "Typography";
@Override @Override
@ -36,18 +41,20 @@ public class TypographyPage extends AbstractPage {
} }
private void createView() { private void createView() {
Spinner<Integer> fontSizeSpinner = fontSizeSpinner(); var controlsGrid = new GridPane();
fontSizeSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); controlsGrid.setVgap(10);
fontSizeSpinner.setPrefWidth(200); controlsGrid.setHgap(20);
var fontSizeBox = new HBox(20, new Label("Font size"), fontSizeSpinner); controlsGrid.add(new Label("Font family"), 0, 0);
fontSizeBox.setAlignment(Pos.CENTER_LEFT); controlsGrid.add(fontFamilyChooser(), 1, 0);
controlsGrid.add(new Label("Font size"), 0, 1);
controlsGrid.add(fontSizeSpinner(), 1, 1);
var fontSizeSample = fontSizeSample(); var fontSizeSample = fontSizeSample();
fontSizeSampleContent = (GridPane) fontSizeSample.getContent(); fontSizeSampleContent = (GridPane) fontSizeSample.getContent();
userContent.getChildren().setAll( userContent.getChildren().setAll(
fontSizeBox, controlsGrid,
fontSizeSample.getRoot(), fontSizeSample.getRoot(),
fontWeightSample().getRoot(), fontWeightSample().getRoot(),
fontStyleSample().getRoot(), fontStyleSample().getRoot(),
@ -57,19 +64,42 @@ public class TypographyPage extends AbstractPage {
); );
} }
private ComboBox<String> fontFamilyChooser() {
final var tm = ThemeManager.getInstance();
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().add(tm.getFontFamily());
comboBox.getItems().addAll(FXCollections.observableArrayList(Font.getFamilies()));
comboBox.setPrefWidth(CONTROL_WIDTH);
comboBox.getSelectionModel().select(tm.getFontFamily());
comboBox.valueProperty().addListener((obs, old, val) -> {
if (val != null) {
tm.setFontFamily(val);
tm.reloadCustomCSS();
updateFontInfo(Duration.ofMillis(1000));
}
});
return comboBox;
}
private Spinner<Integer> fontSizeSpinner() { private Spinner<Integer> fontSizeSpinner() {
var spinner = new Spinner<Integer>(10, 24, 14); final var tm = ThemeManager.getInstance();
var spinner = new Spinner<Integer>(10, 24, tm.getFontSize());
spinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
spinner.setPrefWidth(CONTROL_WIDTH);
// Instead of this we should obtain font size from a rendered node. // Instead of this we should obtain font size from a rendered node.
// But since it's not trivial (thanks to JavaFX doesn't expose relevant API) // But since it's not trivial (thanks to JavaFX doesn't expose relevant API)
// we just keep current font size inside ThemeManager singleton. // we just keep current font size inside ThemeManager singleton.
// It works fine if ThemeManager default font size value matches // It works fine if ThemeManager default font size value matches
// default theme font size value. // default theme font size value.
spinner.getValueFactory().setValue(ThemeManager.getInstance().getFontSize()); spinner.getValueFactory().setValue(tm.getFontSize());
spinner.valueProperty().addListener((obs, old, val) -> { spinner.valueProperty().addListener((obs, old, val) -> {
if (val != null && getScene() != null) { if (val != null) {
var tm = ThemeManager.getInstance();
tm.setFontSize(val); tm.setFontSize(val);
tm.reloadCustomCSS(); tm.reloadCustomCSS();
updateFontInfo(Duration.ofMillis(1000)); updateFontInfo(Duration.ofMillis(1000));

@ -19,6 +19,7 @@ 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"); private static final PseudoClass USER_CUSTOM = PseudoClass.getPseudoClass("user-custom");
private static final String DEFAULT_FONT_FAMILY_NAME = "Application Default";
// KEY | VALUE // KEY | VALUE
// -fx-property | value; // -fx-property | value;
@ -26,9 +27,10 @@ public final class ThemeManager {
// .foo | -fx-property: value; // .foo | -fx-property: value;
private final Map<String, String> customCSSRules = new LinkedHashMap<>(); private final Map<String, String> customCSSRules = new LinkedHashMap<>();
private Theme currentTheme = null;
private int currentFontSize = 14;
private Scene scene; private Scene scene;
private Theme currentTheme = null;
private String fontFamily = DEFAULT_FONT_FAMILY_NAME;
private int fontSize = 14;
public Scene getScene() { public Scene getScene() {
return scene; return scene;
@ -66,10 +68,7 @@ public final class ThemeManager {
public List<Theme> getAvailableThemes() { public List<Theme> getAvailableThemes() {
var themes = new ArrayList<Theme>(); var themes = new ArrayList<Theme>();
var appStylesheets = new URI[] { var appStylesheets = new URI[] { URI.create(Resources.resolve("assets/styles/index.css")) };
URI.create(Resources.resolve("assets/fonts/index.css")),
URI.create(Resources.resolve("assets/styles/index.css"))
};
if (Launcher.IS_DEV_MODE) { if (Launcher.IS_DEV_MODE) {
themes.add(new ExternalTheme("Primer Light", DUMMY_STYLESHEET, merge( themes.add(new ExternalTheme("Primer Light", DUMMY_STYLESHEET, merge(
@ -95,14 +94,23 @@ public final class ThemeManager {
return themes; return themes;
} }
public String getFontFamily() {
return fontFamily;
}
public void setFontFamily(String fontFamily) {
setCustomDeclaration("-fx-font-family", "\"" + fontFamily + "\"");
this.fontFamily = fontFamily;
}
public int getFontSize() { public int getFontSize() {
return currentFontSize; return fontSize;
} }
public void setFontSize(int fontSize) { public void setFontSize(int fontSize) {
setCustomDeclaration("-fx-font-size", fontSize + "px"); setCustomDeclaration("-fx-font-size", fontSize + "px");
setCustomRule(".ikonli-font-icon", String.format("-fx-icon-size: %dpx;", fontSize + 2)); setCustomRule(".ikonli-font-icon", String.format("-fx-icon-size: %dpx;", fontSize + 2));
currentFontSize = fontSize; this.fontSize = fontSize;
} }
private void setCustomDeclaration(String property, String value) { private void setCustomDeclaration(String property, String value) {
@ -139,8 +147,6 @@ public final class ThemeManager {
css.append("}\n"); 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.toString().getBytes(UTF_8)) "data:text/css;base64," + Base64.getEncoder().encodeToString(css.toString().getBytes(UTF_8))

@ -1,30 +0,0 @@
/*
* Note that each font-family have to be unique. That's because unlike web browsers
* OpenJFX CSS parser doesn't support `font-weight` and `font-style` attributes
* in the `@font-face` at-rule. It just silently ignores them. So, the font variant
* can only be expressed via font name. OpenJFX always uses font transformation,
* even when corresponding font variant provided explicitly.
*
* See, CSS Reference guide:
* https://openjfx.io/javadoc/17/javafx.graphics/javafx/scene/doc-files/cssref.html
* > Although the parser will parse the syntax, all @fontface descriptors
* > are ignored except for the src descriptor. The src descriptor is expected
* > to be a <url>. The format hint is ignored.
*/
@font-face {
font-family: "Inter UI Regular";
src: url('Inter/InterUI-Regular.otf');
}
@font-face {
font-family: "Inter UI Medium";
src: url('Inter/InterUI-Medium.otf');
}
@font-face {
font-family: "Inter UI Bold";
src: url('Inter/InterUI-Bold.otf');
}
@font-face {
font-family: "Inter UI Italic";
src: url('Inter/InterUI-Italic.otf');
}

@ -1,7 +1,12 @@
/** SPDX-License-Identifier: MIT */ /** SPDX-License-Identifier: MIT */
@font-face {
font-family: "Application Default";
src: url('../fonts/Inter-Regular.otf');
}
.root { .root {
-fx-font-family: "Inter"; -fx-font-family: "Application Default";
} }
.root:showcase-mode #sidebar, .root:showcase-mode #sidebar,
.root:showcase-mode .page > .header { .root:showcase-mode .page > .header {

@ -72,7 +72,6 @@
// default props inherited by all nodes // default props inherited by all nodes
-fx-background-color: -color-bg-default; -fx-background-color: -color-bg-default;
-fx-font-family: "Inter";
-fx-font-size: cfg.$font-default; -fx-font-size: cfg.$font-default;
// these are needed for Popup // these are needed for Popup