122 lines
4.9 KiB
Markdown
122 lines
4.9 KiB
Markdown
# Sarah’s ScalaFX Utilities #
|
||
|
||
This project contains helper functions, utilities and convenience functions
|
||
for working with JavaFX and ScalaFX in Scala.
|
||
|
||
ScalaFX does a tremendous job at making JavaFX more usable from Scala, but
|
||
it doesn't go as far as it could in facilitating functional and reactive
|
||
programming. This project is an attempt to add additional facilities that
|
||
further bridge the beautiful paradigms of Scala with the powerful UI
|
||
functionality offered JavaFX.
|
||
|
||
In particular, here are some key features:
|
||
|
||
* Monadic and applicative interfaces on top of `Observable` make it easy to
|
||
build up computations.
|
||
* Converters that allow you to use a `Future` or Akka `Stream` as an
|
||
`Observable`.
|
||
|
||
This code is offered as is with no guarantees. You are free to use it if you
|
||
find it useful, but this is not part of any production project and it may have
|
||
serious bugs. These APIs may also change at any time, and I make no guarantees
|
||
that the project will be maintained at all. I welcome any bug reports and I
|
||
will be happy to merge high-quality pull requests if you find a bug.
|
||
|
||
## Installation ##
|
||
|
||
To use ScalaFX, add the following to your SBT build:
|
||
|
||
libraryDependencies += "org.gerweck.scala" %% "scalafx-utils" % "0.14.0"
|
||
|
||
This currently supports both Scala 2.11 and 2.12. Scala 2.11 will remain a
|
||
first-class citizen until Scala 2.13 is released unless it would require
|
||
substantial forking.
|
||
|
||
## Usage ##
|
||
|
||
The primary use of this library is to provide a number of implicit conversions
|
||
and instances, which are all brought into scope with this import:
|
||
|
||
import org.gerweck.scalafx.util._
|
||
|
||
If you use [Cats](https://typelevel.org/cats/), this makes ScalaFX observables
|
||
instances of `Functor`, `Applicative` and `Monad`. It also provides some
|
||
simple extension methods along these lines.
|
||
|
||
### Functional Transformations ###
|
||
|
||
Note that the output of a functional transformation is always a
|
||
`ReadOnlyObjectProperty[A]`, even if there exists a more specific result type
|
||
like `ReadOnlyIntegerProperty` that would work. (The types used by ScalaFX are
|
||
fairly complicated, and no real harm is done by using an `ObjectProperty` in
|
||
all cases.)
|
||
|
||
#### Map ####
|
||
|
||
To facilitate functional programming, the standard `map` function allows you
|
||
to transform an observable value using a pure function.
|
||
|
||
Note that, for performance reasons, these functionally defined observables do
|
||
not trigger an update if an input or output value is changed to one that is
|
||
identical as defined by `equals`.
|
||
|
||
import scalafx.beans.value._
|
||
import scalafx.scene.control._
|
||
import org.gerweck.scalafx.util._
|
||
|
||
val textBox = new TextField { /* ... */ }
|
||
val boxText: ObservableValue[String, String] = textBox.text
|
||
/* Construct a new observable derived from the underlying one using `map` */
|
||
val characterCount: ReadOnlyObjectProperty[String] = textBox.text map (_.size)
|
||
|
||
#### Multiple Function Inputs ####
|
||
|
||
If your function depends on several observable values, you can use the
|
||
applicative behavior provided by the library. The Cats applicative
|
||
functionality is all available, but there is a more convenient mechanism for
|
||
the most common use case where you want to operate on a tuple.
|
||
|
||
import scalafx.beans.property._
|
||
import org.gerweck.scalafx.util._
|
||
|
||
val startedDownloads = IntegerProperty(0)
|
||
val finishedDownloads = IntegerProperty(0)
|
||
val runningDownloads: ReadOnlyObjectProperty[Int] =
|
||
(startedDownloads, finishedDownloads).observe map {
|
||
case (st, fi) => st - fi
|
||
}
|
||
|
||
This `observe` extension method is available on tuples of any arity and
|
||
efficiently processes updates from any of its dependent values.
|
||
|
||
#### Monadically Chained Observables ####
|
||
|
||
In addition to the behavior of an applicative functor, this library also
|
||
provides the ability to act like a monadic functor by providing `flatMap` and
|
||
`flatten`. *Where possible use the applicative syntax defined above rather
|
||
than a chain of `flatMap` applications: the applicative format performs much
|
||
better.*
|
||
|
||
Here is an example of a model where you might have a dialog box or window.
|
||
In this window, you could have a list selection where you choose from one of
|
||
many transformation types. Once you've selected a transformation type, it will
|
||
display a configuration panel that you can use to control the details of that
|
||
transformation.
|
||
|
||
import scalafx.beans.property._
|
||
import org.gerweck.scalafx.util._
|
||
|
||
/** An object that has a config dialog that produces a function */
|
||
trait ConfigurableIntFunction {
|
||
val typeName: String
|
||
val configPanel: scalafx.scene.layout.Pane
|
||
val currentFunction: ReadOnlyObjectProperty[Int => Int]
|
||
}
|
||
val selectedFunctionType: ObjectProperty[ConfigurableIntFunction] = ???
|
||
val selectedFunction = selectedFunctionType flatMap (_.currentFunction)
|
||
val inputInt = IntegerProperty(0)
|
||
val outputInt =
|
||
(selectedFunction, inputInt).observe map {
|
||
case (sf, ii) => sf(ii)
|
||
}
|