You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
4.6 KiB

  1. package org.gerweck.scalafx.util
  2. // import scala.compat.java8.FunctionConverters._
  3. import java.util.function.{ Predicate => JPredicate }
  4. import scalafx.beans.property._
  5. import scalafx.beans.value._
  6. import scalafx.collections.ObservableSet
  7. import scalafx.collections.ObservableArray
  8. import scalafx.collections.ObservableBuffer
  9. import scalafx.collections.transformation.FilteredBuffer
  10. import scala.collection.immutable.ArraySeq
  11. sealed trait ToFlatObservable[-A, +B] extends Calculable[A, B]
  12. object ToFlatObservable extends CalculableObservable[ToFlatObservable[_, _]] {
  13. implicit def obOps[A] = new ToFlatObservable[ObservableBuffer[A], Seq[A]] {
  14. override def recalculate(oba: ObservableBuffer[A]) = oba.toVector
  15. }
  16. implicit def oaOps[A] = new ToFlatObservable[ObservableArray[A, _, _], Seq[A]] {
  17. override def recalculate(oaa: ObservableArray[A, _, _]) = {
  18. ArraySeq.unsafeWrapArray(oaa.toArray)
  19. }
  20. }
  21. implicit def osOps[A] = new ToFlatObservable[ObservableSet[A], collection.immutable.Set[A]] {
  22. override def recalculate(os: ObservableSet[A]) = os.toSet
  23. }
  24. }
  25. sealed trait ObservableSized[-A] extends Calculable[A, Int]
  26. object ObservableSized extends CalculableObservable[ObservableSized[_]] {
  27. implicit def obSize[A] = new ObservableSized[ObservableBuffer[A]] {
  28. def recalculate(oba: ObservableBuffer[A]) = oba.size
  29. }
  30. implicit def oaSize[A] = new ObservableSized[ObservableArray[A, _, _]] {
  31. def recalculate(oaa: ObservableArray[A, _, _]) = oaa.size
  32. }
  33. implicit def osSize[A] = new ObservableSized[ObservableSet[A]] {
  34. def recalculate(os: ObservableSet[A]) = os.size
  35. }
  36. }
  37. sealed trait RichObservableSeqLike[A] extends Any {
  38. def observableSeqValue: ReadOnlyObjectProperty[Seq[A]]
  39. def observableSize: ReadOnlyObjectProperty[Int]
  40. }
  41. final class RichObservableBuffer[A](val obs: ObservableBuffer[A]) extends AnyVal with RichObservableSeqLike[A] {
  42. def observableSeqValue: ReadOnlyObjectProperty[Seq[A]] = ToFlatObservable.toObservable(obs)
  43. def observableSize = ObservableSized.toObservable(obs)
  44. def observeFiltered(predicate: A => Boolean) = {
  45. new FilteredBuffer(obs, predicate)
  46. }
  47. /* The dummy implicit is here to ensure the `observeFiltered` methods all have different post-erasure types */
  48. def observeFiltered[B >: A](predicate: ObservableValue[JPredicate[B], JPredicate[B]])(implicit dummy: DummyImplicit): FilteredBuffer[A] = {
  49. val fb = new FilteredBuffer(obs)
  50. fb.predicate <== predicate
  51. fb
  52. }
  53. // def observeFiltered[B >: A](predicate: Observable[B => Boolean]): FilteredBuffer[A] = {
  54. // observeFiltered(predicate.map[JPredicate[A]](asJavaPredicate))
  55. // }
  56. }
  57. 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] {
  58. def observableSeqValue: ReadOnlyObjectProperty[Seq[A]] = ToFlatObservable.toObservable(oaa)
  59. def observableSize = ObservableSized.toObservable(oaa)
  60. }
  61. final class RichObservableSet[A](val os: ObservableSet[A]) extends AnyVal {
  62. def observableSetValue: ReadOnlyObjectProperty[Set[A]] = ToFlatObservable.toObservable(os)
  63. def observableSize = ObservableSized.toObservable(os)
  64. }
  65. class CalculableObservable[O <: Calculable[_, _]] {
  66. final def toObservable[A, B](a: A)(implicit ops: O with Calculable[A, B], cl: ChangeListenable[A]): ReadOnlyObjectProperty[B] = {
  67. @inline def recalculate(): B = ops.recalculate(a)
  68. val originalValue = recalculate()
  69. val prop = ObjectProperty[B](originalValue)
  70. var prevValue = originalValue
  71. cl.onChange(a) {
  72. prop.synchronized {
  73. val newVal = recalculate()
  74. if (prevValue != newVal) {
  75. prop.value = newVal
  76. prevValue = newVal
  77. }
  78. }
  79. }
  80. prop
  81. }
  82. }
  83. /* Type Classes */
  84. trait Calculable[-A, +B] extends Any {
  85. def recalculate(a: A): B
  86. }
  87. sealed trait ChangeListenable[-A] {
  88. def onChange(a: A)(b: => Unit): Unit
  89. }
  90. object ChangeListenable {
  91. implicit def obListenable[A]: ChangeListenable[ObservableBuffer[A]] = new ChangeListenable[ObservableBuffer[A]] {
  92. def onChange(oba: ObservableBuffer[A])(b: => Unit) = oba onChange b
  93. }
  94. implicit def oaListenable[A]: ChangeListenable[ObservableArray[A, _, _]] = new ChangeListenable[ObservableArray[A, _, _]] {
  95. def onChange(oaa: ObservableArray[A, _, _])(b: => Unit) = oaa onChange b
  96. }
  97. implicit def osListenable[A]: ChangeListenable[ObservableSet[A]] = new ChangeListenable[ObservableSet[A]] {
  98. def onChange(osa: ObservableSet[A])(b: => Unit) = osa onChange b
  99. }
  100. }
  101. sealed trait DeriveChanges[A] {
  102. protected val evChange: ChangeListenable[A]
  103. def onChange(a: A)(b: => Unit) = evChange.onChange(a)(b)
  104. }