Category Archives: ScalaFX

Quickly create ScalaFX project skeleton with sbt new

It was previously possible to create new project skeltons using Gitter8. For instance you could create ScalaFX project using:

g8 scalafx/scalafx.g8

With the SBT release, 0.13.13, you do not need to install Gitter8. You can do it directly using SBT new command:

sbt new scalafx/scalafx.g8

You can read more about SBT 0.13.13 here.

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.

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.