mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
296 lines
9.4 KiB
Kotlin
296 lines
9.4 KiB
Kotlin
package eu.kanade.tachiyomi.widget
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.Context
|
|
import android.graphics.drawable.Drawable
|
|
import android.util.AttributeSet
|
|
import android.view.Gravity
|
|
import android.view.LayoutInflater
|
|
import android.view.MenuItem
|
|
import android.widget.FrameLayout
|
|
import androidx.annotation.ArrayRes
|
|
import androidx.annotation.StringRes
|
|
import androidx.appcompat.view.menu.MenuBuilder
|
|
import androidx.appcompat.widget.PopupMenu
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.core.graphics.ColorUtils
|
|
import androidx.core.view.forEach
|
|
import androidx.core.view.get
|
|
import com.fredporciuncula.flow.preferences.Preference
|
|
import eu.kanade.tachiyomi.R
|
|
import eu.kanade.tachiyomi.databinding.MaterialSpinnerViewBinding
|
|
import eu.kanade.tachiyomi.util.lang.tintText
|
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
|
import eu.kanade.tachiyomi.util.system.toast
|
|
import kotlin.math.max
|
|
|
|
class MaterialSpinnerView constructor(context: Context, attrs: AttributeSet?) :
|
|
FrameLayout(context, attrs) {
|
|
|
|
private var entries = emptyList<String>()
|
|
var selectedPosition = 0
|
|
private set
|
|
var originalPosition: Int? = null
|
|
var originalIcon: Drawable? = null
|
|
private var pref: Preference<Int>? = null
|
|
private var prefOffset = 0
|
|
private var popup: PopupMenu? = null
|
|
var title: CharSequence
|
|
get() {
|
|
return binding.titleView.text
|
|
}
|
|
set(value) {
|
|
binding.titleView.text = value
|
|
}
|
|
|
|
private val blendedAccent = ColorUtils.blendARGB(
|
|
context.getResourceColor(R.attr.colorSecondary),
|
|
context.getResourceColor(R.attr.colorOnBackground),
|
|
0.5f,
|
|
)
|
|
|
|
private val slightAccent by lazy {
|
|
ColorUtils.blendARGB(
|
|
context.getResourceColor(R.attr.colorSecondary),
|
|
context.getResourceColor(R.attr.colorOnBackground),
|
|
0.9f,
|
|
)
|
|
}
|
|
|
|
var onItemSelectedListener: ((Int) -> Unit)? = null
|
|
set(value) {
|
|
field = value
|
|
if (value != null) {
|
|
popup = makeSettingsPopup()
|
|
setOnTouchListener(popup?.dragToOpenListener)
|
|
setOnClickListener {
|
|
popup?.show()
|
|
}
|
|
}
|
|
}
|
|
|
|
private val binding = MaterialSpinnerViewBinding.inflate(
|
|
LayoutInflater.from(context),
|
|
this,
|
|
false,
|
|
)
|
|
|
|
init {
|
|
addView(binding.root)
|
|
val a = context.obtainStyledAttributes(attrs, R.styleable.MaterialSpinnerView, 0, 0)
|
|
|
|
val str = a.getString(R.styleable.MaterialSpinnerView_title) ?: ""
|
|
title = str
|
|
|
|
val entries = (a.getTextArray(R.styleable.MaterialSpinnerView_android_entries) ?: emptyArray()).map { it.toString() }
|
|
this.entries = entries
|
|
|
|
val maxLines = a.getInt(R.styleable.MaterialSpinnerView_android_maxLines, Int.MAX_VALUE)
|
|
binding.titleView.maxLines = maxLines
|
|
this.entries = entries
|
|
|
|
binding.detailView.text = entries.firstOrNull().orEmpty()
|
|
|
|
if (entries.isNotEmpty()) {
|
|
popup = makeSettingsPopup()
|
|
setOnTouchListener(popup?.dragToOpenListener)
|
|
setOnClickListener {
|
|
popup?.show()
|
|
}
|
|
}
|
|
|
|
a.recycle()
|
|
}
|
|
|
|
fun setEntries(entries: List<String>) {
|
|
this.entries = entries
|
|
popup = makeSettingsPopup()
|
|
setOnTouchListener(popup?.dragToOpenListener)
|
|
setOnClickListener {
|
|
popup?.show()
|
|
}
|
|
}
|
|
|
|
fun setSelection(selection: Int) {
|
|
if (selectedPosition >= 0 && selectedPosition < (popup?.menu?.size() ?: 0)) {
|
|
popup?.menu?.get(selectedPosition)?.let {
|
|
it.icon = ContextCompat.getDrawable(context, R.drawable.ic_blank_24dp)
|
|
it.title = entries[selectedPosition]
|
|
}
|
|
}
|
|
selectedPosition = selection
|
|
if (selectedPosition >= 0 && selectedPosition < (popup?.menu?.size() ?: 0)) {
|
|
popup?.menu?.get(selectedPosition)?.let {
|
|
it.icon = tintedCheck()
|
|
it.title = it.title?.tintText(blendedAccent)
|
|
}
|
|
}
|
|
updateOriginalPositionMenu()
|
|
binding.detailView.text = entries.getOrNull(selection).orEmpty()
|
|
}
|
|
|
|
fun setDisabledState(@StringRes messageRes: Int = 0) {
|
|
alpha = 0.5f
|
|
popup = null
|
|
setOnTouchListener(null)
|
|
setOnClickListener {
|
|
if (messageRes != 0) {
|
|
context.toast(messageRes)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun bindToPreference(pref: Preference<Int>, offset: Int = 0, block: ((Int) -> Unit)? = null) {
|
|
setSelection(pref.get() - offset)
|
|
this.pref = pref
|
|
prefOffset = offset
|
|
popup = makeSettingsPopup(pref, prefOffset, block)
|
|
setOnTouchListener(popup?.dragToOpenListener)
|
|
setOnClickListener {
|
|
popup?.show()
|
|
}
|
|
}
|
|
|
|
inline fun <reified T : Enum<T>> bindToPreference(pref: Preference<T>) {
|
|
val enumConstants = T::class.java.enumConstants
|
|
enumConstants?.indexOf(pref.get())?.let { setSelection(it) }
|
|
val popup = makeSettingsPopup(pref)
|
|
setOnTouchListener(popup.dragToOpenListener)
|
|
setOnClickListener {
|
|
popup.show()
|
|
}
|
|
}
|
|
|
|
fun bindToIntPreference(
|
|
pref: Preference<Int>,
|
|
@ArrayRes intValuesResource: Int,
|
|
block: ((Int) -> Unit)? = null,
|
|
) {
|
|
this.pref = pref
|
|
prefOffset = 0
|
|
val intValues = resources.getStringArray(intValuesResource).map { it.toIntOrNull() }
|
|
setSelection(max(0, intValues.indexOf(pref.get())))
|
|
popup = makeSettingsPopup(pref, intValues, block)
|
|
setOnTouchListener(popup?.dragToOpenListener)
|
|
setOnClickListener {
|
|
popup?.show()
|
|
}
|
|
}
|
|
|
|
inline fun <reified T : Enum<T>> makeSettingsPopup(preference: Preference<T>): PopupMenu {
|
|
val popup = popup()
|
|
|
|
// Set a listener so we are notified if a menu item is clicked
|
|
popup.setOnMenuItemClickListener { menuItem ->
|
|
val pos = menuClicked(menuItem)
|
|
onItemSelectedListener?.invoke(pos)
|
|
true
|
|
}
|
|
// Set a listener so we are notified if a menu item is clicked
|
|
popup.setOnMenuItemClickListener { menuItem ->
|
|
val enumConstants = T::class.java.enumConstants
|
|
val pos = menuClicked(menuItem)
|
|
enumConstants?.get(pos)?.let { preference.set(it) }
|
|
true
|
|
}
|
|
return popup
|
|
}
|
|
|
|
private fun makeSettingsPopup(
|
|
preference: Preference<Int>,
|
|
intValues: List<Int?>,
|
|
block: ((Int) -> Unit)? = null,
|
|
): PopupMenu {
|
|
val popup = popup()
|
|
// Set a listener so we are notified if a menu item is clicked
|
|
popup.setOnMenuItemClickListener { menuItem ->
|
|
val pos = menuClicked(menuItem)
|
|
preference.set(intValues[pos] ?: 0)
|
|
block?.invoke(pos)
|
|
true
|
|
}
|
|
return popup
|
|
}
|
|
|
|
private fun makeSettingsPopup(
|
|
preference: Preference<Int>,
|
|
offset: Int = 0,
|
|
block: ((Int) -> Unit)? = null,
|
|
): PopupMenu {
|
|
val popup = popup()
|
|
// Set a listener so we are notified if a menu item is clicked
|
|
popup.setOnMenuItemClickListener { menuItem ->
|
|
val pos = menuClicked(menuItem)
|
|
preference.set(pos + offset)
|
|
block?.invoke(pos)
|
|
true
|
|
}
|
|
return popup
|
|
}
|
|
|
|
private fun makeSettingsPopup(): PopupMenu {
|
|
val popup = popup()
|
|
|
|
// Set a listener so we are notified if a menu item is clicked
|
|
popup.setOnMenuItemClickListener { menuItem ->
|
|
val pos = menuClicked(menuItem)
|
|
onItemSelectedListener?.invoke(pos)
|
|
true
|
|
}
|
|
return popup
|
|
}
|
|
|
|
fun menuClicked(menuItem: MenuItem): Int {
|
|
val pos = menuItem.itemId
|
|
setSelection(pos)
|
|
return pos
|
|
}
|
|
|
|
@SuppressLint("RestrictedApi")
|
|
fun popup(): PopupMenu {
|
|
val popup = PopupMenu(context, this, Gravity.END)
|
|
entries.forEachIndexed { index, entry ->
|
|
popup.menu.add(0, index, 0, entry)
|
|
}
|
|
if (popup.menu is MenuBuilder) {
|
|
val m = popup.menu as MenuBuilder
|
|
m.setOptionalIconsVisible(true)
|
|
}
|
|
popup.menu.forEach {
|
|
it.icon = ContextCompat.getDrawable(context, R.drawable.ic_blank_24dp)
|
|
}
|
|
popup.menu.getItem(selectedPosition)?.let { menuItem ->
|
|
menuItem.icon = tintedCheck()
|
|
menuItem.title =
|
|
menuItem.title?.tintText(blendedAccent)
|
|
}
|
|
this.popup = popup
|
|
updateOriginalPositionMenu()
|
|
return popup
|
|
}
|
|
|
|
private fun updateOriginalPositionMenu() {
|
|
popup ?: return
|
|
val originalPosition = originalPosition ?: return
|
|
if (originalPosition != selectedPosition &&
|
|
originalPosition >= 0 && originalPosition < popup?.menu?.size() ?: 0
|
|
) {
|
|
popup?.menu?.getItem(originalPosition)?.let { menuItem ->
|
|
menuItem.icon = tintedOG()
|
|
menuItem
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun tintedOG(): Drawable? {
|
|
return originalIcon ?: ContextCompat.getDrawable(context, R.drawable.ic_browse_outline_24dp)?.mutate()?.apply {
|
|
setTint(slightAccent)
|
|
}
|
|
}
|
|
|
|
private fun tintedCheck(): Drawable? {
|
|
return ContextCompat.getDrawable(context, R.drawable.ic_check_24dp)?.mutate()?.apply {
|
|
setTint(blendedAccent)
|
|
}
|
|
}
|
|
}
|