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.

This entry was posted in e(fx)clipse. Bookmark the permalink.

5 Responses to Using LESS in JavaFX

  1. 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.

  2. Pingback: JavaFX links of the week, August 11 // JavaFX News, Demos and Insight // FX Experience

  3. Pingback: Java desktop links of the week, August 11 « Jonathan Giles

  4. grill2010 says:

    Hi, thank you for your nice article. I know it is quite old but it proved to me that it is possible to work with less in JavaFX applications. We use maven in our project and there are now some nice plugins available to integrate the less to css conversion in the build process. If anyone is interested in the build configuration you can find it on stackoverflow (http://stackoverflow.com/questions/13566210/declaring-variable-in-javafx-css-file/41805478#41805478).

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.