Skip to content

Instantly share code, notes, and snippets.

@NightRa
Created December 31, 2014 22:03
Show Gist options
  • Select an option

  • Save NightRa/856d11944b3e51967d1a to your computer and use it in GitHub Desktop.

Select an option

Save NightRa/856d11944b3e51967d1a to your computer and use it in GitHub Desktop.

Revisions

  1. NightRa created this gist Dec 31, 2014.
    104 changes: 104 additions & 0 deletions JavaFXUtil.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,104 @@
    package nightra.reversi.util

    import scala.ref.WeakReference
    import scalafx.beans.property.{Property, BooleanProperty, ObjectProperty}
    import scalafx.beans.value.ObservableValue
    import scalafx.collections.ObservableBuffer
    import scalafx.event.subscriptions.Subscription

    object JavaFXUtil {
    /**
    * Memory scheme:
    * b = a.map(f)
    * b a strong reference to a,
    * while a does not hold a strong reference to b;
    * If b loses all strong references,
    * the change listener from a will be cleaned,
    * and b will be garbage collected.
    **/
    def weaklyBindOnChange[A, B](prop: ObservableValue[A, _], to: Property[B, _])(f: A => B): Subscription = {
    to.onChange({prop;()}) // capture prop as a strong reference in to, so that it won't be released until to is released
    val weakNewProp: WeakReference[Property[B, _]] = WeakReference(to) // Don't capture 'to in 'prop, so 'to can be cleaned without prop being cleaned.
    lazy val subscription: Subscription = prop.onChange((_, _, _) => weakNewProp.get match {
    case None => subscription.cancel()
    case Some(newProp) => newProp.value = f(prop.value)
    })
    subscription // Force the lazy val to tie the recursive knot.
    }

    def liftObservableList[A](prop: ObjectProperty[ObservableBuffer[A]]): ObservableBuffer[A] = {
    val newList = ObservableBuffer(prop.value)
    newList.onChange({ prop; () }) // capture the original property in memory.
    val weakNewList = WeakReference(newList)
    lazy val subscription: Subscription =
    prop.onChange { (_, _, _) =>
    weakNewList.get match {
    case None => subscription.cancel()
    case Some(solidNewList) =>
    newList.clear()
    newList ++= prop.value
    }
    }
    subscription // Force the lazy val to tie the recursive knot.
    newList
    }

    /**
    * Nice that the whole basic hierarchy is here:
    * Functor,
    * Applicative,
    * and Monad.
    * Actually needed each and every one of them.
    **/

    def flattenProp[A](prop: ObservableValue[ObservableValue[A, _], _]): ObjectProperty[A] = {
    val newProp: ObjectProperty[A] = ObjectProperty(prop.value.value)
    var lastSubscription: Subscription = null
    newProp.onChange({ prop; () })
    val weakNewProp = WeakReference(newProp)
    lazy val subscription: Subscription = prop.onChange { (_, _, _) =>
    val innerProp = prop.value
    if (lastSubscription != null)
    lastSubscription.cancel()
    weakNewProp.get match {
    case None => subscription.cancel()
    case Some(solidNewProp) => lastSubscription = weaklyBindOnChange(innerProp, newProp)(s => s)
    }
    }
    subscription
    newProp
    }

    def mapProp[A, B](prop: ObservableValue[A, _])(f: A => B): ObjectProperty[B] = {
    val newProp: ObjectProperty[B] = ObjectProperty(f(prop.value))
    weaklyBindOnChange(prop, newProp)(f)
    newProp // Strong reference.
    }

    def map2Prop[A, B, C](prop1: ObservableValue[A, _], prop2: ObservableValue[B, _])(f: (A, B) => C): ObjectProperty[C] = {
    val newProp: ObjectProperty[C] = ObjectProperty(f(prop1.value, prop2.value))
    weaklyBindOnChange(prop1, newProp)(s1 => f(s1, prop2.value))
    weaklyBindOnChange(prop2, newProp)(s2 => f(prop1.value, s2))
    newProp // Strong reference.
    }

    def map3Prop[A, B, C, D](prop1: ObservableValue[A, _], prop2: ObservableValue[B, _], prop3: ObservableValue[C, _])(f: (A, B, C) => D): ObjectProperty[D] = {
    val newProp: ObjectProperty[D] = ObjectProperty(f(prop1.value, prop2.value, prop3.value))
    weaklyBindOnChange(prop1, newProp)(s1 => f(s1, prop2.value, prop3.value))
    weaklyBindOnChange(prop2, newProp)(s2 => f(prop1.value, s2, prop3.value))
    weaklyBindOnChange(prop3, newProp)(s3 => f(prop1.value, prop2.value, s3))
    newProp // Strong reference.
    }

    def merge[A](props: Vector[ObjectProperty[A]]): ObjectProperty[A] = {
    val newProp: ObjectProperty[A] = ObjectProperty(props.head.value)
    props.foreach(prop => weaklyBindOnChange(prop, newProp)(s => s))
    newProp // Strong reference.
    }

    def toBooleanProp(prop: ObjectProperty[Boolean]): BooleanProperty = {
    val newProp: BooleanProperty = BooleanProperty(prop.value)
    weaklyBindOnChange(prop, newProp)(s => s)
    newProp // Strong reference.
    }
    }