New ObservablePref mechanism

This inherits from the `Pref` system in `scala-utils`, extending it so
that preferences can be ScalaFX bound variables. This is the easiest way
to store things like the preferred window size and other per-user local
configuration.
This commit is contained in:
Sarah Gerweck 2017-11-05 21:40:44 -08:00
parent bc76e50d2b
commit bce5a0d39e
3 changed files with 64 additions and 1 deletions

View File

@ -68,3 +68,12 @@ change.
* This is tested against Java 8u144, though it may work with older versions. * This is tested against Java 8u144, though it may work with older versions.
* Improvements to `SingletonStage` * Improvements to `SingletonStage`
* Update Scala to 2.12.4 * Update Scala to 2.12.4
### 0.13
* New `ObservablePref`
* This builds on the `Pref` in `scala-utils`, making it a bindable property.
* `Pref` and `ObservablePref` are the way I recommend to store things like
window sizes, column selections and other UI preferences.
* Use a database like [H2](http://www.h2database.com/) if you have
complicated application state that needs to persist.

View File

@ -23,7 +23,7 @@ object Dependencies {
final val scalaJava8Version = "0.8.0" final val scalaJava8Version = "0.8.0"
final val scalaParserVersion = "1.0.4" final val scalaParserVersion = "1.0.4"
final val scalaXmlVersion = "1.0.5" final val scalaXmlVersion = "1.0.5"
final val gerweckUtilVersion = "2.5.1" final val gerweckUtilVersion = "2.7.0-SNAPSHOT"
final val scalazVersion = "7.2.16" final val scalazVersion = "7.2.16"
final val shapelessVersion = "2.3.2" final val shapelessVersion = "2.3.2"
final val scallopVersion = "1.0.1" final val scallopVersion = "1.0.1"

View File

@ -0,0 +1,54 @@
package org.gerweck.scalafx.util
package prefs
import java.util.prefs.Preferences
import scalafx.application.Platform.runLater
import scalafx.beans.property.ObjectProperty
import org.gerweck.scala.util.prefs._
/* TODO: take an implicit that will deteremine whether this is an `ObjectProperty` or what */
class ObservablePref[A] protected (path: String)(implicit handler: Pref.AccessHandler[A], prefs: Preferences)
extends Pref[A](path) {
lazy val observe: ObjectProperty[A] = {
val initialValue: A = this()
val property = ObjectProperty[A](initialValue)
/* We build two bridges, one that listens for changes in the preferences system and pushes
* them into the observable property, and another that listens for updates to the property and
* pushes them to the preference system. Each bridge is gated so that it only activates if the
* value has actually changed, which prevents the infinite looping that would otherwise occur
* in a bidirectional bridge. */
/* Preferences => Property bridge */
prefs.addPreferenceChangeListener { pce =>
if (pce.getKey == path) {
runLater {
val currentValue = this()
if (property.value != currentValue) {
property.value = currentValue
}
}
}
}
/* Property => Preferences bridge */
property.onChange { (_, _, newV) =>
if (newV != this()) {
this() = newV
}
()
}
property
}
}
object ObservablePref {
def apply[A](path: String)(implicit handler: PrefHandler[A], prefs: Preferences) = {
new ObservablePref(path)(new Pref.AccessHandler.Optional, prefs)
}
def apply[A](path: String, default: A)(implicit handler: PrefHandler[A], prefs: Preferences) = {
new ObservablePref(path)(new Pref.AccessHandler.Defaulted(default), prefs)
}
}