From cf2be25c73610efee44274b4b61e3331065b6dd8 Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Thu, 27 May 2021 20:49:40 +0200 Subject: [PATCH] Popup gets a KeyPressDispatcher (#3973) * Popup gets a KeyPressDispatcher * Popup gets a KeyPressDispatcher - patch1 --- .../jsons/translations/template.properties | 1 + .../cityscreen/CityScreenCityPickerTable.kt | 15 +- .../com/unciv/ui/utils/KeyPressDispatcher.kt | 10 ++ core/src/com/unciv/ui/utils/Popup.kt | 134 +++++++++++++++--- core/src/com/unciv/ui/utils/YesNoPopup.kt | 26 +++- 5 files changed, 152 insertions(+), 34 deletions(-) diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 8ee74c2c57..55f83bbd61 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -52,6 +52,7 @@ Remove from queue = Show stats drilldown = Show construction queue = Save = +Cancel = Diplomacy = War = diff --git a/core/src/com/unciv/ui/cityscreen/CityScreenCityPickerTable.kt b/core/src/com/unciv/ui/cityscreen/CityScreenCityPickerTable.kt index 01abd8e809..d1d917ccf3 100644 --- a/core/src/com/unciv/ui/cityscreen/CityScreenCityPickerTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityScreenCityPickerTable.kt @@ -7,6 +7,8 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextField import com.badlogic.gdx.utils.Align import com.unciv.ui.utils.* +/** Widget for the City Screen - + * the panel at bottom center showing the city name and offering arrows to cycle through the cities. */ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() { fun update() { @@ -16,7 +18,7 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() { clear() if (civInfo.cities.size > 1) { - val prevCityButton = Table() // so we gt a wider clickable area than just the image itself + val prevCityButton = Table() // so we get a wider clickable area than just the image itself val image = ImageGetter.getImage("OtherIcons/BackArrow") image.color = civInfo.nation.getInnerColor() prevCityButton.add(image).size(25f).pad(10f) @@ -26,6 +28,7 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() { } else add() val cityNameTable = Table() + if (city.isBeingRazed) { val fireImage = ImageGetter.getImage("OtherIcons/Fire") cityNameTable.add(fireImage).size(20f).padRight(5f) @@ -41,7 +44,6 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() { cityNameTable.add(starImage).size(20f).padRight(5f) } - if (city.isInResistance()) { val resistanceImage = ImageGetter.getImage("StatIcons/Resistance") cityNameTable.add(resistanceImage).size(20f).padRight(5f) @@ -53,11 +55,13 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() { val textArea = TextField(city.name, CameraStageBaseScreen.skin) textArea.alignment = Align.center editCityNamePopup.add(textArea).colspan(2).row() - editCityNamePopup.addButton("Save") { + //editCityNamePopup.name = "CityNamePopup" // debug help + editCityNamePopup.addButtonInRow("Save", '\r') { city.name = textArea.text cityScreen.game.setScreen(CityScreen(city)) } - editCityNamePopup.addCloseButton() + editCityNamePopup.addCloseButton("Cancel") + editCityNamePopup.keyboardFocus = textArea editCityNamePopup.open() } @@ -65,9 +69,7 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() { add(cityNameTable).width(stage.width / 4) - if (civInfo.cities.size > 1) { - val nextCityButton = Table() // so we gt a wider clickable area than just the image itself val image = ImageGetter.getImage("OtherIcons/BackArrow") image.setSize(25f, 25f) @@ -79,6 +81,7 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() { nextCityButton.onClick { cityScreen.page(1) } add(nextCityButton).pad(10f) } else add() + pack() } diff --git a/core/src/com/unciv/ui/utils/KeyPressDispatcher.kt b/core/src/com/unciv/ui/utils/KeyPressDispatcher.kt index 62960bd79e..d29b4f3ca3 100644 --- a/core/src/com/unciv/ui/utils/KeyPressDispatcher.kt +++ b/core/src/com/unciv/ui/utils/KeyPressDispatcher.kt @@ -85,10 +85,20 @@ class KeyPressDispatcher: HashMap Unit)>() { // access by KeyCharAndCode operator fun set(key: KeyCharAndCode, action: () -> Unit) { super.put(key, action) + // On Android the Enter key will fire with Ascii code `Linefeed`, on desktop as `Carriage Return` + if (key == KeyCharAndCode('\r')) + super.put(KeyCharAndCode('\n'), action) + // Likewise always match Back to ESC + if (key == KeyCharAndCode(Input.Keys.BACK)) + super.put(KeyCharAndCode('\u001B'), action) checkInstall() } override fun remove(key: KeyCharAndCode): (() -> Unit)? { val result = super.remove(key) + if (key == KeyCharAndCode('\r')) + super.remove(KeyCharAndCode('\n')) + if (key == KeyCharAndCode(Input.Keys.BACK)) + super.remove(KeyCharAndCode('\u001B')) checkInstall() return result } diff --git a/core/src/com/unciv/ui/utils/Popup.kt b/core/src/com/unciv/ui/utils/Popup.kt index 90c90c67d3..89151b7c42 100644 --- a/core/src/com/unciv/ui/utils/Popup.kt +++ b/core/src/com/unciv/ui/utils/Popup.kt @@ -1,23 +1,32 @@ package com.unciv.ui.utils +import com.badlogic.gdx.Input import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Touchable -import com.badlogic.gdx.scenes.scene2d.ui.Cell -import com.badlogic.gdx.scenes.scene2d.ui.Label -import com.badlogic.gdx.scenes.scene2d.ui.Table -import com.badlogic.gdx.scenes.scene2d.ui.TextButton +import com.badlogic.gdx.scenes.scene2d.ui.* import com.badlogic.gdx.utils.Align import com.unciv.Constants /** * Base class for all Popups, i.e. Tables that get rendered in the middle of a screen and on top of everything else */ +@Suppress("MemberVisibilityCanBePrivate") open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen.skin) { - val innerTable = Table(CameraStageBaseScreen.skin) // This exists to differentiate the actual popup (the inner table) + // This exists to differentiate the actual popup (the inner table) // from the 'screen blocking' part of the popup (which covers the entire screen) + val innerTable = Table(CameraStageBaseScreen.skin) + + /** The [KeyPressDispatcher] for the popup - Key handlers from the parent screen are inactive + * while the popup is active through the [hasOpenPopups][CameraStageBaseScreen.hasOpenPopups] mechanism. + * @see [KeyPressDispatcher.install] + */ + val keyPressDispatcher = KeyPressDispatcher() init { + // Set actor name for debugging + name = javaClass.simpleName + background = ImageGetter.getBackground(Color.GRAY.cpy().apply { a=.5f }) innerTable.background = ImageGetter.getBackground(ImageGetter.getBlue().lerp(Color.BLACK, 0.5f)) @@ -27,7 +36,7 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen this.isVisible = false touchable = Touchable.enabled // don't allow clicking behind - setFillParent(true) + this.setFillParent(true) } /** @@ -36,7 +45,7 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen */ fun open(force: Boolean = false) { if (force || !screen.hasOpenPopups()) { - this.isVisible = true + show() } screen.stage.addActor(this) @@ -45,17 +54,33 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen center(screen.stage) } - open fun close() { - remove() - val nextPopup = screen.stage.actors.firstOrNull { it is Popup } - if (nextPopup != null) nextPopup.isVisible = true + /** Subroutine for [open] handles only visibility and [keyPressDispatcher] */ + private fun show() { + this.isVisible = true + keyPressDispatcher.install(screen.stage, name) } - /** All additions to the popup are to the inner table - we shouldn't care that there's an inner table at all */ - override fun add(actor: T) = innerTable.add(actor) - override fun row() = innerTable.row() + /** + * Close this popup and - if any other popups are pending - display the next one. + */ + open fun close() { + keyPressDispatcher.uninstall() + remove() + val nextPopup = screen.stage.actors.firstOrNull { it is Popup } + if (nextPopup != null) (nextPopup as Popup).show() + } + + /* All additions to the popup are to the inner table - we shouldn't care that there's an inner table at all */ + override fun add(actor: T): Cell = innerTable.add(actor) + override fun row(): Cell = innerTable.row() fun addSeparator() = innerTable.addSeparator() + /** + * Adds a [caption][text] label: A label with word wrap enabled over half the stage width. + * Will be larger than normal text if the [size] parameter is set to >18f. + * @param text The caption text. + * @param size The font size for the label. + */ fun addGoodSizedLabel(text: String, size:Int=18): Cell