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.

327 lines
9.1 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.eval.Coeval
  10. import monix.execution.Ack
  11. import monix.execution.Cancelable
  12. import monix.execution.Scheduler
  13. import monix.execution.cancelables.SingleAssignCancelable
  14. import monix.reactive.Observable
  15. import monix.reactive.Observer
  16. import monix.reactive.OverflowStrategy
  17. import monix.tail.Iterant
  18. import monix.{eval => me}
  19. import org.gerweck.scalafx.util._
  20. import scalafx.Includes._
  21. import scalafx.beans.property.Property
  22. import scalafx.beans.property.ReadOnlyProperty
  23. import scalafx.collections.ObservableBuffer
  24. import scalafx.event.subscriptions.Subscription
  25. import scalafx.scene.Scene
  26. import scalafx.scene.control.ButtonBase
  27. import scalafx.scene.control.MenuItem
  28. object JavaFXMonixObservables {
  29. implicit final class SceneObservables(private val scene: Scene)
  30. extends AnyVal {
  31. def observableMousePressed(): Observable[jfxsi.MouseEvent] = {
  32. import monix.execution.cancelables.SingleAssignCancelable
  33. Observable.create(OverflowStrategy.Unbounded) { sub =>
  34. val c = SingleAssignCancelable()
  35. val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
  36. override def handle(event: jfxsi.MouseEvent): Unit = {
  37. if (sub.onNext(event) == Ack.Stop) c.cancel()
  38. }
  39. }
  40. scene.onMousePressed = l
  41. c := Cancelable(() =>
  42. scene.removeEventHandler(
  43. jfxsi.MouseEvent.MOUSE_PRESSED,
  44. l
  45. )
  46. )
  47. c
  48. }
  49. }
  50. def observableMouseDragged(): Observable[jfxsi.MouseEvent] = {
  51. import monix.execution.cancelables.SingleAssignCancelable
  52. Observable.create(OverflowStrategy.Unbounded) { sub =>
  53. val c = SingleAssignCancelable()
  54. val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
  55. override def handle(event: jfxsi.MouseEvent): Unit = {
  56. if (sub.onNext(event) == Ack.Stop) c.cancel()
  57. }
  58. }
  59. scene.onMouseDragged = l
  60. c := Cancelable(() =>
  61. scene.removeEventHandler(
  62. jfxsi.MouseEvent.MOUSE_DRAGGED,
  63. l
  64. )
  65. )
  66. c
  67. }
  68. }
  69. }
  70. implicit final class BindObs[T, J](private val prop: Property[T, J])
  71. extends AnyVal {
  72. def -->(op: Observer[T]) = {
  73. op.onNext(prop.value)
  74. }
  75. def ==>(op: Property[T, J]) = {
  76. op <== prop
  77. }
  78. def <--(obs: Observable[T])(implicit s: Scheduler) = {
  79. obs.doOnNextF(v => Coeval(prop.value = v)).subscribe()
  80. }
  81. def asOption = prop.map(Option(_))
  82. def observableChange[J1 >: J]: Observable[J1] = {
  83. import monix.execution.cancelables.SingleAssignCancelable
  84. Observable.create(OverflowStrategy.Unbounded) { sub =>
  85. val c = SingleAssignCancelable()
  86. val canc =
  87. prop.onChange((a, b, c1) =>
  88. if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
  89. )
  90. c := Cancelable(() => canc.cancel())
  91. c
  92. }
  93. }
  94. }
  95. implicit final class BindObs2[A](private val prop: ObjectProperty[A])
  96. extends AnyVal {
  97. def -->(sub: Observer[A]) =
  98. prop.onChange((a, b, c) => if (c != null) sub.onNext(c))
  99. def -->(op: Property[A, A]) = {
  100. prop.onChange((a, b, c) => if (c != null) op() = c)
  101. }
  102. def <--(obs: Observable[A])(implicit s: Scheduler) = {
  103. obs.doOnNextF(v => Coeval(prop() = v)).subscribe()
  104. }
  105. def observableChange[J1 >: A]: Observable[J1] = {
  106. import monix.execution.cancelables.SingleAssignCancelable
  107. Observable.create(OverflowStrategy.Unbounded) { sub =>
  108. val c = SingleAssignCancelable()
  109. val canc = prop.onChange((_, _, c1) =>
  110. if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
  111. )
  112. c := Cancelable(() => canc.cancel())
  113. c
  114. }
  115. }
  116. }
  117. implicit final class ObservableListExt[A](
  118. private val buffer: ObservableList[A]
  119. ) extends AnyVal {
  120. // def -->(sub: Observer[A]) =
  121. // buffer.onChange((a, b, c) => if (c != null) sub.onNext(c))
  122. // def -->(op: Property[A, A]) = {
  123. // buffer.onChange((a, b, c) => if (c != null) op() = c)
  124. // }
  125. def <--(obs: Observable[A])(implicit s: Scheduler) = {
  126. obs
  127. .doOnNextF(v =>
  128. for {
  129. _ <- Coeval(buffer.clear())
  130. _ <- Coeval(buffer += v)
  131. } yield ()
  132. )
  133. .subscribe()
  134. }
  135. def observableChange[J1 >: A]: Observable[J1] = {
  136. import monix.execution.cancelables.SingleAssignCancelable
  137. Observable.create(OverflowStrategy.Unbounded) { sub =>
  138. val c = SingleAssignCancelable()
  139. implicit val s = sub.scheduler
  140. val canc =
  141. buffer.onChange((buf, _) =>
  142. loop(sub, buf.toIterable.iterator, c).runToFuture
  143. )
  144. c := Cancelable(() => canc.cancel())
  145. c
  146. }
  147. }
  148. private def loop(
  149. sub: Observer[A],
  150. it: Iterator[A],
  151. c: Cancelable
  152. ): Task[Unit] =
  153. if (it.hasNext) {
  154. val next = it.next()
  155. Task.deferFuture(sub.onNext(next)).flatMap {
  156. case Ack.Continue => loop(sub, it, c)
  157. case Ack.Stop => Task(c.cancel())
  158. }
  159. } else Task.unit
  160. }
  161. implicit final class BindObs3[T, J](private val prop: ReadOnlyProperty[T, J])
  162. extends AnyVal {
  163. def -->(op: Observer[T]) = {
  164. op.onNext(prop.value)
  165. }
  166. def ==>(op: Property[T, J]) = {
  167. op <== prop
  168. }
  169. def observableChange[J1 >: J]: Observable[J1] = {
  170. import monix.execution.cancelables.SingleAssignCancelable
  171. Observable.create(OverflowStrategy.Unbounded) { sub =>
  172. val c = SingleAssignCancelable()
  173. val canc = prop.onChange((a, b, c1) =>
  174. if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
  175. )
  176. c := Cancelable(() => canc.cancel())
  177. c
  178. }
  179. }
  180. }
  181. implicit final class ObjectPropertyObservableListExt[A](
  182. private val prop: ObjectProperty[ObservableList[A]]
  183. ) extends AnyVal {
  184. def <--(obs: Observable[Seq[A]])(implicit s: Scheduler) = {
  185. obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
  186. }
  187. def -->(sub: Observer[A])(implicit s: Scheduler) = {
  188. val c = SingleAssignCancelable()
  189. val subs: Subscription = prop.onChange((a, b, c1) =>
  190. if (c1 != null)
  191. Iterant[Task]
  192. .fromIterable(c1.toIterable)
  193. .consume
  194. .use(consume(sub, c, _))
  195. .runToFuture
  196. )
  197. c := Cancelable(() => subs.cancel())
  198. }
  199. private def loop(sub: Observer[A], it: Iterator[A]): Task[Unit] =
  200. if (it.hasNext) {
  201. val next = it.next()
  202. Task.deferFuture(sub.onNext(next)).flatMap {
  203. case Ack.Continue => loop(sub, it)
  204. case Ack.Stop => Task.unit
  205. }
  206. } else Task.unit
  207. private def consume(
  208. sub: Observer[A],
  209. c: Cancelable,
  210. consumer: Iterant.Consumer[Task, A]
  211. ): Task[Unit] =
  212. consumer.pull.flatMap {
  213. case Left(value) => Task.unit
  214. case Right(value) =>
  215. Task.deferFuture(sub.onNext(value)).flatMap {
  216. case Ack.Continue => consume(sub, c, consumer)
  217. case Ack.Stop => Task(c.cancel())
  218. }
  219. }
  220. }
  221. implicit final class ObjectPropertyActionEvent(
  222. private val prop: ObjectProperty[EventHandler[ActionEvent]]
  223. ) extends AnyVal {
  224. // def <--(obs: Observable[ActionEvent])(implicit s: Scheduler) = {
  225. // obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
  226. // }
  227. // def -->(sub: Observer[ActionEvent]) =
  228. // prop().
  229. }
  230. // def emit(prop: ObjectProperty[EventHandler[ActionEvent]]) =
  231. implicit final class OnActionObservable(
  232. private val button: ButtonBase
  233. ) extends AnyVal {
  234. def observableAction: Observable[jfxe.ActionEvent] = {
  235. import monix.execution.cancelables.SingleAssignCancelable
  236. Observable.create(OverflowStrategy.Unbounded) { sub =>
  237. val c = SingleAssignCancelable()
  238. val l = new jfxe.EventHandler[jfxe.ActionEvent] {
  239. override def handle(event: jfxe.ActionEvent): Unit = {
  240. if (sub.onNext(event) == Ack.Stop) c.cancel()
  241. }
  242. }
  243. button.onAction = l
  244. c := Cancelable(() =>
  245. button.removeEventHandler(
  246. jfxe.ActionEvent.ACTION,
  247. l
  248. )
  249. )
  250. c
  251. }
  252. }
  253. }
  254. implicit final class MenuItemActionObservable(
  255. private val item: MenuItem
  256. ) extends AnyVal {
  257. def observableAction: Observable[jfxe.ActionEvent] = {
  258. import monix.execution.cancelables.SingleAssignCancelable
  259. Observable.create(OverflowStrategy.Unbounded) { sub =>
  260. val c = SingleAssignCancelable()
  261. val l = new jfxe.EventHandler[jfxe.ActionEvent] {
  262. override def handle(event: jfxe.ActionEvent): Unit = {
  263. if (sub.onNext(event) == Ack.Stop) c.cancel()
  264. }
  265. }
  266. item.onAction = l
  267. c := Cancelable(() =>
  268. item.removeEventHandler(
  269. jfxe.ActionEvent.ACTION,
  270. l
  271. )
  272. )
  273. c
  274. }
  275. }
  276. }
  277. }