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
Type Members
- trait ControllerFX extends AnyRef
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 ... }
- abstract class MVCfx extends AnyRef
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. - trait ModelFX extends AnyRef
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.
Helper methods and classes to simplify ScalaFX use.
Package
org.scalafx.extras
contains basic helper methods for running tasks on threads and showing messages.Package
org.scalafx.extras.image
contains image display component with scrolling and zooming.Package
org.scalafx.extras.mvcfx
contains classes for creating with UI components based on FXML.