├── project ├── build.properties └── plugins.sbt ├── .gitignore ├── demo └── src │ └── main │ ├── resources │ └── scalafxml │ │ └── demo │ │ ├── localization.properties │ │ ├── localization_sv.properties │ │ ├── nested │ │ ├── nested.fxml │ │ └── window.fxml │ │ ├── startscreen.fxml │ │ ├── getcontroller │ │ └── unitconverter.fxml │ │ ├── unitconverter │ │ └── unitconverter.fxml │ │ └── thirdparty │ │ └── unitconverter.fxml │ └── scala │ └── scalafxml │ └── demo │ ├── nested │ ├── WindowController.scala │ ├── NestedDemo.scala │ └── NestedController.scala │ ├── unitconverter │ ├── UnitConverter.scala │ ├── ScalaFXML.scala │ ├── PureScalaFX.scala │ └── RefactoredPureScalaFX.scala │ ├── SimpleDemo.scala │ ├── StartScreenPresenter.scala │ ├── MacWireDemo.scala │ ├── GuiceDemo.scala │ ├── thirdparty │ └── ThirdPartyControlsDemo.scala │ └── getcontroller │ └── GetControllerDemo.scala ├── .bsp └── sbt.json ├── core └── src │ ├── test │ └── scala │ │ └── ProxyGeneratorTest.scala │ └── main │ └── scala │ └── scalafxml │ └── core │ ├── ControllerAccessor.scala │ ├── FXMLView.scala │ ├── FXMLLoader.scala │ ├── FxmlProxyGenerator.scala │ └── ControllerDependencyResolver.scala ├── subcut └── src │ └── main │ └── scala │ └── scalafxml │ └── subcut │ ├── SubCutDependencyResolver.scala │ └── SubCutHelper.scala ├── macwire └── src │ └── main │ └── scala │ └── scalafxml │ └── macwire │ └── MacWireDependencyResolver.scala ├── guice └── src │ └── main │ └── scala │ └── scalafxml │ └── guice │ └── GuiceDependencyResolver.scala ├── .github └── workflows │ ├── ci.yaml │ └── release.yaml ├── README.md └── core-macros └── src └── main └── scala └── scalafxml └── core └── macros └── sfxmlMacro.scala /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.9.9 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea 3 | *.iml 4 | *.DS_Store -------------------------------------------------------------------------------- /demo/src/main/resources/scalafxml/demo/localization.properties: -------------------------------------------------------------------------------- 1 | TEXT_CREATE_NEW_PHOTO_BOOK=Create new photo book 2 | TEXT_OPEN_EXISTING_PHOTO_BOOK=Open existing photo book 3 | TEXT_BROWSE=Browse 4 | TEXT_CREATE=Create 5 | TEXT_HELLO_WORLD=Hello World -------------------------------------------------------------------------------- /demo/src/main/resources/scalafxml/demo/localization_sv.properties: -------------------------------------------------------------------------------- 1 | TEXT_CREATE_NEW_PHOTO_BOOK=Skapa ett nytt album 2 | TEXT_OPEN_EXISTING_PHOTO_BOOK=\u00D6ppna ett album 3 | TEXT_BROWSE=Bl\u00E4ddra 4 | TEXT_CREATE=Skapa 5 | TEXT_HELLO_WORLD=Hejsan V\u00E4rlden -------------------------------------------------------------------------------- /.bsp/sbt.json: -------------------------------------------------------------------------------- 1 | {"name":"sbt","version":"1.9.9","bspVersion":"2.1.0-M1","languages":["scala"],"argv":["/Users/vigoo/.sdkman/candidates/java/21-zulu/zulu-21.jdk/Contents/Home/bin/java","-Xms100m","-Xmx100m","-classpath","/Users/vigoo/.sdkman/candidates/sbt/1.9.6/bin/sbt-launch.jar","-Dsbt.script=/Users/vigoo/.sdkman/candidates/sbt/current/bin/sbt","xsbt.boot.Boot","-bsp"]} -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/" 2 | 3 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") 4 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.7") 5 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") 6 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.0") 7 | 8 | -------------------------------------------------------------------------------- /core/src/test/scala/ProxyGeneratorTest.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import org.scalatest._ 4 | import scala.reflect.runtime.universe._ 5 | import scalafxml.core.FxmlProxyGenerator 6 | import FxmlProxyGenerator._ 7 | import org.scalatest.flatspec.AnyFlatSpec 8 | import org.scalatest.matchers.should.Matchers 9 | 10 | class ProxyGeneratorTest extends AnyFlatSpec with Matchers { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /demo/src/main/resources/scalafxml/demo/nested/nested.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /core/src/main/scala/scalafxml/core/ControllerAccessor.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.core 2 | 3 | /** 4 | * Provides access for a wrapped controller. 5 | * 6 | * Implemented by the macro-generated controller classes. 7 | */ 8 | trait ControllerAccessor { 9 | 10 | /** 11 | * Gets the controller implementation casted to the given type 12 | * @tparam T a supertype of the original controller class 13 | * @return returns the original controller instance 14 | */ 15 | def as[T](): T 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/nested/WindowController.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo.nested 2 | 3 | import scalafx.scene.layout.VBox 4 | import scalafxml.core.macros.{nested, sfxml} 5 | 6 | @sfxml 7 | class WindowController(nested: VBox, 8 | @nested[NestedController] nestedController: NestedControllerInterface) { 9 | 10 | println(s"Window controller initialized with nested control $nested and controller $nestedController") 11 | nestedController.doSomething() 12 | } 13 | -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/nested/NestedDemo.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo.nested 2 | 3 | import scalafx.application.JFXApp 4 | import scalafx.scene.Scene 5 | import scalafx.Includes._ 6 | import scalafxml.core.{DependenciesByType, FXMLView} 7 | 8 | object NestedDemo extends JFXApp { 9 | 10 | val root = FXMLView(getClass.getResource("window.fxml"), 11 | new DependenciesByType(Map.empty)) 12 | 13 | stage = new JFXApp.PrimaryStage() { 14 | title = "Nested controllers demo" 15 | scene = new Scene(root) 16 | } 17 | } -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/nested/NestedController.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo.nested 2 | 3 | import scalafx.scene.control.Label 4 | import scalafxml.core.macros.sfxml 5 | 6 | trait NestedControllerInterface { 7 | def doSomething(): Unit 8 | } 9 | 10 | @sfxml 11 | class NestedController(label: Label) extends NestedControllerInterface { 12 | 13 | println(s"Nested controller initialized with label: $label") 14 | 15 | override def doSomething(): Unit = { 16 | label.text = "Nested controller called!" 17 | println("Nested controller called") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /subcut/src/main/scala/scalafxml/subcut/SubCutDependencyResolver.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.subcut 2 | 3 | import com.escalatesoft.subcut.inject.BindingModule 4 | import scala.reflect.runtime.universe.Type 5 | import scalafxml.core.ControllerDependencyResolver 6 | import scalafxml.subcut.SubCutHelper._ 7 | 8 | /** SubCut based dependency resolver for ScalaFXML controllers */ 9 | class SubCutDependencyResolver(implicit val bindingModule: BindingModule) extends ControllerDependencyResolver { 10 | 11 | def get(paramName: String, dependencyType: Type): Option[Any] = { 12 | injectOptional(bindingModule, dependencyType) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /macwire/src/main/scala/scalafxml/macwire/MacWireDependencyResolver.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.macwire 2 | 3 | import com.softwaremill.macwire.Wired 4 | import scala.reflect.runtime.universe._ 5 | import scalafxml.core.ControllerDependencyResolver 6 | 7 | /** MacWire based dependency resolver for ScalaFXML controllers */ 8 | class MacWireDependencyResolver(wired: Wired) extends ControllerDependencyResolver { 9 | 10 | def get(paramName: String, dependencyType: Type): Option[Any] = { 11 | val rm = runtimeMirror(getClass.getClassLoader) 12 | val cls = Class.forName(rm.runtimeClass(dependencyType).getName) 13 | try { 14 | Some(wired.lookupSingleOrThrow(cls)) 15 | } catch { 16 | case _: Throwable => None 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /guice/src/main/scala/scalafxml/guice/GuiceDependencyResolver.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.guice 2 | 3 | import scala.reflect.runtime.universe._ 4 | 5 | import scalafxml.core.ControllerDependencyResolver 6 | import com.google.inject.Injector 7 | 8 | import scala.util.{Try, Failure, Success} 9 | 10 | /** Guice based dependency resolver for ScalaFXML controllers */ 11 | class GuiceDependencyResolver(implicit val injector: Injector) extends ControllerDependencyResolver { 12 | 13 | def get(paramName: String, dependencyType: Type): Option[Any] = { 14 | val rm = runtimeMirror(getClass.getClassLoader) 15 | val cls = Class.forName(rm.runtimeClass(dependencyType).getName) 16 | Try(injector.getInstance(cls)) match { 17 | case Success(instance) => Some(instance) 18 | case Failure(_) => None 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/unitconverter/UnitConverter.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo.unitconverter 2 | 3 | trait UnitConverter { 4 | val description: String 5 | def run(input: String): String 6 | 7 | override def toString = description 8 | } 9 | 10 | class UnitConverters(converters: UnitConverter*) { 11 | val available = List(converters : _*) 12 | } 13 | 14 | object MMtoInches extends UnitConverter { 15 | val description: String = "Millimeters to inches" 16 | def run(input: String): String = try { (input.toDouble / 25.4).toString } catch { case ex: Throwable => ex.toString } 17 | } 18 | 19 | object InchesToMM extends UnitConverter { 20 | val description: String = "Inches to millimeters" 21 | def run(input: String): String = try { (input.toDouble * 25.4).toString } catch { case ex: Throwable => ex.toString } 22 | } -------------------------------------------------------------------------------- /core/src/main/scala/scalafxml/core/FXMLView.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.core 2 | 3 | import javafx.{scene => jfxs} 4 | import java.net.URL 5 | import java.util.ResourceBundle 6 | 7 | /** Factory for FXML based views */ 8 | object FXMLView { 9 | 10 | /** Creates the JavaFX node representing the control described in FXML 11 | * 12 | * @param fxml URL to the FXML to be loaded 13 | * @param dependencies dependency resolver for finding non-bound dependencies 14 | * @param bundle optional bundle for localization resources. 15 | * @return the JavaFX node 16 | */ 17 | def apply(fxml: URL, dependencies: ControllerDependencyResolver, bundle : Option[ResourceBundle] = None): jfxs.Parent = { 18 | val loader = new FXMLLoader(fxml, dependencies, bundle) 19 | loader.load() 20 | loader.getRoot[jfxs.Parent]() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/SimpleDemo.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo 2 | 3 | import java.util.{Locale, MissingResourceException, ResourceBundle} 4 | 5 | import scalafx.application.JFXApp 6 | import scalafx.Includes._ 7 | import scalafx.scene.Scene 8 | import scala.reflect.runtime.universe.typeOf 9 | import scalafxml.core.{DependenciesByType, FXMLView} 10 | 11 | object SimpleDemo extends JFXApp { 12 | val resourceBundle = ResourceBundle.getBundle("scalafxml.demo.Localization", new Locale("sv", "SE")) 13 | 14 | val root = FXMLView(getClass.getResource("startscreen.fxml"), 15 | new DependenciesByType(Map( 16 | typeOf[TestDependency] -> new TestDependency("hello world"))), 17 | Some(resourceBundle)) 18 | 19 | stage = new JFXApp.PrimaryStage() { 20 | title = resourceBundle.getString("TEXT_HELLO_WORLD") 21 | scene = new Scene(root) 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/StartScreenPresenter.scala: -------------------------------------------------------------------------------- 1 | 2 | package scalafxml.demo 3 | 4 | import scalafx.scene.control.TextField 5 | import scalafx.scene.control.Button 6 | import scalafx.scene.control.ListView 7 | import scalafx.event.ActionEvent 8 | import scalafxml.core.macros.sfxml 9 | 10 | case class TestDependency(initialPath: String) 11 | 12 | @sfxml 13 | class StartScreenPresenter( 14 | newPhotoBookPath: TextField, 15 | btCreate: Button, 16 | recentPaths: ListView[String], 17 | testDep: TestDependency) { 18 | 19 | println(s"testDep is $testDep") 20 | 21 | newPhotoBookPath.text = testDep.initialPath 22 | 23 | def onBrowse(event: ActionEvent) { 24 | println(newPhotoBookPath.text) 25 | println("onBrowse") 26 | } 27 | 28 | def onBrowseForOpen(event: ActionEvent) { 29 | println("onBrowseForOpen") 30 | } 31 | 32 | def onCreate(event: ActionEvent) { 33 | println("onCreate") 34 | } 35 | } -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/MacWireDemo.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo 2 | 3 | import java.util.{MissingResourceException, ResourceBundle} 4 | 5 | import scalafx.application.JFXApp 6 | import scalafx.scene.Scene 7 | import scalafx.Includes._ 8 | import scalafxml.core.FXMLView 9 | import scalafxml.macwire.MacWireDependencyResolver 10 | import com.softwaremill.macwire._ 11 | 12 | object MacWireDemo extends JFXApp { 13 | 14 | class Module { 15 | def testDependency = TestDependency("MacWire dependency") 16 | } 17 | 18 | lazy val wired: Wired = wiredInModule(new Module) 19 | 20 | stage = new JFXApp.PrimaryStage() { 21 | val resourceBundle = ResourceBundle.getBundle("scalafxml.demo.Localization") 22 | 23 | title = resourceBundle.getString("TEXT_HELLO_WORLD") 24 | scene = new Scene(FXMLView(getClass.getResource("startscreen.fxml"), new MacWireDependencyResolver(wired), Some(resourceBundle))) 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/GuiceDemo.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo 2 | 3 | import java.util.{Locale, MissingResourceException, ResourceBundle} 4 | 5 | import scalafx.Includes._ 6 | import scalafx.application.JFXApp 7 | import scalafx.scene.Scene 8 | import scalafxml.core.FXMLView 9 | import scalafxml.guice.GuiceDependencyResolver 10 | import com.google.inject.{AbstractModule, Guice} 11 | 12 | object GuiceDemo extends JFXApp { 13 | 14 | val module = new AbstractModule { 15 | override def configure() { 16 | bind(classOf[TestDependency]).toInstance(new TestDependency("guice dependency")) 17 | } 18 | } 19 | implicit val injector = Guice.createInjector(module) 20 | 21 | stage = new JFXApp.PrimaryStage() { 22 | val resourceBundle = ResourceBundle.getBundle("scalafxml.demo.Localization") 23 | title = resourceBundle.getString("TEXT_HELLO_WORLD") 24 | 25 | scene = new Scene(FXMLView(getClass.getResource("startscreen.fxml"), new GuiceDependencyResolver(), Some(resourceBundle))) 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/scala/scalafxml/core/FXMLLoader.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.core 2 | 3 | import java.net.URL 4 | import java.util.ResourceBundle 5 | import javafx.{fxml => jfxf} 6 | import javafx.{util => jfxu} 7 | 8 | /** 9 | * Extends the JavaFX [[javafx.fxml.FXMLLoader]] to support ScalaFXML controller classes 10 | * 11 | * The [[scalafxml.core.FXMLLoader.getController()]] method is overridden to work with 12 | * the original, wrapped controller instances. 13 | * 14 | * @param fxml URL to the FXML to be loaded 15 | * @param dependencies dependency resolver for finding non-bound dependencies 16 | * @param bundle optional bundle for localization resources 17 | */ 18 | class FXMLLoader(fxml: URL, dependencies: ControllerDependencyResolver, bundle : Option[ResourceBundle] = None) 19 | extends jfxf.FXMLLoader( 20 | fxml, 21 | bundle.orNull, 22 | new jfxf.JavaFXBuilderFactory(), 23 | new jfxu.Callback[Class[_], Object] { 24 | override def call(cls: Class[_]): Object = 25 | FxmlProxyGenerator(cls, dependencies) 26 | }) { 27 | 28 | override def getController[T](): T = super.getController[ControllerAccessor].as[T] 29 | } 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | pull_request: 5 | jobs: 6 | build-test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | scala: ['2.11.12', '2.12.14', '2.13.6'] 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | - uses: actions/setup-java@v2 16 | with: 17 | distribution: 'zulu' 18 | java-version: '11' 19 | java-package: jdk+fx 20 | - name: Setup SBT 21 | shell: bash 22 | run: | 23 | # update this only when sbt-the-bash-script needs to be updated 24 | export SBT_LAUNCHER=1.5.5 25 | export SBT_OPTS="-Dfile.encoding=UTF-8" 26 | curl -L --silent "https://github.com/sbt/sbt/releases/download/v$SBT_LAUNCHER/sbt-$SBT_LAUNCHER.tgz" > $HOME/sbt.tgz 27 | tar zxf $HOME/sbt.tgz -C $HOME 28 | sudo rm -f /usr/local/bin/sbt 29 | sudo ln -s $HOME/sbt/bin/sbt /usr/local/bin/sbt 30 | - name: Coursier cache 31 | uses: coursier/cache-action@v5 32 | - name: Build and test 33 | run: sbt ++${{ matrix.scala }} clean test 34 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: ['master'] 5 | release: 6 | types: 7 | - published 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - uses: actions/setup-java@v2 17 | with: 18 | distribution: 'zulu' 19 | java-version: '11' 20 | java-package: jdk+fx 21 | - name: Setup SBT 22 | shell: bash 23 | run: | 24 | # update this only when sbt-the-bash-script needs to be updated 25 | export SBT_LAUNCHER=1.5.5 26 | export SBT_OPTS="-Dfile.encoding=UTF-8" 27 | curl -L --silent "https://github.com/sbt/sbt/releases/download/v$SBT_LAUNCHER/sbt-$SBT_LAUNCHER.tgz" > $HOME/sbt.tgz 28 | tar zxf $HOME/sbt.tgz -C $HOME 29 | sudo rm -f /usr/local/bin/sbt 30 | sudo ln -s $HOME/sbt/bin/sbt /usr/local/bin/sbt 31 | - uses: olafurpg/setup-gpg@v3 32 | - run: sbt ci-release 33 | env: 34 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 35 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 36 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 37 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} -------------------------------------------------------------------------------- /subcut/src/main/scala/scalafxml/subcut/SubCutHelper.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.subcut 2 | 3 | import scala.reflect.runtime.{universe => ru} 4 | import com.escalatesoft.subcut.inject.BindingModule 5 | 6 | /** Helper class for [[scalafxml.subcut.SubCutDependencyResolver]] */ 7 | object SubCutHelper { 8 | 9 | /** Invokes dynamically the SubCut binding module's injectOptional method 10 | * 11 | * @param bindingModule binding module to invoke 12 | * @param dependencyType type to pass to injectOptional as a type argument 13 | * @return returns the result of the invoked method 14 | */ 15 | def injectOptional(bindingModule: BindingModule, dependencyType: ru.Type): Option[Any] = { 16 | import ru._ 17 | 18 | val rm = runtimeMirror(bindingModule.getClass.getClassLoader) 19 | val instanceMirror = rm.reflect(bindingModule) 20 | val injectOptionalSymbols = typeOf[BindingModule].decl(TermName("injectOptional")).asTerm.alternatives 21 | val injectOptionalSym = injectOptionalSymbols.filter { 22 | case m: MethodSymbol => { 23 | m.paramLists.size == 2 && 24 | m.paramLists(0).size == 1 && 25 | m.paramLists(0)(0).typeSignature =:= typeOf[Option[String]] 26 | } 27 | }.head.asMethod 28 | 29 | val methodMirror = instanceMirror.reflectMethod(injectOptionalSym) 30 | methodMirror.apply(None, Manifest.classType(Class.forName(rm.runtimeClass(dependencyType).getName))).asInstanceOf[Option[Any]] 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /demo/src/main/scala/scalafxml/demo/unitconverter/ScalaFXML.scala: -------------------------------------------------------------------------------- 1 | package scalafxml.demo.unitconverter 2 | 3 | import scala.reflect.runtime.universe.typeOf 4 | import scalafx.application.{Platform, JFXApp} 5 | import scalafx.Includes._ 6 | import scalafx.scene.Scene 7 | import scalafx.scene.control.{ComboBox, TextField} 8 | import scalafx.event.ActionEvent 9 | import scalafxml.core.{DependenciesByType, FXMLView} 10 | import scalafxml.core.macros.sfxml 11 | import javafx.beans.binding.StringBinding 12 | 13 | @sfxml 14 | class UnitConverterPresenter(from: TextField, 15 | to: TextField, 16 | types: ComboBox[UnitConverter], 17 | converters: UnitConverters) { 18 | 19 | // Filling the combo box 20 | for (converter <- converters.available) { 21 | types += converter 22 | } 23 | types.getSelectionModel.selectFirst() 24 | 25 | // Data binding 26 | to.text <== new StringBinding { 27 | bind(from.text.delegate, types.getSelectionModel.selectedItemProperty) 28 | 29 | def computeValue() = types.getSelectionModel.getSelectedItem.run(from.text.value) 30 | } 31 | 32 | // Close button event handler 33 | def onClose(event: ActionEvent) { 34 | Platform.exit() 35 | } 36 | } 37 | 38 | object ScalaFXML extends JFXApp { 39 | 40 | val root = FXMLView(getClass.getResource("unitconverter.fxml"), 41 | new DependenciesByType(Map( 42 | typeOf[UnitConverters] -> new UnitConverters(InchesToMM, MMtoInches)))) 43 | 44 | stage = new JFXApp.PrimaryStage() { 45 | title = "Unit conversion" 46 | scene = new Scene(root) 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /demo/src/main/resources/scalafxml/demo/startscreen.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |