Skip to content

Cascara UI

Theme support for JavaFX.

Features

  • Changing themes without restarting you application
  • Importing Visual Studio Code themes
  • JavaFX 24+

Demonstration

These screenshots are of the Cascara UI Demp app which is available in the cascara-docs-examples Github repository under examples/ui.

Default Cascara Theme

Cascara Theme

Noellch VS Code Theme

This screenshot is was taken with the language set to Spanish.

View theme on visualstudio.com.

Noellch VS Code Theme

Aloe VS Code Theme

This screenshot was taken with the language set to Arabic.

The dropdown list shows Enum values being translated automatically.

View theme on visualstudio.com.

Aloe VS Code Theme

Installing VS Code Themes

To install a VS Code theme for the Cascara Theme Engine, copy the .vsix file into ~/.cascara/packages/.

Gradle

Cascara UI and its dependencies are available in the Maven Central repository.

To use it in a Gradle project, add the following dependencies:

dependencies {
    implementation "io.github.qishr:cascara-common:1.1.11"
    implementation "io.github.qishr:cascara-common-io:0.8.0"
    implementation "io.github.qishr:cascara-lang-json:0.8.0"
    implementation "io.github.qishr:cascara-lang-xml:0.8.0"
    implementation "io.github.qishr:cascara-lang-yaml:0.8.0"
    implementation "io.github.qishr:cascara-schema:0.8.0"
    implementation "io.github.qishr:cascara-ui:0.8.0"
}

Example Usage

This example uses the following Cascara classes to display a drop down list of installed themes and apply the chosen one to the scene:

Scene scene = new Scene(layout, 800, 500);

OptionChooser themeChooser = new OptionChooser(
    ThemeEngine.getThemeOptionProvider()
);

themeChooser.getSelectionModel().selectedItemProperty().addListener((obs, old, theme) -> {
    ThemeEngine.setTheme(theme);
});

ThemeEngine.applyTheme(scene);

Example App

This is the source code for the Cascara UI Demo app.

public class Launcher extends Application {
    private Scene scene;

    public static void main(String[] args) {
        // GlobalReporter.globalInstance().setPrintStackTrace(true);
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        registerTranslations();

        Label themeLabel = new Label();
        Localization.bind(themeLabel, "label.theme");

        Label languageLabel = new Label();
        Localization.bind(languageLabel, "label.language");

        OptionChooser themeChooser = new OptionChooser(
            ThemeEngine.getThemeOptionProvider()
        );

        themeChooser.getSelectionModel().selectedItemProperty().addListener((obs, old, theme) -> {
            ThemeEngine.setTheme(theme);
        });

        OptionChooser languageChooser = new OptionChooser(
            Localization.getLanguageOptionProvider()
        );

        languageChooser.getSelectionModel().selectedItemProperty().addListener((obs, old, language) -> {
            Localization.setActiveLanguage(language);
        });

        HBox choserBox = new HBox(
            8,
            themeLabel,
            themeChooser,
            new Rectangle(24, 0),
            languageLabel,
            languageChooser
        );
        choserBox.setAlignment(Pos.CENTER);
        Localization.bindDirection(choserBox);

        VBox layout = new VBox();
        layout.setSpacing(8);
        layout.setPadding(new Insets(16));
        layout.getChildren().add(choserBox);

        layout.getChildren().add(new Samples().getView());

        scene = new Scene(layout, 800, 500);

        ThemeEngine.bind(scene);
        Localization.bindDirection(scene);

        primaryStage.setScene(scene);
        primaryStage.show();

        String version = ThemeEngine.class.getModule().getDescriptor() == null
                ? ""
                : ThemeEngine.class.getModule().getDescriptor().toNameAndVersion();
        Localization.bind(
            primaryStage, "app.window-title",
            version
        );
    }

    @Override
    public void stop() throws Exception {
        // When the OptionProvider is used, the theme engine uses a
        // background thread to watch the theme directory for updates.
        // We close it here to allow the app to close cleanly.
        FileWatcher.clearAll();
    }

    private void registerTranslations() {
        registerLanguage("ar-AE");
        registerLanguage("en-US");
        registerLanguage("es-ES");
        registerLanguage("fr-FR");
    }

    private void registerLanguage(String languageTag) {
        InputStream translations = getClass().getResourceAsStream(languageTag + ".yaml");
        if (translations != null) {
            if (!Localization.registerTranslations(translations)) {
                System.exit(0);
            }
        }
    }
}

API Documentation

Javadoc is available here.