UI Double/Multi Click Functionality and Double Click on Tech to Exit Tech Screen (#8560)

* Multi-Click Framework and Double Click on Tech to Exit Tech Screen

* Update condition and add comment

* Extra criteria in case there are multiple listeners (Android Studio throws an error without the redundant 'is' check)

* Re-add exiting paper sound effect on double click
This commit is contained in:
OptimizedForDensity 2023-02-03 02:39:22 -05:00 committed by GitHub
parent 9a35f4a283
commit 772615cffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 21 deletions

View File

@ -23,6 +23,7 @@ import com.unciv.ui.utils.extensions.colorFromRGB
import com.unciv.ui.utils.extensions.darken
import com.unciv.ui.utils.extensions.disable
import com.unciv.ui.utils.extensions.onClick
import com.unciv.ui.utils.extensions.onDoubleClick
import com.unciv.ui.utils.extensions.toLabel
import com.unciv.utils.concurrency.Concurrency
import kotlin.math.abs
@ -80,19 +81,7 @@ class TechPickerScreen(
pickerPane.bottomTable.background = skinStrings.getUiBackground("TechPickerScreen/BottomTable", tintColor = skinStrings.skinConfig.clearColor)
rightSideButton.setText("Pick a tech".tr())
rightSideButton.onClick(UncivSound.Paper) {
if (freeTechPick) {
val freeTech = selectedTech!!.name
// More evil people fast-clicking to cheat - #4977
if (!researchableTechs.contains(freeTech)) return@onClick
civTech.getFreeTechnology(selectedTech!!.name)
}
else civTech.techsToResearch = tempTechsToResearch
game.settings.addCompletedTutorialTask("Pick technology")
game.popScreen()
}
rightSideButton.onClick(UncivSound.Paper) { tryExit() }
// per default show current/recent technology,
// and possibly select it to show description,
@ -112,6 +101,20 @@ class TechPickerScreen(
}
}
private fun tryExit() {
if (freeTechPick) {
val freeTech = selectedTech!!.name
// More evil people fast-clicking to cheat - #4977
if (!researchableTechs.contains(freeTech)) return
civTech.getFreeTechnology(selectedTech!!.name)
}
else civTech.techsToResearch = tempTechsToResearch
game.settings.addCompletedTutorialTask("Pick technology")
game.popScreen()
}
private fun createTechTable() {
for (label in eraLabels) label.remove()
@ -182,6 +185,7 @@ class TechPickerScreen(
table.add(techButton)
techNameToButton[tech.name] = techButton
techButton.onClick { selectTechnology(tech, false) }
techButton.onDoubleClick(UncivSound.Paper) { tryExit() }
techTable.add(table).fillX()
}
}

View File

@ -276,25 +276,62 @@ fun Actor.centerX(parent: Stage) { x = parent.width / 2 - width / 2 }
fun Actor.centerY(parent: Stage) { y = parent.height / 2 - height / 2 }
fun Actor.center(parent: Stage) { centerX(parent); centerY(parent) }
class OnClickListener(val sound: UncivSound = UncivSound.Click, val function: (event: InputEvent?, x: Float, y: Float) -> Unit):ClickListener(){
class ClickListenerInstance(val sound: UncivSound, val function: (event: InputEvent?, x: Float, y: Float) -> Unit, val tapCount: Int)
class OnClickListener(val sound: UncivSound = UncivSound.Click,
val function: (event: InputEvent?, x: Float, y: Float) -> Unit,
tapCount: Int = 1,
tapInterval: Float = 0.0f): ClickListener() {
private val clickFunctions = mutableMapOf<Int, ClickListenerInstance>()
init {
setTapCountInterval(tapInterval)
clickFunctions[tapCount] = ClickListenerInstance(sound, function, tapCount)
}
fun addClickFunction(sound: UncivSound = UncivSound.Click, tapCount: Int, function: (event: InputEvent?, x: Float, y: Float) -> Unit) {
clickFunctions[tapCount] = ClickListenerInstance(sound, function, tapCount)
}
override fun clicked(event: InputEvent?, x: Float, y: Float) {
Concurrency.run("Sound") { SoundPlayer.play(sound) }
function(event, x, y)
var effectiveTapCount = tapCount
if (clickFunctions[effectiveTapCount] == null) {
effectiveTapCount = clickFunctions.keys.filter { it < tapCount }.maxOrNull() ?: return // happens if there's a double (or more) click function but no single click
}
val clickInstance = clickFunctions[effectiveTapCount]!!
Concurrency.run("Sound") { SoundPlayer.play(clickInstance.sound) }
val func = clickInstance.function
func(event, x, y)
}
}
/** same as [onClick], but sends the [InputEvent] and coordinates along */
fun Actor.onClickEvent(sound: UncivSound = UncivSound.Click, function: (event: InputEvent?, x: Float, y: Float) -> Unit) {
this.addListener(OnClickListener(sound, function))
fun Actor.onClickEvent(sound: UncivSound = UncivSound.Click,
tapCount: Int = 1,
tapInterval: Float = 0.0f,
function: (event: InputEvent?, x: Float, y: Float) -> Unit) {
val previousListener = this.listeners.firstOrNull { it is OnClickListener }
if (previousListener != null && previousListener is OnClickListener) {
previousListener.addClickFunction(sound, tapCount, function)
previousListener.setTapCountInterval(tapInterval)
} else {
this.addListener(OnClickListener(sound, function, tapCount, tapInterval))
}
}
// If there are other buttons that require special clicks then we'll have an onclick that will accept a string parameter, no worries
fun Actor.onClick(sound: UncivSound = UncivSound.Click, function: () -> Unit) {
onClickEvent(sound) { _, _, _ -> function() }
fun Actor.onClick(sound: UncivSound = UncivSound.Click, tapCount: Int = 1, tapInterval: Float = 0.0f, function: () -> Unit) {
onClickEvent(sound, tapCount, tapInterval) { _, _, _ -> function() }
}
fun Actor.onClick(function: () -> Unit): Actor {
onClick(UncivSound.Click, function)
onClick(UncivSound.Click, 1, 0f, function)
return this
}
fun Actor.onDoubleClick(sound: UncivSound = UncivSound.Click, tapInterval: Float = 0.25f, function: () -> Unit): Actor {
onClick(sound, 2, tapInterval, function)
return this
}