ScalaFX – Alerts and Dialogs

One of the new features on ScalaFX 8.0.40 are Alerts and Dialogs. Dialog API allows for opening a dialog window and returning result from the user. The result can be as simple as the type of button used to close the dialog. Custom dialog allows for returning an arbitrary result.

We will present examples of ScalaFX Alerts and Dialogs based on examples presented in JavaFX Dialogs blog post.

Simple Alerts

Information Alert

There are several predefined dialogs called alerts that can be easily presented to the user. A simplest alert can be shown with a single line of code:

 new Alert(AlertType.Information, "Hello Dialogs!!!").showAndWait()

013 - simple information alert

In general use you will typically customize it a bit more:

new Alert(AlertType.Information) {
  initOwner(stage)
  title = "Information Dialog"
  headerText = "Look, an Information Dialog"
  contentText = "I have a great message for you!"
}.showAndWait()

013 - information alert

initOwner() specifies the owner for a dialog. It is not required, but specifying an owner is a good style. It allows the dialog to use the same icon as the owner. It also will let block the parent when dialog is shown modal.

Warning Alert

new Alert(AlertType.Warning) {
  initOwner(stage)
  title = "Warning Dialog"
  headerText = "Look, an Warning Dialog."
  contentText = "Careful with the next step!"
}.showAndWait()

013 - warning alert

Error Alert

new Alert(AlertType.Error) {
  initOwner(stage)
  title = "Error Dialog"
  headerText = "Look, an Error Dialog."
  contentText = "Ooops, there was an error!"
}.showAndWait()

013 - error alert

Confirmation Alert

Alerts and dialogs can be used to query user for information. Every alert returns type of button that was pressed to close the dialog window. A simplest form is a confirmation dialog that indicates whether user pressed OK or Cancel buttons. Strictly speaking a dialog returns an Option containing type of button pressed or None.

// Create and show confirmation alert
val alert = new Alert(AlertType.Confirmation) {
  initOwner(stage)
  title = "Confirmation Dialog"
  headerText = "Look, a Confirmation Dialog."
  contentText = "Are you ok with this?"
}

val result = alert.showAndWait()

// React to user's selectioon
result match {
  case Some(ButtonType.OK) => println("OK")
  case _                   => println("Cancel or closed")
}

013 - confirmation alert

You can create a dialog returning any content, an example will be shown later. First let see how to use custom buttons in an alert.

Alerts with Custom Buttons

We can customize the button in an alert by defining ButtonType objects and passing them to Alert’s buttonTypes property. Notice that we overwrite the content of the property (rather than append to it):

    val ButtonTypeOne = new ButtonType("One")
    val ButtonTypeTwo = new ButtonType("Two")
    val ButtonTypeThree = new ButtonType("Three")

    val alert = new Alert(AlertType.Confirmation) {
      initOwner(stage)
      title = "Confirmation Dialog with Custom Actions"
      headerText = "Look, a Confirmation Dialog with Custom Actions."
      contentText = "Choose your option."
      // Note that we override here default dialog buttons, OK and Cancel, with new ones.
      buttonTypes = Seq(ButtonTypeOne, ButtonTypeTwo, ButtonTypeThree, ButtonType.Cancel)
    }

    val result = alert.showAndWait()

    result match {
      case Some(ButtonTypeOne)   => println("... user chose "One"")
      case Some(ButtonTypeTwo)   => println("... user chose "Two"")
      case Some(ButtonTypeThree) => println("... user chose "Three"")
      case _                     => println("... user chose CANCEL or closed the dialog")
    }

013 - confirmation custom alert

Alerts with Custom Content

You are not limited to simple text in an alert. You can add your custom content. For instance, there is no predefined Alert for showing exceptions, but you can add your own implementation:

// Create expandable Exception.
val exceptionText = {
  val ex = new FileNotFoundException("Could not find file blabla.txt")
  val sw = new StringWriter()
  val pw = new PrintWriter(sw)
  ex.printStackTrace(pw)
  sw.toString
}
val label = new Label("The exception stacktrace was:")
 val textArea = new TextArea {
  text = exceptionText
  editable = false
  wrapText = true
  maxWidth = Double.MaxValue
  maxHeight = Double.MaxValue
  vgrow = Priority.Always
  hgrow = Priority.Always
}
val expContent = new GridPane {
  maxWidth = Double.MaxValue
  add(label, 0, 0)
  add(textArea, 0, 1)
}

new Alert(AlertType.Error) {
  initOwner(stage)
  title = "Exception Dialog"
  headerText = "Look, an Exception Dialog."
  contentText = "Could not find file blabla.txt!"
  // Set expandable Exception into the dialog pane.
  dialogPane().expandableContent = expContent
}.showAndWait()

013 - exception alert

Text Input Dialog

You can get simple text input using the TextInputDialog. It works similar to alerts, but it returns an Option containing the text entered by the use (alerts return the button pressed):

val dialog = new TextInputDialog(defaultValue = "walter") {
  initOwner(stage)
  title = "Text Input Dialog"
  headerText = "Look, a Text Input Dialog."
  contentText = "Please enter your name:"
}

val result = dialog.showAndWait()

result match {
  case Some(name) => println("Your name: " + name)
  case None       => println("Dialog was canceled.")
}

013 - text input dialog

Choice Dialog

There is also a predefined dialog for selecting from a list of available choices: ChoiceDialog.The list can be collection of arbitrary objects. The choice dialog will return Option selected by the user. You create the dialog by specifying default choice and the collection of available choices.

val choices = Seq("a", "b", "c")

val dialog = new ChoiceDialog(defaultChoice = "b", choices = choices) {
  initOwner(stage)
  title = "Choice Dialog"
  headerText = "Look, a Choice Dialog."
  contentText = "Choose your letter:"
}

val result = dialog.showAndWait()

result match {
  case Some(choice) => println("Your choice: " + choice)
  case None         => println("No selection")
}

013 - choice dialog

Custom Dialog

Custom dialogs are created using Dialog class. Below is an example of a login dialog. Class Result defines result returned by the dialog. The dialog contains a custom graphic, two input fields (“Username” and “Password”), and custom buttons (“Login” and “Cancel”).

case class Result(username: String, password: String)

// Create the custom dialog.
val dialog = new Dialog[Result]() {
  initOwner(stage)
  title = "Login Dialog"
  headerText = "Look, a Custom Login Dialog"
}

// Set the button types.
val loginButtonType = new ButtonType("Login", ButtonData.OKDone)
dialog.dialogPane().buttonTypes = Seq(loginButtonType, ButtonType.Cancel)

// Create the username and password labels and fields.
val username = new TextField() {
  promptText = "Username"
}
val password = new PasswordField() {
  promptText = "Password"
}

val grid = new GridPane() {
  hgap = 10
  vgap = 10
  padding = Insets(20, 100, 10, 10)

  add(new Label("Username:"), 0, 0)
  add(username, 1, 0)
  add(new Label("Password:"), 0, 1)
  add(password, 1, 1)
}

// Enable/Disable login button depending on whether a username was entered.
val loginButton = dialog.dialogPane().lookupButton(loginButtonType)
loginButton.disable = true

// Do some validation (disable when username is empty).
username.text.onChange { (_, _, newValue) => 
  loginButton.disable = newValue.trim().isEmpty
}

dialog.dialogPane().content = grid

// Request focus on the username field by default.
Platform.runLater(username.requestFocus())

// When the login button is clicked, convert the result to a username-password-pair.
dialog.resultConverter = dialogButton =>
  if (dialogButton == loginButtonType) Result(username.text(), password.text())
  else null

val result = dialog.showAndWait()

result match {
  case Some(Result(u, p)) => println("Username=" + u + ", Password=" + p)
  case None               => println("Dialog returned: None")
}

013 - custom login dialog

Summary

ScalaFX 8.0.40-R8 brings support for Alerts and Dialogs. There are several predefined dialogs: Information, Warning, Error, Confirmation, Text Input, and Choice. Predefined dialog allow some level of customization of their content and buttons. Source code for the examples of pre-defined dialogs, including customization, are in DialogsDemo.

Completely customized dialogs can be created using Dialog class. Source code for a custom login dialog is in LoginDialogDemo.

To use ScalaFX 8.0.40 add following to your SBT:

libraryDependencies += "org.scalafx" %% "scalafx" % “8.0.40-R8”

ScalaFX 8u40 – TextFormatter – part 2

JavaFX 8.0_u40 brings several enhancements, the new ScalaFX release 8.0.40 supports them the. The new Spinner and the basic use of TextFormatter were described in previous blogs. ScalaFX use of Dialogs and Alerts will be described in following posts.

TextFormatter restricts and formats the text that can be displayed and typed in a TextInputControl. TextFormatter has one or both:

  • Filter – intercepts and can modify or reject text changes,
  • ValueConverter – a StringConverter that converts between the values and text. There are several predefined converters in scalafx.util.converter package.

In the first part we presented a simple scenario where we specify a string converter to display TextField content as currency. Here we will look at an example that uses a filter and a converter together.

Imagine, for the sake of this example, that we have a TextField that should contain one part of the text that is fixed and the rest that can be freely edited. User types messages in that text field and presses Enter to send. The restriction is that that text field always contains a prompt: > in front of the message:

011 - input

User should be able to freely edit the message, but should not be able to delete the prompt or move caret on or in front of the prompt. As in the previous example a converter is used to ensure that there is a prompt in front of the message when content of the text field is set. However the converter is not activated while user is editing content of a text input field, only when the value is set. TextFormatter’s filter gives you ability to intercept and modify any change done to the text content. It is called as changes happen before they are applied or displayed on the screen. The filter is a function that takes as an input change event; it can modify that it before it returns it back:

def filter(change:Change) : Change

In our case we will look for changes that would remove prompt or misplace the caret.

val prompt = "> "
...  
val filter: (Change) => Change = { change: Change =>
  // Restore prompt if part was deleted
  if (change.controlNewText.length <= prompt.length) {
    change.text = prompt.substring(change.controlNewText.length)
  }
  // Restore caret position if it moved over the prompt
  if (change.anchor < prompt.length) { 
    change.anchor = prompt.length
  }
  if (change.caretPosition < prompt.length) {
    change.caretPosition = prompt.length
  }
  change
}

As in the previous example we will use a custom text converter that will add prompt when text is set:

case class Message(text: String) {
  override def toString = s""$text""
}

val converter = new StringConverter[Message] {
  override def fromString(s: String): Message = {
    val r = if (s.startsWith(prompt)) s.substring(prompt.length)
            else s
      Message(r)
  }
  override def toString(v: Message): String = prompt + v.text
}

Now we use our converter and filter to create a text formatter, with some default content:

val formatter = new TextFormatter[Message](converter, Message("hello"), filter)

And the text field with our text formatter and action that sends the messages when Enter is pressed.

val textField = new TextField {
  text = prompt
  textFormatter = formatter
  onAction = (a: ActionEvent) => {
    val str = text()
    val message = converter.fromString(str) + "\n"
    outputTextArea.text = message + outputTextArea.text()
    text() = ""
  }
}

Here is the complete example:

011 - input-output

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.event.ActionEvent
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.TextFormatter.Change
import scalafx.scene.control.{Label, TextArea, TextField, TextFormatter}
import scalafx.scene.layout.{BorderPane, VBox}
import scalafx.util.StringConverter

object TextFormatterWithChangeFilterDemo extends JFXApp {

  case class Message(text: String) {
    override def toString = '"' + text + '"'
  }

  val prompt = "> "

  val converter = new StringConverter[Message] {
    override def fromString(s: String): Message = {
      val r =
        if (s.startsWith(prompt)) s.substring(prompt.length)
        else s
      Message(r)
    }
    override def toString(v: Message): String = {
      prompt + v.text
    }
  }

  // Filter the change restoring prompt if it was removed and 
  // correcting caret position
  val filter: (Change) => Change = { change: Change =>
    // Restore prompt if part was deleted
    if (change.controlNewText.length <= prompt.length) {
      change.text = prompt.substring(change.controlNewText.length)
    }
    // Restore caret position if it moved over the prompt
    if (change.anchor < prompt.length) change.anchor = prompt.length
    if (change.caretPosition < prompt.length) change.caretPosition = prompt.length
    change
  }
  val formatter = new TextFormatter[Message](converter, Message("hello"), filter)

  val outputTextArea = new TextArea {
    editable = false
    focusTraversable = false
  }

  val textField = new TextField {
    text = prompt
    textFormatter = formatter
    onAction = (a: ActionEvent) => {
      val str = text()
      val message =
        if (outputTextArea.text().length == 0)
          converter.fromString(str)
        else
          converter.fromString(str) + "n"
      outputTextArea.text = message + outputTextArea.text()
      text() = ""
    }
  }

  stage = new PrimaryStage {
    scene = new Scene(300, 200) {
      title = "TextFormatter Demo"
      root = new VBox {
        spacing = 6
        padding = Insets(10)
        children = Seq(
          new BorderPane {
            top = textField
            center = outputTextArea
          }
        )
      }
    }
  }
}

The TextFormatter support is available in latest SNAPSHOT releases of ScalaFX. To use it add following to your SBT build file:

libraryDependencies += "org.scalafx" %% "scalafx" % “8.0.40-SNAPSHOT”

ScalaFX – Using Methods with Ambiguous Signature

In Java there is a distinction between primitive types and their wrappers. For instance, int is a primitive type and Integer is its wrapper. In Scala they are expressed by the same type: Int. Using ScalaFX classes that are parametrized with type corresponding to Java wrapper type, like Integer, can lead to compilation errors.

Assume here that we are using a ListView that contains Int items, so we will have

val listView = new ListView[Int] { ... }

Consider method scrollTo. It has two variants scrollTo(Int index), that scrolls to item with given index, and scrollTo(T object), that scrolls to given item on the list. Since T is Int, we have two methods with the same signature. If we try to use scrollTo:

listView.scrollTo(5)

we will get compilation error:

    Error:(160, 16) ambiguous reference to overloaded definition,
        both method scrollTo in class ListView of type (o: Int)Unit
        and  method scrollTo in class ListView of type (index: Int)Unit
        match argument types (Int)
            listView.scrollTo(5)
                     ^

A work around is to use parameter names to disambiguate between invocations:

listView.scrollTo(index = 5)

Now the code will compile fine, and we will scroll by the list index (rather than item value).

ScalaFX 8u40 – TextFormatter – part 1

Upcoming release of JavaFX 8u40 will bring several enhancements, including dialogs and new spinner control. ScalaFX started new branch, SFX8u40, to support JavaFX 8u40. Earlier post described Spinner control. In this two-part post we will cover TextFormatter and related features.

TextFormatter lets you edit custom values in text controls, like TexFIeld, Text Area, and other derived from TextInputControl. Say that you want to use a TextField to enter numbers. The new `textFormatter` property lets you directly interact with a number in a TextInputControl rather than its textual representation. For instance, you can bind other properties to that number.

Let’s look at a simple example where a TextField represent some amount of a currency, say US dollars. First thing is to create a StringConverter for converting between string like “$67.98” and a number. We can use number format provide by Java’s NumberFormat:

val currencyFormat = NumberFormat.getCurrencyInstance(Locale.US)
val converter = new FormatStringConverter[Number](currencyFormat)

To see how binding works, lets create a simple slider:

val slider = new Slider(0, 10000, 1000)

TextField can bind it’s a numeric value to the slider. The numeric value is accessible though textFormatter’s value property:

new TextField {
  textFormatter = new TextFormatter (converter) {
    value <==> slider.value
  }
}

Here is ScalaFX demo application that displays text field (formatted as a currency) and a slider that can modify value of the text field.

011 - ScalaFX 8u40 - TextFormatter

import java.text.NumberFormat
import java.util.Locale

import scala.language.implicitConversions
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.{Slider, TextField, TextFormatter}
import scalafx.scene.layout.{Region, VBox}
import scalafx.util.converter.FormatStringConverter

object TextFormatterDemo extends JFXApp {

  val slider = new Slider(0, 10000, 1000)

  val textField = {
    val currencyFormat = NumberFormat.getCurrencyInstance(Locale.US)
    val converter = new FormatStringConverter[Number](currencyFormat)
    new TextField {
      textFormatter = new TextFormatter(converter) {
        value <==> slider.value
      }
      maxWidth = 140
      maxHeight = Region.USE_COMPUTED_SIZE
    }
  }

  stage = new PrimaryStage {
    scene = new Scene(300, 200) {
      title = "TextFormatter Demo"
      root = new VBox {
        content = Seq(textField, slider)
        padding = Insets(20)
        spacing = 12
      }
    }
  }

  slider.requestFocus()
}

The TextFormatter support is available in latest SNAPSHOT releases of ScalaFX 8u40. To use it add following to your SBT build file:

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

Above we showed a simpler scenario of using TextFormatter where we specified string converter. There are another ways, with higher degree of control. TextFormatter can have a filter that monitors and modifies changes to the text, caret, and selection before they are displayed on the screen. This will be subject of the part 2 of this blog post.

Developing ImageJ plugins with SBT using sbt-imagej

ImageJ is open source image processing and analysis software. It is written primarily in Java. One of its killer features is the ability to be extended by adding plugins. Simple plugins are just a couple lines of macro code in a text file. Complex ones built from thousands lines of code a packaged in a Java jar with a special manifest. They can depend on external libraries or other plugins. Packaged plugins can be written in Java, Scala, or other JVM languages.

Developing ImageJ plugins is quite simple. Though, one annoyance comes when you want to run/debug your plugin from within ImageJ. This is due to the way ImageJ discovers plugins and dependencies. It looks into its runtime directory for subdirectory called `plugins`. Then searches the `plugins` directory (or its direct subdirectories) for jars and other files containing plugins. The annoyance is that you have to package the plugin you are working on into a jar and then copy it, and possibly also all dependencies, to ImageJ `plugins` directory.

Good thing is that you can run ImageJ from you project pointing a custom runtime directory you create. You just need to copy your plugin jars there every time you build you project and run ImageJ. It is handy to have a tool that does all the work, even better if it integrates with your IDE of choice.

sbt-imagej plugin for SBT

I am using SBT as my build system. It is works with Scala, Java, and other languages. It is much simpler than Ant or Maven, in a way it is similar to Gradle.

In the past I creating a couple of ImageJ plugins project in SBT. The setup for preparing ImageJ for testing a plugin was quite similar. I was just copying and pasting configuration to new projects. Since SBT plugins are relatively easy to create, I decided to write one for development of ImageJ plugins, called sbt-imagej. The idea was to have tool that is easy to setup that works in most cases with ability to customize, if needed. Using the sbt-imagej plugin is as simple as declaring and enabling it in your build. Here is a complete example of SBT build file (`build.sbt`) that can build, setup, and run ImageJ project:

// Your project information
name := "awesome-imagej-plugin"

version := "1.0"

// Add dependency on ImageJ
libraryDependencies += "net.imagej" % "ij" % "1.49k"

// Enable sbt-imagej plugin tasks
enablePlugins(SbtImageJ)

The only thing that needs to be added to `build.sbt` is the line containing `enablePlugins(SbtImageJ)`. This enables two tasks `ijRun`, `ijPrepareRun`, and a bunch of customization options.

To let SBT know where to find `SbtImageJ`, you will also need to add `sbt-imagej` declaration to `project/settings.sbt` file:

addSbtPlugin("net.sf.ij-plugins" % "sbt-imagej" % "2.0.0")

Though not strictly necessary, it is also recommended specifying which version of SBT you are using, so also add `project/build.properties` with a single line:

sbt.version=0.13.7

Those three files: `build.sbt`, `project/settings.sbt`, and `project/build.properties` constitute complete specification of a SBT build project. It can be used in IntelliJ IDEA, Eclipse, and NetBeans.

The sbt-imagej plugin provides two tasks:

  • `ijPrepareRun` – it builds your plugin and copies it to proper directory, but does not run ImageJ. It is intended to be run from your IDE before run/debug.
  • `ijRun` – to builds your plugin, copies it to proper directory (using `ijPrepareRun`) and runs ImageJ. It is intended for running from a command prompt.

There is also a bunch of configuration options, they are described on the sbt-imagej project page.

OK but where do you put your source code. SBT, by default, is using the same directory structure as Maven and Gradle. Your sources are in `src\main`. If you are using Java, the code will go into `src\main\java`. If you are using Scala, into `src\main\scala`, and so on. One important thing to keep in mind is that resources go into `src\main\resources`, so your plugin manifest needs to go into `src\main\resources\plugin.properties`. You can see that in the example project. It contains two plugins that add effects to color images “Sepia Tone”, and “Vignette”. The plugin manifest src\main\resources\plugin.properties looks as follows:

Plugins>Effects, "Sepia Tone", effects.SepiaTonePlugIn
Plugins>Effects, "Vignette", effects.VignettePlugIn

It indicates that the plugins will be install under ImageJ menu `Plugin > Effects`.

You can write ImageJ plugins in different languages. For the sake of illustration one plugin is written in Java (src/main/java/effects/VignettePlugIn.java) and the other in Scala (src/main/scala/effects/ SepiaTonePlugIn.scala)

You can find complete example project in the `sbt-imagej` example directory.

Using SBT from command line

To run the example code from SBT command prompt, start SBT (in the project directory) and type command `ijRun`. This will download needed dependencies, build the plugins, package them, copy jar to `plugins` directory, and start ImageJ with plugins loaded into menu Plugins > Effect.

ImageJ runing

Integrating with an IDE

The basic idea of integration is to execute ` ijPrepareRun` before run/debug. It can be done from all major IDEs: Eclipse, IntelliJ IDEA, and NetBeans. IntelliJ IDEA, and NetBeans support SBT projects, you can load them directly using Scala plugins for each IDE. Eclipse requires additional step of generating Eclipse project from SBT project.

IntelliJ IDEA

You will need to add two plugins to IDEA “Scala” (for loading SBT projects into IDE) and “SBT” (for executing SBT tasks). To add the plugins select File > Settings > Plugins > Browse Repositories. In the search box type “SBT” then install “Scala” and “SBT” plugins. Restart IDE.

IntelliJ plugins

Load/import the example projects. To run or debug ImageJ you will need to create new run configuration. Select Run > Edit Configurations. Then:

  1. Click on “+” in top left to create new configuration and select “Application”
  2. Give you configuration a name, say “ImageJ”
  3. As the main class select ij.ImageJ (The ImageJ’s main class)
  4. Select your working directory, here we use the default value “sandbox”
  5. Select the module, here it is called “example”
  6. Now the critical part. In the “Before launch” configuration are in the bottom, click on “+” and select “SBT”. Replace the content of the combo box typing “ijPrepareRun” hit Enter (it is important) then click OK.
    010 - IntelliJ SBT task

At his point you should have your run/debug configuration ready.

010 - IntelliJ Run Configuration

Click OK to save it.

Now you can run or debug ImageJ with your plugin. First you run can take a bit longer as SBT setups the build, after that, runs should be pretty quick. You can of cause also run in debug mode too, putting breakpoints in your plugin code.

Eclipse

Unlike IntelliJ IDEA or NetBeans, at the time of writing, Eclipse cannot directly read SBT projects. However you can generate Eclipse project using sbteclipse plugin. Then you load it into Eclipse as any other project.

Eclipse cannot directly run SBT tasks, but it can run Ant targets, so we use a simple Ant target to start SBT task. A sample Ant file is provided with the example. So right click on the `build.xml` and select “Run as Ant build…”. In the Edit Configuration dialog select “sbt-imagej-run” and click Run.

010 - Eclipse run configuration

Same thing works for debug.

NetBeans

Similar to IntelliJ IDEA, NetBeans can load SBT projects directly using its Scala plugin. So first install the plugin, actually it is divided in several smaller plugins, so you will need to install all the pieces. Go to Tools > Plugins > Available Plugins. In the search box type “Scala”, select all the plugins in “Scala” category and click “Install”. After you restart NetBeans you will be able to simply load SBT project as regular NetBeans project.

010 - NetBeans load project

To build and run ImageJ you will use SBT console provided by the Scala plugin. In the console type `ijRun` and press “Enter”

010 - NetBeans SBT console

For the debugging in NetBeans you will need to attach debugger to the running process.

Wrap-up

Testing ImageJ plugins during development requires packaging and copying around plugin jars and dependencies. sbt-imagej plugin for SBT simplifies project setup. It can build and copy files as needed. It can also be integrated into your IDE run/debug process. You can find additional information about, including custom settings, on the sbt-imagej project page.

ScalaFX 8.0.40-SNAPSHOT + Spinner

Upcoming release of JavaFX 8u40 will bring several enhancements, including dialogs and new spinner control. ScalaFX started new branch, SFX8u40, to support JavaFX 8u40. One of the first things added is Spinner. A spinner is a single line text field that lets the user select a number or an object value from an ordered sequence. There are predefined Spinner classes for holding values of Integer, Double, and List of arbitrary values.

009-SpinnersDemo

Here is a sample ScalaFX code that generates above Spinners for Integers, Doubles, and lists of Strings

import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.collections.ObservableBuffer
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.Spinner
import scalafx.scene.layout.{HBox, VBox}

/**
 * A sample that demonstrates the Spinner control.
 */
object SpinnersDemo extends JFXApp {

  val styles = Seq(
    "spinner", // defaults to arrows on right stacked vertically
    Spinner.StyleClassArrowsOnRightHorizontal,
    Spinner.StyleClassArrowsOnLeftVertical,
    Spinner.StyleClassArrowsOnLeftHorizontal,
    Spinner.StyleClassSplitArrowsVertical,
    Spinner.StyleClassSplitArrowsHorizontal
  )

  val intSpinners = for (s <- styles) yield
    new Spinner[Integer](1, 99, 5) {
      styleClass += s
      prefWidth = 100
    }

  val stringSpinners = for (s <- styles) yield
    new Spinner[String](ObservableBuffer("Grace", "Matt", "Katie")) {
      styleClass += s
      prefWidth = 100
    }

  val doubleSpinners = for (s <- styles) yield
    new Spinner[Double](0.0, 1.0, 0.5, 0.01) {
      styleClass += s
      prefWidth = 100
    }

  stage = new PrimaryStage {
    title = "Spinners Demo"
    scene = new Scene {
      content = new VBox(30) {
        content = Seq(
          new HBox(30, intSpinners: _*),
          new HBox(30, doubleSpinners: _*),
          new HBox(30, stringSpinners: _*)
        )
        padding = Insets(24)
      }
    }
  }
}

To take a SNAPSHOT for a spin add following to your SBT build file:

libraryDependencies += "org.scalafx" %% "scalafx" % “8.0.40-SNAPSHOT”

Use Ensemble to learn ScalaFX by example

ScalaFX-Ensemble is an application that showcases over 60 simple examples of using ScalaFX API. The examples cover charts, shapes, effects, UI components, UI layouts, and web controls.

ScalaFX-Ensemble was created in 2012 by members of JUGChennai. It was inspired by the JavaFX Ensemble. Recently, there was a major overhaul of the examples. The code was simplified and updated to work with latest release of ScalaFX. ScalaFX-Ensemble is now a part of the newly created ScalaFX organization on GitHub.

To run ScalaFX-Ensemble you will need to download the source code. You can do it either by checking out the source using Git or simply downloading zipped source using “Download ZIP” button on ScalaFX-Ensemble project page.

You will need to install SBT and set JAVA_HOME environment variable to point to the directory where your JDK is installed. For Java 7, JAVA_HOME will be used to locate JavaFX runtime, it is assumed to be in $JAVA_HOME/ jre/lib/jfxrt.jar

With SBT and JAVA_HOME set, you can change to directory to where you saved ScalaFX-Ensemble and from command prompt execute command:  `sbt run`. This will download needed dependencies, build and run the ScalaFX-Ensemble application.

ScalaFX-Ensemble application lets you browse code examples using the three view on the left or the grid in the center.

111313_0302_UseEnsemble1.png

Selected example is displayed in the central pane (under “Demo” tab).

111313_0302_UseEnsemble2.png

You can view source code selecting “Source” tab.

111313_0302_UseEnsemble3.png

Each example is a self-contained ScalaFX application. You can use the “Save SBT Project” to save the example together with SBT build configuration. To  build it and run outside the ScalaFX-Ensemble use SBT command `run`.  To create project configurations for IntelliJ IDEA and Eclipse, use SBT commands `gen-idea` and `eclipse`.  Detailed instructions how to run examples using SBT and generate IDE setups are in README.md file saved with the project.

New ScalaFX 1 Milestone 6 and ScalaFX 8 Milestone 2

New milestones for ScalaFX are available. There are two parallel branches, ScalaFX 1 supports JavaFX 2, and ScalaFX 8 supports JavaFX 8.

Summary of Changes in ScalaFX 1 since Milestone 5

  • Fixed Issue 82: DragEvent.gestureTarget returns gestureSource.
  • Fixed Issue 83: “java.util.NoSuchElementException: None.get” in `scalafx.delegate.SFXEnumDelegateCompanion#jfxEnum2sfx`
  • Fixed Issue 88: JavaFX 2.2.40 introduced new MediaPlayer.Status enum value DISPOSED
  • Fixed Issue 89: JavaFX 2.2.40 introduced new property initialFileName in FileChooser
  • Fixed Issue 91: Setting property value to null causes NPE.
  • Fixed Issue 94: Generated scaladocs should have ScalaFX and version in the title.
  • Fixed Issue 95: Add convenience methods for assigning content to Clipboard.
  • Fixed Issue 97: Add wrapper for CheckMenuItem.
  • Add test for `Camera`, part of Issue 98.
  • In SBT, use project name as shell prompt.
  • Upgrade Scala 2.10 to latest release 2.10.3, drop support for Scala 2.9.2, keep only the latest in 2.9: 2.9.3.
  • Make it easier to assign data to a chart when observing data changes is not needed: allow `Seq` in addition to `ObservableBuffer`.

Summary of Changes in ScalaFX 8 since Milestone 1

  • Merge in all improvements from ScalaFX 1 Milestone 6
  • Fixed issue 84 – removed alignWithContentOrigin from scalafx.stage.PopupWindow. This is due to the equivalent member of javafx.stage.PopupWindow, alignWithContentOriginProperty, being removed in JFX 8 b108.
  • Fixed Issue 86 – JavaFX 8 b108 introduced new properties and enum in PopupWindows.
  • Fixed Issue 87 – JavaFX 8 b108 introduced new properties and in WebEngine.
  • Fixed Issue 98: Camera is missing wrappers for farClip and nearClips properties.
  • ZERO constant added to Point2D and 3D.
  • Several new assignment methods in TriangleMesh
  • Improvements to TriangleMeshDemo.
  • Some Scaladoc improvements.
  • Add to Scene new constructors that allow control of anti-aliasing. Update 3D examples to use anti-aliasing to improve rendering.
  • Set JVM target/source to “1.8”, ScalaFX 8 will not work with earlier versions of Java anyway.

What is New in ScalaFX 8 Milestone 1

ScalaFX helps you simplify creation of JavaFX-based user interfaces in Scala. ScalaFX uses a simple, hierarchical pattern for creating new objects and building up the scene graph. ScalaFX supports full interoperability with Java and can run anywhere the Java Virtual Machine (JVM) and JavaFX are supported.

ScalaFX 8 provides support for JavaFX that is part of the upcoming release of Java 8. Wherever possible ScalaFX 8 shares code with ScalaFX 1, in particular, ScalaFX 8 M1 incorporates improvements from ScalaFX 1 M5.

JavaFX 8, beside many new features, introduced some backward compatibility breaking changes. The two main goals of this first milestone of ScalaFX 8 were: 1) make existing code work with Java 8, fix and issues related to failing tests, 2) add some key wrappers for the new 3D functionality. For instance, JavaFX 8 deprecated Builders and actually removed some of them. ScalaFX 8 code was modified not to use Builders where appropriate. This was a particular problem for many tests that relied on Builders.

ScalaFX 8 was tested with Java 8 early access build 106, more info can be found on the ScalaFX 8 Development page.

Summary of changes compared to ScalaFX 1.0 Milestone 5

  • Tested against Java 8 early access build 106 (September 3, 2013)
  • Ported existing code from ScalaFX 1 Milestone 5
  • Some class hierarchies were changed to match changes in JavaFX 8
  • About 27 new rappers were added, initially mostly for the new 3D functionality.
  • 4 new demos added to illustrate the new 3D API.
  • Over 30 existing classes were modified to add new properties and methods.

Changes in inheritance hierarchy to match changes in JavaFX 8

  • `Camera` extends `Node`
  • `Control` extends `Region`

Many new wrappers added with corresponding implicit conversions and tests

  • scalafx.collections
    • ObservableArray
    • ObverservableFloatArray
    • ObservableIntegerArray
  • scalafx.geometry.NodeOrientation
  • scalafx.scene
    • AmbientLight
    • LightBase
    • PointLight
    • SubScene
  • scalafx.scene.control.TableColumnBase
  • scalafx.scene.input.PickResult
  • scalafx.scene.layout
    • Background
    • Border
  • scalafx.scene.paint
    • Material
    • PhongMaterial
  • scalafx.scene.shape
    • Box
    • CullFace
    • Cylinder
    • DrawMode
    • Mesh
    • MeshView
    • Shape3D
    • Sphere
    • TriangleMesh
  • scalafx.scene.transform
    • MatrixType
    • NonInvertibleTransformException
    • TransformChangedEvent
  • scalafx.stage.Stage

New properties and `enum`s added to existing wrappers

AreaChart, ComboBox, ConditionalFeature, EventType, FileChooser, Font, Image, Labelled, ListView, MediaPlayer, MouseEvent, Node, Region, PerspectiveCamera, PopupWindow, Scene, SceneProperty, StackedAreaChart, Stage, StageStyle, Tab, TableView, TreeItem, TreeView, TextBoundsType, TextInputControl, Text, Transform, WebEngine, WebView.

New demos for 3D functionality

Where to Get It

You can download ScalaFX binaries and source code from the project website.

Artifacts are also published on the Maven Central, so you can use them from you favorite build system SBT, Gradle, Maven, etc.

What is New in ScalaFX 1.0 Milestone 5

ScalaFX helps you simplify creation of JavaFX-based user interfaces in Scala. ScalaFX uses a simple, hierarchical pattern for creating new objects and building up the scene graph. ScalaFX supports full interoperability with Java and can run anywhere the Java Virtual Machine (JVM) and JavaFX2 are supported.
ScalaFX 1.0 M5 supports most of the JavaFX 2.2.25 functionality, (distributed with Java 1.7.0_u25).

Summary of Changes since Milestone 4

  • Major improvements to Chart’s API – charts can be created without need to use JavaFX type parameters and the same hierarchical build pattern as in the rest of ScalaFX.
  • Easier way to create, add and remove event handlers and event filters that handle more than one event.
  • New implicit conversions from color from tuples.
  • New wrapper for `VerticalDirection`.
  • Added several of new demos, improved a couple of the existing ones.
  • Several bug fixes, including issues 61, 66, 67 and 70.
  • Internal code cleanup and simplification.
  • Several improvements to build system, tests, upgrades to dependent libraries and SBT plugins.

New and Improved

Major improvements to chart’s API

The focus was on making the chart API easier to use, in particular, to remove to able to create charts without using JavaFX type parameters (Issue 79) and being able to use ScalaFX’s hierarchical build pattern (Issue 72). The improvements are illustrated in this complete example of creating an application that displays a Pie Chart:

import scalafx.application.JFXApp
import scalafx.collections.ObservableBuffer
import scalafx.scene.Scene
object PieChartDemo extends JFXApp {
  val dataPairs = Seq(("Sun", 25), ("IBM", 17), ("HP", 25), ("Dell", 27), ("Apple", 5))
  stage = new JFXApp.PrimaryStage {
    title = "PieChartDemo"
    scene = new Scene {
      root = new PieChart() {
        title = "Pie Chart"
        clockwise = false
        data = ObservableBuffer(dataPairs.map {case (x, y) => PieChart.Data(x, y)})
      }
    }
  }
}

pie-chart

There are a couple of new demos of charts available.

Easier way to create, add and remove event handlers and event filters

Focus here was on making it easier to create event handlers and event filters that deal with more than one event (Issue 77). That may be useful for instance in code that handles mouse interaction, especially when that interaction is context dependent. Here is a code snipped illustrating use of the new/improved `handleEvent` construct:

  val pane = new Pane() {...}
  // Define handling of mouse events and add it to the pane
  val subscription = pane.handleEvent(MouseEvent.Any) {
    me: MouseEvent => {
      me.eventType match {
        case MouseEvent.MousePressed => {...}
        case MouseEvent.MouseDragged => {...}
        case _ => {...}
      }
    }
  }
  ...
  // Remove event handler when interaction is no longer needed
  subscription.cancel()

There are two new demos that illustrate this in more details: RectangleDrawingDemo and MultipleShapeDrawingDemo.

New implicit conversion for colors

You can now assign RGB color using tuples instead us using `Color.rgb(…)` methods, for instance:

import scalafx.Includes._
import scalafx.scene.shape.Rectangle
...
  val r = new Rectangle {
    // Instead of: fill = Color.rgb(64, 128, 196, 0.5)
    fill = (64, 128, 196, 0.5)
    // Instead of: stroke = Color.rgb(187, 56, 19)
    stroke = (187, 56, 26)
    // or using interpreting Int as hex values
    stroke = 0xBB381A
  }

New demos

There are several new demo, most illustrate improvements to charts and event handling.

Improved demos

A few demos were improved. It is now also possible to easily run any demo from SBT, it used to default to ColorfulCircles demo.

  • MenuBarTest – improve layout – menu fill the top line, is not truncated on the right.
  • MenuTest – add examples of event handlers for menu items.
  • VideoCubesDemo – do not use hard coded locations to external files, use system property

Internal code cleanup and simplification

  • New `EventHandlerDelegate` trait to remove duplicate code for add/removeEventHandler/Filter in `Node`, `MenuItem`, `TableColumn`, `TreeItem`, and `Window`.
  • Provide explicit ScalaFX types in some additional cases when JavaFX types were inferred, to simplify API usage.
  • Improved handling of `null` assigned to a property – Some properties could be legally assigned `null` value. In this release more places are fixed where this used to lead to `NullPointerException`.

In SBT, fork tests so they are run on a separate JVM, but do not run individual tests is parallel

ScalaFX tests require initialization of JavaFX application thread. Running tests on a separate JVM avoids conflicts caused by JavaFX application thread initializations. It used to be that test can be run only once and SBT has to be restarted; now you can rerun tests without in the same SBT instance.

Build system and dependency upgrades

  • Use SBT 0.13
  • Scala 2.10.1 upgraded to 2.10.2
  • JUnit 4.10 to 4.11
  • Sbt-idea 1.3.0 to 1.5.1
  • Sbteclipse-plugin 2.1.1 to 2.2.0

Bug Fixes

  • Issue 61 – method ProgressBarTableCell#forTableColumn() did not work correctly.
  • Issue 66 – Wrapper classes should not be `final` – this prevents use of hierarchical builder pattern. Also update related VideoCubeDemo demo.
  • Issue 67 – MediaPlayer – simplify handling of Runnable. Do it without introducing overly generic implicit conversion of a code block to Runnable.
  • Issue 70 – could not compile code that used ‘EventHandler#handleEvent(…){…}'”.
  • TableColumn#removeEventHandler actually called delegated call to addEventHandler
  • Node#onTouchReleased and Scene#onTouchReleased actually delegated to onTouchPressed

Where to Get It

You can download ScalaFX binaries and source code from the project website.

Artifacts are also published on the Maven Central, so you can use them from you favorite build system SBT, Gradle, Maven, etc.