Category Archives: JavaFX

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”
Advertisements

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.

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.

Getting Started with ScalaFX: Compile and Run

ScalaFX is a Scala library for creation user interface applications based on JavaFX platform. Its intention is to be simpler to use and more “Scala-like” compared to the underlying JavaFX API.

This post is intended to help you setup a basic Scala project that is using ScalaFX library. The setup only requires dependencies on the ScalaFX, JavaFX, and the Scala libraries. If you add those three libraries you are good to go and develop ScalaFX application.

We will cover steps to do the setup using SBT build script. SBT is a build tool for Scala and Java projects.  SBT is de-facto standard build system for many Scala projects. Even if you do not use it, it is good to be familiar with it. You can also use an SBT build script for creating Eclipse, IDEA, NetBeans, and Maven setups (covered in a future post).

In our example we will compile and run a simple ScalaFX demo:

package hello

import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.Label
import scalafx.scene.layout.BorderPane

object HelloSBT extends JFXApp {
  stage = new PrimaryStage {
    scene = new Scene {
      root = new BorderPane {
        padding = Insets(25)
        center = new Label("Hello SBT")
      }
    }
  }
}

The demo opens a window and displays text “Hello SBT” in the center:

hello-sbt

Prerequisites

As the minimum starting requirement you only need to install Java SDK and SBT. Everything else needed to compile and run ScalaFX applications will be downloaded by SBT automatically.

You will also need a text editor to edit. Any decent test editor will do, for instance, jEdit or Sublime Text.

1. Install Java 7

I assume that you have Java 7 installed on your system. If you do not, you can download latest version from Oracle’s Java SE page.

2. Set JAVA_HOME environment variable

JavaFX is distributed with recent Java 7 releases, but it is not automatically added to class path. You need to define environment variable JAVA_HOME pointing to the installation directory of the JDK. This variable will be used by SBT script to locate JavaFX binaries: jfxrt.jar.

On Windows the installation directory will be something like:

  C:\Program Files\Java\jdk1.7.0_21

on Linux it could be:

  /usr/lib/jvm/default-java

3. Install SBT

If you do not have SBT on your system install it following steps on “SBT Setup” page. To test the installation open command prompt and type (without “>”):

  > sbt sbt-version

If SBT is installed properly, it should respond with a series of printouts. If the is the first time you run SBT it will complete installation by downloading some dependencies needed by SBT. At the end SBT should print version information, for instance:

  ...
  [info] 0.12.3

Building and Running ScalaFX Apps using SBT

SBT can be used to easily build and run Scala and Java. SBT assumes default organization of files in you project:

  src/
    main/
      resources/
        <files to include in main jar here>
      scala
        <main Scala sources>
      java/
        <main Java sources>
    test/
      resources
        <files to include in test jar here>
      scala/
        <test Scala sources>
      java/
        <test Java sources>

If you are familiar with Maven, the SBT directory structure is the same. For instance, in your project directory Scala sources located in subdirectory src/main/scala.

1. Create “hello-sbt” project directory

Let’s first create a directory for our sample projects, call it, for instance, hello-sbt. We will add content as we progress. At the beginning our project directory is empty.

2. Create SBT configuration file

In the newly create directory create new file called build.sbt with the following content:

// Name of the project
name := "Hello SBT"

// Project version
version := "1.0"

// Version of Scala used by the project
scalaVersion := "2.10.1"

// Add dependency on ScalaFX library
libraryDependencies += "org.scalafx" %% "scalafx" % "1.0.0-M3"

// Add dependency on JavaFX library based on JAVA_HOME variable
unmanagedJars in Compile += Attributed.blank(file(System.getenv("JAVA_HOME") + "/jre/lib/jfxrt.jar"))

Note that each declaration is separated by a blank line. This is required by SBT. The first two declarations define our projects name and version. The third one specifies which version of Scala we want to use. The forth one, libraryDependencies += …, defines managed dependency on the ScalaFX library. Managed dependencies are automatically downloaded by SBT from Maven Central. The last declaration “unmanagedJars in Compile” instructs SBT where to look for JavaFX library. It is unmanaged meaning that SBT will not attempt to download it on its own. We make use of the JAVA_HOME environment variable defined earlier to point to location of JavaFX runtime: jfxrt.jar.

3. Create simple ScalaFX application

By default, SBT looks for Scala source files in subdirectory src/main/scala of you project. For our example we will use package name hello. Inside our project directory, next to build.sbt create nested directories src/main/scala/hello.

Inside inside src/main/scala/hello create HelloSBT.scala:

package hello

import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.Label
import scalafx.scene.layout.BorderPane

object HelloSBT extends JFXApp {
  stage = new PrimaryStage {
    scene = new Scene {
      root = new BorderPane {
        padding = Insets(25)
        center = new Label("Hello SBT")
      }
    }
  }
}

4. Run your application using SBT

At this point you should have everything needed to build and run the sample application. Open command prompt and change directory to the hello-sbt project (where build.sbt is located).

  > sbt run

If everything is installed and configured fine, SBT will compile the code and run your code. The SBT output will look something like:

  The system cannot find the file C:\Apps\sbt\bin\sbtconfig.txt.
  [info] Updating ...  
  [info] Resolving ...
  ...
  [info] Done updating.
  [info] Set current project to Hello SBT (…)
  [info] Updating {file:…}...
  [info] Resolving org.scala-lang#scala-library;2.10.1 ...
  [info] Resolving org.scalafx#scalafx_2.10;1.0.0-M3 ...
  [info] Done updating.
  [info] Compiling 1 Scala source to …/hello-sbt/target/scala-2.10/classes...
  [info] Running hello.HelloSBT

hello-sbt

Congratulations! Now you know how to compile and run ScalaFX code using SBT.

Closing Thoughts

You can follow steps in this post to create your own SBT project file for ScalaFX. SBT is well documented with an easy to follow “Getting Started” guide.

To learn more about ScalaFX visit the home page, check links and user groups there. The best ways to learn ScalaFX is to learn JavaFX first. There are numerous JavaFX tutorials on the Oracle website. I recommend getting a copy of the “Pro JavaFX 2” book, one of the coauthors is creator of ScalaFX Steve Chin. ScalaFX versions of the examples from the “Pro JavaFX 2” book are on GitHub project ProScalaFX.

The source for this post is in GitHub ScalaFX-Tutorials project under hello-sbt.