361 lines
11 KiB
Scala
361 lines
11 KiB
Scala
package wow.doge.mygame.implicits
|
|
|
|
import javafx.beans.property.ObjectProperty
|
|
import javafx.collections.ObservableList
|
|
import javafx.event.ActionEvent
|
|
import javafx.event.EventHandler
|
|
import javafx.scene.{input => jfxsi}
|
|
import javafx.{event => jfxe}
|
|
import monix.bio.Task
|
|
import monix.eval.Coeval
|
|
import monix.execution.Ack
|
|
import monix.execution.Cancelable
|
|
import monix.execution.Scheduler
|
|
import monix.execution.cancelables.CompositeCancelable
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
import monix.reactive.Observable
|
|
import monix.reactive.Observer
|
|
import monix.reactive.OverflowStrategy
|
|
import monix.tail.Iterant
|
|
import monix.{eval => me}
|
|
import org.gerweck.scalafx.util._
|
|
import scalafx.Includes._
|
|
import scalafx.beans.property.Property
|
|
import scalafx.beans.property.ReadOnlyProperty
|
|
import scalafx.collections.ObservableBuffer
|
|
import scalafx.event.subscriptions.Subscription
|
|
import scalafx.scene.Scene
|
|
import scalafx.scene.control.ButtonBase
|
|
import scalafx.scene.control.MenuItem
|
|
|
|
trait JavaFXMonixObservables {
|
|
import JavaFXMonixObservables._
|
|
implicit def extendedScene(scene: Scene) = new SceneExt(scene)
|
|
implicit def extendedProperty[T, J](
|
|
propery: Property[T, J]
|
|
): PropertyExt[T, J] =
|
|
new PropertyExt(propery)
|
|
implicit def extendedObjectPropety[A](prop: ObjectProperty[A]) =
|
|
new ObjectPropertyExt[A](prop)
|
|
implicit def extendedReadOnlyObjectPropety[T, J](
|
|
prop: ReadOnlyProperty[T, J]
|
|
) =
|
|
new ReadOnlyPropertyExt[T, J](prop)
|
|
implicit def extendedObservableList[A](
|
|
list: ObservableBuffer[A]
|
|
) = new ObservableListExt(list)
|
|
implicit def extendedStringObservableList(
|
|
list: ObservableList[String]
|
|
) = new StringObservableListExt(list)
|
|
implicit def extendedObjectPropertyObservableList[A](
|
|
prop: ObjectProperty[ObservableList[A]]
|
|
) = new ObjectPropertyObservableListExt(prop)
|
|
implicit def extendedButton(button: ButtonBase) = new ButtonBaseExt(button)
|
|
implicit def extendedMenuItem(item: MenuItem) = new MenuItemExt(item)
|
|
|
|
// implicit val implShowForOsRelPath = Show.fromToString[os.Path]
|
|
implicit def osRelPath2String(path: os.RelPath): String = path.toString()
|
|
}
|
|
|
|
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
|
object JavaFXMonixObservables {
|
|
|
|
final class SceneExt(private val scene: Scene) extends AnyVal {
|
|
|
|
def observableMousePressed(): Observable[jfxsi.MouseEvent] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
|
|
override def handle(event: jfxsi.MouseEvent): Unit =
|
|
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
|
}
|
|
|
|
scene.onMousePressed = l
|
|
c := Cancelable(() =>
|
|
scene.removeEventHandler(
|
|
jfxsi.MouseEvent.MOUSE_PRESSED,
|
|
l
|
|
)
|
|
)
|
|
c
|
|
}
|
|
}
|
|
|
|
def observableMouseDragged(): Observable[jfxsi.MouseEvent] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
|
|
override def handle(event: jfxsi.MouseEvent): Unit =
|
|
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
|
}
|
|
|
|
scene.onMouseDragged = l;
|
|
c := Cancelable(() =>
|
|
scene.removeEventHandler(
|
|
jfxsi.MouseEvent.MOUSE_DRAGGED,
|
|
l
|
|
)
|
|
)
|
|
c
|
|
}
|
|
}
|
|
}
|
|
|
|
final class PropertyExt[T, J](private val prop: Property[T, J])
|
|
extends AnyVal {
|
|
def -->[J1 >: J](sub: Observer[J1]) =
|
|
prop.onChange((a, b, c) => if (c != null) sub.onNext(c))
|
|
|
|
def ==>(op: Property[T, J]) =
|
|
op <== prop
|
|
|
|
def <--(
|
|
obs: Observable[T]
|
|
)(implicit s: Scheduler, c: CompositeCancelable): Unit =
|
|
c += obs.doOnNextF(v => Coeval(prop.value = v)).subscribe()
|
|
|
|
def asOption = prop.map(Option(_))
|
|
|
|
def observableChange[J1 >: J]: Observable[J1] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
|
|
val canc =
|
|
prop.onChange((a, b, c1) =>
|
|
if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
|
|
)
|
|
|
|
c := Cancelable(() => canc.cancel())
|
|
c
|
|
}
|
|
}
|
|
}
|
|
|
|
final class ObjectPropertyExt[A](private val prop: ObjectProperty[A])
|
|
extends AnyVal {
|
|
|
|
@SuppressWarnings(Array("org.wartremover.warts.Throw"))
|
|
def -->(sub: Observer[A]) =
|
|
prop.onChange((a, b, c) =>
|
|
if (c != null)
|
|
if (sub.onNext(c) == Ack.Stop) throw new Exception("boom")
|
|
)
|
|
|
|
def ==>(op: Property[A, A]) =
|
|
prop.onChange((a, b, c) => if (c != null) op() = c)
|
|
|
|
def <--(obs: Observable[A])(implicit s: Scheduler) =
|
|
obs.doOnNextF(v => Coeval(prop() = v)).subscribe()
|
|
|
|
def observableChange[J1 >: A]: Observable[J1] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
|
|
val canc = prop.onChange((_, _, c1) =>
|
|
if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
|
|
)
|
|
|
|
c := Cancelable(() => canc.cancel())
|
|
c
|
|
}
|
|
}
|
|
}
|
|
|
|
final class ObservableListExt[A](
|
|
private val buffer: ObservableBuffer[A]
|
|
) extends AnyVal {
|
|
|
|
// def -->(sub: Observer[A]) =
|
|
// buffer.onChange((a, b, c) => if (c != null) sub.onNext(c))
|
|
|
|
// def -->(op: Property[A, A]) = {
|
|
// buffer.onChange((a, b, c) => if (c != null) op() = c)
|
|
// }
|
|
|
|
def <--(
|
|
obs: Observable[A]
|
|
)(implicit s: Scheduler, c: CompositeCancelable): Unit =
|
|
c += obs
|
|
.doOnNextF(v =>
|
|
for {
|
|
_ <- Coeval(
|
|
println(s"Current Thread: ${Thread.currentThread().getName}")
|
|
)
|
|
_ <- Coeval(buffer.clear())
|
|
_ <- Coeval(buffer += v)
|
|
} yield ()
|
|
)
|
|
.subscribe()
|
|
|
|
def observableChange[J1 >: A]: Observable[J1] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
|
|
implicit val s = sub.scheduler
|
|
|
|
val canc =
|
|
buffer.onChange((buf, _) =>
|
|
loop(sub, buf.toIterable.iterator, c).runToFuture
|
|
)
|
|
|
|
c := Cancelable(() => canc.cancel())
|
|
c
|
|
}
|
|
}
|
|
|
|
private def loop(
|
|
sub: Observer[A],
|
|
it: Iterator[A],
|
|
c: Cancelable
|
|
): Task[Unit] =
|
|
if (it.hasNext) {
|
|
val next = it.next()
|
|
Task.deferFuture(sub.onNext(next)).flatMap {
|
|
case Ack.Continue => loop(sub, it, c)
|
|
case Ack.Stop => Task(c.cancel())
|
|
}
|
|
} else Task.unit
|
|
}
|
|
|
|
final class StringObservableListExt(
|
|
private val buffer: ObservableList[String]
|
|
) extends AnyVal {
|
|
// def ++=[T](that: Seq[T])(implicit S: Show[T]): Unit =
|
|
// buffer ++= that.map(S.show)
|
|
// def ++=[T](that: Seq[T])(implicit C: CssPath[T]): Unit =
|
|
// buffer ++= that.map(C.path)
|
|
}
|
|
|
|
final class ReadOnlyPropertyExt[T, J](
|
|
private val prop: ReadOnlyProperty[T, J]
|
|
) extends AnyVal {
|
|
def -->[J1 >: J](sub: Observer[J1]) =
|
|
prop.onChange((a, b, c) => if (c != null) sub.onNext(c))
|
|
|
|
def ==>(op: Property[T, J]) =
|
|
op <== prop
|
|
|
|
def observableChange[J1 >: J]: Observable[J1] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
|
|
val canc = prop.onChange((a, b, c1) =>
|
|
if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
|
|
)
|
|
|
|
c := Cancelable(() => canc.cancel())
|
|
c
|
|
}
|
|
}
|
|
}
|
|
|
|
final class ObjectPropertyObservableListExt[A](
|
|
private val prop: ObjectProperty[ObservableList[A]]
|
|
) extends AnyVal {
|
|
def <--(obs: Observable[Seq[A]])(implicit s: Scheduler) =
|
|
obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
|
|
|
|
def -->(sub: Observer[A])(implicit s: Scheduler) = {
|
|
val c = SingleAssignCancelable()
|
|
val subs: Subscription = prop.onChange((a, b, c1) =>
|
|
if (c1 != null)
|
|
Iterant[Task]
|
|
.fromIterable(c1.toIterable)
|
|
.consume
|
|
.use(consume(sub, c, _))
|
|
.runToFuture
|
|
)
|
|
c := Cancelable(() => subs.cancel())
|
|
}
|
|
|
|
private def loop(sub: Observer[A], it: Iterator[A]): Task[Unit] =
|
|
if (it.hasNext) {
|
|
val next = it.next()
|
|
Task.deferFuture(sub.onNext(next)).flatMap {
|
|
case Ack.Continue => loop(sub, it)
|
|
case Ack.Stop => Task.unit
|
|
}
|
|
} else Task.unit
|
|
|
|
private def consume(
|
|
sub: Observer[A],
|
|
c: Cancelable,
|
|
consumer: Iterant.Consumer[Task, A]
|
|
): Task[Unit] =
|
|
consumer.pull.flatMap {
|
|
case Left(value) => Task.unit
|
|
case Right(value) =>
|
|
Task.deferFuture(sub.onNext(value)).flatMap {
|
|
case Ack.Continue => consume(sub, c, consumer)
|
|
case Ack.Stop => Task(c.cancel())
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
final class ObjectPropertyActionEvent(
|
|
private val prop: ObjectProperty[EventHandler[ActionEvent]]
|
|
) extends AnyVal {
|
|
// def <--(obs: Observable[ActionEvent])(implicit s: Scheduler) = {
|
|
// obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
|
|
// }
|
|
|
|
// def -->(sub: Observer[ActionEvent]) =
|
|
// prop().
|
|
|
|
}
|
|
|
|
// def emit(prop: ObjectProperty[EventHandler[ActionEvent]]) =
|
|
|
|
final class ButtonBaseExt(private val button: ButtonBase) extends AnyVal {
|
|
def observableAction: Observable[jfxe.ActionEvent] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.DropNew(50)) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
val l = new jfxe.EventHandler[jfxe.ActionEvent] {
|
|
override def handle(event: jfxe.ActionEvent): Unit =
|
|
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
|
}
|
|
|
|
button.onAction = l
|
|
c := Cancelable(() =>
|
|
button.removeEventHandler(
|
|
jfxe.ActionEvent.ACTION,
|
|
l
|
|
)
|
|
)
|
|
c
|
|
}
|
|
}
|
|
}
|
|
|
|
final class MenuItemExt(private val item: MenuItem) extends AnyVal {
|
|
|
|
def observableAction: Observable[jfxe.ActionEvent] = {
|
|
import monix.execution.cancelables.SingleAssignCancelable
|
|
Observable.create(OverflowStrategy.DropNew(50)) { sub =>
|
|
val c = SingleAssignCancelable()
|
|
val l = new jfxe.EventHandler[jfxe.ActionEvent] {
|
|
override def handle(event: jfxe.ActionEvent): Unit =
|
|
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
|
}
|
|
|
|
item.onAction = l
|
|
c := Cancelable(() =>
|
|
item.removeEventHandler(
|
|
jfxe.ActionEvent.ACTION,
|
|
l
|
|
)
|
|
)
|
|
c
|
|
}
|
|
}
|
|
}
|
|
}
|