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.

371 lines
11 KiB

3 years ago
3 years ago
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. 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: ObservableBuffer[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) =>
  128. if (c != null)
  129. if (sub.onNext(c) == Ack.Stop) throw new Exception("boom")
  130. )
  131. def ==>(op: Property[A, A]) = {
  132. prop.onChange((a, b, c) => if (c != null) op() = c)
  133. }
  134. def <--(obs: Observable[A])(implicit s: Scheduler) = {
  135. obs.doOnNextF(v => Coeval(prop() = v)).subscribe()
  136. }
  137. def observableChange[J1 >: A]: Observable[J1] = {
  138. import monix.execution.cancelables.SingleAssignCancelable
  139. Observable.create(OverflowStrategy.Unbounded) { sub =>
  140. val c = SingleAssignCancelable()
  141. val canc = prop.onChange((_, _, c1) =>
  142. if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
  143. )
  144. c := Cancelable(() => canc.cancel())
  145. c
  146. }
  147. }
  148. }
  149. final class ObservableListExt[A](
  150. private val buffer: ObservableBuffer[A]
  151. ) extends AnyVal {
  152. // def -->(sub: Observer[A]) =
  153. // buffer.onChange((a, b, c) => if (c != null) sub.onNext(c))
  154. // def -->(op: Property[A, A]) = {
  155. // buffer.onChange((a, b, c) => if (c != null) op() = c)
  156. // }
  157. def <--(obs: Observable[A])(implicit s: Scheduler) = {
  158. obs
  159. .doOnNextF(v =>
  160. for {
  161. _ <- Coeval(buffer.clear())
  162. _ <- Coeval(buffer += v)
  163. } yield ()
  164. )
  165. .subscribe()
  166. }
  167. def observableChange[J1 >: A]: Observable[J1] = {
  168. import monix.execution.cancelables.SingleAssignCancelable
  169. Observable.create(OverflowStrategy.Unbounded) { sub =>
  170. val c = SingleAssignCancelable()
  171. implicit val s = sub.scheduler
  172. val canc =
  173. buffer.onChange((buf, _) =>
  174. loop(sub, buf.toIterable.iterator, c).runToFuture
  175. )
  176. c := Cancelable(() => canc.cancel())
  177. c
  178. }
  179. }
  180. private def loop(
  181. sub: Observer[A],
  182. it: Iterator[A],
  183. c: Cancelable
  184. ): Task[Unit] =
  185. if (it.hasNext) {
  186. val next = it.next()
  187. Task.deferFuture(sub.onNext(next)).flatMap {
  188. case Ack.Continue => loop(sub, it, c)
  189. case Ack.Stop => Task(c.cancel())
  190. }
  191. } else Task.unit
  192. }
  193. final class StringObservableListExt(
  194. private val buffer: ObservableList[String]
  195. ) extends AnyVal {
  196. // def ++=[T](that: Seq[T])(implicit S: Show[T]): Unit =
  197. // buffer ++= that.map(S.show)
  198. // def ++=[T](that: Seq[T])(implicit C: CssPath[T]): Unit =
  199. // buffer ++= that.map(C.path)
  200. }
  201. final class ReadOnlyPropertyExt[T, J](
  202. private val prop: ReadOnlyProperty[T, J]
  203. ) extends AnyVal {
  204. def -->[J1 >: J](sub: Observer[J1]) = {
  205. prop.onChange((a, b, c) => if (c != null) sub.onNext(c))
  206. }
  207. def ==>(op: Property[T, J]) = {
  208. op <== prop
  209. }
  210. def observableChange[J1 >: J]: Observable[J1] = {
  211. import monix.execution.cancelables.SingleAssignCancelable
  212. Observable.create(OverflowStrategy.Unbounded) { sub =>
  213. val c = SingleAssignCancelable()
  214. val canc = prop.onChange((a, b, c1) =>
  215. if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
  216. )
  217. c := Cancelable(() => canc.cancel())
  218. c
  219. }
  220. }
  221. }
  222. final class ObjectPropertyObservableListExt[A](
  223. private val prop: ObjectProperty[ObservableList[A]]
  224. ) extends AnyVal {
  225. def <--(obs: Observable[Seq[A]])(implicit s: Scheduler) = {
  226. obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
  227. }
  228. def -->(sub: Observer[A])(implicit s: Scheduler) = {
  229. val c = SingleAssignCancelable()
  230. val subs: Subscription = prop.onChange((a, b, c1) =>
  231. if (c1 != null)
  232. Iterant[Task]
  233. .fromIterable(c1.toIterable)
  234. .consume
  235. .use(consume(sub, c, _))
  236. .runToFuture
  237. )
  238. c := Cancelable(() => subs.cancel())
  239. }
  240. private def loop(sub: Observer[A], it: Iterator[A]): Task[Unit] =
  241. if (it.hasNext) {
  242. val next = it.next()
  243. Task.deferFuture(sub.onNext(next)).flatMap {
  244. case Ack.Continue => loop(sub, it)
  245. case Ack.Stop => Task.unit
  246. }
  247. } else Task.unit
  248. private def consume(
  249. sub: Observer[A],
  250. c: Cancelable,
  251. consumer: Iterant.Consumer[Task, A]
  252. ): Task[Unit] =
  253. consumer.pull.flatMap {
  254. case Left(value) => Task.unit
  255. case Right(value) =>
  256. Task.deferFuture(sub.onNext(value)).flatMap {
  257. case Ack.Continue => consume(sub, c, consumer)
  258. case Ack.Stop => Task(c.cancel())
  259. }
  260. }
  261. }
  262. final class ObjectPropertyActionEvent(
  263. private val prop: ObjectProperty[EventHandler[ActionEvent]]
  264. ) extends AnyVal {
  265. // def <--(obs: Observable[ActionEvent])(implicit s: Scheduler) = {
  266. // obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
  267. // }
  268. // def -->(sub: Observer[ActionEvent]) =
  269. // prop().
  270. }
  271. // def emit(prop: ObjectProperty[EventHandler[ActionEvent]]) =
  272. final class ButtonBaseExt(
  273. private val button: ButtonBase
  274. ) extends AnyVal {
  275. def observableAction: Observable[jfxe.ActionEvent] = {
  276. import monix.execution.cancelables.SingleAssignCancelable
  277. Observable.create(OverflowStrategy.Unbounded) { sub =>
  278. val c = SingleAssignCancelable()
  279. val l = new jfxe.EventHandler[jfxe.ActionEvent] {
  280. override def handle(event: jfxe.ActionEvent): Unit = {
  281. if (sub.onNext(event) == Ack.Stop) c.cancel()
  282. }
  283. }
  284. button.onAction = l
  285. c := Cancelable(() =>
  286. button.removeEventHandler(
  287. jfxe.ActionEvent.ACTION,
  288. l
  289. )
  290. )
  291. c
  292. }
  293. }
  294. }
  295. final class MenuItemExt(
  296. private val item: MenuItem
  297. ) extends AnyVal {
  298. def observableAction: Observable[jfxe.ActionEvent] = {
  299. import monix.execution.cancelables.SingleAssignCancelable
  300. Observable.create(OverflowStrategy.Unbounded) { sub =>
  301. val c = SingleAssignCancelable()
  302. val l = new jfxe.EventHandler[jfxe.ActionEvent] {
  303. override def handle(event: jfxe.ActionEvent): Unit = {
  304. if (sub.onNext(event) == Ack.Stop) c.cancel()
  305. }
  306. }
  307. item.onAction = l
  308. c := Cancelable(() =>
  309. item.removeEventHandler(
  310. jfxe.ActionEvent.ACTION,
  311. l
  312. )
  313. )
  314. c
  315. }
  316. }
  317. }
  318. }