Joachim Hofer is a long-time senior consultant and lead developer on the Java platform who has recently become a fan of Scala. He regularly contributes to Open Source community projects and shares his experience in his blog and talks. Joachim is a DZone MVB and is not an employee of DZone and has posted 12 posts at DZone. You can read more from them at their website. View Full User Profile

Getting Started with ScalaFX and Resizable UIs

01.08.2013
| 5060 views |
  • submit to reddit

When I was a school kid oh so long ago, the most permanent subject of my programming hobby was an AI for “Connect Four” (besides lots of genetic algorithm and neural network stuff, sigh). I remember very well the thousands of lines of code and the weeks I spent trying to get that right (using Turbo Pascal), and to get it to be able to beat me at the game. However, I got better at the game faster than my AI, I’m afraid.

Today I know that “Connect Four” is a solved problem. Still, I think it’s a nice programming playground. And as I wanted to find out how far I’ve come with my development skills since I was a school kid, I decided to ignore that research paper and use “Connect Four” as a playground for trying out Akka and ScalaFX for a bit over the holidays – if you want to skip ahead, you’ll find the results at Bitbucket.

So, here’s a blog about how I started out with ScalaFX in  that little project.

The first steps to getting started with ScalaFX were not immediately clear to me. There’s a tutorial about how to get started with ScalaFX within Eclipse alright, but for using it from SBT, I fortunately remembered a Herbstcampus talk by @phdoerfler. You can find the (German) slides and an SBT build file here. Turns out it’s quite easy.

The first thing you need to know about ScalaFX is that it’s unfortunately not published anywhere. That means that you have to publish it locally in order to be able to use it as a library.

Doing this means installing Mercurial if you haven’t got it already, and then cloning the ScalaFX repository:

hg clone https://code.google.com/p/scalafx/

Next, you have to package and publish ScalaFX. If you’ve got git installed, too, then this should do the trick:

sbt publish-local

However, if you don’t have git installed, you have to remove the project/plugins/project/PluginBuild.scala file first, which references an sbt plugin (used for the test target) cloned from git. This plugin is not really required for locally publishing ScalaFX, anyway.

Now you’re set for referencing ScalaFX in your own SBT build – just add ScalaFX as a library dependency, and also add the JavaFX runtime (coming from your local Java 7 installation, if it’s up-to-date as it should be) as a dependency:

libraryDependencies += "org.scalafx" % "scalafx" % "1.0-SNAPSHOT"

unmanagedJars in Compile += Attributed.blank(
    file(scala.util.Properties.javaHome) / "lib" / "jfxrt.jar")

fork in run := true

As you notice, I have also told the run command to fork in order to avoid “UnsatisfiedLinkError“s due to various classloader “magics”…

On to coding: For my Connect Four board, I wanted to start with a pane within a resizable window where I just do the layout myself, basically drawing the board with the help of ScalaFX shapes instead of using Graphics2D. At first I thought that that would be easy, but then I spend a lot of time on one single detail: Namely, how to get the ScalaFX UI to resize with the window. Incidentally, this is what spawned this blog post.

My main class started out quite simple, as any ScalaFX application does:

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.stage.Stage

object Main extends JFXApp {
  stage = new Stage {
    title = "Akka Connect Four"
    width = 800
    height = 600
  }
}

This sets the stage, and that’s all. – Next, you have to add the scene graph to the stage. Normally, you’d do this by simply adding scene = new Scene { ... } to the stage. But that’s exactly what broke my neck when trying to make the scene resize together with the window. And the reason for this is that ScalaFX internally puts a JavaFX “group” as the root of the scene here, instead of a single component.

Using a single component as root for the ScalaFX scene graph unfortunately doesn’t seem to be well supported by ScalaFX. The only way I was able to make it work is by doing it like this, directly creating a scene via JavaFX:

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.stage.Stage
import scalafx.scene.Scene
import scalafx.scene.layout.BorderPane

object Main extends JFXApp {
  stage = new Stage {
    title = "Akka Connect Four"
    width = 800
    height = 600

    scene = new Scene(new javafx.scene.Scene(root))
  }

  lazy val root = new BorderPane {}
}

I’m using a BorderPane here as a container so that I can arrange additional stuff around my Connect Four board later on.

Next, let’s add the basic board rectangle to the center of the root pane.

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.stage.Stage
import scalafx.scene.Scene
import scalafx.scene.layout.{ Pane, BorderPane }
import scalafx.scene.shape.Rectangle
import javafx.scene.paint.{ Color => JFXColor }

object Main extends JFXApp {
  stage = new Stage {
    title = "Akka Connect Four"
    width = 800
    height = 600

    scene = new Scene(new javafx.scene.Scene(root))
  }

  lazy val root = new BorderPane {
    center = gamePane
  }

  lazy val gamePane: Pane = new Pane {
    content = showBoard(784.0, 562.0)
  }

  def showBoard(paneWidth: Double, paneHeight: Double) = {
    val offX = 50.0
    val offY = 50.0
    val boardWidth = paneWidth - offX * 2
    val boardHeight = paneHeight - offY * 2
    
    new Rectangle {
      x = offX
      y = offY
      width = boardWidth
      height = boardHeight
      fill = JFXColor.DEEPSKYBLUE
    }
  }
}

This way, we get a nice centered blue rectangle (I won’t add more details to it for the purpose of this blog post). However, you may have already noticed that the board still doesn’t resize itself when you resize the window. At least the pane itself does resize itself thanks to our trick with the root component above (you can check that by setting the background color, for example, or by using Scenic View – which is an awesome tool for debugging JavaFX UIs, by the way).

In order to get a resizing board, we still have to bind the width and height of the game pane to redrawing our board. Now, if there’s something that’s really awesome in ScalaFX, then it’s how simple this can be done, using “bind expressions”. In our case, we just want to show an updated board every time the width or height of the window changes. Therefore, we want to set width.onChange() and height.onChange().

Here’s what I did:

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.stage.Stage
import scalafx.scene.Scene
import scalafx.scene.layout.{ Pane, BorderPane }
import scalafx.scene.shape.Rectangle
import javafx.scene.paint.{ Color => JFXColor }

object Main extends JFXApp {
  stage = new Stage {
    title = "Akka Connect Four"
    width = 800
    height = 600

    scene = new Scene(new javafx.scene.Scene(root))

    width onChange show
    height onChange show
  }

  lazy val root = new BorderPane {
    center = gamePane
  }

  lazy val gamePane: Pane = new Pane {
    content = showBoard(784.0, 562.0)
  }

  def show: Unit = {
    gamePane.content = showBoard(gamePane.width.get, gamePane.height.get)
  }


  def showBoard(paneWidth: Double, paneHeight: Double) = {
    val offX = 50.0
    val offY = 50.0
    val boardWidth = paneWidth - offX * 2
    val boardHeight = paneHeight - offY * 2
    
    new Rectangle {
      x = offX
      y = offY
      width = boardWidth
      height = boardHeight
      fill = JFXColor.DEEPSKYBLUE
    }
  }
}

And that’s it already: A resizable game board for my Connect Four UI.

And as I’m still a bloody ScalaFX newbie: Should you know a way to do any of the above easier, please write a comment below!

You can also check out the source code of the Connect Four application at BitBucket. – That said, it’s not complete in any way (no parallelism at all yet, no permanent brain, nearly completely unoptimized for performance, for example).

However, I got it to being able to play on par with me within a few days of eating christmas cookies and casually hacking, and that’s awesome and depressing at the same time! :)


Published at DZone with permission of Joachim Hofer, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Ab Cde replied on Mon, 2013/01/14 - 12:00am

You may want to take a look at the Reversi game implemented in ScalaFX. The example is from the book "Pro JavaFX2":

https://github.com/jsacha/ProScalaFX/tree/master/src/proscalafx/ch04

There are several examples of ensuring that content is centered. Generally you use standard JaxaFX techniques.

In ScalaFX you can assign to "scene.root" for easy resizing (avoid wrapping in a group), but due to a bug in ScalaFX you need to reference underlying JavaFX element (".delegate"). It looks quite simple:

object ReversiSquareTest extends JFXApp {
  stage = new Stage {
    scene = new Scene() {
      root = new StackPane {
        content = new ReversiSquare(0, 0)
      }.delegate
    }
  }
}

Details are at:

https://github.com/jsacha/ProScalaFX/blob/master/src/proscalafx/ch04/reversi/reversipieces/ReversiSquareTest.scala

Alain Couniot replied on Sun, 2014/02/09 - 7:24am

 

Joachim, thank you for this brief introduction to ScalaFX.  I have found it helpful.

For your information, I have encountered some difficulties building it with Eclipse/Maven and recent versions of ScalaFX but all went well in the end.

I experienced only 1 syntax problem with the code itself.  In Main.scala, method columnButton, I had to replace

  onAction = (uiActor ! PlayerMove(column))

with
    onAction = { (_: ActionEvent) => uiActor ! PlayerMove(column) }

The rest had to do mostly with Maven (among which bizarre error messages with jars from Maven repositories, which only disappeared after I included ScalaFX as source code in my Eclipse project)

I can provide more information if somebody is interested.

Best regards,

Alain

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.