mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-11 11:28:03 +07:00
Popup gets a KeyPressDispatcher (#3973)
* Popup gets a KeyPressDispatcher * Popup gets a KeyPressDispatcher - patch1
This commit is contained in:
parent
5f523cb548
commit
cf2be25c73
@ -52,6 +52,7 @@ Remove from queue =
|
||||
Show stats drilldown =
|
||||
Show construction queue =
|
||||
Save =
|
||||
Cancel =
|
||||
|
||||
Diplomacy =
|
||||
War =
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -85,10 +85,20 @@ class KeyPressDispatcher: HashMap<KeyCharAndCode, (() -> 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
|
||||
}
|
||||
|
@ -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 <T : Actor?> 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 <T : Actor?> add(actor: T): Cell<T> = innerTable.add(actor)
|
||||
override fun row(): Cell<Actor> = 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<Label> {
|
||||
val label = text.toLabel(fontSize = size)
|
||||
label.wrap = true
|
||||
@ -63,25 +88,88 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen
|
||||
return add(label).width(screen.stage.width / 2)
|
||||
}
|
||||
|
||||
fun addButton(text: String, action: () -> Unit): Cell<TextButton> {
|
||||
/**
|
||||
* Adds an inline [TextButton].
|
||||
* @param text The button's caption.
|
||||
* @param key Associate a key with this button's action.
|
||||
* @param action A lambda to be executed when the button is clicked.
|
||||
* @return The new [Cell]
|
||||
*/
|
||||
fun addButtonInRow(text: String, key: KeyCharAndCode? = null, action: () -> Unit): Cell<TextButton> {
|
||||
val button = text.toTextButton().apply { color = ImageGetter.getBlue() }
|
||||
button.onClick(action)
|
||||
return add(button).apply { row() }
|
||||
if (key != null) {
|
||||
keyPressDispatcher[key] = action
|
||||
}
|
||||
return add(button)
|
||||
}
|
||||
fun addButtonInRow(text: String, key: Char, action: () -> Unit)
|
||||
= addButtonInRow(text, KeyCharAndCode(key), action)
|
||||
fun addButtonInRow(text: String, key: Int, action: () -> Unit)
|
||||
= addButtonInRow(text, KeyCharAndCode(key), action)
|
||||
|
||||
/**
|
||||
* Adds a [TextButton] and ends the current row.
|
||||
* @param text The button's caption.
|
||||
* @param key Associate a key with this button's action.
|
||||
* @param action A lambda to be executed when the button is clicked.
|
||||
* @return The new [Cell]
|
||||
*/
|
||||
fun addButton(text: String, key: KeyCharAndCode? = null, action: () -> Unit)
|
||||
= addButtonInRow(text, key, action).apply { row() }
|
||||
/** @link [addButton] */
|
||||
fun addButton(text: String, key: Char, action: () -> Unit)
|
||||
= addButtonInRow(text, key, action).apply { row() }
|
||||
fun addButton(text: String, key: Int, action: () -> Unit)
|
||||
= addButtonInRow(text, key, action).apply { row() }
|
||||
|
||||
/**
|
||||
* Adds a [TextButton] that closes the popup.
|
||||
* @param text The button's caption, defaults to "Close".
|
||||
* @param additionalKey An additional key that should close the popup, Back and ESC are assigned by default.
|
||||
* @param action A lambda to be executed after closing the popup when the button is clicked.
|
||||
* @return The new [Cell]
|
||||
*/
|
||||
fun addCloseButton(
|
||||
text: String = Constants.close,
|
||||
additionalKey: KeyCharAndCode? = null,
|
||||
action: (()->Unit)? = null
|
||||
): Cell<TextButton> {
|
||||
val closeAction = { close(); if(action!=null) action() }
|
||||
keyPressDispatcher[Input.Keys.BACK] = closeAction
|
||||
return addButton(text, additionalKey, closeAction)
|
||||
}
|
||||
|
||||
|
||||
fun addCloseButton(action: (()->Unit)? = null): Cell<TextButton> {
|
||||
return if (action==null)
|
||||
addButton(Constants.close) { close() }
|
||||
else
|
||||
addButton(Constants.close) { close(); action() }
|
||||
}
|
||||
/**
|
||||
* Sets or retrieves the [Actor] that currently has keyboard focus.
|
||||
*
|
||||
* Setting focus on a [TextField] will select all contained text unless a
|
||||
* [FocusListener][com.badlogic.gdx.scenes.scene2d.utils.FocusListener] cancels the event.
|
||||
*/
|
||||
var keyboardFocus: Actor?
|
||||
get() = screen.stage.keyboardFocus
|
||||
set(value) {
|
||||
if (screen.stage.setKeyboardFocus(value))
|
||||
(value as? TextField)?.selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are visible [Popup]s.
|
||||
* @return `true` if any were found.
|
||||
*/
|
||||
fun CameraStageBaseScreen.hasOpenPopups(): Boolean = stage.actors.any { it is Popup && it.isVisible }
|
||||
|
||||
/** Closes all [Popup]s. */
|
||||
fun CameraStageBaseScreen.closeAllPopups() = popups.forEach { it.close() }
|
||||
|
||||
/**
|
||||
* Closes the topmost visible [Popup].
|
||||
* @return The [name][Popup.name] of the closed [Popup] if any popup was closed and if it had a name.
|
||||
*/
|
||||
fun CameraStageBaseScreen.closeOneVisiblePopup() = popups.lastOrNull { it.isVisible }?.apply { close() }?.name
|
||||
|
||||
/** @return A [List] of currently active or pending [Popup] screens. */
|
||||
val CameraStageBaseScreen.popups: List<Popup>
|
||||
get() = stage.actors.filterIsInstance<Popup>()
|
||||
|
||||
|
@ -1,12 +1,28 @@
|
||||
package com.unciv.ui.utils
|
||||
|
||||
import com.badlogic.gdx.Input
|
||||
import com.unciv.UncivGame
|
||||
|
||||
class YesNoPopup(question:String, action:()->Unit,
|
||||
screen: CameraStageBaseScreen = UncivGame.Current.worldScreen, restoreDefault:()->Unit = {}) : Popup(screen){
|
||||
init{
|
||||
/** Variant of [Popup] pre-populated with one label, plus yes and no buttons
|
||||
* @param question The text for the label
|
||||
* @param action A lambda to execute when "Yes" is chosen
|
||||
* @param screen The parent screen - see [Popup.screen]. Optional, defaults to the current [WorldScreen][com.unciv.ui.worldscreen.WorldScreen]
|
||||
* @param restoreDefault A lambda to execute when "No" is chosen
|
||||
*/
|
||||
class YesNoPopup (
|
||||
question:String,
|
||||
action:()->Unit,
|
||||
screen: CameraStageBaseScreen = UncivGame.Current.worldScreen,
|
||||
restoreDefault:()->Unit = {}
|
||||
) : Popup(screen) {
|
||||
private val yes = { close(); action() }
|
||||
private val no = { close(); restoreDefault() }
|
||||
|
||||
init {
|
||||
add(question.toLabel()).colspan(2).row()
|
||||
add("No".toTextButton().onClick { close(); restoreDefault() })
|
||||
add("Yes".toTextButton().onClick { close(); action() })
|
||||
addButtonInRow("Yes", 'y', yes)
|
||||
addButtonInRow("No", 'n', no)
|
||||
keyPressDispatcher['\r'] = yes
|
||||
keyPressDispatcher[Input.Keys.BACK] = no
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user