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.

262 lines
7.4 KiB

  1. package nova.monadic_sfx.implicits
  2. import javafx.beans.property.ObjectProperty
  3. import javafx.collections.ObservableList
  4. import javafx.event.ActionEvent
  5. import javafx.event.EventHandler
  6. import javafx.scene.{input => jfxsi}
  7. import javafx.{event => jfxe}
  8. import monix.bio.Task
  9. import monix.execution.Ack
  10. import monix.execution.Cancelable
  11. import monix.execution.Scheduler
  12. import monix.reactive.Observable
  13. import monix.reactive.Observer
  14. import monix.reactive.OverflowStrategy
  15. import monix.tail.Iterant
  16. import monix.{eval => me}
  17. import org.gerweck.scalafx.util._
  18. import scalafx.Includes._
  19. import scalafx.beans.property.Property
  20. import scalafx.beans.property.ReadOnlyProperty
  21. import scalafx.beans.value.ObservableValue
  22. import scalafx.collections.ObservableBuffer
  23. import scalafx.scene.Scene
  24. import scalafx.scene.control.ButtonBase
  25. import scalafx.scene.control.MenuItem
  26. object JavaFXMonixObservables {
  27. implicit final class SceneObservables(private val scene: Scene)
  28. extends AnyVal {
  29. def observableMousePressed(): Observable[jfxsi.MouseEvent] = {
  30. import monix.execution.cancelables.SingleAssignCancelable
  31. Observable.create(OverflowStrategy.Unbounded) { sub =>
  32. val c = SingleAssignCancelable()
  33. val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
  34. override def handle(event: jfxsi.MouseEvent): Unit = {
  35. if (sub.onNext(event) == Ack.Stop) c.cancel()
  36. }
  37. }
  38. scene.onMousePressed = l
  39. c := Cancelable(() =>
  40. scene.removeEventHandler(
  41. jfxsi.MouseEvent.MOUSE_PRESSED,
  42. l
  43. )
  44. )
  45. c
  46. }
  47. }
  48. def observableMouseDragged(): Observable[jfxsi.MouseEvent] = {
  49. import monix.execution.cancelables.SingleAssignCancelable
  50. Observable.create(OverflowStrategy.Unbounded) { sub =>
  51. val c = SingleAssignCancelable()
  52. val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
  53. override def handle(event: jfxsi.MouseEvent): Unit = {
  54. if (sub.onNext(event) == Ack.Stop) c.cancel()
  55. }
  56. }
  57. scene.onMouseDragged = l
  58. c := Cancelable(() =>
  59. scene.removeEventHandler(
  60. jfxsi.MouseEvent.MOUSE_DRAGGED,
  61. l
  62. )
  63. )
  64. c
  65. }
  66. }
  67. }
  68. implicit final class BindObs[T, J](private val prop: Property[T, J])
  69. extends AnyVal {
  70. def -->(op: Observer[T]) = {
  71. op.onNext(prop.value)
  72. }
  73. def ==>(op: Property[T, J]) = {
  74. op <== prop
  75. }
  76. def <--(obs: Observable[T])(implicit s: Scheduler) = {
  77. obs.doOnNext(v => me.Task(prop.value = v)).subscribe()
  78. }
  79. def asOption = prop.map(Option(_))
  80. def observableChange[J1 >: J](): Observable[J1] = {
  81. import monix.execution.cancelables.SingleAssignCancelable
  82. Observable.create(OverflowStrategy.Unbounded) { sub =>
  83. val c = SingleAssignCancelable()
  84. val canc = prop.onChange((a, b, c) => sub.onNext(c))
  85. c := Cancelable(() => canc.cancel())
  86. c
  87. }
  88. }
  89. }
  90. implicit final class BindObs2[A](private val prop: ObjectProperty[A])
  91. extends AnyVal {
  92. def -->(sub: Observer[A]) =
  93. prop.onChange((a, b, c) => if (c != null) sub.onNext(c))
  94. def -->(op: Property[A, A]) = {
  95. prop.onChange((a, b, c) => if (c != null) op() = c)
  96. }
  97. def <--(obs: Observable[A])(implicit s: Scheduler) = {
  98. obs.doOnNext(v => me.Task(prop() = v)).subscribe()
  99. }
  100. def observableChange[J1 >: A]()
  101. : Observable[(ObservableValue[A, A], J1, J1)] = {
  102. import monix.execution.cancelables.SingleAssignCancelable
  103. Observable.create(OverflowStrategy.Unbounded) { sub =>
  104. val c = SingleAssignCancelable()
  105. val canc = prop.onChange((a, b, c) => sub.onNext((a, b, c)))
  106. c := Cancelable(() => canc.cancel())
  107. c
  108. }
  109. }
  110. }
  111. implicit final class BindObs3[T, J](private val prop: ReadOnlyProperty[T, J])
  112. extends AnyVal {
  113. def -->(op: Observer[T]) = {
  114. op.onNext(prop.value)
  115. }
  116. def ==>(op: Property[T, J]) = {
  117. op <== prop
  118. }
  119. def observableChange[J1 >: J](): Observable[J1] = {
  120. import monix.execution.cancelables.SingleAssignCancelable
  121. Observable.create(OverflowStrategy.Unbounded) { sub =>
  122. val c = SingleAssignCancelable()
  123. val canc = prop.onChange((a, b, c) => sub.onNext(c))
  124. c := Cancelable(() => canc.cancel())
  125. c
  126. }
  127. }
  128. }
  129. implicit final class ObjectPropertyObservableListExt[A](
  130. private val prop: ObjectProperty[ObservableList[A]]
  131. ) extends AnyVal {
  132. def <--(obs: Observable[Seq[A]])(implicit s: Scheduler) = {
  133. obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
  134. }
  135. def -->(sub: Observer[A])(implicit s: Scheduler) =
  136. prop.onChange((a, b, c) =>
  137. if (c != null)
  138. Iterant[Task]
  139. .fromIterable(c.toIterable)
  140. .consume
  141. .use(consume(sub, _))
  142. .runToFuture
  143. )
  144. private def loop(sub: Observer[A], it: Iterator[A]): Task[Unit] =
  145. if (it.hasNext) {
  146. val next = it.next()
  147. Task.deferFuture(sub.onNext(next)).flatMap {
  148. case Ack.Continue => loop(sub, it)
  149. case Ack.Stop => Task.unit
  150. }
  151. } else Task.unit
  152. private def consume(
  153. sub: Observer[A],
  154. consumer: Iterant.Consumer[Task, A]
  155. ): Task[Unit] =
  156. consumer.pull.flatMap {
  157. case Left(value) => Task.unit
  158. case Right(value) =>
  159. Task.deferFuture(sub.onNext(value)).flatMap {
  160. case Ack.Continue => consume(sub, consumer)
  161. case Ack.Stop => Task.unit
  162. }
  163. }
  164. }
  165. implicit final class ObjectPropertyActionEvent(
  166. private val prop: ObjectProperty[EventHandler[ActionEvent]]
  167. ) extends AnyVal {
  168. // def <--(obs: Observable[ActionEvent])(implicit s: Scheduler) = {
  169. // obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
  170. // }
  171. // def -->(sub: Observer[ActionEvent]) =
  172. // prop().
  173. }
  174. // def emit(prop: ObjectProperty[EventHandler[ActionEvent]]) =
  175. implicit final class OnActionObservable(
  176. private val button: ButtonBase
  177. ) extends AnyVal {
  178. def observableAction(): Observable[jfxe.ActionEvent] = {
  179. import monix.execution.cancelables.SingleAssignCancelable
  180. Observable.create(OverflowStrategy.Unbounded) { sub =>
  181. val c = SingleAssignCancelable()
  182. val l = new jfxe.EventHandler[jfxe.ActionEvent] {
  183. override def handle(event: jfxe.ActionEvent): Unit = {
  184. if (sub.onNext(event) == Ack.Stop) c.cancel()
  185. }
  186. }
  187. button.onAction = l
  188. c := Cancelable(() =>
  189. button.removeEventHandler(
  190. jfxe.ActionEvent.ACTION,
  191. l
  192. )
  193. )
  194. c
  195. }
  196. }
  197. }
  198. implicit final class MenuItemActionObservable(
  199. private val item: MenuItem
  200. ) extends AnyVal {
  201. def observableAction(): Observable[jfxe.ActionEvent] = {
  202. import monix.execution.cancelables.SingleAssignCancelable
  203. Observable.create(OverflowStrategy.Unbounded) { sub =>
  204. val c = SingleAssignCancelable()
  205. val l = new jfxe.EventHandler[jfxe.ActionEvent] {
  206. override def handle(event: jfxe.ActionEvent): Unit = {
  207. if (sub.onNext(event) == Ack.Stop) c.cancel()
  208. }
  209. }
  210. item.onAction = l
  211. c := Cancelable(() =>
  212. item.removeEventHandler(
  213. jfxe.ActionEvent.ACTION,
  214. l
  215. )
  216. )
  217. c
  218. }
  219. }
  220. }
  221. }