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.

288 lines
10 KiB

  1. package org.gerweck.scalafx.util
  2. import language.implicitConversions
  3. import cats._
  4. import scalafx.beans.property._
  5. import scalafx.beans.value._
  6. import scalafx.collections._
  7. trait ObservableImplicits {
  8. /* NOTE: (Sarah) I believe that the synchronization in these helpers is not
  9. * _really_ required in the JavaFX threading model. However, the overhead of
  10. * uncontended synchronization is relatively low, and typical UIs won't have
  11. * enough change events for it to be a serious issue. (If you're updating
  12. * a property in a tight loop, I expect you'll have bigger performance
  13. * issues.)
  14. */
  15. implicit val observableInstances: Applicative[Observable] with Functor[Observable] with Monad[Observable] = ObservableImplicits.ObservableInstances
  16. implicit val readOnlyObjectPropertyInstances: Applicative[ReadOnlyObjectProperty] with Functor[ReadOnlyObjectProperty] with Monad[ReadOnlyObjectProperty] = ObservableImplicits.ReadOnlyObjectPropertyInstances
  17. implicit def enrichObservable[A, B](o: ObservableValue[A, B]) = new RichObservable(o)
  18. implicit def enrichObservableOfIterable[A, B](ooi: ObservableValue[B, B])(implicit ev1: B => Iterable[A]) = new ObservableOfIterable[A, B](ooi)
  19. implicit def enrichObservableOfMapLike[A, B, C](ooml: ObservableValue[C, C])(implicit ev1: C => Iterable[(A, B)]) = new ObservableOfMapLike[A, B, C](ooml)
  20. implicit def enrichProperty[A, B](o: Property[A, B]) = new RichProperty(o)
  21. implicit def enrichObjectProperty[A](o: ObjectProperty[A]) = new RichObjectProperty(o)
  22. implicit def enrichTuple[A <: Product](a: A) = new RichTuple(a)
  23. implicit def enrichObservableBuffer[A](ob: ObservableBuffer[A]) = new RichObservableBuffer(ob)
  24. implicit def enrichObservableArray[A, B <: ObservableArray[A, B, C], C <: javafx.collections.ObservableArray[C]](oa: ObservableArray[A, B, C]) = new RichObservableArray(oa)
  25. implicit def enrichObservableSet[A](os: ObservableSet[A]) = new RichObservableSet(os)
  26. }
  27. private object ObservableImplicits {
  28. object ObservableInstances extends Applicative[Observable] with Functor[Observable] with Monad[Observable] {
  29. /* Map can be derived from `ap`, but this adds less overhead. */
  30. override def map[A, B](a: Observable[A])(f: A => B): ReadOnlyObjectProperty[B] = {
  31. @inline def recalculate(): B = f(a.value)
  32. val originalValue = recalculate()
  33. val prop = ObjectProperty[B](originalValue)
  34. var prevValue = originalValue
  35. def changeHandler() = prop.synchronized {
  36. val newVal = recalculate()
  37. if (prevValue != newVal) {
  38. prop.value = newVal
  39. prevValue = newVal
  40. }
  41. }
  42. a onChange changeHandler()
  43. prop
  44. }
  45. override def pure[A](a: A): ReadOnlyObjectProperty[A] = {
  46. ObjectProperty[A](a)
  47. }
  48. /* Ap can be derived from `point` and `bind`, but this has less overhead. */
  49. override def ap[A, B](f: Observable[A => B])(fa: Observable[A]): ReadOnlyObjectProperty[B] = {
  50. @inline def recalculate(): B = (f.value)(fa.value)
  51. val originalValue = recalculate()
  52. val prop = ObjectProperty[B](originalValue)
  53. var prevValue = originalValue
  54. def changeHandler() = prop.synchronized {
  55. val newVal = recalculate()
  56. if (prevValue != newVal) {
  57. prop.value = newVal
  58. prevValue = newVal
  59. }
  60. }
  61. fa onChange changeHandler()
  62. f onChange changeHandler()
  63. prop
  64. }
  65. override def flatMap[A, B](fa: Observable[A])(f: A => Observable[B]): ReadOnlyObjectProperty[B] = {
  66. flatten(map(fa)(f))
  67. }
  68. override def tailRecM[A, B](a: A)(f: A => Observable[Either[A, B]]): Observable[B] = {
  69. this.flatMap(f(a)) {
  70. case Right(b) => pure(b)
  71. case Left(nextA) => tailRecM(nextA)(f)
  72. }
  73. }
  74. override def flatten[A](ooa: Observable[Observable[A]]): ReadOnlyObjectProperty[A] = {
  75. @inline def oa() = ooa.value
  76. @inline def calc(): A = oa().value
  77. val originalValue = calc()
  78. val prop = ObjectProperty[A](originalValue)
  79. var prevValue = originalValue
  80. def innerHandle() = prop.synchronized {
  81. val newVal = calc()
  82. if (prevValue != newVal) {
  83. prop.value = newVal
  84. prevValue = newVal
  85. }
  86. }
  87. var innerSub = oa() onChange innerHandle()
  88. var prevOuter = oa()
  89. def outerHandle() = prop.synchronized {
  90. val newOuter = oa()
  91. /* We need reference equality here: we're subscribing to a specific object. */
  92. if (prevOuter ne newOuter) {
  93. innerSub.cancel()
  94. innerSub = newOuter onChange innerHandle()
  95. prevOuter = newOuter
  96. innerHandle()
  97. }
  98. }
  99. ooa onChange outerHandle()
  100. prop
  101. }
  102. }
  103. object ReadOnlyObjectPropertyInstances extends Applicative[ReadOnlyObjectProperty] with Functor[ReadOnlyObjectProperty] with Monad[ReadOnlyObjectProperty] {
  104. override def map[A, B](a: ReadOnlyObjectProperty[A])(f: A => B): ReadOnlyObjectProperty[B] = ObservableInstances.map(a)(f)
  105. override def pure[A](a: A): ReadOnlyObjectProperty[A] = ObservableInstances.pure(a)
  106. override def ap[A, B](f: ReadOnlyObjectProperty[A => B])(fa: ReadOnlyObjectProperty[A]): ReadOnlyObjectProperty[B] = ObservableInstances.ap(f)(fa)
  107. override def flatMap[A, B](fa: ReadOnlyObjectProperty[A])(f: A => ReadOnlyObjectProperty[B]): ReadOnlyObjectProperty[B] = ObservableInstances.flatMap(fa)(f)
  108. override def tailRecM[A, B](a: A)(f: A => ReadOnlyObjectProperty[Either[A, B]]): ReadOnlyObjectProperty[B] = {
  109. flatMap(f(a)) {
  110. case Right(b) => pure(b)
  111. case Left(nextA) => tailRecM(nextA)(f)
  112. }
  113. }
  114. override def flatten[A](ooa: ReadOnlyObjectProperty[ReadOnlyObjectProperty[A]]): ReadOnlyObjectProperty[A] = {
  115. /* NOTE: this is copy-pasted from `observableInstances`. TBD: Find a way to share this. */
  116. @inline def oa() = ooa.value
  117. @inline def calc(): A = oa().value
  118. val originalValue = calc()
  119. val prop = ObjectProperty[A](originalValue)
  120. var prevValue = originalValue
  121. def innerHandle() = prop.synchronized {
  122. val newVal = calc()
  123. if (prevValue != newVal) {
  124. prop.value = newVal
  125. prevValue = newVal
  126. }
  127. }
  128. var innerSub = oa() onChange innerHandle()
  129. var prevOuter = oa()
  130. def outerHandle() = prop.synchronized {
  131. val newOuter = oa()
  132. /* We need reference equality here: we're subscribing to a specific object. */
  133. if (prevOuter ne newOuter) {
  134. innerSub.cancel()
  135. innerSub = newOuter onChange innerHandle()
  136. prevOuter = newOuter
  137. innerHandle()
  138. }
  139. }
  140. ooa onChange outerHandle()
  141. prop
  142. }
  143. }
  144. }
  145. final class RichTuple[A <: Product](val self: A) extends AnyVal {
  146. import shapeless._
  147. import shapeless.ops.hlist._
  148. /* It's possible to do this operation without conversion directly using
  149. * Shapeless's `tuple` package, but it can't infer the exact output type,
  150. * which is far less useful.
  151. */
  152. def observe[L <: HList, Unwrap <: HList, Tupled <: Product]
  153. (implicit tohl: Generic.Aux[A, L],
  154. lister: ToTraversable.Aux[L, List, Observable[_]],
  155. uw: Mapper.Aux[ObservableUnwrapper.type, L, Unwrap],
  156. tplr: Tupler.Aux[Unwrap, Tupled]): ObservableValue[Tupled, Tupled] = {
  157. val asHList: L = tohl.to(self)
  158. def calculate(): Tupled = uw(asHList).tupled
  159. val original = calculate()
  160. val prop = ObjectProperty[Tupled](original)
  161. for (component <- asHList.to[List]) {
  162. component onChange {
  163. prop.value = calculate()
  164. }
  165. }
  166. prop
  167. }
  168. }
  169. final class RichObservable[A, C](val self: ObservableValue[A, C]) extends AnyVal {
  170. private[this] type ObjObs[X] = ObservableValue[X, X]
  171. @inline private[this] def oapp = ObservableImplicits.ObservableInstances
  172. def map[B](f: A => B) = oapp.map(self)(f)
  173. def flatMap[B](f: A => Observable[B]) = oapp.flatMap(self)(f)
  174. def <*>[B](f: Observable[A => B]): ObservableValue[B, B] = oapp.ap(f)(self)
  175. def tuple[B](f: Observable[B]): Observable[(A,B)] = oapp.tuple2(self, f)
  176. final def *>[B](fb: ObjObs[B]): Observable[B] = oapp.map2(self,fb)((_,b) => b)
  177. final def <*[B](fb: ObjObs[B]): Observable[A] = oapp.map2(self,fb)((a,_) => a)
  178. final def >>[B](fb: => ObjObs[B]): Observable[B] = oapp.map2(self, fb)((_, b) => b)
  179. final def <<[B](fb: => ObjObs[B]): Observable[A] = oapp.map2(self, fb)((a, _) => a)
  180. final def |@|[B, B1](fb: ObservableValue[B, B1]) = ObservableTupler(self, fb)
  181. /** Alias for `|@|` */
  182. final def [B, B1](fb: ObservableValue[B, B1]) = |@|(fb)
  183. }
  184. final class ObservableOfIterable[A, B](val self: ObservableValue[B, B])(implicit ev1: B => Iterable[A]) {
  185. def observeBuffer: ObservableBuffer[A] = {
  186. val buff = ObservableBuffer(self.value.toSeq)
  187. self onChange { (_, oldV, newV) => fillCollection(buff.delegate, newV) }
  188. buff
  189. }
  190. def observeSet: ObservableSet[A] = {
  191. val set = ObservableSet[A](self.value.toSet.toSeq: _*)
  192. self onChange { (_, oldV, newV) =>
  193. val newSet = newV.toSet
  194. if (oldV.toSet != newSet) {
  195. set.clear()
  196. set ++= newSet
  197. }
  198. }
  199. set
  200. }
  201. }
  202. final class ObservableOfMapLike[A, B, C](val self: ObservableValue[C, C])(implicit ev1: C => Iterable[(A, B)]) {
  203. def observeMap: ObservableMap[A, B] = {
  204. val map = ObservableMap[A, B](self.value.toMap.toSeq: _*)
  205. self onChange { (_, oldV, newV) =>
  206. val newMap = newV.toMap
  207. if (oldV.toMap != newV.toMap) {
  208. map.clear()
  209. map ++= newMap
  210. }
  211. }
  212. map
  213. }
  214. }
  215. final class RichProperty[A, B](val inner: Property[A, B]) extends AnyVal {
  216. def biMap[A1 <: AnyRef](push: A => A1, pull: A1 => A): ObjectProperty[A1] = {
  217. val original = push(inner.value)
  218. val op = ObjectProperty[A1](original)
  219. inner onChange {
  220. val oldVal = op.value
  221. val newVal = push(inner.value)
  222. if (oldVal != newVal) {
  223. op.value = push(inner.value)
  224. }
  225. }
  226. op onChange {
  227. val oldVal = inner.value
  228. val newVal = pull(op.value)
  229. if (oldVal != newVal) {
  230. inner.value = newVal
  231. }
  232. }
  233. op
  234. }
  235. def readOnly: ReadOnlyProperty[A, B] = inner
  236. }
  237. final class RichObjectProperty[A](val inner: ObjectProperty[A]) extends AnyVal {
  238. def readOnly: ReadOnlyObjectProperty[A] = inner
  239. }