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.

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