
tornadofx.Collections.kt Maven / Gradle / Ivy
@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