android: Create generic single selection list adapter
This commit is contained in:
		| @@ -0,0 +1,105 @@ | ||||
| // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| package org.yuzu.yuzu_emu.adapters | ||||
|  | ||||
| import org.yuzu.yuzu_emu.model.SelectableItem | ||||
| import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder | ||||
|  | ||||
| /** | ||||
|  * Generic list class meant to take care of single selection UI updates | ||||
|  * @param currentList The list to show initially | ||||
|  * @param defaultSelection The default selection to use if no list items are selected by | ||||
|  * [SelectableItem.selected] or if the currently selected item is removed from the list | ||||
|  */ | ||||
| abstract class AbstractSingleSelectionList< | ||||
|     Model : SelectableItem, | ||||
|     Holder : AbstractViewHolder<Model> | ||||
|     >( | ||||
|     final override var currentList: List<Model>, | ||||
|     private val defaultSelection: DefaultSelection = DefaultSelection.Start | ||||
| ) : AbstractListAdapter<Model, Holder>(currentList) { | ||||
|     var selectedItem = getDefaultSelection() | ||||
|  | ||||
|     init { | ||||
|         findSelectedItem() | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Changes the selection state of the [SelectableItem] that was selected and the previously selected | ||||
|      * item and notifies the underlying adapter of the change for those items. Invokes [callback] last. | ||||
|      * Does nothing if [position] is the same as the currently selected item. | ||||
|      * @param position Index of the item that was selected | ||||
|      * @param callback Lambda that's called at the end of the list changes and has the selected list | ||||
|      * position passed in as a parameter | ||||
|      */ | ||||
|     fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) { | ||||
|         if (position == selectedItem) { | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         val previouslySelectedItem = selectedItem | ||||
|         selectedItem = position | ||||
|         if (currentList.indices.contains(selectedItem)) { | ||||
|             currentList[selectedItem].onSelectionStateChanged(true) | ||||
|         } | ||||
|         if (currentList.indices.contains(previouslySelectedItem)) { | ||||
|             currentList[previouslySelectedItem].onSelectionStateChanged(false) | ||||
|         } | ||||
|         onItemChanged(previouslySelectedItem) | ||||
|         onItemChanged(selectedItem) | ||||
|         callback?.invoke(position) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Removes a given item from the list and notifies the underlying adapter of the change. If the | ||||
|      * currently selected item was the item that was removed, the item at the position provided | ||||
|      * by [defaultSelection] will be made the new selection. Invokes [callback] last. | ||||
|      * @param position Index of the item that was removed | ||||
|      * @param callback Lambda that's called at the end of the list changes and has the removed and | ||||
|      * selected list positions passed in as parameters | ||||
|      */ | ||||
|     fun removeSelectableItem( | ||||
|         position: Int, | ||||
|         callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)? | ||||
|     ) { | ||||
|         removeItem(position) | ||||
|         if (position == selectedItem) { | ||||
|             selectedItem = getDefaultSelection() | ||||
|             currentList[selectedItem].onSelectionStateChanged(true) | ||||
|             onItemChanged(selectedItem) | ||||
|         } else if (position < selectedItem) { | ||||
|             selectedItem-- | ||||
|         } | ||||
|         callback?.invoke(position, selectedItem) | ||||
|     } | ||||
|  | ||||
|     override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) { | ||||
|         super.addItem(item, position, callback) | ||||
|         if (position <= selectedItem && position != -1) { | ||||
|             selectedItem++ | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun replaceList(newList: List<Model>) { | ||||
|         super.replaceList(newList) | ||||
|         findSelectedItem() | ||||
|     } | ||||
|  | ||||
|     private fun findSelectedItem() { | ||||
|         for (i in currentList.indices) { | ||||
|             if (currentList[i].selected) { | ||||
|                 selectedItem = i | ||||
|                 break | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun getDefaultSelection(): Int = | ||||
|         when (defaultSelection) { | ||||
|             DefaultSelection.Start -> currentList.indices.first | ||||
|             DefaultSelection.End -> currentList.indices.last | ||||
|         } | ||||
|  | ||||
|     enum class DefaultSelection { Start, End } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| // SPDX-FileCopyrightText: 2024 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| package org.yuzu.yuzu_emu.model | ||||
|  | ||||
| interface SelectableItem { | ||||
|     var selected: Boolean | ||||
|     fun onSelectionStateChanged(selected: Boolean) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user