de.sciss.lucre.swing.impl.CellViewFactory.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lucreswing_2.13.0-RC2 Show documentation
Show all versions of lucreswing_2.13.0-RC2 Show documentation
Swing support for Lucre, and common views
The newest version!
/*
* ExprViewFactory.scala
* (LucreSwing)
*
* Copyright (c) 2014-2019 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU Lesser General Public License v2.1+
*
*
* For further information, please contact Hanns Holger Rutz at
* [email protected]
*/
package de.sciss.lucre.swing.impl
import de.sciss.lucre.expr.{CellView, Expr, Type}
import de.sciss.lucre.stm.{Disposable, Sys}
import de.sciss.lucre.swing.LucreSwing.deferTx
import de.sciss.lucre.swing.edit.{EditCellView, EditExprMap}
import de.sciss.lucre.{stm, event => evt}
import de.sciss.model.Change
import javax.swing.undo.UndoableEdit
import scala.concurrent.stm.Ref
import scala.language.higherKinds
object CellViewFactory {
trait View[A] {
def update(value: A): Unit
}
trait Committer[S <: Sys[S], A] {
def commit(newValue: A)(implicit tx: S#Tx): UndoableEdit
}
def mkCommitter[S <: Sys[S], A](cell: CellView[S#Tx, A], name: String)
(implicit tx: S#Tx, cursor: stm.Cursor[S]): (A, Option[Committer[S, A]]) = {
val com = CellView.Var.unapply(cell).map { vr =>
new Committer[S, A] {
def commit(newValue: A)(implicit tx: S#Tx): UndoableEdit =
EditCellView[S, A](s"Change $name", cell = vr, value = newValue)
}
}
val value0 = cell()
(value0, com)
}
def mkObserver[S <: Sys[S], A](cell: CellView[S#Tx, A], view: CellViewFactory.View[A])
(implicit tx: S#Tx): Disposable[S#Tx] =
cell.react {
implicit tx => upd => deferTx(view.update(upd))
}
}
trait CellViewFactory[A] {
import CellViewFactory.Committer
protected def mkMapCommitter[S <: Sys[S], K, Ex[~ <: Sys[~]] <: Expr[~, A]](map: evt.Map[S, K, Ex], key: K,
default: A, name: String)
(implicit tx: S#Tx, cursor: stm.Cursor[S],
keyType: evt.Map.Key[K],
tpe: Type.Expr[A, Ex]): (A, Option[Committer[S, A]]) = {
import tpe.newConst
val com = map.modifiableOption.map { mapMod =>
val mapH = tx.newHandle(mapMod)
new Committer[S, A] {
def commit(newValue: A)(implicit tx: S#Tx): UndoableEdit = {
EditExprMap[S, K, A, Ex](s"Change $name", map = mapH(), key = key, value = Some(newConst[S](newValue)))
}
}
}
val value0 = map.get(key).map(_.value).getOrElse(default)
(value0, com)
}
protected def mkMapObserver[S <: Sys[S], K, Ex[~ <: Sys[~]] <: Expr[~, A]](map: evt.Map[S, K, Ex], key: K,
view: CellViewFactory.View[A])
(implicit tx: S#Tx): Disposable[S#Tx] =
new MapObserver(map, key, view, tx)
private[this] final class MapObserver[S <: Sys[S], K, Ex[~ <: Sys[~]] <: Expr[~, A]](map: evt.Map[S, K, Ex], key: K,
view: CellViewFactory.View[A], tx0: S#Tx)
extends Disposable[S#Tx] {
private val valObs = Ref(null: Disposable[S#Tx])
private val mapObs = map.changed.react {
implicit tx => upd => upd.changes.foreach {
case evt.Map.Added (`key`, expr) =>
valueAdded(expr)
// XXX TODO -- if we moved this into `valueAdded`, the contract
// could be that initially the view is updated
val now0 = expr.value
deferTx(view.update(now0))
case evt.Map.Removed(`key`, _ ) =>
valueRemoved()
case _ =>
}
} (tx0)
map.get(key)(tx0).foreach(valueAdded(_)(tx0))
private def valueAdded(expr: Ex[S])(implicit tx: S#Tx): Unit = {
val res = expr.changed.react { implicit tx => {
case Change(_, now) =>
deferTx(view.update(now))
case _ => // XXX TODO -- should we ask for expr.value ?
}}
val v = valObs.swap(res)(tx.peer)
if (v != null) v.dispose()
}
private def valueRemoved()(implicit tx: S#Tx): Boolean = {
val v = valObs.swap(null)(tx.peer)
val res = v != null
if (res) v.dispose()
res
}
def dispose()(implicit tx: S#Tx): Unit = {
valueRemoved()
mapObs.dispose()
}
}
}