tornadofx.Collections.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tornadofx Show documentation
Show all versions of tornadofx Show documentation
Lightweight JavaFX Framework for Kotlin
@file:Suppress("unused")
package tornadofx
import javafx.beans.Observable
import javafx.beans.WeakListener
import javafx.collections.*
import tornadofx.FX.IgnoreParentBuilder.No
import tornadofx.FX.IgnoreParentBuilder.Once
import java.lang.ref.WeakReference
import java.util.*
/**
* Returns an empty new [ObservableIntegerArray].
*/
fun observableIntArrayOf(): ObservableIntegerArray = FXCollections.observableIntegerArray()
/**
* Returns a new [ObservableIntegerArray] with the given [elements].
*/
fun observableIntArrayOf(vararg elements: Int): ObservableIntegerArray = FXCollections.observableIntegerArray(*elements)
/**
* Returns an empty new [ObservableFloatArray].
*/
fun observableFloatArrayOf(): ObservableFloatArray = FXCollections.observableFloatArray()
/**
* Returns a new [ObservableFloatArray] with the given [elements].
*/
fun observableFloatArrayOf(vararg elements: Float): ObservableFloatArray = FXCollections.observableFloatArray(*elements)
/**
* Returns an empty new [ObservableList].
*/
fun observableListOf(): ObservableList = FXCollections.observableArrayList()
/**
* Returns a new [ObservableList] with the given [elements].
*/
fun observableListOf(vararg elements: T): ObservableList = FXCollections.observableArrayList(*elements)
/**
* Returns a new [ObservableList] containing all elements from the given [collection].
*/
fun observableListOf(collection: Collection): ObservableList = FXCollections.observableArrayList(collection)
/**
* Returns an empty new [ObservableList] with the given [extractor]. This list reports element updates.
*/
fun observableListOf(extractor: (T)->Array): ObservableList = FXCollections.observableArrayList(extractor)
/**
* Returns an empty new [ObservableSet]
*/
fun observableSetOf(): ObservableSet = FXCollections.observableSet()
/**
* Returns a new [ObservableSet] with the given elements.
*/
fun observableSetOf(vararg elements: T): ObservableSet = FXCollections.observableSet(*elements)
/**
* Returns an empty new [ObservableMap]
*/
fun observableMapOf(): ObservableMap = FXCollections.observableHashMap()
/**
* Returns a new [ObservableMap] with the specified contents, given as a list of pairs
* where the first component is the key and the second is the value.
*/
fun observableMapOf(vararg pairs: Pair): ObservableMap = FXCollections.observableMap(pairs.toMap(hashMapOf()))
/**
* Returns a new [ObservableIntegerArray] with the elements from the original array.
*/
fun IntArray.toObservable(): ObservableIntegerArray = FXCollections.observableIntegerArray(*this)
/**
* Returns a new [ObservableIntegerArray] with the elements from the original array.
*/
fun Array.toObservable(): ObservableIntegerArray = FXCollections.observableIntegerArray(*this.toIntArray())
/**
* Returns a new [ObservableFloatArray] with the elements from the original array.
*/
fun FloatArray.toObservable(): ObservableFloatArray = FXCollections.observableFloatArray(*this)
/**
* Returns a new [ObservableFloatArray] with the elements from the original array.
*/
fun Array.toObservable(): ObservableFloatArray = FXCollections.observableFloatArray(*this.toFloatArray())
/**
* Returns a new [ObservableList] with the elements from the original list.
*/
fun List.toObservable(): ObservableList = FXCollections.observableList(toMutableList())
/**
* Returns a new [ObservableSet] with the elements from the original set.
*/
fun Set.toObservable(): ObservableSet = FXCollections.observableSet(toMutableSet())
/**
* Returns a new [ObservableMap] with the elements from the original map.
*/
fun Map.toObservable(): ObservableMap = FXCollections.observableMap(toMutableMap())
/**
* Returns a new [ObservableList] that is backed by the original list.
*
* **Note:** If the original list is read-only, attempting to modify the returned list will result in an [UnsupportedOperationException]
*/
fun List.asObservable(): ObservableList = FXCollections.observableList(this)
/**
* Returns a new [ObservableSet] that is backed by the original set.
*
* **Note:** If the original set is read-only, attempting to modify the returned set will result in an [UnsupportedOperationException]
*/
fun Set.asObservable(): ObservableSet = FXCollections.observableSet(this)
/**
* Returns a new [ObservableMap] that is backed by the original map.
*
* **Note:** If the original map is read-only, attempting to modify the returned map will result in an [UnsupportedOperationException]
*/
fun Map.asObservable(): ObservableMap = FXCollections.observableMap(this)
/**
* Returns an unmodifiable [ObservableList] that wraps the original list.
*/
fun ObservableList.asUnmodifiable(): ObservableList = FXCollections.unmodifiableObservableList(this)
/**
* Returns an unmodifiable [ObservableSet] that wraps the original set.
*/
fun ObservableSet.asUnmodifiable(): ObservableSet = FXCollections.unmodifiableObservableSet(this)
/**
* Returns an unmodifiable [ObservableMap] that wraps the original map.
*/
fun ObservableMap.asUnmodifiable(): ObservableMap = FXCollections.unmodifiableObservableMap(this)
/**
* Fills the observable list with the provided [value].
* Fires only **one** change notification on the list.
*/
fun ObservableList.fill(value: T): Unit = FXCollections.fill(this, value)
/**
* Reverse the order in the observable list.
* Fires only **one** change notification on the list.
*/
fun ObservableList.reverse(): Unit = FXCollections.reverse(this)
/**
* Randomly shuffles elements in this observable list.
* Fires only **one** change notification on the list.
*/
fun ObservableList.shuffle(): Unit = FXCollections.shuffle(this)
/**
* Randomly shuffles elements in this observable list using the specified [random] instance as the source of randomness.
* Fires only **one** change notification on the list.
*/
fun ObservableList.shuffle(random: Random): Unit = FXCollections.shuffle(this, random)
/**
* Sorts elements in the observable list according to their natural sort order.
* Fires only **one** change notification on the list.
*/
fun > ObservableList.sort() {
if (size > 1) FXCollections.sort(this)
}
/**
* Sorts elements in the observable list according to the order specified with [comparator].
* Fires only **one** change notification on the list.
*/
fun ObservableList.sortWith(comparator: Comparator) {
if (size > 1) FXCollections.sort(this, comparator)
}
/**
* Sorts elements in the observable list according to natural sort order of the value returned by specified [selector] function.
* Fires only **one** change notification on the list.
*/
inline fun > ObservableList.sortBy(crossinline selector: (T) -> R?) {
if (size > 1) sortWith(compareBy(selector))
}
/**
* Sorts elements in the observable list descending according to natural sort order of the value returned by specified [selector] function.
* Fires only **one** change notification on the list.
*/
inline fun > ObservableList.sortByDescending(crossinline selector: (T) -> R?) {
if (size > 1) sortWith(compareByDescending(selector))
}
/**
* Moves the given **T** item to the specified index
*/
fun MutableList.move(item: T, newIndex: Int) {
check(newIndex in 0 until size)
val currentIndex = indexOf(item)
if (currentIndex < 0) return
removeAt(currentIndex)
add(newIndex, item)
}
/**
* Moves the given item at the `oldIndex` to the `newIndex`
*/
fun MutableList.moveAt(oldIndex: Int, newIndex: Int) {
check(oldIndex in 0 until size)
check(newIndex in 0 until size)
val item = this[oldIndex]
removeAt(oldIndex)
add(newIndex, item)
}
/**
* Moves all items meeting a predicate to the given index
*/
fun MutableList.moveAll(newIndex: Int, predicate: (T) -> Boolean) {
check(newIndex in 0 until size)
val split = partition(predicate)
clear()
addAll(split.second)
addAll(if (newIndex >= size) size else newIndex, split.first)
}
/**
* Moves the given element at specified index up the **MutableList** by one increment
* unless it is at the top already which will result in no movement
*/
fun MutableList.moveUpAt(index: Int) {
if (index == 0) return
check(index in indices, { "Invalid index $index for MutableList of size $size" })
val newIndex = index - 1
val item = this[index]
removeAt(index)
add(newIndex, item)
}
/**
* Moves the given element **T** up the **MutableList** by one increment
* unless it is at the bottom already which will result in no movement
*/
fun MutableList.moveDownAt(index: Int) {
if (index == size - 1) return
check(index in indices, { "Invalid index $index for MutableList of size $size" })
val newIndex = index + 1
val item = this[index]
removeAt(index)
add(newIndex, item)
}
/**
* Moves the given element **T** up the **MutableList** by an index increment
* unless it is at the top already which will result in no movement.
* Returns a `Boolean` indicating if move was successful
*/
fun MutableList.moveUp(item: T): Boolean {
val currentIndex = indexOf(item)
if (currentIndex == -1) return false
val newIndex = (currentIndex - 1)
if (currentIndex <= 0) return false
remove(item)
add(newIndex, item)
return true
}
/**
* Moves the given element **T** up the **MutableList** by an index increment
* unless it is at the bottom already which will result in no movement.
* Returns a `Boolean` indicating if move was successful
*/
fun MutableList.moveDown(item: T): Boolean {
val currentIndex = indexOf(item)
if (currentIndex == -1) return false
val newIndex = (currentIndex + 1)
if (newIndex >= size) return false
remove(item)
add(newIndex, item)
return true
}
/**
* Moves first element **T** up an index that satisfies the given **predicate**, unless its already at the top
*/
inline fun MutableList.moveUp(crossinline predicate: (T) -> Boolean) = find(predicate)?.let { moveUp(it) }
/**
* Moves first element **T** down an index that satisfies the given **predicate**, unless its already at the bottom
*/
inline fun MutableList.moveDown(crossinline predicate: (T) -> Boolean) = find(predicate)?.let { moveDown(it) }
/**
* Moves all **T** elements up an index that satisfy the given **predicate**, unless they are already at the top
*/
inline fun MutableList.moveUpAll(crossinline predicate: (T) -> Boolean) = asSequence().withIndex()
.filter { predicate.invoke(it.value) }
.forEach { moveUpAt(it.index) }
/**
* Moves all **T** elements down an index that satisfy the given **predicate**, unless they are already at the bottom
*/
inline fun MutableList.moveDownAll(crossinline predicate: (T) -> Boolean) = asSequence().withIndex()
.filter { predicate.invoke(it.value) }
.forEach { moveDownAt(it.index) }
fun MutableList.moveToTopWhere(predicate: (T) -> Boolean) {
asSequence().filter(predicate).toList().asSequence().forEach {
remove(it)
add(0, it)
}
}
fun MutableList.moveToBottomWhere(predicate: (T) -> Boolean) {
val end = size - 1
asSequence().filter(predicate).toList().asSequence().forEach {
remove(it)
add(end, it)
}
}
/**
* Swaps the position of two items at two respective indices
*/
fun MutableList.swap(indexOne: Int, indexTwo: Int) {
if (this is ObservableList<*>) {
if (indexOne == indexTwo) return
val min = Math.min(indexOne, indexTwo)
val max = Math.max(indexOne, indexTwo)
val o2 = removeAt(max)
val o1 = removeAt(min)
add(min, o2)
add(max, o1)
} else {
Collections.swap(this, indexOne, indexTwo)
}
}
/**
* Swaps the index position of two items
*/
fun MutableList.swap(itemOne: T, itemTwo: T) = swap(indexOf(itemOne), indexOf(itemTwo))
/**
* Bind this list to the given observable list by converting them into the correct type via the given converter.
* Changes to the observable list are synced.
*/
fun MutableList.bind(sourceList: ObservableList, converter: (SourceType) -> TargetType): ListConversionListener {
val ignoringParentConverter: (SourceType) -> TargetType = {
FX.ignoreParentBuilder = Once
try {
converter(it)
} finally {
FX.ignoreParentBuilder = No
}
}
val listener = ListConversionListener(this, ignoringParentConverter)
(this as? ObservableList)?.setAll(sourceList.map(ignoringParentConverter)) ?: run {
clear()
addAll(sourceList.map(ignoringParentConverter))
}
sourceList.removeListener(listener)
sourceList.addListener(listener)
return listener
}
/**
* Bind this list to the given observable list by converting them into the correct type via the given converter.
* Changes to the observable list are synced.
*/
fun MutableList.bind(sourceSet: ObservableSet, converter: (SourceType) -> TargetType): SetConversionListener {
val ignoringParentConverter: (SourceType) -> TargetType = {
FX.ignoreParentBuilder = Once
try {
converter(it)
} finally {
FX.ignoreParentBuilder = No
}
}
val listener = SetConversionListener(this, ignoringParentConverter)
if (this is ObservableList<*>) {
sourceSet.forEach { source ->
val converted = ignoringParentConverter(source)
listener.sourceToTarget[source] = converted
}
(this as ObservableList).setAll(listener.sourceToTarget.values)
} else {
clear()
addAll(sourceSet.map(ignoringParentConverter))
}
sourceSet.removeListener(listener)
sourceSet.addListener(listener)
return listener
}
fun MutableList.bind(
sourceMap: ObservableMap,
converter: (SourceTypeKey, SourceTypeValue) -> TargetType
): MapConversionListener {
val ignoringParentConverter: (SourceTypeKey, SourceTypeValue) -> TargetType = { key, value ->
FX.ignoreParentBuilder = FX.IgnoreParentBuilder.Once
try {
converter(key, value)
} finally {
FX.ignoreParentBuilder = FX.IgnoreParentBuilder.No
}
}
val listener = MapConversionListener(this, ignoringParentConverter)
if (this is ObservableList<*>) {
sourceMap.forEach { source ->
val converted = ignoringParentConverter(source.key,source.value)
listener.sourceToTarget[source] = converted
}
(this as ObservableList).setAll(listener.sourceToTarget.values)
} else {
clear()
addAll(sourceMap.map{ignoringParentConverter(it.key, it.value) })
}
sourceMap.removeListener(listener)
sourceMap.addListener(listener)
return listener
}
/**
* Listens to changes on a list of SourceType and keeps the target list in sync by converting
* each object into the TargetType via the supplied converter.
*/
class ListConversionListener(targetList: MutableList, val converter: (SourceType) -> TargetType) : ListChangeListener, WeakListener {
internal val targetRef: WeakReference> = WeakReference(targetList)
override fun onChanged(change: ListChangeListener.Change) {
val list = targetRef.get()
if (list == null) {
change.list.removeListener(this)
} else {
while (change.next()) {
if (change.wasPermutated()) {
list.subList(change.from, change.to).clear()
list.addAll(change.from, change.list.subList(change.from, change.to).map(converter))
} else {
if (change.wasRemoved()) {
list.subList(change.from, change.from + change.removedSize).clear()
}
if (change.wasAdded()) {
list.addAll(change.from, change.addedSubList.map(converter))
}
}
}
}
}
override fun wasGarbageCollected() = targetRef.get() == null
override fun hashCode() = targetRef.get().hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
val ourList = targetRef.get() ?: return false
if (other is ListConversionListener<*, *>) {
val otherList = other.targetRef.get()
return ourList === otherList
}
return false
}
}
/**
* Listens to changes on a Map of SourceTypeKey to SourceTypeValue and keeps the target list in sync by converting
* each object into the TargetType via the supplied converter.
*/
class MapConversionListener(
targetList: MutableList,
val converter: (SourceTypeKey, SourceTypeValue) -> TargetType
) : MapChangeListener, WeakListener {
internal val targetRef: WeakReference> = WeakReference(targetList)
internal val sourceToTarget = HashMap, TargetType>()
override fun onChanged(change: MapChangeListener.Change) {
val list = targetRef.get()
if (list == null) {
change.map.removeListener(this)
} else {
if (change.wasRemoved()) {
list.remove(converter(change.key, change.valueRemoved))
}
if (change.wasAdded()) {
list.add(converter(change.key, change.valueAdded))
}
}
}
override fun wasGarbageCollected() = targetRef.get() == null
override fun hashCode() = targetRef.get().hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
val ourList = targetRef.get() ?: return false
if (other is MapConversionListener<*, *, *>) {
val otherList = other.targetRef.get()
return ourList === otherList
}
return false
}
}
/**
* Listens to changes on a set of SourceType and keeps the target list in sync by converting
* each object into the TargetType via the supplied converter.
*/
class SetConversionListener(targetList: MutableList, val converter: (SourceType) -> TargetType) : SetChangeListener, WeakListener {
internal val targetRef: WeakReference> = WeakReference(targetList)
internal val sourceToTarget = HashMap()
override fun onChanged(change: SetChangeListener.Change) {
val list = targetRef.get()
if (list == null) {
change.set.removeListener(this)
sourceToTarget.clear()
} else {
if (change.wasRemoved()) {
list.remove(sourceToTarget[change.elementRemoved])
sourceToTarget.remove(change.elementRemoved)
}
if (change.wasAdded()) {
val converted = converter(change.elementAdded)
sourceToTarget[change.elementAdded] = converted
list.add(converted)
}
}
}
override fun wasGarbageCollected() = targetRef.get() == null
override fun hashCode() = targetRef.get().hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
val ourList = targetRef.get() ?: return false
if (other is SetConversionListener<*, *>) {
val otherList = other.targetRef.get()
return ourList === otherList
}
return false
}
}
fun ObservableList.invalidate() {
if (isNotEmpty()) this[0] = this[0]
}
@Deprecated("Use `observableListOf()` instead.", ReplaceWith("observableListOf(entries)", "tornadofx.observableListOf"))
fun observableList(vararg entries: T) : ObservableList = FXCollections.observableArrayList(entries.toList())
© 2015 - 2025 Weber Informatics LLC | Privacy Policy