2015-04-15 06:30:20 +00:00
|
|
|
package org.gerweck.scalafx
|
|
|
|
|
|
|
|
import language.implicitConversions
|
|
|
|
|
|
|
|
import scalafx.beans.property._
|
|
|
|
import scalafx.beans.value._
|
|
|
|
import scalafx.event.subscriptions.Subscription
|
|
|
|
import scalafx.scene.Node
|
|
|
|
import scalafx.scene.control._
|
|
|
|
import scalafx.scene.layout.GridPane
|
|
|
|
import scalafx.util.StringConverter
|
|
|
|
|
|
|
|
import scalaz._
|
|
|
|
import Scalaz._
|
|
|
|
|
|
|
|
/** Various implicits and global utilities for ScalaFX work.
|
|
|
|
*
|
|
|
|
* @author Sarah Gerweck <sarah@atscale.com>
|
|
|
|
*/
|
|
|
|
package object util {
|
|
|
|
type Observable[A] = ObservableValue[A, _]
|
2015-04-16 04:05:38 +00:00
|
|
|
type SimpleProperty[A] = Property[A, _]
|
2015-04-15 06:30:20 +00:00
|
|
|
|
2015-04-16 07:38:58 +00:00
|
|
|
implicit val observableFunctor = new Functor[Observable] {
|
|
|
|
def map[A, B](a: Observable[A])(f: A => B): Observable[B] = {
|
|
|
|
@inline def recalculate(): B = f(a.value)
|
|
|
|
|
|
|
|
val originalValue = recalculate()
|
|
|
|
|
|
|
|
val prop = ObjectProperty[B](originalValue)
|
|
|
|
|
|
|
|
def changeHandler = {
|
|
|
|
prop.value = recalculate()
|
|
|
|
}
|
|
|
|
|
|
|
|
a onChange changeHandler
|
|
|
|
prop
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-04-15 06:30:20 +00:00
|
|
|
implicit val observableApplicative = new Applicative[Observable] {
|
|
|
|
def point[A](a: => A): Observable[A] = {
|
|
|
|
ObjectProperty[A](a)
|
|
|
|
}
|
|
|
|
|
|
|
|
def ap[A, B](fa: => Observable[A])(f: => Observable[A => B]): Observable[B] = {
|
|
|
|
def recalculate: B = (f.value)(fa.value)
|
|
|
|
|
|
|
|
val originalValue = recalculate
|
|
|
|
|
|
|
|
val prop = ObjectProperty[B](originalValue)
|
|
|
|
|
|
|
|
var prevValue = originalValue
|
|
|
|
|
|
|
|
def changeHandler = {
|
|
|
|
val newVal = recalculate
|
|
|
|
if (prevValue != newVal) {
|
|
|
|
prop.value = recalculate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fa onChange changeHandler
|
|
|
|
f onChange changeHandler
|
|
|
|
|
|
|
|
prop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-16 20:24:21 +00:00
|
|
|
implicit class RichObservable[A](val self: Observable[A]) {
|
|
|
|
private type F[X] = Observable[X]
|
|
|
|
@inline private def F = observableApplicative
|
|
|
|
|
|
|
|
def map[A1 >: A, B](f: A1 => B) = F.map(self)(f)
|
|
|
|
def <*>[B](f: Observable[A => B]): Observable[B] = F.ap(self)(f)
|
|
|
|
def tuple[B](f: Observable[B]): Observable[(A,B)] = F.tuple2(self, f)
|
|
|
|
final def *>[B](fb: F[B]): F[B] = F.apply2(self,fb)((_,b) => b)
|
|
|
|
final def <*[B](fb: F[B]): F[A] = F.apply2(self,fb)((a,_) => a)
|
|
|
|
|
|
|
|
import shapeless._
|
|
|
|
import shapeless.syntax._
|
|
|
|
import shapeless.ops.hlist._
|
|
|
|
import HList._
|
|
|
|
implicitly[HListOps[Int::Int::HNil]]
|
|
|
|
final def |@|[B](fb: F[B]) = new ObservableTupler(self::fb::HNil)
|
|
|
|
|
|
|
|
/** Alias for `|@|` */
|
|
|
|
final def ⊛[B](fb: F[B]) = |@|(fb)
|
|
|
|
}
|
|
|
|
|
|
|
|
object unwrapObservable extends Poly1 {
|
|
|
|
implicit def apply[T, A <% Observable[T]]: Case.Aux[A, T] = at[A]{ o => o.value }
|
|
|
|
}
|
|
|
|
|
|
|
|
trait TupleBuilder[]
|
|
|
|
|
2015-04-16 04:05:38 +00:00
|
|
|
implicit class RichProperty[A](val inner: SimpleProperty[A]) extends AnyVal {
|
2015-04-15 06:30:20 +00:00
|
|
|
def biMap[B <: AnyRef](push: A => B, pull: B => A): ObjectProperty[B] = {
|
|
|
|
val original = push(inner.value)
|
|
|
|
val op = ObjectProperty[B](original)
|
|
|
|
inner onChange {
|
|
|
|
val oldVal = op.value
|
|
|
|
val newVal = push(inner.value)
|
|
|
|
if (oldVal != newVal) {
|
|
|
|
op.value = push(inner.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
op onChange {
|
|
|
|
val oldVal = inner.value
|
|
|
|
val newVal = pull(op.value)
|
|
|
|
if (oldVal != newVal) {
|
|
|
|
inner.value = newVal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
op
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
implicit class RichGridPane(val inner: GridPane) extends AnyVal {
|
|
|
|
def addToRow(ri: Int, children: Node*) = inner.addRow(ri, children map {_.delegate}: _*)
|
|
|
|
}
|
|
|
|
}
|