diff --git a/src/main/scala/org/gerweck/scalafx/util/observable.scala b/src/main/scala/org/gerweck/scalafx/util/observable.scala index 149bb5b..3b8d631 100644 --- a/src/main/scala/org/gerweck/scalafx/util/observable.scala +++ b/src/main/scala/org/gerweck/scalafx/util/observable.scala @@ -273,63 +273,3 @@ final class RichProperty[A, B](val inner: Property[A, B]) extends AnyVal { final class RichObjectProperty[A](val inner: ObjectProperty[A]) extends AnyVal { def readOnly: ReadOnlyObjectProperty[A] = inner } - -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]): ReadOnlyObjectProperty[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: ReadOnlyObjectProperty[Seq[A]] -} - -final class RichObservableBuffer[A](val obs: ObservableBuffer[A]) extends AnyVal with RichObservableSeqLike[A] { - def observableSeqValue: ReadOnlyObjectProperty[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: ReadOnlyObjectProperty[Seq[A]] = { - RichToObservable.toObservable(oaa) - } -} - -final class RichObservableSet[A](val os: ObservableSet[A]) extends AnyVal { - def observableSetValue: ReadOnlyObjectProperty[Set[A]] = { - RichToObservable.toObservable(os) - } -} diff --git a/src/main/scala/org/gerweck/scalafx/util/observableCollection.scala b/src/main/scala/org/gerweck/scalafx/util/observableCollection.scala new file mode 100644 index 0000000..3803baa --- /dev/null +++ b/src/main/scala/org/gerweck/scalafx/util/observableCollection.scala @@ -0,0 +1,97 @@ +package org.gerweck.scalafx.util + +import language.implicitConversions + +import scalafx.beans.property._ +import scalafx.collections._ + +sealed trait ToFlatObservable[-A, +B] extends Calculable[A, B] +object ToFlatObservable extends CalculableObservable[ToFlatObservable[_, _]] { + implicit def obOps[A] = new ToFlatObservable[ObservableBuffer[A], Seq[A]] { + def recalculate(oba: ObservableBuffer[A]) = oba.toVector + } + implicit def oaOps[A] = new ToFlatObservable[ObservableArray[A, _, _], Seq[A]] { + def recalculate(oaa: ObservableArray[A, _, _]) = oaa.toVector + } + implicit def osOps[A] = new ToFlatObservable[ObservableSet[A], collection.immutable.Set[A]] { + def recalculate(os: ObservableSet[A]) = os.toSet + } +} + +sealed trait ObservableSized[-A] extends Calculable[A, Int] +object ObservableSized extends CalculableObservable[ObservableSized[_]] { + implicit def obSize[A] = new ObservableSized[ObservableBuffer[A]] { + def recalculate(oba: ObservableBuffer[A]) = oba.size + } + implicit def oaSize[A] = new ObservableSized[ObservableArray[A, _, _]] { + def recalculate(oaa: ObservableArray[A, _, _]) = oaa.size + } + implicit def osSize[A] = new ObservableSized[ObservableSet[A]] { + def recalculate(os: ObservableSet[A]) = os.size + } +} + +sealed trait RichObservableSeqLike[A] extends Any { + def observableSeqValue: ReadOnlyObjectProperty[Seq[A]] + def observableSize: ReadOnlyObjectProperty[Int] +} + +final class RichObservableBuffer[A](val obs: ObservableBuffer[A]) extends AnyVal with RichObservableSeqLike[A] { + def observableSeqValue: ReadOnlyObjectProperty[Seq[A]] = ToFlatObservable.toObservable(obs) + def observableSize = ObservableSized.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: ReadOnlyObjectProperty[Seq[A]] = ToFlatObservable.toObservable(oaa) + def observableSize = ObservableSized.toObservable(oaa) +} + +final class RichObservableSet[A](val os: ObservableSet[A]) extends AnyVal { + def observableSetValue: ReadOnlyObjectProperty[Set[A]] = ToFlatObservable.toObservable(os) + def observableSize = ObservableSized.toObservable(os) +} + +class CalculableObservable[O <: Calculable[_, _]] { + final def toObservable[A, B](a: A)(implicit ops: O with Calculable[A, B], cl: ChangeListenable[A]): ReadOnlyObjectProperty[B] = { + @inline def recalculate(): B = ops.recalculate(a) + val originalValue = recalculate() + val prop = ObjectProperty[B](originalValue) + var prevValue = originalValue + cl.onChange(a) { + prop.synchronized { + val newVal = recalculate() + if (prevValue != newVal) { + prop.value = newVal + prevValue = newVal + } + } + } + prop + } +} + +/* Type Classes */ + +trait Calculable[-A, +B] extends Any { + def recalculate(a: A): B +} + +sealed trait ChangeListenable[-A] { + def onChange(a: A)(b: => Unit): Unit +} +object ChangeListenable { + implicit def obListenable[A]: ChangeListenable[ObservableBuffer[A]] = new ChangeListenable[ObservableBuffer[A]] { + def onChange(oba: ObservableBuffer[A])(b: => Unit) = oba onChange b + } + implicit def oaListenable[A]: ChangeListenable[ObservableArray[A, _, _]] = new ChangeListenable[ObservableArray[A, _, _]] { + def onChange(oaa: ObservableArray[A, _, _])(b: => Unit) = oaa onChange b + } + implicit def osListenable[A]: ChangeListenable[ObservableSet[A]] = new ChangeListenable[ObservableSet[A]] { + def onChange(osa: ObservableSet[A])(b: => Unit) = osa onChange b + } +} + +sealed trait DeriveChanges[A] { + protected val evChange: ChangeListenable[A] + def onChange(a: A)(b: => Unit) = evChange.onChange(a)(b) +} diff --git a/version.sbt b/version.sbt index 5877151..338b0ba 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.8.4-SNAPSHOT" +version in ThisBuild := "0.9.0-SNAPSHOT"