Using LESS in JavaFX


Inspired by a posting to openjfx-dev where I claimed: Using LESS in JavaFX applications is possible – I thought I should proof my statement and get LESS working in a JavaFX app.

The LESS-File i’ve created for looks like this:

.opacity(@opacity) {
  -fx-opacity: @opacity / 100;
}

Rectangle {
    .opacity(30);
    -fx-fill: red;
}

// Selector interpolation only works in 1.3.1+. Try it!
@theGoodThings: ~".food, .beer, .sleep, .javafx";

@{theGoodThings} {
  -fx-font-weight: bold;
  -fx-font-size: 20pt;
}

which written in CSS looks like this:

Rectangle {
  -fx-opacity: 0.3;
  -fx-fill: red;
}
.food, .beer, .sleep, .javafx {
  -fx-font-weight: bold;
  -fx-font-size: 20pt;
}

So I create a library project, download the less.css for rhino and tried to get it running. My first tries always failed the because the script did not appropriately detect that it was running in rhino (or nashorn when executed in Java8) – so I tweaked the file a bit. To make it damn easy for me to invoke the parser from Java I created a small JS-Script:

var parseString = function(value) {
	var parser = new(less.Parser);
	var rv;
	parser.parse(value, function (err, tree) {
		if (!err) {
			rv = tree.toCSS();	
		}
	});
	return rv;
}

So my Java-Code can pass in the content of the less-file and gets the css-content returned:

public class LessLoader {
  // ...
  public URL loadLess(URL lessFile) {
    try {
      StringBuilder lessContent = new StringBuilder();
      readFileContent(lessFile, lessContent);
			
      Object rv = ((Invocable)engine).invokeFunction("parseString", lessContent.toString());
      File f = File.createTempFile("less_", ".css");
      f.deleteOnExit();

      try(FileOutputStream out = new FileOutputStream(f) ) {
        out.write(rv.toString().getBytes());
        out.close();
      }
      return f.toURI().toURL();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

// ...

So I can now make use of it in my JavaFX application:

public class Main extends Application {
  @Override
  public void start(Stage primaryStage) {
    try {
      Group root = new Group();
      root.getChildren().add(new Rectangle(100,100,200,200));
			
      Text t = new Text("JavaFX");
      t.getStyleClass().add("javafx");
      t.relocate(150, 320);
      root.getChildren().add(t);
			
      Scene scene = new Scene(root,400,400);
			
      LessCSSLoader ls = new LessCSSLoader();
      scene.getStylesheets().add(ls.loadLess(getClass().getResource("sample.less")).toExternalForm());
      primaryStage.setScene(scene);
      primaryStage.show();
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
	
  public static void main(String[] args) {
    launch(args);
  }
}

This makes the application look like this:

screen

I’ve pushed the library to our efxclipse-addons-github-repo.

4 thoughts on “Using LESS in JavaFX

  1. Sébastien Boulet (@gontardd) August 9, 2013 / 7:37 am

    This is a great idea !
    May be this css conversion could be to integrated in the build process. Then the generated css could be optimized in a binary css.

    • Tom Schindl August 9, 2013 / 9:00 am

      This would be possible as well yes – maybe if I have time I’ll add an ant support

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s