Compile themes to BSS format

Every built-in theme is now available in both CSS and BSS formats.
This commit is contained in:
mkpaz 2023-05-27 12:28:47 +04:00
parent d4d0526df0
commit 5f81fc2c5b
11 changed files with 183 additions and 0 deletions

@ -58,6 +58,27 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<!-- compile themes to BSS -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>compile-to-bss</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>atlantafx.base.theme.ThemeCompiler</mainClass>
<arguments>
<argument>${project.build.directory}/classes/atlantafx/base/theme</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

@ -3,6 +3,7 @@
package atlantafx.base.theme; package atlantafx.base.theme;
import atlantafx.base.Preview; import atlantafx.base.Preview;
import org.jetbrains.annotations.Nullable;
/** /**
* A theme based on <a href="https://developer.apple.com/design/">IOS</a> color palette. * A theme based on <a href="https://developer.apple.com/design/">IOS</a> color palette.
@ -30,6 +31,14 @@ public class CupertinoDark implements Theme {
return "/atlantafx/base/theme/cupertino-dark.css"; return "/atlantafx/base/theme/cupertino-dark.css";
} }
/**
* {@inheritDoc}
*/
@Override
public String getUserAgentStylesheetBSS() {
return "/atlantafx/base/theme/cupertino-dark.bss";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -30,6 +30,14 @@ public class CupertinoLight implements Theme {
return "/atlantafx/base/theme/cupertino-light.css"; return "/atlantafx/base/theme/cupertino-light.css";
} }
/**
* {@inheritDoc}
*/
@Override
public String getUserAgentStylesheetBSS() {
return "/atlantafx/base/theme/cupertino-light.bss";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -30,6 +30,14 @@ public class Dracula implements Theme {
return "/atlantafx/base/theme/dracula.css"; return "/atlantafx/base/theme/dracula.css";
} }
/**
* {@inheritDoc}
*/
@Override
public String getUserAgentStylesheetBSS() {
return "/atlantafx/base/theme/dracula.bss";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -27,6 +27,14 @@ public final class NordDark implements Theme {
return "/atlantafx/base/theme/nord-dark.css"; return "/atlantafx/base/theme/nord-dark.css";
} }
/**
* {@inheritDoc}
*/
@Override
public String getUserAgentStylesheetBSS() {
return "/atlantafx/base/theme/nord-dark.bss";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -27,6 +27,14 @@ public final class NordLight implements Theme {
return "/atlantafx/base/theme/nord-light.css"; return "/atlantafx/base/theme/nord-light.css";
} }
/**
* {@inheritDoc}
*/
@Override
public String getUserAgentStylesheetBSS() {
return "/atlantafx/base/theme/nord-light.bss";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -27,6 +27,14 @@ public final class PrimerDark implements Theme {
return "/atlantafx/base/theme/primer-dark.css"; return "/atlantafx/base/theme/primer-dark.css";
} }
/**
* {@inheritDoc}
*/
@Override
public String getUserAgentStylesheetBSS() {
return "/atlantafx/base/theme/primer-dark.bss";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -27,6 +27,14 @@ public final class PrimerLight implements Theme {
return "/atlantafx/base/theme/primer-light.css"; return "/atlantafx/base/theme/primer-light.css";
} }
/**
* {@inheritDoc}
*/
@Override
public String getUserAgentStylesheetBSS() {
return "/atlantafx/base/theme/primer-light.bss";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -6,6 +6,7 @@ import static javafx.application.Application.STYLESHEET_CASPIAN;
import static javafx.application.Application.STYLESHEET_MODENA; import static javafx.application.Application.STYLESHEET_MODENA;
import javafx.application.Application; import javafx.application.Application;
import org.jetbrains.annotations.Nullable;
/** /**
* Basic theme interface. * Basic theme interface.
@ -23,6 +24,14 @@ public interface Theme {
*/ */
String getUserAgentStylesheet(); 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 * Signifies whether the theme uses a light font on a dark background
* or vise versa. * or vise versa.
@ -52,6 +61,11 @@ public interface Theme {
return userAgentStylesheet; return userAgentStylesheet;
} }
@Override
public @Nullable String getUserAgentStylesheetBSS() {
return null;
}
@Override @Override
public boolean isDarkMode() { public boolean isDarkMode() {
return darkMode; return darkMode;

@ -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<Path> 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('.'));
}
}

@ -30,6 +30,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javafx.application.Application; import javafx.application.Application;
import javafx.scene.Scene; import javafx.scene.Scene;
import org.jetbrains.annotations.Nullable;
/** /**
* The {@link Theme} decorator to work around some JavaFX CSS limitations. * 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(); return IS_DEV_MODE ? DUMMY_STYLESHEET : getResource().toURI().toString();
} }
@Override
public @Nullable String getUserAgentStylesheetBSS() {
return theme.getUserAgentStylesheetBSS();
}
@Override @Override
public boolean isDarkMode() { public boolean isDarkMode() {
return theme.isDarkMode(); return theme.isDarkMode();