mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 06:16:37 -04:00
Allow closing a Popup by clicking outside its area (#9306)
* Allow closing a Popup by clicking outside its area * Allow closing a Popup by clicking outside its area - upd1
This commit is contained in:
parent
817764ec38
commit
43b044740c
@ -621,6 +621,8 @@ open class TabbedPager(
|
|||||||
addOKButton {
|
addOKButton {
|
||||||
if (passEntry.text.hashCode() == secretHashCode) unlockAction() else lockAction()
|
if (passEntry.text.hashCode() == secretHashCode) unlockAction() else lockAction()
|
||||||
}
|
}
|
||||||
|
clickBehindToClose = true
|
||||||
|
onCloseCallback = lockAction
|
||||||
this.keyboardFocus = passEntry
|
this.keyboardFocus = passEntry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.unciv.ui.popups
|
|||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.math.Rectangle
|
import com.badlogic.gdx.math.Rectangle
|
||||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputEvent
|
||||||
import com.badlogic.gdx.scenes.scene2d.Stage
|
import com.badlogic.gdx.scenes.scene2d.Stage
|
||||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Button
|
import com.badlogic.gdx.scenes.scene2d.ui.Button
|
||||||
@ -12,6 +13,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextField
|
import com.badlogic.gdx.scenes.scene2d.ui.TextField
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.logic.event.EventBus
|
import com.unciv.logic.event.EventBus
|
||||||
@ -42,12 +44,23 @@ open class Popup(
|
|||||||
|
|
||||||
// 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)
|
// from the 'screen blocking' part of the popup (which covers the entire screen)
|
||||||
val innerTable = Table(BaseScreen.skin)
|
protected val innerTable = Table(BaseScreen.skin)
|
||||||
|
|
||||||
|
/** Callbacks that will be called whenever this Popup is shown */
|
||||||
val showListeners = mutableListOf<() -> Unit>()
|
val showListeners = mutableListOf<() -> Unit>()
|
||||||
|
/** Callbacks that will be called whenever this Popup is closed, no matter how (e.g. no distinction OK/Cancel) */
|
||||||
val closeListeners = mutableListOf<() -> Unit>()
|
val closeListeners = mutableListOf<() -> Unit>()
|
||||||
|
|
||||||
val events = EventBus.EventReceiver()
|
protected val events = EventBus.EventReceiver()
|
||||||
|
|
||||||
|
/** Enables/disables closing by clicking/trapping outside [innerTable].
|
||||||
|
*
|
||||||
|
* Automatically set when [addCloseButton] is called but may be changed back or enabled without such a button.
|
||||||
|
*/
|
||||||
|
protected var clickBehindToClose = false
|
||||||
|
|
||||||
|
/** Unlike [closeListeners] this is only fired on "click-behind" closing */
|
||||||
|
protected var onCloseCallback: (() -> Unit)? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Set actor name for debugging
|
// Set actor name for debugging
|
||||||
@ -55,7 +68,7 @@ open class Popup(
|
|||||||
|
|
||||||
background = BaseScreen.skinStrings.getUiBackground(
|
background = BaseScreen.skinStrings.getUiBackground(
|
||||||
"General/Popup/Background",
|
"General/Popup/Background",
|
||||||
tintColor = Color.GRAY.cpy().apply { a=.5f })
|
tintColor = Color.GRAY.cpy().apply { a = 0.5f })
|
||||||
innerTable.background = BaseScreen.skinStrings.getUiBackground(
|
innerTable.background = BaseScreen.skinStrings.getUiBackground(
|
||||||
"General/Popup/InnerTable",
|
"General/Popup/InnerTable",
|
||||||
tintColor = BaseScreen.skinStrings.skinConfig.baseColor.darken(0.5f)
|
tintColor = BaseScreen.skinStrings.skinConfig.baseColor.darken(0.5f)
|
||||||
@ -66,9 +79,11 @@ open class Popup(
|
|||||||
|
|
||||||
super.add(if (scrollable) AutoScrollPane(innerTable, BaseScreen.skin) else innerTable)
|
super.add(if (scrollable) AutoScrollPane(innerTable, BaseScreen.skin) else innerTable)
|
||||||
|
|
||||||
this.isVisible = false
|
isVisible = false
|
||||||
touchable = Touchable.enabled // don't allow clicking behind
|
touchable = Touchable.enabled
|
||||||
this.setFillParent(true)
|
// clicking behind gets special treatment
|
||||||
|
super.addListener(getBehindClickListener())
|
||||||
|
super.setFillParent(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,11 +129,28 @@ open class Popup(
|
|||||||
if (nextPopup != null) (nextPopup as Popup).show()
|
if (nextPopup != null) (nextPopup as Popup).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Allow closing a popup by clicking 'outside', Android-style, but only if a Close button exists */
|
||||||
|
private fun getBehindClickListener() = object : ClickListener() {
|
||||||
|
override fun clicked(event: InputEvent?, x: Float, y: Float) {
|
||||||
|
if (!clickBehindToClose) return
|
||||||
|
// Since Gdx doesn't limit events to the actually `hit` actors...
|
||||||
|
if (event?.target != this@Popup) return
|
||||||
|
close()
|
||||||
|
onCloseCallback?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* All additions to the popup are to the inner table - we shouldn't care that there's an inner table at all */
|
/* All additions to the popup are to the inner table - we shouldn't care that there's an inner table at all */
|
||||||
final override fun <T : Actor?> add(actor: T): Cell<T> = innerTable.add(actor)
|
final override fun <T : Actor?> add(actor: T): Cell<T> = innerTable.add(actor)
|
||||||
override fun row(): Cell<Actor> = innerTable.row()
|
override fun row(): Cell<Actor> = innerTable.row()
|
||||||
override fun defaults(): Cell<Actor> = innerTable.defaults()
|
override fun defaults(): Cell<Actor> = innerTable.defaults()
|
||||||
fun addSeparator() = innerTable.addSeparator()
|
fun addSeparator() = innerTable.addSeparator()
|
||||||
|
override fun clear() {
|
||||||
|
innerTable.clear()
|
||||||
|
clickBehindToClose = false
|
||||||
|
onCloseCallback = null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a [caption][text] label: A label with word wrap enabled over half the stage width.
|
* Adds a [caption][text] label: A label with word wrap enabled over half the stage width.
|
||||||
@ -126,7 +158,7 @@ open class Popup(
|
|||||||
* @param text The caption text.
|
* @param text The caption text.
|
||||||
* @param size The font size for the label.
|
* @param size The font size for the label.
|
||||||
*/
|
*/
|
||||||
fun addGoodSizedLabel(text: String, size:Int=Constants.defaultFontSize): Cell<Label> {
|
fun addGoodSizedLabel(text: String, size: Int = Constants.defaultFontSize): Cell<Label> {
|
||||||
val label = text.toLabel(fontSize = size)
|
val label = text.toLabel(fontSize = size)
|
||||||
label.wrap = true
|
label.wrap = true
|
||||||
label.setAlignment(Align.center)
|
label.setAlignment(Align.center)
|
||||||
@ -140,7 +172,12 @@ open class Popup(
|
|||||||
* @param action A lambda to be executed when the button is clicked.
|
* @param action A lambda to be executed when the button is clicked.
|
||||||
* @return The new [Cell]
|
* @return The new [Cell]
|
||||||
*/
|
*/
|
||||||
fun addButton(text: String, key: KeyCharAndCode? = null, style: TextButtonStyle? = null, action: () -> Unit): Cell<TextButton> {
|
fun addButton(
|
||||||
|
text: String,
|
||||||
|
key: KeyCharAndCode? = null,
|
||||||
|
style: TextButtonStyle? = null,
|
||||||
|
action: () -> Unit
|
||||||
|
): Cell<TextButton> {
|
||||||
val button = text.toTextButton(style)
|
val button = text.toTextButton(style)
|
||||||
button.onActivation { action() }
|
button.onActivation { action() }
|
||||||
button.keyShortcuts.add(key)
|
button.keyShortcuts.add(key)
|
||||||
@ -148,6 +185,7 @@ open class Popup(
|
|||||||
}
|
}
|
||||||
fun addButton(text: String, key: Char, style: TextButtonStyle? = null, action: () -> Unit)
|
fun addButton(text: String, key: Char, style: TextButtonStyle? = null, action: () -> Unit)
|
||||||
= addButton(text, KeyCharAndCode(key), style, action).apply { row() }
|
= addButton(text, KeyCharAndCode(key), style, action).apply { row() }
|
||||||
|
@Suppress("unused") // Keep the offer to pass an Input.keys value
|
||||||
fun addButton(text: String, key: Int, style: TextButtonStyle? = null, action: () -> Unit)
|
fun addButton(text: String, key: Int, style: TextButtonStyle? = null, action: () -> Unit)
|
||||||
= addButton(text, KeyCharAndCode(key), style, action).apply { row() }
|
= addButton(text, KeyCharAndCode(key), style, action).apply { row() }
|
||||||
|
|
||||||
@ -164,7 +202,12 @@ open class Popup(
|
|||||||
style: TextButtonStyle? = null,
|
style: TextButtonStyle? = null,
|
||||||
action: (()->Unit)? = null
|
action: (()->Unit)? = null
|
||||||
): Cell<TextButton> {
|
): Cell<TextButton> {
|
||||||
val cell = addButton(text, additionalKey, style) { close(); if(action!=null) action() }
|
clickBehindToClose = true
|
||||||
|
onCloseCallback = action
|
||||||
|
val cell = addButton(text, additionalKey, style) {
|
||||||
|
close()
|
||||||
|
action?.invoke()
|
||||||
|
}
|
||||||
cell.actor.keyShortcuts.add(KeyCharAndCode.BACK)
|
cell.actor.keyShortcuts.add(KeyCharAndCode.BACK)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
@ -224,7 +267,7 @@ open class Popup(
|
|||||||
* and a close button if requested
|
* and a close button if requested
|
||||||
*/
|
*/
|
||||||
fun reuseWith(newText: String, withCloseButton: Boolean = false) {
|
fun reuseWith(newText: String, withCloseButton: Boolean = false) {
|
||||||
innerTable.clear()
|
clear()
|
||||||
addGoodSizedLabel(newText)
|
addGoodSizedLabel(newText)
|
||||||
if (withCloseButton) {
|
if (withCloseButton) {
|
||||||
row()
|
row()
|
||||||
@ -264,12 +307,5 @@ val BaseScreen.activePopup: Popup?
|
|||||||
fun BaseScreen.hasOpenPopups(): Boolean = stage.hasOpenPopups()
|
fun BaseScreen.hasOpenPopups(): Boolean = stage.hasOpenPopups()
|
||||||
private fun Stage.hasOpenPopups(): Boolean = actors.any { it is Popup && it.isVisible }
|
private fun Stage.hasOpenPopups(): Boolean = actors.any { it is Popup && it.isVisible }
|
||||||
|
|
||||||
/**
|
|
||||||
* Counts number of visible[Popup]s.
|
|
||||||
*
|
|
||||||
* Used for key dispatcher precedence.
|
|
||||||
*/
|
|
||||||
private fun Stage.countOpenPopups() = actors.count { it is Popup && it.isVisible }
|
|
||||||
|
|
||||||
/** Closes all [Popup]s. */
|
/** Closes all [Popup]s. */
|
||||||
fun BaseScreen.closeAllPopups() = popups.forEach { it.close() }
|
fun BaseScreen.closeAllPopups() = popups.forEach { it.close() }
|
||||||
|
@ -547,6 +547,7 @@ private class RandomNationPickerPopup(
|
|||||||
closeButton.keyShortcuts.add(KeyCharAndCode.BACK)
|
closeButton.keyShortcuts.add(KeyCharAndCode.BACK)
|
||||||
closeButton.setPosition(buttonsOffsetFromEdge, buttonsOffsetFromEdge, Align.bottomLeft)
|
closeButton.setPosition(buttonsOffsetFromEdge, buttonsOffsetFromEdge, Align.bottomLeft)
|
||||||
innerTable.addActor(closeButton)
|
innerTable.addActor(closeButton)
|
||||||
|
clickBehindToClose = true
|
||||||
|
|
||||||
val okButton = "OtherIcons/Checkmark".toImageButton(Color.LIME)
|
val okButton = "OtherIcons/Checkmark".toImageButton(Color.LIME)
|
||||||
okButton.onClick { returnSelected() }
|
okButton.onClick { returnSelected() }
|
||||||
|
@ -460,6 +460,7 @@ private class NationPickerPopup(
|
|||||||
closeButton.keyShortcuts.add(KeyCharAndCode.BACK)
|
closeButton.keyShortcuts.add(KeyCharAndCode.BACK)
|
||||||
closeButton.setPosition(buttonsOffsetFromEdge, buttonsOffsetFromEdge, Align.bottomLeft)
|
closeButton.setPosition(buttonsOffsetFromEdge, buttonsOffsetFromEdge, Align.bottomLeft)
|
||||||
innerTable.addActor(closeButton)
|
innerTable.addActor(closeButton)
|
||||||
|
clickBehindToClose = true
|
||||||
|
|
||||||
val okButton = "OtherIcons/Checkmark".toImageButton(Color.LIME)
|
val okButton = "OtherIcons/Checkmark".toImageButton(Color.LIME)
|
||||||
okButton.onClick { returnSelected() }
|
okButton.onClick { returnSelected() }
|
||||||
|
@ -302,7 +302,7 @@ class WorldScreen(
|
|||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
launchOnGLThread {
|
launchOnGLThread {
|
||||||
val (message) = LoadGameScreen.getLoadExceptionMessage(ex, "Couldn't download the latest game state!")
|
val (message) = LoadGameScreen.getLoadExceptionMessage(ex, "Couldn't download the latest game state!")
|
||||||
loadingGamePopup.innerTable.clear()
|
loadingGamePopup.clear()
|
||||||
loadingGamePopup.addGoodSizedLabel(message).colspan(2).row()
|
loadingGamePopup.addGoodSizedLabel(message).colspan(2).row()
|
||||||
loadingGamePopup.addButton("Retry") {
|
loadingGamePopup.addButton("Retry") {
|
||||||
launchOnThreadPool("Load latest multiplayer state after error") {
|
launchOnThreadPool("Load latest multiplayer state after error") {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user