Browse Source

Updated project further

Updated scala version to 2.13.4
Compiles to Java 11 now
Updated sbt to 1.4.3
Updated scalafx version to 14
Removed unneeded dependencies
Removed Akka code
Added lint and fatal warnings scalac options
Added parantheses to side effecting methods
Added >> and << methods to observables
master
Rohan Sircar 3 years ago
parent
commit
7484570e40
  1. 7
      .gitignore
  2. 1
      .sbtopts
  3. 16
      build.sbt
  4. 6
      project/BasicSettings.scala
  5. 7
      project/Dependencies.scala
  6. 2
      project/ProjectSettings.scala
  7. 2
      project/build.properties
  8. 1
      project/plugins.sbt
  9. 136
      src/main/scala/org/gerweck/scalafx/akka/AkkaFX.scala
  10. 116
      src/main/scala/org/gerweck/scalafx/akka/AkkaFXCollections.scala
  11. 2
      src/main/scala/org/gerweck/scalafx/util/SingletonStage.scala
  12. 5
      src/main/scala/org/gerweck/scalafx/util/control/ControlPanel.scala
  13. 4
      src/main/scala/org/gerweck/scalafx/util/control/DialogButtons.scala
  14. 24
      src/main/scala/org/gerweck/scalafx/util/observable.scala
  15. 8
      src/main/scala/org/gerweck/scalafx/util/observableCollection.scala
  16. 124
      src/main/scala/org/gerweck/scalafx/util/prefs/ObservablePref.scala
  17. 2
      version.sbt

7
.gitignore

@ -46,3 +46,10 @@ bin
/log /log
/*.script /*.script
typescript typescript
.metals
.sbt
.vscode
.bsp
.bloop
metals.sbt

1
.sbtopts

@ -1,4 +1,3 @@
-J-Xmx4G -J-Xmx4G
-J-XX:MaxMetaspaceSize=4g -J-XX:MaxMetaspaceSize=4g
-J-XX:+UnlockCommercialFeatures
-J-XX:+FlightRecorder -J-XX:+FlightRecorder

16
build.sbt

@ -27,21 +27,21 @@ lazy val root = (project in file ("."))
jclBridge % "runtime,optional", jclBridge % "runtime,optional",
log4jBridge % "runtime,optional", log4jBridge % "runtime,optional",
logback % "runtime,optional", logback % "runtime,optional",
gerweckUtil,
scalaJava8,
// gerweckUtil,
// scalaJava8,
scalaFx, scalaFx,
cats, cats,
shapeless shapeless
), ),
libraryDependencies ++= javaFXModules.map( m => libraryDependencies ++= javaFXModules.map( m =>
"org.openjfx" % s"javafx-$m" % "12.0.2" classifier osName
"org.openjfx" % s"javafx-$m" % "11.0.2" classifier osName
), ),
/* Akka dependencies */ /* Akka dependencies */
libraryDependencies ++= Seq (
akkaActor % "optional",
akkaStream % "optional",
gerweckUtilAkka % "optional"
)
// libraryDependencies ++= Seq (
// akkaActor % "optional",
// akkaStream % "optional",
// gerweckUtilAkka % "optional"
// )
) )

6
project/BasicSettings.scala

@ -73,6 +73,8 @@ trait BasicSettings extends ProjectSettings { st: SettingTemplate =>
var options = Seq.empty[String] var options = Seq.empty[String]
val sv = sver.value val sv = sver.value
options :+= "-Xlint"
options :+= "-Xfatal-warnings"
options :+= "-unchecked" options :+= "-unchecked"
options :+= "-feature" options :+= "-feature"
if (deprecation) { if (deprecation) {
@ -160,8 +162,8 @@ trait BasicSettings extends ProjectSettings { st: SettingTemplate =>
if (sv.requireJava8) { if (sv.requireJava8) {
options ++= Seq[String]( options ++= Seq[String](
"-target", "1.8",
"-source", "1.8"
"-target", "11",
"-source", "11"
) )
} else { } else {
options ++= Seq[String]( options ++= Seq[String](

7
project/Dependencies.scala

@ -34,10 +34,10 @@ object Dependencies {
val logback = "ch.qos.logback" % "logback-classic" % logbackVersion val logback = "ch.qos.logback" % "logback-classic" % logbackVersion
val spire = "org.spire-math" %% "spire" % spireVersion val spire = "org.spire-math" %% "spire" % spireVersion
val commonsIo = "commons-io" % "commons-io" % commonsIoVersion val commonsIo = "commons-io" % "commons-io" % commonsIoVersion
val groovy = "org.codehaus.groovy" % "groovy-all" % groovyVersion
// val groovy = "org.codehaus.groovy" % "groovy-all" % groovyVersion
val gerweckUtil = "org.gerweck.scala" %% "gerweck-utils" % gerweckUtilVersion val gerweckUtil = "org.gerweck.scala" %% "gerweck-utils" % gerweckUtilVersion
val gerweckUtilAkka = "org.gerweck.scala" %% "gerweck-utils-akka" % gerweckUtilVersion val gerweckUtilAkka = "org.gerweck.scala" %% "gerweck-utils-akka" % gerweckUtilVersion
val scalaJava8 = "org.scala-lang.modules" %% "scala-java8-compat" % scalaJava8Version
// val scalaJava8 = "org.scala-lang.modules" %% "scala-java8-compat" % scalaJava8Version
val cats = "org.typelevel" %% "cats-core" % catsVersion val cats = "org.typelevel" %% "cats-core" % catsVersion
val shapeless = "com.chuusai" %% "shapeless" % shapelessVersion val shapeless = "com.chuusai" %% "shapeless" % shapelessVersion
val scallop = "org.rogach" %% "scallop" % scallopVersion val scallop = "org.rogach" %% "scallop" % scallopVersion
@ -81,8 +81,7 @@ object Dependencies {
/* ********************************************************************** */ /* ********************************************************************** */
/* ScalaFX */ /* ScalaFX */
/* ********************************************************************** */ /* ********************************************************************** */
// final val scalaFxVersion = "8.0.144-R12"
final val scalaFxVersion = "12.0.2-R18"
final val scalaFxVersion = "14-R19"
val scalaFx = "org.scalafx" %% "scalafx" % scalaFxVersion val scalaFx = "org.scalafx" %% "scalafx" % scalaFxVersion

2
project/ProjectSettings.scala

@ -11,7 +11,7 @@ trait ProjectSettings
override final val githubOrganization = "sarahgerweck" override final val githubOrganization = "sarahgerweck"
override final val githubProject = "scalafx-utils" override final val githubProject = "scalafx-utils"
override final val buildScalaVersion = "2.13.1"
override final val buildScalaVersion = "2.13.4"
override final val extraScalaVersions = Seq("2.11.12", "2.12.5") override final val extraScalaVersions = Seq("2.11.12", "2.12.5")
override final val defaultOptimize = true override final val defaultOptimize = true
override final val defaultOptimizeGlobal = false override final val defaultOptimizeGlobal = false

2
project/build.properties

@ -1 +1 @@
sbt.version=1.3.8
sbt.version=1.4.3

1
project/plugins.sbt

@ -0,0 +1 @@
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "2.0.2")

136
src/main/scala/org/gerweck/scalafx/akka/AkkaFX.scala

@ -1,74 +1,74 @@
package org.gerweck.scalafx.akka
// package org.gerweck.scalafx.akka
import scala.concurrent._
// import scala.concurrent._
import akka.Done
import akka.stream.OverflowStrategy
import akka.stream.scaladsl._
// import akka.Done
// import akka.stream.OverflowStrategy
// import akka.stream.scaladsl._
import scalafx.application.Platform.runLater
import scalafx.beans.property.Property
import scalafx.beans.value.ObservableValue
// import scalafx.application.Platform.runLater
// import scalafx.beans.property.Property
// import scalafx.beans.value.ObservableValue
/** A master object that exposes all the Akka-ScalaFX bridges.
*
* @author Sarah Gerweck <sarah.a180@gmail.com>
*/
object AkkaFX extends AkkaStreamFX with AkkaFXCollections
// /** A master object that exposes all the Akka-ScalaFX bridges.
// *
// * @author Sarah Gerweck <sarah.a180@gmail.com>
// */
// object AkkaFX extends AkkaStreamFX with AkkaFXCollections
trait AkkaStreamFX extends Any {
/** A [[akka.stream.scaladsl.Sink]] that sends all values to a
* [[scalafx.beans.property.Property]].
*
* Each event that's written into the `Sink` will trigger an update of the
* `Property` with the streamed value.
*/
def observableSink[A](prop: Property[A, _]): Sink[A, Future[Done]] = {
Sink.foreach[A] { a =>
runLater {
prop.value = a
}
}
}
// trait AkkaStreamFX extends Any {
// /** A [[akka.stream.scaladsl.Sink]] that sends all values to a
// * [[scalafx.beans.property.Property]].
// *
// * Each event that's written into the `Sink` will trigger an update of the
// * `Property` with the streamed value.
// */
// def observableSink[A](prop: Property[A, _]): Sink[A, Future[Done]] = {
// Sink.foreach[A] { a =>
// runLater {
// prop.value = a
// }
// }
// }
/** A [[akka.stream.scaladsl.Source]] that generates an event for each
* change of an [[scalafx.beans.value.ObservableValue]].
*
* This source adds an `onChange` handler to the given `ObservableValue`.
* Each time it observes a change, the new value is pushed from the
* `Source`. The change handler is registered as soon as the source is
* materialized into a graph. It should be safe to use a single source
* in several graphs, as each will register its own change listener upon
* materialization.
*
* @param prop The value to observe.
*
* @param queueSize The maximum number of values to queue while waiting for
* the downstream flow to consume more data.
*
* @param overflow What to do when the queue is full because the downstream
* flow cannot keep up. The default behavior is to block, slowing the UI's
* main thread until some events are consumed, freeing space in the queue.
*/
def observableSource[A](prop: ObservableValue[_, A],
queueSize: Int = 10,
overflow: OverflowStrategy = OverflowStrategy.backpressure
)(implicit ec: ExecutionContext) = {
val src = Source
.queue[A](queueSize, overflow)
.mapMaterializedValue { m =>
val sub = {
prop.onChange { (dta, oldV, newV) =>
m.offer(newV)
}
}
m.watchCompletion foreach { c =>
runLater {
sub.cancel()
}
}
m
}
src
}
}
// /** A [[akka.stream.scaladsl.Source]] that generates an event for each
// * change of an [[scalafx.beans.value.ObservableValue]].
// *
// * This source adds an `onChange` handler to the given `ObservableValue`.
// * Each time it observes a change, the new value is pushed from the
// * `Source`. The change handler is registered as soon as the source is
// * materialized into a graph. It should be safe to use a single source
// * in several graphs, as each will register its own change listener upon
// * materialization.
// *
// * @param prop The value to observe.
// *
// * @param queueSize The maximum number of values to queue while waiting for
// * the downstream flow to consume more data.
// *
// * @param overflow What to do when the queue is full because the downstream
// * flow cannot keep up. The default behavior is to block, slowing the UI's
// * main thread until some events are consumed, freeing space in the queue.
// */
// def observableSource[A](prop: ObservableValue[_, A],
// queueSize: Int = 10,
// overflow: OverflowStrategy = OverflowStrategy.backpressure
// )(implicit ec: ExecutionContext) = {
// val src = Source
// .queue[A](queueSize, overflow)
// .mapMaterializedValue { m =>
// val sub = {
// prop.onChange { (dta, oldV, newV) =>
// m.offer(newV)
// }
// }
// m.watchCompletion() foreach { c =>
// runLater {
// sub.cancel()
// }
// }
// m
// }
// src
// }
// }

116
src/main/scala/org/gerweck/scalafx/akka/AkkaFXCollections.scala

@ -1,72 +1,72 @@
package org.gerweck.scalafx.akka
// package org.gerweck.scalafx.akka
import language.implicitConversions
// import language.implicitConversions
import scala.collection.mutable.{ Clearable, Growable }
import scala.concurrent._
import scala.concurrent.duration._
import scala.util._
// import scala.collection.mutable.{ Clearable, Growable }
// import scala.concurrent._
// import scala.concurrent.duration._
// import scala.util._
import akka.{ Done, NotUsed }
import akka.stream.Materializer
import akka.stream.scaladsl._
// import akka.{ Done, NotUsed }
// import akka.stream.Materializer
// import akka.stream.scaladsl._
import scalafx.application.Platform.runLater
import scalafx.beans.Observable
import scalafx.beans.property._
// import scalafx.application.Platform.runLater
// import scalafx.beans.Observable
// import scalafx.beans.property._
import org.gerweck.scalafx.util.FutureObservable
// import org.gerweck.scalafx.util.FutureObservable
trait AkkaFXCollections extends Any {
implicit def sourceToRichSource[A, B](source: Source[A, B]) = new AkkaFXCollections.RichSource(source)
}
// trait AkkaFXCollections extends Any {
// implicit def sourceToRichSource[A, B](source: Source[A, B]) = new AkkaFXCollections.RichSource(source)
// }
object AkkaFXCollections {
def collectionWriterSink[A]
(buffer: Observable with Growable[A] with Clearable,
clearFirst: Boolean,
groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds)
: Sink[A, Future[Done]] = {
sealed trait PopulateAction
case class InsertRows(data: Seq[A]) extends PopulateAction
case object ClearData extends PopulateAction
// object AkkaFXCollections {
// def collectionWriterSink[A]
// (buffer: Observable with Growable[A] with Clearable,
// clearFirst: Boolean,
// groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds)
// : Sink[A, Future[Done]] = {
// sealed trait PopulateAction
// case class InsertRows(data: Seq[A]) extends PopulateAction
// case object ClearData extends PopulateAction
val grouping = {
Flow[A]
.groupedWithin(groupingSize, groupingTimeout)
.map(InsertRows)
.named("GroupInsertActions")
}
// val grouping = {
// Flow[A]
// .groupedWithin(groupingSize, groupingTimeout)
// .map(InsertRows)
// .named("GroupInsertActions")
// }
val clearData = {
if (clearFirst)
Source.single(ClearData)
else
Source.empty
}.named("OptionalClearAction")
// val clearData = {
// if (clearFirst)
// Source.single(ClearData)
// else
// Source.empty
// }.named("OptionalClearAction")
val combinedSource: Flow[A, PopulateAction, NotUsed] = grouping.prepend(clearData)
combinedSource .toMat {
Sink .foreach[PopulateAction] {
case ClearData => runLater { buffer.clear() }
case InsertRows(data) => runLater { buffer ++= data }
} .named("BufferWriter")
}(Keep.right)
}
// val combinedSource: Flow[A, PopulateAction, NotUsed] = grouping.prepend(clearData)
// combinedSource .toMat {
// Sink .foreach[PopulateAction] {
// case ClearData => runLater { buffer.clear() }
// case InsertRows(data) => runLater { buffer ++= data }
// } .named("BufferWriter")
// }(Keep.right)
// }
class RichSource[A, B](val inner: Source[A, B]) extends AnyVal {
def populateCollection[C >: A]
(buffer: Observable with Growable[C] with Clearable,
clearFirst: Boolean,
groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds)
(implicit mat: Materializer, ec: ExecutionContext)
: ReadOnlyObjectProperty[Option[Try[Done]]] = {
// class RichSource[A, B](val inner: Source[A, B]) extends AnyVal {
// def populateCollection[C >: A]
// (buffer: Observable with Growable[C] with Clearable,
// clearFirst: Boolean,
// groupingSize: Int = 50, groupingTimeout: FiniteDuration = 100.milliseconds)
// (implicit mat: Materializer, ec: ExecutionContext)
// : ReadOnlyObjectProperty[Option[Try[Done]]] = {
val sink = collectionWriterSink(buffer, clearFirst, groupingSize, groupingTimeout)
// val sink = collectionWriterSink(buffer, clearFirst, groupingSize, groupingTimeout)
val graph = inner.toMat(sink)(Keep.right)
// val graph = inner.toMat(sink)(Keep.right)
FutureObservable.ofTryOption(graph.run())
}
}
}
// FutureObservable.ofTryOption(graph.run())
// }
// }
// }

2
src/main/scala/org/gerweck/scalafx/util/SingletonStage.scala

@ -46,7 +46,7 @@ abstract class SingletonStage {
/** Create the stage, or give it focus if it already exists. /** Create the stage, or give it focus if it already exists.
* *
* This needs to be executed within the UI thread. If you're not already within some kind * This needs to be executed within the UI thread. If you're not already within some kind
* of event handler, you need to use [[scalafx.application.Platform.runLater]].
* of event handler, you need to use [[scalafx.application.Platform$.runLater[R](op:=>R):Unit*]].
*/ */
def showStage(): Unit = { def showStage(): Unit = {
State.currentStage match { State.currentStage match {

5
src/main/scala/org/gerweck/scalafx/util/control/ControlPanel.scala

@ -28,7 +28,7 @@ object HorizontalControlPanel {
alignment = Pos.CenterRight alignment = Pos.CenterRight
} }
case SeparatorEntry => case SeparatorEntry =>
Separators.vertical
Separators.vertical()
} }
} }
} }
@ -56,9 +56,6 @@ object ControlPanel {
def apply(controls: ControlPanelEntry*): Pane = VerticalControlPanel(controls: _*) def apply(controls: ControlPanelEntry*): Pane = VerticalControlPanel(controls: _*)
private[this] lazy val defaultFont = new Font(Font.default) private[this] lazy val defaultFont = new Font(Font.default)
private[this] lazy val boldFont = {
Font(defaultFont.family, FontWeight.Bold, defaultFont.size)
}
private[this] lazy val labelsFont = defaultFont private[this] lazy val labelsFont = defaultFont

4
src/main/scala/org/gerweck/scalafx/util/control/DialogButtons.scala

@ -5,7 +5,6 @@ import org.log4s._
import scalafx.Includes._ import scalafx.Includes._
import scalafx.event.ActionEvent import scalafx.event.ActionEvent
import scalafx.event.ActionEvent
import scalafx.geometry.Insets import scalafx.geometry.Insets
import scalafx.scene.Node import scalafx.scene.Node
import scalafx.scene.control._ import scalafx.scene.control._
@ -13,7 +12,6 @@ import scalafx.scene.layout._
import scalafx.stage.Stage import scalafx.stage.Stage
import javafx.scene.{ Node => JNode } import javafx.scene.{ Node => JNode }
import javafx.scene.layout.{ Region => JRegion } import javafx.scene.layout.{ Region => JRegion }
import scalafx.delegate.SFXDelegate
/** A standard button bar with the major dialog buttons. /** A standard button bar with the major dialog buttons.
* *
@ -72,7 +70,7 @@ class DialogButtons(stage: Stage, showOk: Boolean = true, showApply: Boolean = t
} }
} }
protected[control] def mainLayout = new VBox(Separators.horizontal, bar) {
protected[control] def mainLayout = new VBox(Separators.horizontal(), bar) {
hgrow = Priority.Always hgrow = Priority.Always
} }

24
src/main/scala/org/gerweck/scalafx/util/observable.scala

@ -42,7 +42,7 @@ private object ObservableImplicits {
val prop = ObjectProperty[B](originalValue) val prop = ObjectProperty[B](originalValue)
var prevValue = originalValue var prevValue = originalValue
def changeHandler = prop.synchronized {
def changeHandler() = prop.synchronized {
val newVal = recalculate() val newVal = recalculate()
if (prevValue != newVal) { if (prevValue != newVal) {
prop.value = newVal prop.value = newVal
@ -50,7 +50,7 @@ private object ObservableImplicits {
} }
} }
a onChange changeHandler
a onChange changeHandler()
prop prop
} }
@ -68,7 +68,7 @@ private object ObservableImplicits {
var prevValue = originalValue var prevValue = originalValue
def changeHandler = prop.synchronized {
def changeHandler() = prop.synchronized {
val newVal = recalculate() val newVal = recalculate()
if (prevValue != newVal) { if (prevValue != newVal) {
prop.value = newVal prop.value = newVal
@ -76,8 +76,8 @@ private object ObservableImplicits {
} }
} }
fa onChange changeHandler
f onChange changeHandler
fa onChange changeHandler()
f onChange changeHandler()
prop prop
} }
@ -110,7 +110,7 @@ private object ObservableImplicits {
prevValue = newVal prevValue = newVal
} }
} }
var innerSub = oa() onChange innerHandle
var innerSub = oa() onChange innerHandle()
var prevOuter = oa() var prevOuter = oa()
def outerHandle() = prop.synchronized { def outerHandle() = prop.synchronized {
@ -118,13 +118,13 @@ private object ObservableImplicits {
/* We need reference equality here: we're subscribing to a specific object. */ /* We need reference equality here: we're subscribing to a specific object. */
if (prevOuter ne newOuter) { if (prevOuter ne newOuter) {
innerSub.cancel() innerSub.cancel()
innerSub = newOuter onChange innerHandle
innerSub = newOuter onChange innerHandle()
prevOuter = newOuter prevOuter = newOuter
innerHandle() innerHandle()
} }
} }
ooa onChange outerHandle
ooa onChange outerHandle()
prop prop
} }
@ -159,7 +159,7 @@ private object ObservableImplicits {
prevValue = newVal prevValue = newVal
} }
} }
var innerSub = oa() onChange innerHandle
var innerSub = oa() onChange innerHandle()
var prevOuter = oa() var prevOuter = oa()
def outerHandle() = prop.synchronized { def outerHandle() = prop.synchronized {
@ -167,13 +167,13 @@ private object ObservableImplicits {
/* We need reference equality here: we're subscribing to a specific object. */ /* We need reference equality here: we're subscribing to a specific object. */
if (prevOuter ne newOuter) { if (prevOuter ne newOuter) {
innerSub.cancel() innerSub.cancel()
innerSub = newOuter onChange innerHandle
innerSub = newOuter onChange innerHandle()
prevOuter = newOuter prevOuter = newOuter
innerHandle() innerHandle()
} }
} }
ooa onChange outerHandle
ooa onChange outerHandle()
prop prop
} }
@ -218,6 +218,8 @@ final class RichObservable[A, C](val self: ObservableValue[A, C]) extends AnyVal
def tuple[B](f: Observable[B]): Observable[(A,B)] = oapp.tuple2(self, f) def tuple[B](f: Observable[B]): Observable[(A,B)] = oapp.tuple2(self, f)
final def *>[B](fb: ObjObs[B]): Observable[B] = oapp.map2(self,fb)((_,b) => b) final def *>[B](fb: ObjObs[B]): Observable[B] = oapp.map2(self,fb)((_,b) => b)
final def <*[B](fb: ObjObs[B]): Observable[A] = oapp.map2(self,fb)((a,_) => a) final def <*[B](fb: ObjObs[B]): Observable[A] = oapp.map2(self,fb)((a,_) => a)
final def >>[B](fb: => ObjObs[B]): Observable[B] = oapp.map2(self, fb)((_, b) => b)
final def <<[B](fb: => ObjObs[B]): Observable[A] = oapp.map2(self, fb)((a, _) => a)
final def |@|[B, B1](fb: ObservableValue[B, B1]) = ObservableTupler(self, fb) final def |@|[B, B1](fb: ObservableValue[B, B1]) = ObservableTupler(self, fb)

8
src/main/scala/org/gerweck/scalafx/util/observableCollection.scala

@ -1,6 +1,6 @@
package org.gerweck.scalafx.util package org.gerweck.scalafx.util
import scala.compat.java8.FunctionConverters._
// import scala.compat.java8.FunctionConverters._
import java.util.function.{ Predicate => JPredicate } import java.util.function.{ Predicate => JPredicate }
@ -58,9 +58,9 @@ final class RichObservableBuffer[A](val obs: ObservableBuffer[A]) extends AnyVal
fb.predicate <== predicate fb.predicate <== predicate
fb fb
} }
def observeFiltered[B >: A](predicate: Observable[B => Boolean]): FilteredBuffer[A] = {
observeFiltered(predicate.map[JPredicate[A]](asJavaPredicate))
}
// def observeFiltered[B >: A](predicate: Observable[B => Boolean]): FilteredBuffer[A] = {
// observeFiltered(predicate.map[JPredicate[A]](asJavaPredicate))
// }
} }
final class RichObservableArray[A, B <: ObservableArray[A, B, C], C <: javafx.collections.ObservableArray[C]](val oaa: ObservableArray[A, B, C]) extends AnyVal with RichObservableSeqLike[A] { final class RichObservableArray[A, B <: ObservableArray[A, B, C], C <: javafx.collections.ObservableArray[C]](val oaa: ObservableArray[A, B, C]) extends AnyVal with RichObservableSeqLike[A] {

124
src/main/scala/org/gerweck/scalafx/util/prefs/ObservablePref.scala

@ -1,62 +1,62 @@
package org.gerweck.scalafx.util
package prefs
import java.util.prefs._
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) { thisPref =>
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 */
/* In Scala 2.12, the callback could just be bare inside `addPreferenceChangeListener`.
* However, it must be created explicitly since we cross-compile against Scala 2.11. */
val changeListener = new PreferenceChangeListener {
def preferenceChange(pce: PreferenceChangeEvent): Unit = {
if (pce.getKey == path) {
runLater {
val currentValue = thisPref()
if (property.value != currentValue) {
property.value = currentValue
}
}
}
}
}
prefs.addPreferenceChangeListener(changeListener)
/* Property => Preferences bridge */
property.onChange { (_, _, newV) =>
if (newV != this()) {
this() = newV
}
}
/* Return the bridged property */
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)
}
}
// package org.gerweck.scalafx.util
// package prefs
// import java.util.prefs._
// 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) { thisPref =>
// 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 */
// /* In Scala 2.12, the callback could just be bare inside `addPreferenceChangeListener`.
// * However, it must be created explicitly since we cross-compile against Scala 2.11. */
// val changeListener = new PreferenceChangeListener {
// def preferenceChange(pce: PreferenceChangeEvent): Unit = {
// if (pce.getKey == path) {
// runLater {
// val currentValue = thisPref()
// if (property.value != currentValue) {
// property.value = currentValue
// }
// }
// }
// }
// }
// prefs.addPreferenceChangeListener(changeListener)
// /* Property => Preferences bridge */
// property.onChange { (_, _, newV) =>
// if (newV != this()) {
// this() = newV
// }
// }
// /* Return the bridged property */
// 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)
// }
// }

2
version.sbt

@ -1 +1 @@
version in ThisBuild := "0.14.4-SNAPSHOT"
version in ThisBuild := "0.15.0"
Loading…
Cancel
Save