All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.sciss.mellite.impl.component.NavigationHistory.scala Maven / Gradle / Ivy

/*
 *  NavigationHistory.scala
 *  (Mellite)
 *
 *  Copyright (c) 2012-2023 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU Affero General Public License v3+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.mellite.impl.component

import de.sciss.lucre.Observable
import de.sciss.lucre.impl.ObservableImpl
import de.sciss.lucre.Txn
import de.sciss.lucre.Txn.peer

import scala.collection.immutable.{IndexedSeq => Vec}
import scala.concurrent.stm.Ref

object NavigationHistory {
  def empty[T <: Txn[T], A]: NavigationHistory[T, A] = new Impl[T, A](Vector.empty)
  
  def apply[T <: Txn[T], A](xs: A*): NavigationHistory[T, A] = new Impl[T, A](xs.toIndexedSeq)
  
  private final class Impl[T <: Txn[T], A](init: Vec[A])
    extends NavigationHistory[T, A] with ObservableImpl[T, Update[T, A]] { nav =>
    
    private[this] val ref   = Ref(init)
    private[this] val _pos  = Ref(init.size)
    
    def current(implicit tx: T): Option[A] = {
      val idx   = position - 1
      val items = ref()
      if (idx < 0 || idx >= items.size) None else Some(items(idx))
    }
    
    def position(implicit tx: T): Int = _pos()
    def position_=(value: Int)(implicit tx: T): Unit = {
      val sz = size
      require(value >= 0 && value <= sz)
      val oldPos = _pos.swap(value)
      if (value != oldPos) {
        fire(Update(nav, position = value, size = sz, current = current))
      }
    }

    def size        (implicit tx: T): Int      = ref().size

    def isEmpty     (implicit tx: T): Boolean  = size == 0
    def nonEmpty    (implicit tx: T): Boolean  = !isEmpty

    def canGoBack   (implicit tx: T): Boolean  = position > 1
    def canGoForward(implicit tx: T): Boolean  = position < size

    def backward()  (implicit tx: T): Unit     = position = position - 1
    def forward ()  (implicit tx: T): Unit     = position = position + 1

    def resetTo(elem: A)(implicit tx: T): Unit =
      update(0, elem)

    def push(elem: A)(implicit tx: T): Unit =
      update(position, elem)

    private def update(index: Int, elem: A)(implicit tx: T): Unit = {
      val newColl = ref.transformAndGet { in =>
        in.take(index) :+ elem
      }
      val newSize = newColl.size
      val newPos  = index + 1
      _pos() = newPos
      fire(Update(nav, position = newPos, size = newSize, current = Some(elem)))
    }
  }
  
  final case class Update[T <: Txn[T], A](nav: NavigationHistory[T, A], position: Int, size: Int, current: Option[A]) {
    def canGoBack   : Boolean = position > 1
    def canGoForward: Boolean = position < size
  }
}
trait NavigationHistory[T <: Txn[T], A] extends Observable[T, NavigationHistory.Update[T, A]] {
  def position              (implicit tx: T): Int
  def position_=(value: Int)(implicit tx: T): Unit
  
  def size        (implicit tx: T): Int

  def current     (implicit tx: T): Option[A]

  def isEmpty     (implicit tx: T): Boolean
  def nonEmpty    (implicit tx: T): Boolean

  def canGoBack   (implicit tx: T): Boolean
  def canGoForward(implicit tx: T): Boolean

  def backward()  (implicit tx: T): Unit
  def forward ()  (implicit tx: T): Unit

  /** Adds element to current positions (and wipes future positions). */
  def push   (elem: A)(implicit tx: T): Unit

  /** Clears contents and sets initial element. */
  def resetTo(elem: A)(implicit tx: T): Unit
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy