Made cell implementations internal and added some factory methods.

This commit is contained in:
Daan Vanden Bosch 2022-11-01 15:00:19 +01:00
parent 2bcde67e18
commit e10ac484d7
39 changed files with 77 additions and 69 deletions

View File

@ -2,7 +2,7 @@ package world.phantasmal.cell
import world.phantasmal.core.unsafe.unsafeCast
abstract class AbstractDependentCell<T, Event : ChangeEvent<T>> : AbstractCell<T>(), Dependent {
internal abstract class AbstractDependentCell<T, Event : ChangeEvent<T>> : AbstractCell<T>(), Dependent {
protected var _value: T? = null
final override val value: T

View File

@ -2,7 +2,7 @@ package world.phantasmal.cell
import world.phantasmal.core.unsafe.unsafeAssertNotNull
abstract class AbstractFlatteningDependentCell<T, ComputedCell : Cell<T>, Event : ChangeEvent<T>>(
internal abstract class AbstractFlatteningDependentCell<T, ComputedCell : Cell<T>, Event : ChangeEvent<T>>(
private val dependencies: Array<out Cell<*>>,
private val compute: () -> ComputedCell,
) : AbstractDependentCell<T, Event>() {

View File

@ -6,7 +6,7 @@ import world.phantasmal.core.unsafe.unsafeCast
/**
* Calls [callback] when [dependency] changes.
*/
class CallbackChangeObserver<T, E : ChangeEvent<T>>(
internal class CallbackChangeObserver<T, E : ChangeEvent<T>>(
private val dependency: Cell<T>,
// We don't use ChangeObserver<T> because of type system limitations. It would break e.g.
// AbstractListCell.observeListChange.

View File

@ -5,7 +5,7 @@ import world.phantasmal.core.disposable.TrackedDisposable
/**
* Calls [callback] when one or more cells in [dependencies] change.
*/
class CallbackObserver(
internal class CallbackObserver(
private vararg val dependencies: Cell<*>,
private val callback: () -> Unit,
) : TrackedDisposable(), LeafDependent {

View File

@ -1,6 +1,6 @@
package world.phantasmal.cell
class DelegatingCell<T>(
internal class DelegatingCell<T>(
private val getter: () -> T,
private val setter: (T) -> Unit,
) : AbstractCell<T>(), MutableCell<T> {

View File

@ -3,7 +3,7 @@ package world.phantasmal.cell
/**
* Cell of which the value depends on 0 or more dependencies.
*/
class DependentCell<T>(
internal class DependentCell<T>(
private vararg val dependencies: Cell<*>,
private val compute: () -> T,
) : AbstractDependentCell<T, ChangeEvent<T>>() {

View File

@ -3,7 +3,7 @@ package world.phantasmal.cell
/**
* Similar to [DependentCell], except that this cell's [compute] returns a cell.
*/
class FlatteningDependentCell<T>(
internal class FlatteningDependentCell<T>(
vararg dependencies: Cell<*>,
compute: () -> Cell<T>,
) : AbstractFlatteningDependentCell<T, Cell<T>, ChangeEvent<T>>(dependencies, compute) {

View File

@ -3,7 +3,7 @@ package world.phantasmal.cell
import world.phantasmal.core.disposable.Disposable
import world.phantasmal.core.disposable.nopDisposable
class ImmutableCell<T>(override val value: T) : Dependency<T>, Cell<T> {
internal class ImmutableCell<T>(override val value: T) : Dependency<T>, Cell<T> {
override val changeEvent: ChangeEvent<T>? get() = null
override fun addDependent(dependent: Dependent) {

View File

@ -1,6 +1,6 @@
package world.phantasmal.cell
class SimpleCell<T>(value: T) : AbstractCell<T>(), MutableCell<T> {
internal class SimpleCell<T>(value: T) : AbstractCell<T>(), MutableCell<T> {
override var value: T = value
set(value) {
if (value != field) {

View File

@ -5,7 +5,7 @@ import world.phantasmal.cell.Dependent
import world.phantasmal.cell.MutationManager
import world.phantasmal.core.unsafe.unsafeCast
abstract class AbstractFilteredListCell<E>(
internal abstract class AbstractFilteredListCell<E>(
protected val list: ListCell<E>,
) : AbstractListCell<E>(), Dependent {

View File

@ -8,7 +8,7 @@ import world.phantasmal.cell.AbstractCell
import world.phantasmal.cell.Cell
import world.phantasmal.cell.DependentCell
abstract class AbstractListCell<E> : AbstractCell<List<E>>(), ListCell<E> {
internal abstract class AbstractListCell<E> : AbstractCell<List<E>>(), ListCell<E> {
private var _size: Cell<Int>? = null
final override val size: Cell<Int>

View File

@ -7,7 +7,7 @@ import world.phantasmal.cell.Cell
/**
* ListCell of which the value depends on 0 or more other cells.
*/
class DependentListCell<E>(
internal class DependentListCell<E>(
private vararg val dependencies: Cell<*>,
private val computeElements: () -> List<E>,
) : AbstractListCell<E>(), Dependent {

View File

@ -8,7 +8,7 @@ import world.phantasmal.core.assert
import world.phantasmal.core.assertUnreachable
import world.phantasmal.core.unsafe.unsafeCast
class FilteredListCell<E>(
internal class FilteredListCell<E>(
list: ListCell<E>,
private val predicate: Cell<(E) -> Cell<Boolean>>,
) : AbstractFilteredListCell<E>(list) {

View File

@ -9,7 +9,7 @@ import world.phantasmal.core.unsafe.unsafeAssertNotNull
*/
// TODO: Improve performance when transitive cell changes. At the moment a change event is generated
// that just pretends the whole list has changed.
class FlatteningDependentListCell<E>(
internal class FlatteningDependentListCell<E>(
vararg dependencies: Cell<*>,
computeElements: () -> ListCell<E>,
) :

View File

@ -1,16 +1,13 @@
package world.phantasmal.cell.list
import world.phantasmal.cell.*
import world.phantasmal.core.disposable.Disposable
import world.phantasmal.core.disposable.nopDisposable
import world.phantasmal.cell.ChangeObserver
import world.phantasmal.cell.Dependency
import world.phantasmal.cell.Dependent
import world.phantasmal.cell.Cell
import world.phantasmal.cell.cell
import world.phantasmal.cell.falseCell
import world.phantasmal.cell.trueCell
class ImmutableListCell<E>(private val elements: List<E>) : Dependency<List<E>>, ListCell<E> {
internal class ImmutableListCell<E>(
private val elements: List<E>,
) : Dependency<List<E>>, ListCell<E> {
override val size: Cell<Int> = cell(elements.size)
override val empty: Cell<Boolean> = if (elements.isEmpty()) trueCell() else falseCell()
override val notEmpty: Cell<Boolean> = if (elements.isNotEmpty()) trueCell() else falseCell()

View File

@ -9,6 +9,10 @@ private val EMPTY_LIST_CELL = ImmutableListCell<Nothing>(emptyList())
/** Returns an immutable list cell containing [elements]. */
fun <E> listCell(vararg elements: E): ListCell<E> = ImmutableListCell(elements.toList())
/** Returns a list cell backed by [elements]. */
fun <E> listCell(elements: List<E>): ListCell<E> =
ImmutableListCell(elements)
/** Returns a singleton empty immutable cell. */
fun <E> emptyListCell(): ListCell<E> = EMPTY_LIST_CELL
@ -16,6 +20,10 @@ fun <E> emptyListCell(): ListCell<E> = EMPTY_LIST_CELL
fun <E> mutableListCell(vararg elements: E): MutableListCell<E> =
SimpleListCell(mutableListOf(*elements))
/** Returns a mutable list cell initially backed by [elements]. */
fun <E> mutableListCell(elements: MutableList<E>): MutableListCell<E> =
SimpleListCell(elements)
/**
* Returns a cell that changes whenever this list cell is structurally changed or when its
* individual elements change.

View File

@ -10,7 +10,7 @@ import world.phantasmal.cell.Cell
/**
* Depends on a [ListCell] and zero or more cells per element in the list.
*/
class ListElementsDependentCell<E>(
internal class ListElementsDependentCell<E>(
private val list: ListCell<E>,
private val extractCells: (element: E) -> Array<out Cell<*>>,
) : AbstractCell<List<E>>(), Dependent {

View File

@ -4,7 +4,7 @@ import world.phantasmal.core.assertUnreachable
import world.phantasmal.cell.Dependency
import world.phantasmal.cell.Cell
class SimpleFilteredListCell<E>(
internal class SimpleFilteredListCell<E>(
list: ListCell<E>,
private val predicate: Cell<(E) -> Boolean>,
) : AbstractFilteredListCell<E>(list) {

View File

@ -6,7 +6,7 @@ import world.phantasmal.core.unsafe.unsafeCast
/**
* @param elements The backing list for this [ListCell].
*/
class SimpleListCell<E>(
internal class SimpleListCell<E>(
private var elements: MutableList<E>,
) : AbstractListCell<E>(), MutableListCell<E> {

View File

@ -14,7 +14,7 @@ class DelegatingCellTests : RegularCellTests, MutableCellTests<Int> {
override fun createValue(): Int = v + 1
}
override fun <T> createWithValue(value: T): DelegatingCell<T> {
override fun <T> createWithValue(value: T): Cell<T> {
var v = value
return DelegatingCell({ v }, { v = it })
}

View File

@ -4,7 +4,7 @@ package world.phantasmal.cell
class DependentCellTests : RegularCellTests, CellWithDependenciesTests {
override fun createProvider() = Provider()
override fun <T> createWithValue(value: T): DependentCell<T> {
override fun <T> createWithValue(value: T): Cell<T> {
val dependency = SimpleCell(value)
return DependentCell(dependency) { dependency.value }
}
@ -13,7 +13,7 @@ class DependentCellTests : RegularCellTests, CellWithDependenciesTests {
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
DependentCell(dependency1, dependency2, dependency3) {
dependency1.value + dependency2.value + dependency3.value
}
@ -21,7 +21,7 @@ class DependentCellTests : RegularCellTests, CellWithDependenciesTests {
class Provider : CellTests.Provider {
private val dependencyCell = SimpleCell(1)
override val cell = DependentCell(dependencyCell) { 2 * dependencyCell.value }
override val cell: Cell<Any> = DependentCell(dependencyCell) { 2 * dependencyCell.value }
override fun emit() {
dependencyCell.value += 2

View File

@ -9,7 +9,7 @@ class DependentCellWithSimpleListCellTests : CellTests {
class Provider : CellTests.Provider {
private val dependencyCell = SimpleListCell(mutableListOf("a", "b", "c"))
override val cell = DependentCell(dependencyCell) { dependencyCell.value }
override val cell: Cell<Any> = DependentCell(dependencyCell) { dependencyCell.value }
override fun emit() {
dependencyCell.add("x")

View File

@ -12,7 +12,7 @@ class FlatteningDependentCellDirectAndTransitiveDependencyEmitTests : CellTests
// This cell is both the direct and transitive dependency.
private val dependencyCell = SimpleCell('a')
override val cell = FlatteningDependentCell(dependencyCell) { dependencyCell }
override val cell: Cell<Any> = FlatteningDependentCell(dependencyCell) { dependencyCell }
override fun emit() {
dependencyCell.value += 1

View File

@ -22,7 +22,7 @@ class FlatteningDependentCellDirectDependencyEmitsTests : RegularCellTests {
}
}
override fun <T> createWithValue(value: T): FlatteningDependentCell<T> {
override fun <T> createWithValue(value: T): Cell<T> {
val v = ImmutableCell(ImmutableCell(value))
return FlatteningDependentCell(v) { v.value }
}

View File

@ -10,7 +10,7 @@ class FlatteningDependentCellTransitiveDependencyEmitsTests :
override fun createProvider() = Provider()
override fun <T> createWithValue(value: T): FlatteningDependentCell<T> {
override fun <T> createWithValue(value: T): Cell<T> {
val dependency = ImmutableCell(ImmutableCell(value))
return FlatteningDependentCell(dependency) { dependency.value }
}
@ -19,7 +19,7 @@ class FlatteningDependentCellTransitiveDependencyEmitsTests :
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
FlatteningDependentCell(dependency1, dependency2, dependency3) {
ImmutableCell(dependency1.value + dependency2.value + dependency3.value)
}
@ -31,7 +31,7 @@ class FlatteningDependentCellTransitiveDependencyEmitsTests :
// The direct dependency of the cell under test can't change.
private val directDependency = ImmutableCell(transitiveDependency)
override val cell =
override val cell: Cell<Any> =
FlatteningDependentCell(directDependency) { directDependency.value }
override fun emit() {

View File

@ -12,5 +12,5 @@ class SimpleCellTests : RegularCellTests, MutableCellTests<Int> {
override fun createValue(): Int = cell.value + 1
}
override fun <T> createWithValue(value: T) = SimpleCell(value)
override fun <T> createWithValue(value: T): Cell<T> = SimpleCell(value)
}

View File

@ -13,7 +13,7 @@ class DependentListCellTests : ListCellTests, CellWithDependenciesTests {
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
DependentListCell(dependency1, dependency2, dependency3) {
listOf(dependency1.value, dependency2.value, dependency3.value)
}
@ -22,7 +22,7 @@ class DependentListCellTests : ListCellTests, CellWithDependenciesTests {
private val dependencyCell =
SimpleListCell(if (empty) mutableListOf() else mutableListOf(5))
override val cell =
override val cell: ListCell<Any> =
DependentListCell(dependencyCell) { dependencyCell.value.map { 2 * it } }
override fun addElement() {

View File

@ -31,7 +31,7 @@ class FilteredListCellListDependencyEmitsTests : ListCellTests, CellWithDependen
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
FilteredListCell(
list = dependency1.mapToList { listOf(it) },
predicate = dependency2.map { value2 ->

View File

@ -9,6 +9,7 @@ import world.phantasmal.cell.*
class FilteredListCellPredicateDependencyEmitsTests : ListCellTests, CellWithDependenciesTests {
override fun createListProvider(empty: Boolean) = object : ListCellTests.Provider {
private var maxValue = if (empty) 0 else 1
// The predicate cell changes, the predicate results don't.
private val predicateCell = SimpleCell<(Int) -> Cell<Boolean>> { cell(it <= maxValue) }
@ -30,7 +31,7 @@ class FilteredListCellPredicateDependencyEmitsTests : ListCellTests, CellWithDep
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
FilteredListCell(
list = listCell(1, 2, 3, 4, 5, 6, 7, 8, 9),
predicate = map(dependency1, dependency2, dependency3) { value1, value2, value3 ->

View File

@ -31,7 +31,7 @@ class FilteredListCellPredicateResultDependenciesEmitTests : ListCellTests,
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
): FilteredListCell<Int> {
): Cell<Any> {
val deps = listOf(dependency1, dependency2, dependency3)
return FilteredListCell(

View File

@ -12,6 +12,9 @@ import world.phantasmal.cell.map
*/
@Suppress("unused")
class FilteredListCellTests : AbstractFilteredListCellTests {
override fun <E> createFilteredListCell(list: ListCell<E>, predicate: Cell<(E) -> Boolean>) =
override fun <E> createFilteredListCell(
list: ListCell<E>,
predicate: Cell<(E) -> Boolean>,
): ListCell<E> =
FilteredListCell(list, predicate.map { p -> { cell(p(it)) } })
}

View File

@ -21,7 +21,7 @@ class FlatteningDependentListCellTransitiveDependencyEmitsTests :
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
FlatteningDependentListCell(dependency1, dependency2, dependency3) {
ImmutableListCell(listOf(dependency1.value, dependency2.value, dependency3.value))
}
@ -34,7 +34,7 @@ class FlatteningDependentListCellTransitiveDependencyEmitsTests :
// The direct dependency of the list under test can't change.
private val directDependency = ImmutableCell<ListCell<Int>>(transitiveDependency)
override val cell =
override val cell: ListCell<Any> =
FlatteningDependentListCell(directDependency) { directDependency.value }
override fun addElement() {

View File

@ -1,11 +1,6 @@
package world.phantasmal.cell.list
import world.phantasmal.cell.Cell
import world.phantasmal.cell.CellTests
import world.phantasmal.cell.CellWithDependenciesTests
import world.phantasmal.cell.DependentCell
import world.phantasmal.cell.MutableCell
import world.phantasmal.cell.SimpleCell
import world.phantasmal.cell.*
/**
* In these tests, the direct list cell dependency of the [ListElementsDependentCell] doesn't
@ -33,7 +28,7 @@ class ListElementsDependentCellElementEmitsTests : CellWithDependenciesTests {
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
ListElementsDependentCell(
ImmutableListCell(listOf(dependency1, dependency2, dependency3))
) { arrayOf(it) }

View File

@ -30,7 +30,7 @@ class SimpleFilteredListCellListDependencyEmitsTests :
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
SimpleFilteredListCell(
list = mapToList(dependency1, dependency2, dependency3) { value1, value2, value3 ->
listOf(value1, value2, value3)

View File

@ -33,7 +33,7 @@ class SimpleFilteredListCellPredicateDependencyEmitsTests :
dependency1: Cell<Int>,
dependency2: Cell<Int>,
dependency3: Cell<Int>,
) =
): Cell<Any> =
SimpleFilteredListCell(
list = listCell(1, 2, 3, 4, 5, 6, 7, 8, 9),
predicate = map(dependency1, dependency2, dependency3) { value1, value2, value3 ->

View File

@ -10,6 +10,9 @@ import world.phantasmal.cell.Cell
*/
@Suppress("unused")
class SimpleFilteredListCellTests : AbstractFilteredListCellTests {
override fun <E> createFilteredListCell(list: ListCell<E>, predicate: Cell<(E) -> Boolean>) =
override fun <E> createFilteredListCell(
list: ListCell<E>,
predicate: Cell<(E) -> Boolean>,
): ListCell<E> =
SimpleFilteredListCell(list, predicate)
}

View File

@ -2,7 +2,7 @@ package world.phantasmal.web.questEditor.models
import world.phantasmal.cell.Cell
import world.phantasmal.cell.list.ListCell
import world.phantasmal.cell.list.SimpleListCell
import world.phantasmal.cell.list.mutableListCell
import world.phantasmal.cell.map
import world.phantasmal.cell.mutableCell
@ -19,7 +19,7 @@ class QuestEventModel(
private val _sectionId = mutableCell(sectionId)
private val _waveId = mutableCell(waveId)
private val _delay = mutableCell(delay)
private val _actions = SimpleListCell(actions)
private val _actions = mutableListCell(actions)
val id: Cell<Int> = _id
val sectionId: Cell<Int> = _sectionId

View File

@ -1,15 +1,15 @@
package world.phantasmal.web.questEditor.models
import world.phantasmal.cell.Cell
import world.phantasmal.cell.list.ListCell
import world.phantasmal.cell.list.flatMapToList
import world.phantasmal.cell.list.listCell
import world.phantasmal.cell.list.mutableListCell
import world.phantasmal.cell.map
import world.phantasmal.cell.mutableCell
import world.phantasmal.psolib.Episode
import world.phantasmal.psolib.asm.BytecodeIr
import world.phantasmal.psolib.fileFormats.quest.DatUnknown
import world.phantasmal.cell.Cell
import world.phantasmal.cell.list.ListCell
import world.phantasmal.cell.list.SimpleListCell
import world.phantasmal.cell.list.flatMapToList
import world.phantasmal.cell.list.listCell
import world.phantasmal.cell.map
import world.phantasmal.cell.mutableCell
class QuestModel(
id: Int,
@ -36,9 +36,9 @@ class QuestModel(
private val _shortDescription = mutableCell("")
private val _longDescription = mutableCell("")
private val _mapDesignations = mutableCell(mapDesignations)
private val _npcs = SimpleListCell(npcs)
private val _objects = SimpleListCell(objects)
private val _events = SimpleListCell(events)
private val _npcs = mutableListCell(npcs)
private val _objects = mutableListCell(objects)
private val _events = mutableListCell(events)
val id: Cell<Int> = _id
val language: Cell<Int> = _language

View File

@ -3,7 +3,8 @@ package world.phantasmal.webui
import kotlinx.coroutines.*
import mu.KotlinLogging
import world.phantasmal.cell.Cell
import world.phantasmal.cell.SimpleCell
import world.phantasmal.cell.MutableCell
import world.phantasmal.cell.mutableCell
import kotlin.time.measureTime
private val logger = KotlinLogging.logger {}
@ -26,14 +27,14 @@ class LoadingStatusCellImpl private constructor(
private val dataName: String,
/** Will be called with [Dispatchers.Main] context. */
private val loadData: suspend () -> Unit,
private val cellDelegate: SimpleCell<LoadingStatus>,
private val cellDelegate: MutableCell<LoadingStatus>,
) : LoadingStatusCell, Cell<LoadingStatus> by cellDelegate {
constructor(
scope: CoroutineScope,
dataName: String,
loadData: suspend () -> Unit,
) : this(scope, dataName, loadData, SimpleCell(LoadingStatus.Uninitialized))
) : this(scope, dataName, loadData, mutableCell(LoadingStatus.Uninitialized))
private var currentJob: Job? = null