mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-20 09:17:47 +07:00
Key dispatcher precedence with several Popups open (#4040)
* Key dispatcher precedence with several Popups open * Key dispatcher precedence with several Popups open - remove instrumentation
This commit is contained in:
parent
6614d1050d
commit
e22c53ca96
@ -28,7 +28,7 @@ open class CameraStageBaseScreen : Screen {
|
||||
|
||||
protected val tutorialController by lazy { TutorialController(this) }
|
||||
|
||||
val keyPressDispatcher = KeyPressDispatcher()
|
||||
val keyPressDispatcher = KeyPressDispatcher(this.javaClass.simpleName)
|
||||
|
||||
init {
|
||||
val resolutions: List<Float> = game.settings.resolution.split("x").map { it.toInt().toFloat() }
|
||||
@ -37,7 +37,7 @@ open class CameraStageBaseScreen : Screen {
|
||||
/** The ExtendViewport sets the _minimum_(!) world size - the actual world size will be larger, fitted to screen/window aspect ratio. */
|
||||
stage = Stage(ExtendViewport(height, height), SpriteBatch())
|
||||
|
||||
keyPressDispatcher.install(stage, this.javaClass.simpleName) { hasOpenPopups() }
|
||||
keyPressDispatcher.install(stage) { hasOpenPopups() }
|
||||
}
|
||||
|
||||
override fun show() {}
|
||||
|
@ -36,7 +36,6 @@ fun Button.enable() {
|
||||
* which is more appropriate to toggle On/Off buttons, while this one is good for 'click-to-do-something' buttons.
|
||||
*/
|
||||
var Button.isEnabled: Boolean
|
||||
//Todo: Use in PromotionPickerScreen, TradeTable, WorldScreen.updateNextTurnButton
|
||||
get() = touchable == Touchable.enabled
|
||||
set(value) = if (value) enable() else disable()
|
||||
|
||||
|
@ -5,7 +5,6 @@ import com.badlogic.gdx.scenes.scene2d.EventListener
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener
|
||||
import com.badlogic.gdx.scenes.scene2d.Stage
|
||||
import java.util.HashMap
|
||||
|
||||
/*
|
||||
* For now, combination keys cannot easily be expressed.
|
||||
@ -40,6 +39,7 @@ data class KeyCharAndCode(val char: Char, val code: Int) {
|
||||
// debug helper
|
||||
return when {
|
||||
char == Char.MIN_VALUE -> Input.Keys.toString(code)
|
||||
char == '\u001B' -> "ESC"
|
||||
char < ' ' -> "Ctrl-" + (char.toInt()+64).toChar()
|
||||
else -> "\"$char\""
|
||||
}
|
||||
@ -57,14 +57,14 @@ data class KeyCharAndCode(val char: Char, val code: Int) {
|
||||
* keyPressDispatcher['+'] = { zoomIn() }
|
||||
* ```
|
||||
* Optionally use [setCheckpoint] and [revertToCheckPoint] to remember and restore one state.
|
||||
*
|
||||
* @param name Optional name of the container screen or popup for debugging
|
||||
*/
|
||||
class KeyPressDispatcher: HashMap<KeyCharAndCode, (() -> Unit)>() {
|
||||
class KeyPressDispatcher(val name: String? = null) : HashMap<KeyCharAndCode, (() -> Unit)>() {
|
||||
private var checkpoint: Set<KeyCharAndCode> = setOf() // set of keys marking a checkpoint
|
||||
private var listener: EventListener? = null // holds listener code, captures its params in install() function
|
||||
private var listenerInstalled = false // flag for lazy Stage.addListener()
|
||||
private var installStage: Stage? = null // Keep stage passed by install() for lazy addListener and uninstall
|
||||
var name: String? = null // optional debug label
|
||||
private set
|
||||
|
||||
// access by Char
|
||||
operator fun get(char: Char) = this[KeyCharAndCode(char)]
|
||||
@ -130,8 +130,7 @@ class KeyPressDispatcher: HashMap<KeyCharAndCode, (() -> Unit)>() {
|
||||
* @param stage The [Stage] to add the listener to
|
||||
* @param checkIgnoreKeys An optional lambda - when it returns true all keys are ignored
|
||||
*/
|
||||
fun install(stage: Stage, name: String? = null, checkIgnoreKeys: (() -> Boolean)? = null) {
|
||||
this.name = name
|
||||
fun install(stage: Stage, checkIgnoreKeys: (() -> Boolean)? = null) {
|
||||
if (installStage != null) uninstall()
|
||||
listener =
|
||||
object : InputListener() {
|
||||
@ -141,7 +140,7 @@ class KeyPressDispatcher: HashMap<KeyCharAndCode, (() -> Unit)>() {
|
||||
// see if we want to handle this key, and if not, let it propagate
|
||||
if (!contains(key) || (checkIgnoreKeys?.invoke() == true))
|
||||
return super.keyTyped(event, character)
|
||||
|
||||
|
||||
//try-catch mainly for debugging. Breakpoints in the vicinity can make the event fire twice in rapid succession, second time the context can be invalid
|
||||
try {
|
||||
this@KeyPressDispatcher[key]?.invoke()
|
||||
@ -169,11 +168,9 @@ class KeyPressDispatcher: HashMap<KeyCharAndCode, (() -> Unit)>() {
|
||||
private fun checkInstall(forceRemove: Boolean = false) {
|
||||
if (listener == null || installStage == null) return
|
||||
if (listenerInstalled && (isEmpty() || isPaused || forceRemove)) {
|
||||
println(toString() + ": Removing listener" + (if(forceRemove) " for uninstall" else ""))
|
||||
listenerInstalled = false
|
||||
installStage!!.removeListener(listener)
|
||||
} else if (!listenerInstalled && !(isEmpty() || isPaused)) {
|
||||
println(toString() + ": Adding listener")
|
||||
installStage!!.addListener(listener)
|
||||
listenerInstalled = true
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen
|
||||
* while the popup is active through the [hasOpenPopups][CameraStageBaseScreen.hasOpenPopups] mechanism.
|
||||
* @see [KeyPressDispatcher.install]
|
||||
*/
|
||||
val keyPressDispatcher = KeyPressDispatcher()
|
||||
val keyPressDispatcher = KeyPressDispatcher(this.javaClass.simpleName)
|
||||
|
||||
init {
|
||||
// Set actor name for debugging
|
||||
@ -44,20 +44,21 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen
|
||||
* closed. Use [force] = true if you want to open this popup above the other one anyway.
|
||||
*/
|
||||
fun open(force: Boolean = false) {
|
||||
if (force || !screen.hasOpenPopups()) {
|
||||
show()
|
||||
}
|
||||
|
||||
screen.stage.addActor(this)
|
||||
innerTable.pack()
|
||||
pack()
|
||||
center(screen.stage)
|
||||
if (force || !screen.hasOpenPopups()) {
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
/** Subroutine for [open] handles only visibility and [keyPressDispatcher] */
|
||||
private fun show() {
|
||||
this.isVisible = true
|
||||
keyPressDispatcher.install(screen.stage, name)
|
||||
val currentCount = screen.countOpenPopups()
|
||||
// the lambda is for stacked key dispatcher precedence:
|
||||
keyPressDispatcher.install(screen.stage) { screen.countOpenPopups() > currentCount }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,6 +161,13 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen
|
||||
*/
|
||||
fun CameraStageBaseScreen.hasOpenPopups(): Boolean = stage.actors.any { it is Popup && it.isVisible }
|
||||
|
||||
/**
|
||||
* Counts number of visible[Popup]s.
|
||||
*
|
||||
* Used for key dispatcher precedence.
|
||||
*/
|
||||
fun CameraStageBaseScreen.countOpenPopups() = stage.actors.count { it is Popup && it.isVisible }
|
||||
|
||||
/** Closes all [Popup]s. */
|
||||
fun CameraStageBaseScreen.closeAllPopups() = popups.forEach { it.close() }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user