Add operations to flatten arrays, lists and sets

This makes it really convenient to take a data structure like a
`ObservableBuffer`, which normally has special operations to watch for
different CRUD operations on its individual members, and treat the whole
thing as a solid value. This lets you turn it into an `ObservableValue`
where you get a notice of changes and then you can use the normal
operations like `map` and `flatMap` to react to changes.

Because this requires reprocessing the entirety of the contained values,
this may not perform as well as if you subscribe to more granular update
messages.

It is possible to round-trip these collections back to the ScalaFX
native collections using the other utilities available. This will be
required if you want to use the output to back something like a table.
This commit is contained in:
Sarah Gerweck 2016-04-16 17:21:27 -07:00
parent d9ffc45d69
commit 6956bd05da
2 changed files with 72 additions and 0 deletions

8
CHANGELOG.md Normal file
View File

@ -0,0 +1,8 @@
# ScalaFX-Utils changelog
For a full change history, see the Git history. Only changes likely to affect
end users will be listed here.
### 0.6.0
* Add new operations to flatten observable collections.

View File

@ -114,6 +114,10 @@ trait ObservableImplicits {
implicit def enrichObservableOfMapLike[A, B, C](ooml: ObservableValue[C, C])(implicit ev1: C => Iterable[(A, B)]) = new ObservableOfMapLike[A, B, C](ooml) implicit def enrichObservableOfMapLike[A, B, C](ooml: ObservableValue[C, C])(implicit ev1: C => Iterable[(A, B)]) = new ObservableOfMapLike[A, B, C](ooml)
implicit def enrichProperty[A, B](o: Property[A, B]) = new RichProperty(o) implicit def enrichProperty[A, B](o: Property[A, B]) = new RichProperty(o)
implicit def enrichTuple[A <: Product](a: A) = new RichTuple(a) implicit def enrichTuple[A <: Product](a: A) = new RichTuple(a)
implicit def enrichObservableBuffer[A](ob: ObservableBuffer[A]) = new RichObservableBuffer(ob)
implicit def enrichObservableArray[A, B <: ObservableArray[A, B, C], C <: javafx.collections.ObservableArray[C]](oa: ObservableArray[A, B, C]) = new RichObservableArray(oa)
implicit def enrichObservableSet[A](os: ObservableSet[A]) = new RichObservableSet(os)
} }
final class RichTuple[A <: Product](val self: A) extends AnyVal { final class RichTuple[A <: Product](val self: A) extends AnyVal {
@ -219,3 +223,63 @@ final class RichProperty[A, B](val inner: Property[A, B]) extends AnyVal {
op op
} }
} }
sealed trait ToObservableOps[-A, +B] {
def recalculate(a: A): B
def onChange(a: A)(b: => Unit): Unit
}
object ToObservableOps {
implicit def obOps[A] = new ToObservableOps[ObservableBuffer[A], Seq[A]] {
def recalculate(oba: ObservableBuffer[A]) = oba.toVector
def onChange(oba: ObservableBuffer[A])(b: => Unit) = oba onChange b
}
implicit def oaOps[A] = new ToObservableOps[ObservableArray[A, _, _], Seq[A]] {
def recalculate(oaa: ObservableArray[A, _, _]) = oaa.toVector
def onChange(oaa: ObservableArray[A, _, _])(b: => Unit) = oaa onChange b
}
implicit def osOps[A] = new ToObservableOps[ObservableSet[A], collection.immutable.Set[A]] {
def recalculate(os: ObservableSet[A]) = os.toSet
def onChange(os: ObservableSet[A])(b: => Unit) = os onChange b
}
}
object RichToObservable {
@inline final def toObservable[A, B](a: A)(implicit ops: ToObservableOps[A, B]): ObservableValue[B, B] = {
@inline def recalculate(): B = ops.recalculate(a)
val originalValue = recalculate()
val prop = ObjectProperty[B](originalValue)
var prevValue = originalValue
ops.onChange(a) {
prop.synchronized {
val newVal = recalculate()
if (prevValue != newVal) {
prop.value = newVal
prevValue = newVal
}
}
}
prop
}
}
sealed trait RichObservableSeqLike[A] extends Any {
def observableSeqValue: ObservableValue[Seq[A], Seq[A]]
}
final class RichObservableBuffer[A](val obs: ObservableBuffer[A]) extends AnyVal with RichObservableSeqLike[A] {
def observableSeqValue: ObservableValue[Seq[A], Seq[A]] = {
RichToObservable.toObservable(obs)
}
}
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] {
def observableSeqValue: ObservableValue[Seq[A], Seq[A]] = {
RichToObservable.toObservable(oaa)
}
}
final class RichObservableSet[A](val os: ObservableSet[A]) extends AnyVal {
def observableSetValue: ObservableValue[Set[A], Set[A]] = {
RichToObservable.toObservable(os)
}
}