ZioByte ZioByte - 3 months ago 20
CSS Question

In JavaFX 8 can I provide a stylesheet from a String?

Is it possible to wrap a whole Stylesheet in a string and apply it to a certain node?
Usage case would be to add specific (non changeble) behavior for PseudoClass.
I know I can use

pane.getStylesheets().add(getClass().getResource("mycss.css").toExternalForm());
, but I would like to know if there's some way to embrd it direcly in source; something along the lines:

pane.getStylesheets().add(
".button:ok { -fx-background-color: green; }\n"+
".button:ko { -fx-background-color: red; }");

Answer

I found a way of doing this by defining a new URL connection:

private String css;

public void initialize() {
    ...
    // to be done only once.
    URL.setURLStreamHandlerFactory(new StringURLStreamHandlerFactory());
    ...
}

private void updateCss(Node node) {
    // can be done multiple times.
    css = createCSS();
    node.getStylesheets().setAll("internal:stylesheet.css");
}

private class StringURLConnection extends URLConnection {
    public StringURLConnection(URL url){
        super(url);
    }

    @Override public void connect() throws IOException {}

    @Override public InputStream getInputStream() throws IOException {
        return new StringBufferInputStream(css);
    }
}

private class StringURLStreamHandlerFactory implements URLStreamHandlerFactory {
    URLStreamHandler streamHandler = new URLStreamHandler(){
        @Override protected URLConnection openConnection(URL url) throws IOException {
            if (url.toString().toLowerCase().endsWith(".css")) {
                return new StringURLConnection(url);
            }
            throw new FileNotFoundException();
        }
    };
    @Override public URLStreamHandler createURLStreamHandler(String protocol) {
        if ("internal".equals(protocol)) {
            return streamHandler;
        }
        return null;
    }
}

Obviously protocol "internal" can be any (non clashing) well-formed string and (in this simple example) filepath is compeltely ignored.

I use this to set the global .css, so I do not need to remember multiple strings. It seems the Stream is opened just once, but I do not know if this holds true in all cases.

Feel free to complicate the code as needed ;)

Credit for this method goes to Jasper Potts (see this example)