diff options
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt | 105 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt | 9 |
2 files changed, 114 insertions, 0 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt new file mode 100644 index 000000000..52163f9d7 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.adapters | ||
| 5 | |||
| 6 | import org.yuzu.yuzu_emu.model.SelectableItem | ||
| 7 | import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder | ||
| 8 | |||
| 9 | /** | ||
| 10 | * Generic list class meant to take care of single selection UI updates | ||
| 11 | * @param currentList The list to show initially | ||
| 12 | * @param defaultSelection The default selection to use if no list items are selected by | ||
| 13 | * [SelectableItem.selected] or if the currently selected item is removed from the list | ||
| 14 | */ | ||
| 15 | abstract class AbstractSingleSelectionList< | ||
| 16 | Model : SelectableItem, | ||
| 17 | Holder : AbstractViewHolder<Model> | ||
| 18 | >( | ||
| 19 | final override var currentList: List<Model>, | ||
| 20 | private val defaultSelection: DefaultSelection = DefaultSelection.Start | ||
| 21 | ) : AbstractListAdapter<Model, Holder>(currentList) { | ||
| 22 | var selectedItem = getDefaultSelection() | ||
| 23 | |||
| 24 | init { | ||
| 25 | findSelectedItem() | ||
| 26 | } | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Changes the selection state of the [SelectableItem] that was selected and the previously selected | ||
| 30 | * item and notifies the underlying adapter of the change for those items. Invokes [callback] last. | ||
| 31 | * Does nothing if [position] is the same as the currently selected item. | ||
| 32 | * @param position Index of the item that was selected | ||
| 33 | * @param callback Lambda that's called at the end of the list changes and has the selected list | ||
| 34 | * position passed in as a parameter | ||
| 35 | */ | ||
| 36 | fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) { | ||
| 37 | if (position == selectedItem) { | ||
| 38 | return | ||
| 39 | } | ||
| 40 | |||
| 41 | val previouslySelectedItem = selectedItem | ||
| 42 | selectedItem = position | ||
| 43 | if (currentList.indices.contains(selectedItem)) { | ||
| 44 | currentList[selectedItem].onSelectionStateChanged(true) | ||
| 45 | } | ||
| 46 | if (currentList.indices.contains(previouslySelectedItem)) { | ||
| 47 | currentList[previouslySelectedItem].onSelectionStateChanged(false) | ||
| 48 | } | ||
| 49 | onItemChanged(previouslySelectedItem) | ||
| 50 | onItemChanged(selectedItem) | ||
| 51 | callback?.invoke(position) | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Removes a given item from the list and notifies the underlying adapter of the change. If the | ||
| 56 | * currently selected item was the item that was removed, the item at the position provided | ||
| 57 | * by [defaultSelection] will be made the new selection. Invokes [callback] last. | ||
| 58 | * @param position Index of the item that was removed | ||
| 59 | * @param callback Lambda that's called at the end of the list changes and has the removed and | ||
| 60 | * selected list positions passed in as parameters | ||
| 61 | */ | ||
| 62 | fun removeSelectableItem( | ||
| 63 | position: Int, | ||
| 64 | callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)? | ||
| 65 | ) { | ||
| 66 | removeItem(position) | ||
| 67 | if (position == selectedItem) { | ||
| 68 | selectedItem = getDefaultSelection() | ||
| 69 | currentList[selectedItem].onSelectionStateChanged(true) | ||
| 70 | onItemChanged(selectedItem) | ||
| 71 | } else if (position < selectedItem) { | ||
| 72 | selectedItem-- | ||
| 73 | } | ||
| 74 | callback?.invoke(position, selectedItem) | ||
| 75 | } | ||
| 76 | |||
| 77 | override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) { | ||
| 78 | super.addItem(item, position, callback) | ||
| 79 | if (position <= selectedItem && position != -1) { | ||
| 80 | selectedItem++ | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | override fun replaceList(newList: List<Model>) { | ||
| 85 | super.replaceList(newList) | ||
| 86 | findSelectedItem() | ||
| 87 | } | ||
| 88 | |||
| 89 | private fun findSelectedItem() { | ||
| 90 | for (i in currentList.indices) { | ||
| 91 | if (currentList[i].selected) { | ||
| 92 | selectedItem = i | ||
| 93 | break | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | private fun getDefaultSelection(): Int = | ||
| 99 | when (defaultSelection) { | ||
| 100 | DefaultSelection.Start -> currentList.indices.first | ||
| 101 | DefaultSelection.End -> currentList.indices.last | ||
| 102 | } | ||
| 103 | |||
| 104 | enum class DefaultSelection { Start, End } | ||
| 105 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt new file mode 100644 index 000000000..11c22d323 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.model | ||
| 5 | |||
| 6 | interface SelectableItem { | ||
| 7 | var selected: Boolean | ||
| 8 | fun onSelectionStateChanged(selected: Boolean) | ||
| 9 | } | ||