diff --git a/base/pom.xml b/base/pom.xml
index a246360..ee845ad 100755
--- a/base/pom.xml
+++ b/base/pom.xml
@@ -58,6 +58,27 @@
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.6.0
+
+
+ compile-to-bss
+ compile
+
+ java
+
+
+ atlantafx.base.theme.ThemeCompiler
+
+ ${project.build.directory}/classes/atlantafx/base/theme
+
+
+
+
+
diff --git a/base/src/main/java/atlantafx/base/theme/CupertinoDark.java b/base/src/main/java/atlantafx/base/theme/CupertinoDark.java
index b1ac54f..64fe63e 100644
--- a/base/src/main/java/atlantafx/base/theme/CupertinoDark.java
+++ b/base/src/main/java/atlantafx/base/theme/CupertinoDark.java
@@ -3,6 +3,7 @@
package atlantafx.base.theme;
import atlantafx.base.Preview;
+import org.jetbrains.annotations.Nullable;
/**
* A theme based on IOS color palette.
@@ -30,6 +31,14 @@ public class CupertinoDark implements Theme {
return "/atlantafx/base/theme/cupertino-dark.css";
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ return "/atlantafx/base/theme/cupertino-dark.bss";
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/base/src/main/java/atlantafx/base/theme/CupertinoLight.java b/base/src/main/java/atlantafx/base/theme/CupertinoLight.java
index 65c2170..f0e9d11 100644
--- a/base/src/main/java/atlantafx/base/theme/CupertinoLight.java
+++ b/base/src/main/java/atlantafx/base/theme/CupertinoLight.java
@@ -30,6 +30,14 @@ public class CupertinoLight implements Theme {
return "/atlantafx/base/theme/cupertino-light.css";
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ return "/atlantafx/base/theme/cupertino-light.bss";
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/base/src/main/java/atlantafx/base/theme/Dracula.java b/base/src/main/java/atlantafx/base/theme/Dracula.java
index 9757e5c..4aaae42 100644
--- a/base/src/main/java/atlantafx/base/theme/Dracula.java
+++ b/base/src/main/java/atlantafx/base/theme/Dracula.java
@@ -30,6 +30,14 @@ public class Dracula implements Theme {
return "/atlantafx/base/theme/dracula.css";
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ return "/atlantafx/base/theme/dracula.bss";
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/base/src/main/java/atlantafx/base/theme/NordDark.java b/base/src/main/java/atlantafx/base/theme/NordDark.java
index 6e62be4..102805a 100755
--- a/base/src/main/java/atlantafx/base/theme/NordDark.java
+++ b/base/src/main/java/atlantafx/base/theme/NordDark.java
@@ -27,6 +27,14 @@ public final class NordDark implements Theme {
return "/atlantafx/base/theme/nord-dark.css";
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ return "/atlantafx/base/theme/nord-dark.bss";
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/base/src/main/java/atlantafx/base/theme/NordLight.java b/base/src/main/java/atlantafx/base/theme/NordLight.java
index 598a546..2175120 100755
--- a/base/src/main/java/atlantafx/base/theme/NordLight.java
+++ b/base/src/main/java/atlantafx/base/theme/NordLight.java
@@ -27,6 +27,14 @@ public final class NordLight implements Theme {
return "/atlantafx/base/theme/nord-light.css";
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ return "/atlantafx/base/theme/nord-light.bss";
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/base/src/main/java/atlantafx/base/theme/PrimerDark.java b/base/src/main/java/atlantafx/base/theme/PrimerDark.java
index 39e24fe..5051bfc 100755
--- a/base/src/main/java/atlantafx/base/theme/PrimerDark.java
+++ b/base/src/main/java/atlantafx/base/theme/PrimerDark.java
@@ -27,6 +27,14 @@ public final class PrimerDark implements Theme {
return "/atlantafx/base/theme/primer-dark.css";
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ return "/atlantafx/base/theme/primer-dark.bss";
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/base/src/main/java/atlantafx/base/theme/PrimerLight.java b/base/src/main/java/atlantafx/base/theme/PrimerLight.java
index a9319a6..9928806 100755
--- a/base/src/main/java/atlantafx/base/theme/PrimerLight.java
+++ b/base/src/main/java/atlantafx/base/theme/PrimerLight.java
@@ -27,6 +27,14 @@ public final class PrimerLight implements Theme {
return "/atlantafx/base/theme/primer-light.css";
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ return "/atlantafx/base/theme/primer-light.bss";
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/base/src/main/java/atlantafx/base/theme/Theme.java b/base/src/main/java/atlantafx/base/theme/Theme.java
index 9794d50..99ee5df 100755
--- a/base/src/main/java/atlantafx/base/theme/Theme.java
+++ b/base/src/main/java/atlantafx/base/theme/Theme.java
@@ -6,6 +6,7 @@ import static javafx.application.Application.STYLESHEET_CASPIAN;
import static javafx.application.Application.STYLESHEET_MODENA;
import javafx.application.Application;
+import org.jetbrains.annotations.Nullable;
/**
* Basic theme interface.
@@ -23,6 +24,14 @@ public interface Theme {
*/
String getUserAgentStylesheet();
+ /**
+ * Returns the path to the theme user-agent stylesheet in binary
+ * (BSS) format. See {@link Application#setUserAgentStylesheet(String)} for more info.
+ * All built-in themes are available in BSS format, but custom themes may not,
+ * hence the method may return null value.
+ */
+ @Nullable String getUserAgentStylesheetBSS();
+
/**
* Signifies whether the theme uses a light font on a dark background
* or vise versa.
@@ -52,6 +61,11 @@ public interface Theme {
return userAgentStylesheet;
}
+ @Override
+ public @Nullable String getUserAgentStylesheetBSS() {
+ return null;
+ }
+
@Override
public boolean isDarkMode() {
return darkMode;
diff --git a/base/src/main/java/atlantafx/base/theme/ThemeCompiler.java b/base/src/main/java/atlantafx/base/theme/ThemeCompiler.java
new file mode 100644
index 0000000..4d30e02
--- /dev/null
+++ b/base/src/main/java/atlantafx/base/theme/ThemeCompiler.java
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: MIT */
+
+package atlantafx.base.theme;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.stream.Stream;
+import javafx.css.Stylesheet;
+
+/**
+ * Lazy man CSS to BSS compiler wrapper.
+ */
+public class ThemeCompiler {
+
+ public static void main(String[] args) {
+ try {
+ if (args.length < 1) {
+ throw new IllegalArgumentException("You must provide the source directory path");
+ }
+
+ if (args.length > 1) {
+ throw new IllegalArgumentException(
+ "Unexpected arguments were found: "
+ + Arrays.toString(Arrays.copyOfRange(args, 1, args.length))
+ );
+ }
+
+ var dir = Paths.get(args[0]);
+ new ThemeCompiler().convertToBinary(dir);
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Converts all CSS files in the specified directory to BSS.
+ *
+ * @param dir the source directory to scan for CSS files
+ * @throws IOException to punish you for using Java
+ */
+ public void convertToBinary(Path dir) throws IOException {
+ if (dir == null || !Files.exists(dir) || !Files.isDirectory(dir)) {
+ throw new IllegalArgumentException("Invalid directory: " + dir);
+ }
+
+ try (Stream stream = Files.list(dir)) {
+ stream.filter(f -> f.toString().endsWith(".css"))
+ .forEach(f -> {
+ try {
+ convertToBinary(f, f.resolveSibling(getFilename(f) + ".bss"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+ }
+
+ /**
+ * Converts the specified CSS file to BSS. If no output file is given,
+ * then the input file name is used with an extension of 'bss'.
+ *
+ * @param in input file path
+ * @param out output file path
+ * @throws IOException to punish you for using Java
+ */
+ public void convertToBinary(Path in, Path out) throws IOException {
+ if (in == null || out == null) {
+ throw new IllegalArgumentException("Both input and output files must be specified.");
+ }
+
+ if (in.equals(out)) {
+ throw new IllegalArgumentException("Input file and output file cannot be the same.");
+ }
+
+ Stylesheet.convertToBinary(in.toFile(), out.toFile());
+ }
+
+ private String getFilename(Path f) {
+ String name = f.getFileName().toString();
+ return name.substring(0, name.lastIndexOf('.'));
+ }
+}
diff --git a/sampler/src/main/java/atlantafx/sampler/theme/SamplerTheme.java b/sampler/src/main/java/atlantafx/sampler/theme/SamplerTheme.java
index 544a0a6..62141d8 100644
--- a/sampler/src/main/java/atlantafx/sampler/theme/SamplerTheme.java
+++ b/sampler/src/main/java/atlantafx/sampler/theme/SamplerTheme.java
@@ -30,6 +30,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.scene.Scene;
+import org.jetbrains.annotations.Nullable;
/**
* The {@link Theme} decorator to work around some JavaFX CSS limitations.
@@ -87,6 +88,11 @@ public final class SamplerTheme implements Theme {
return IS_DEV_MODE ? DUMMY_STYLESHEET : getResource().toURI().toString();
}
+ @Override
+ public @Nullable String getUserAgentStylesheetBSS() {
+ return theme.getUserAgentStylesheetBSS();
+ }
+
@Override
public boolean isDarkMode() {
return theme.isDarkMode();