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.

384 lines
11 KiB

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