cssresourcesstylesheetembedded-resourcejavafx-8

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; }");

Solution

  • 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:"+System.nanoTime()+"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)