Package

org.scalafx.extras

mvcfx

Permalink

package mvcfx

Package mvcfx helps in implementation of Model-View-Controller-like patters, we call it MVCfx. The pattern is build around use of views defined in FXML (the view), with binding to ScalaFX using ScalaFXML library.

There are two cooperating classes ControllerFX for binding FXML to Scala code and ModelFX that contains logic for the component. An additional helper MVCfx class is provided to instantiate the ControllerFX and the corresponding ModelFX.

The structure of the UI component is defined in a standard JavaFX FXML file. The Scala side of the FXML is in a class ControllerFX. Part of the ControllerFX is automatically generated by ScalaFXML macro, the rest can be customized as needed, including binding of the UI to appropriate parts of the component logic represented by the ModelFX. Use of the MVCfx is optional and primarily intended to simplify instantiation of the ControllerFX and the ModelFX.

Below is an example using the classes in mvcfx. The code implements a simple Stop Watch. The complete code is in demos part of the ScalaFX Extras project.

Note the recommended naming convention used: * StopWatch extends MVCfx * StopWatchModel extends ModelFX * StopWatchController extends ControllerFX * StopWatch.fxml the FXVM declaration of the view

First we have the FXML definitions representing the structure of the user interface (org/scalafx/extras/mvcfx/stopwatch/StopWatch.fxml)

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.*?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1"
            fx:controller="org.scalafx.extras.mvcfx.stopwatch.StopWatchController">
    <bottom>
        <ButtonBar BorderPane.alignment="CENTER">
            <buttons>
                <Button fx:id="startButton" mnemonicParsing="false" text="Start"/>
                <Button fx:id="stopButton" mnemonicParsing="false" text="Stop"/>
                <Button fx:id="resetButton" mnemonicParsing="false" text="Reset"/>
            </buttons>
        </ButtonBar>
    </bottom>
    <center>
        <HBox>
            <children>
                <Label fx:id="minutesLabel" alignment="CENTER" contentDisplay="CENTER" text="99" textAlignment="CENTER"
                       BorderPane.alignment="CENTER">
                </Label>
                <Label text=":">
                </Label>
                <Label fx:id="secondsLabel" alignment="CENTER" contentDisplay="CENTER" text="99" textAlignment="CENTER">
                </Label>
                <Label text=".">
                </Label>
                <Label fx:id="fractionLabel" alignment="CENTER" contentDisplay="CENTER" text="99"
                       textAlignment="CENTER">
                </Label>
            </children>
        </HBox>
    </center>
</BorderPane>

The ControllerFX creates connection of the FXML to Scala code and underlying ModelFX for the application logic. Note the @sfxml annotation and the constructor arguments corresponding to controls defined in FXML, like minutesLabel. The last argument to the constructor is the ModelFX. (org.scalafx.extras.mvcfx.stopwatch.StopWatchController)

package org.scalafx.extras.mvcfx.stopwatch

import org.scalafx.extras.mvcfx.ControllerFX

import scalafx.Includes._
import scalafx.scene.control.{Button, Label}
import scalafxml.core.macros.sfxml

@sfxml
class StopWatchController(minutesLabel: Label,
                          secondsLabel: Label,
                          fractionLabel: Label,
                          startButton: Button,
                          stopButton: Button,
                          resetButton: Button,
                          model: StopWatchModel) extends ControllerFX {

  minutesLabel.text.value = format2d(model.minutes.longValue)
  model.minutes.onChange { (_, _, newValue) =>
    minutesLabel.text.value = format2d(newValue.longValue)
  }
  secondsLabel.text.value = format2d(model.seconds.longValue())
  model.seconds.onChange { (_, _, newValue) =>
    secondsLabel.text.value = format2d(newValue.longValue())
  }
  fractionLabel.text.value = format2d(model.secondFraction.longValue() / 10)
  model.secondFraction.onChange { (_, _, newValue) =>
    fractionLabel.text.value = format2d(newValue.longValue() / 10)
  }

  startButton.disable <== model.running
  stopButton.disable <== !model.running
  resetButton.disable <== model.running

  startButton.onAction = handle {
    model.onStart()
  }
  stopButton.onAction = handle {
    model.onStop()
  }
  resetButton.onAction = handle {
    model.onReset()
  }

  private def format2d(t: Number) = f"${t.longValue()}%02d"
}

The ModelFX implements the logic for the operation of the Stop Watch. Notice that there are no direct references to UI controls. The connection to the UI is through the properties (like minutes). The ModelFX is not aware how the ControllerFX is implemented. (org.scalafx.extras.mvcfx.stopwatch.StopWatchModel)

package org.scalafx.extras.mvcfx.stopwatch

import javafx.{concurrent => jfxc}

import org.scalafx.extras._
import org.scalafx.extras.mvcfx.ModelFX

import scalafx.Includes._
import scalafx.beans.property.{LongProperty, ReadOnlyBooleanProperty, ReadOnlyBooleanWrapper}

class StopWatchModel extends ModelFX {

  private val _running = ReadOnlyBooleanWrapper(false)

  val running: ReadOnlyBooleanProperty = _running.readOnlyProperty

  private val counterService = new CounterService()
  counterService.period = 10.ms

  val minutes = new LongProperty()
  val seconds = new LongProperty()
  val secondFraction = new LongProperty()

  counterService.elapsedTime.onChange { (_, _, newValue) =>
    val t = newValue.longValue()
    secondFraction.value = t % 1000
    seconds.value = (t / 1000) % 60
    minutes.value = t / 1000 / 60
  }

  def onStart(): Unit = {
    counterService.doResume()
    _running.value = true
  }

  def onStop(): Unit = {
    counterService.doPause()
    _running.value = false
  }

  def onReset(): Unit = counterService.doReset()

  private class CounterService extends jfxc.ScheduledService[Long] {

    private var timeAccumulator: Long = 0
    private var restartTime: Long = 0

    val elapsedTime = new LongProperty()

    override def createTask(): jfxc.Task[Long] = {
      new jfxc.Task[Long]() {
        override protected def call(): Long = {
          val ct = System.currentTimeMillis()
          val et = timeAccumulator + (ct - restartTime)
          onFX {elapsedTime.value = et}
          et
        }
      }
    }

    def doPause(): Unit = {
      val ct = System.currentTimeMillis()
      timeAccumulator += (ct - restartTime)
      onFX {elapsedTime.value = timeAccumulator}
      this.cancel()
    }

    def doResume(): Unit = {
      restartTime = System.currentTimeMillis()
      this.restart()
    }

    def doReset(): Unit = {
      timeAccumulator = 0
      onFX {elapsedTime.value = 0}
    }
  }
}

The MVCfx implementation is very simple, it only needs instance of the model and information about location of the FXML resource. (org.scalafx.extras.mvcfx.stopwatch.StopWatch)

package org.scalafx.extras.mvcfx.stopwatch

import org.scalafx.extras.mvcfx.MVCfx

class StopWatch(val model: StopWatchModel = new StopWatchModel())
  extends MVCfx("/org/scalafx/extras/mvcfx/stopwatch/StopWatchView.fxml")

The MVCfx can be easily used in an Application class to create UI. (org.scalafx.extras.mvcfx.stopwatch.StopWatchApp)

package org.scalafx.extras.mvcfx.stopwatch

import scala.language.implicitConversions
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.Scene
import scalafx.scene.layout.BorderPane

object StopWatchApp extends JFXApp {
  stage = new PrimaryStage {
    scene = new Scene {
      title = "StopWatch"
      root = new BorderPane {
        center = new StopWatch().view
      }
    }
  }
}
Source
package.scala
Linear Supertypes
AnyRef, Any
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. mvcfx
  2. AnyRef
  3. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. All

Type Members

  1. trait ControllerFX extends AnyRef

    Permalink

    The ControllerFX creates connection of the FXML to Scala code and underlying ModelFX for the application logic.

    The ControllerFX creates connection of the FXML to Scala code and underlying ModelFX for the application logic.

    Constructor argument names correspond to controls defined in FXML and the model. The constructor is used by ScalaFXML macro to automatically expose FXML controls in Scala code of the view class.

    See more details in the org.scalafx.extras.mvcfx documentation.

    Example:

    import org.scalafx.extras.mvcfx.ControllerFX
    
    import scalafx.Includes._
    import scalafx.scene.control.{Button, Label}
    import scalafxml.core.macros.sfxml
    
    @sfxml
    class StopWatchController(minutesLabel: Label,
                              secondsLabel: Label,
                              fractionLabel: Label,
                              startButton: Button,
                              stopButton: Button,
                              resetButton: Button,
                              model: StopWatchModel) extends ControllerFX {
    ...
      startButton.disable <== model.running
      stopButton.disable <== !model.running
      resetButton.disable <== model.running
    ...
    }
  2. abstract class MVCfx extends AnyRef

    Permalink

    MVCfx is the "root" class for creation of UI components using MVCfx pattern.

    MVCfx is the "root" class for creation of UI components using MVCfx pattern. It instantiates and binds together the model, the controller, and the view (FXML).

    The implementation of a class that extends MVCfx is very simple, it only needs instance of the model and information about location of the FXML resource. For example:

    import org.scalafx.extras.mvcfx.MVCfx
    
    class StopWatch(val model: StopWatchModel = new StopWatchModel())
      extends MVCfx("/org/scalafx/extras/mvcfx/stopwatch/StopWatch.fxml")

    The implementation will include: * StopWatch extends MVCfx * StopWatchModel extends ModelFX * StopWatchController extends ControllerFX * StopWatch.fxml

    The complete example in in demo module.

    See more details on MVCfx see org.scalafx.extras.mvcfx documentation.

  3. trait ModelFX extends AnyRef

    Permalink

    Trait for for implementing component logic.

    Trait for for implementing component logic. Is not aware how the UI structure is implemented. Contains references to parent and parentWindow to help display dialogs.

    See more details in the org.scalafx.extras.mvcfx documentation.

Inherited from AnyRef

Inherited from Any

Ungrouped