Resolved #3470 - popups now make the rest of the screen unclickable to avoid exploits

Resolved #3468 the same way
This commit is contained in:
Yair Morgenstern 2020-12-28 17:37:29 +02:00
parent e07fd6d0bc
commit ca04093321
3 changed files with 81 additions and 90 deletions

View File

@ -1,6 +1,8 @@
package com.unciv.ui.utils package com.unciv.ui.utils
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Colors
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Cell import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Label
@ -13,13 +15,18 @@ 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 * Base class for all Popups, i.e. Tables that get rendered in the middle of a screen and on top of everything else
*/ */
open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen.skin) { open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen.skin) {
val innerTable = Table(CameraStageBaseScreen.skin)
init { init {
background = ImageGetter.getBackground(ImageGetter.getBlue().lerp(Color.BLACK, 0.5f)) background = ImageGetter.getBackground(Color.GRAY.cpy().apply { a=.5f })
innerTable.background = ImageGetter.getBackground(ImageGetter.getBlue().lerp(Color.BLACK, 0.5f))
this.pad(20f) innerTable.pad(20f)
this.defaults().pad(5f) innerTable.defaults().pad(5f)
super.add(innerTable)
this.isVisible = false this.isVisible = false
touchable = Touchable.enabled // don't allow clicking behind
setFillParent(true)
} }
/** /**
@ -32,6 +39,7 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen
} }
screen.stage.addActor(this) screen.stage.addActor(this)
innerTable.pack()
pack() pack()
center(screen.stage) center(screen.stage)
} }
@ -42,6 +50,11 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen
if (nextPopup != null) nextPopup.isVisible = true if (nextPopup != null) nextPopup.isVisible = true
} }
/** 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()
fun addSeparator() = innerTable.addSeparator()
fun addGoodSizedLabel(text: String, size:Int=18): Cell<Label> { fun addGoodSizedLabel(text: String, size:Int=18): Cell<Label> {
val label = text.toLabel(fontSize = size) val label = text.toLabel(fontSize = size)
label.setWrap(true) label.setWrap(true)
@ -55,13 +68,6 @@ open class Popup(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen
return add(button).apply { row() } return add(button).apply { row() }
} }
fun addSquareButton(text: String, action: () -> Unit): Cell<Table> {
val button = Table()
button.add(text.toLabel())
button.onClick(action)
button.touchable = Touchable.enabled
return add(button).apply { row() }
}
fun addCloseButton(action: (()->Unit)? = null): Cell<TextButton> { fun addCloseButton(action: (()->Unit)? = null): Cell<TextButton> {
return if (action==null) return if (action==null)

View File

@ -28,14 +28,14 @@ class Language(val language:String, val percentComplete:Int){
class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScreen) { class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScreen) {
var selectedLanguage: String = "English" var selectedLanguage: String = "English"
private val settings = previousScreen.game.settings private val settings = previousScreen.game.settings
private val innerTable = Table(CameraStageBaseScreen.skin) val innerTable2 = Table(CameraStageBaseScreen.skin)
init { init {
settings.addCompletedTutorialTask("Open the options table") settings.addCompletedTutorialTask("Open the options table")
rebuildInnerTable() rebuildInnerTable()
val scrollPane = ScrollPane(innerTable, skin) val scrollPane = ScrollPane(innerTable2, skin)
scrollPane.setOverscroll(false, false) scrollPane.setOverscroll(false, false)
scrollPane.fadeScrollBars = false scrollPane.fadeScrollBars = false
scrollPane.setScrollingDisabled(true, false) scrollPane.setScrollingDisabled(true, false)
@ -51,18 +51,18 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
} }
private fun addHeader (text: String) { private fun addHeader (text: String) {
innerTable.add(text.toLabel(fontSize = 24)).colspan(2).padTop(if (innerTable.cells.isEmpty) 0f else 20f).row() innerTable2.add(text.toLabel(fontSize = 24)).colspan(2).padTop(if (innerTable2.cells.isEmpty) 0f else 20f).row()
} }
private fun addYesNoRow (text: String, initialValue: Boolean, updateWorld: Boolean = false, action: ((Boolean) -> Unit)) { private fun addYesNoRow (text: String, initialValue: Boolean, updateWorld: Boolean = false, action: ((Boolean) -> Unit)) {
innerTable.add(text.toLabel()) innerTable2.add(text.toLabel())
val button = YesNoButton(initialValue, skin) { val button = YesNoButton(initialValue, CameraStageBaseScreen.skin) {
action(it) action(it)
settings.save() settings.save()
if (updateWorld && previousScreen is WorldScreen) if (updateWorld && previousScreen is WorldScreen)
previousScreen.shouldUpdate = true previousScreen.shouldUpdate = true
} }
innerTable.add(button).row() innerTable2.add(button).row()
} }
private fun reloadWorldAndOptions() { private fun reloadWorldAndOptions() {
@ -78,7 +78,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
private fun rebuildInnerTable() { private fun rebuildInnerTable() {
settings.save() settings.save()
innerTable.clear() innerTable2.clear()
addHeader("Display options") addHeader("Display options")
@ -102,7 +102,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
} }
val continuousRenderingDescription = "When disabled, saves battery life but certain animations will be suspended" val continuousRenderingDescription = "When disabled, saves battery life but certain animations will be suspended"
innerTable.add(continuousRenderingDescription.toLabel(fontSize = 14)).colspan(2).padTop(20f).row() innerTable2.add(continuousRenderingDescription.toLabel(fontSize = 14)).colspan(2).padTop(20f).row()
addHeader("Gameplay options") addHeader("Gameplay options")
@ -136,8 +136,8 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
addModPopup() addModPopup()
addSetUserId() addSetUserId()
innerTable.add("Version".toLabel()).pad(10f) innerTable2.add("Version".toLabel()).pad(10f)
innerTable.add(previousScreen.game.version.toLabel()).pad(10f).row() innerTable2.add(previousScreen.game.version.toLabel()).pad(10f).row()
} }
private fun addSetUserId() { private fun addSetUserId() {
@ -159,8 +159,8 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
idSetLabel.setFontColor(Color.RED).setText("Invalid ID!".tr()) idSetLabel.setFontColor(Color.RED).setText("Invalid ID!".tr())
} }
} }
innerTable.add(takeUserIdFromClipboardButton).pad(5f).colspan(2).row() innerTable2.add(takeUserIdFromClipboardButton).pad(5f).colspan(2).row()
innerTable.add(idSetLabel).colspan(2).row() innerTable2.add(idSetLabel).colspan(2).row()
} }
private fun addNotificationOptions() { private fun addNotificationOptions() {
@ -190,7 +190,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
generateTranslationsButton.setText("Translation files are generated successfully.".tr()) generateTranslationsButton.setText("Translation files are generated successfully.".tr())
generateTranslationsButton.disable() generateTranslationsButton.disable()
} }
innerTable.add(generateTranslationsButton).colspan(2).row() innerTable2.add(generateTranslationsButton).colspan(2).row()
} }
} }
@ -209,11 +209,11 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
popup.addCloseButton() popup.addCloseButton()
popup.open(true) popup.open(true)
} }
innerTable.add(generateTranslationsButton).colspan(2).row() innerTable2.add(generateTranslationsButton).colspan(2).row()
} }
private fun addSoundEffectsVolumeSlider() { private fun addSoundEffectsVolumeSlider() {
innerTable.add("Sound effects volume".tr()) innerTable2.add("Sound effects volume".tr())
val soundEffectsVolumeSlider = Slider(0f, 1.0f, 0.1f, false, skin) val soundEffectsVolumeSlider = Slider(0f, 1.0f, 0.1f, false, skin)
soundEffectsVolumeSlider.value = settings.soundEffectsVolume soundEffectsVolumeSlider.value = settings.soundEffectsVolume
@ -222,13 +222,13 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
settings.save() settings.save()
Sounds.play(UncivSound.Click) Sounds.play(UncivSound.Click)
} }
innerTable.add(soundEffectsVolumeSlider).pad(10f).row() innerTable2.add(soundEffectsVolumeSlider).pad(10f).row()
} }
private fun addMusicVolumeSlider() { private fun addMusicVolumeSlider() {
val musicLocation = Gdx.files.local(previousScreen.game.musicLocation) val musicLocation = Gdx.files.local(previousScreen.game.musicLocation)
if (musicLocation.exists()) { if (musicLocation.exists()) {
innerTable.add("Music volume".tr()) innerTable2.add("Music volume".tr())
val musicVolumeSlider = Slider(0f, 1.0f, 0.1f, false, skin) val musicVolumeSlider = Slider(0f, 1.0f, 0.1f, false, skin)
musicVolumeSlider.value = settings.musicVolume musicVolumeSlider.value = settings.musicVolume
@ -242,12 +242,12 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
music?.volume = 0.4f * musicVolumeSlider.value music?.volume = 0.4f * musicVolumeSlider.value
} }
innerTable.add(musicVolumeSlider).pad(10f).row() innerTable2.add(musicVolumeSlider).pad(10f).row()
} else { } else {
val downloadMusicButton = "Download music".toTextButton() val downloadMusicButton = "Download music".toTextButton()
innerTable.add(downloadMusicButton).colspan(2).row() innerTable2.add(downloadMusicButton).colspan(2).row()
val errorTable = Table() val errorTable = Table()
innerTable.add(errorTable).colspan(2).row() innerTable2.add(errorTable).colspan(2).row()
downloadMusicButton.onClick { downloadMusicButton.onClick {
downloadMusicButton.disable() downloadMusicButton.disable()
@ -275,14 +275,14 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
} }
private fun addResolutionSelectBox() { private fun addResolutionSelectBox() {
innerTable.add("Resolution".toLabel()) innerTable2.add("Resolution".toLabel())
val resolutionSelectBox = SelectBox<String>(skin) val resolutionSelectBox = SelectBox<String>(skin)
val resolutionArray = Array<String>() val resolutionArray = Array<String>()
resolutionArray.addAll("750x500", "900x600", "1050x700", "1200x800", "1500x1000") resolutionArray.addAll("750x500", "900x600", "1050x700", "1200x800", "1500x1000")
resolutionSelectBox.items = resolutionArray resolutionSelectBox.items = resolutionArray
resolutionSelectBox.selected = settings.resolution resolutionSelectBox.selected = settings.resolution
innerTable.add(resolutionSelectBox).minWidth(240f).pad(10f).row() innerTable2.add(resolutionSelectBox).minWidth(240f).pad(10f).row()
resolutionSelectBox.onChange { resolutionSelectBox.onChange {
settings.resolution = resolutionSelectBox.selected settings.resolution = resolutionSelectBox.selected
@ -291,7 +291,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
} }
private fun addTileSetSelectBox() { private fun addTileSetSelectBox() {
innerTable.add("Tileset".toLabel()) innerTable2.add("Tileset".toLabel())
val tileSetSelectBox = SelectBox<String>(skin) val tileSetSelectBox = SelectBox<String>(skin)
val tileSetArray = Array<String>() val tileSetArray = Array<String>()
@ -300,7 +300,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
for (tileset in tileSets) tileSetArray.add(tileset) for (tileset in tileSets) tileSetArray.add(tileset)
tileSetSelectBox.items = tileSetArray tileSetSelectBox.items = tileSetArray
tileSetSelectBox.selected = settings.tileSet tileSetSelectBox.selected = settings.tileSet
innerTable.add(tileSetSelectBox).minWidth(240f).pad(10f).row() innerTable2.add(tileSetSelectBox).minWidth(240f).pad(10f).row()
tileSetSelectBox.onChange { tileSetSelectBox.onChange {
settings.tileSet = tileSetSelectBox.selected settings.tileSet = tileSetSelectBox.selected
@ -309,7 +309,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
} }
private fun addAutosaveTurnsSelectBox() { private fun addAutosaveTurnsSelectBox() {
innerTable.add("Turns between autosaves".toLabel()) innerTable2.add("Turns between autosaves".toLabel())
val autosaveTurnsSelectBox = SelectBox<Int>(skin) val autosaveTurnsSelectBox = SelectBox<Int>(skin)
val autosaveTurnsArray = Array<Int>() val autosaveTurnsArray = Array<Int>()
@ -317,7 +317,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
autosaveTurnsSelectBox.items = autosaveTurnsArray autosaveTurnsSelectBox.items = autosaveTurnsArray
autosaveTurnsSelectBox.selected = settings.turnsBetweenAutosaves autosaveTurnsSelectBox.selected = settings.turnsBetweenAutosaves
innerTable.add(autosaveTurnsSelectBox).pad(10f).row() innerTable2.add(autosaveTurnsSelectBox).pad(10f).row()
autosaveTurnsSelectBox.onChange { autosaveTurnsSelectBox.onChange {
settings.turnsBetweenAutosaves = autosaveTurnsSelectBox.selected settings.turnsBetweenAutosaves = autosaveTurnsSelectBox.selected
@ -326,7 +326,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
} }
private fun addMultiplayerTurnCheckerDelayBox() { private fun addMultiplayerTurnCheckerDelayBox() {
innerTable.add("Time between turn checks out-of-game (in minutes)".toLabel()) innerTable2.add("Time between turn checks out-of-game (in minutes)".toLabel())
val checkDelaySelectBox = SelectBox<Int>(skin) val checkDelaySelectBox = SelectBox<Int>(skin)
val possibleDelaysArray = Array<Int>() val possibleDelaysArray = Array<Int>()
@ -334,7 +334,7 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
checkDelaySelectBox.items = possibleDelaysArray checkDelaySelectBox.items = possibleDelaysArray
checkDelaySelectBox.selected = settings.multiplayerTurnCheckerDelayInMinutes checkDelaySelectBox.selected = settings.multiplayerTurnCheckerDelayInMinutes
innerTable.add(checkDelaySelectBox).pad(10f).row() innerTable2.add(checkDelaySelectBox).pad(10f).row()
checkDelaySelectBox.onChange { checkDelaySelectBox.onChange {
settings.multiplayerTurnCheckerDelayInMinutes = checkDelaySelectBox.selected settings.multiplayerTurnCheckerDelayInMinutes = checkDelaySelectBox.selected
@ -351,11 +351,11 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
.forEach { languageArray.add(it) } .forEach { languageArray.add(it) }
if (languageArray.size == 0) return if (languageArray.size == 0) return
innerTable.add("Language".toLabel()) innerTable2.add("Language".toLabel())
languageSelectBox.items = languageArray languageSelectBox.items = languageArray
val matchingLanguage = languageArray.firstOrNull { it.language == settings.language } val matchingLanguage = languageArray.firstOrNull { it.language == settings.language }
languageSelectBox.selected = if (matchingLanguage != null) matchingLanguage else languageArray.first() languageSelectBox.selected = if (matchingLanguage != null) matchingLanguage else languageArray.first()
innerTable.add(languageSelectBox).minWidth(240f).pad(10f).row() innerTable2.add(languageSelectBox).minWidth(240f).pad(10f).row()
languageSelectBox.onChange { languageSelectBox.onChange {
// Sometimes the "changed" is triggered even when we didn't choose something // Sometimes the "changed" is triggered even when we didn't choose something

View File

@ -1,9 +1,11 @@
package com.unciv.ui.worldscreen.mainmenu package com.unciv.ui.worldscreen.mainmenu
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.Constants import com.unciv.Constants
import com.unciv.MainMenuScreen import com.unciv.MainMenuScreen
import com.unciv.models.translations.tr
import com.unciv.ui.CivilopediaScreen import com.unciv.ui.CivilopediaScreen
import com.unciv.ui.newgamescreen.GameSetupInfo import com.unciv.ui.newgamescreen.GameSetupInfo
import com.unciv.ui.newgamescreen.NewGameScreen import com.unciv.ui.newgamescreen.NewGameScreen
@ -11,69 +13,52 @@ import com.unciv.ui.saves.LoadGameScreen
import com.unciv.ui.saves.SaveGameScreen import com.unciv.ui.saves.SaveGameScreen
import com.unciv.ui.utils.Popup import com.unciv.ui.utils.Popup
import com.unciv.ui.utils.addSeparator import com.unciv.ui.utils.addSeparator
import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.toLabel
import com.unciv.ui.victoryscreen.VictoryScreen import com.unciv.ui.victoryscreen.VictoryScreen
import com.unciv.ui.worldscreen.WorldScreen import com.unciv.ui.worldscreen.WorldScreen
class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) { class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) {
val buttonWidth = 200f
val buttonHeight = 30f
init { init {
val width = 200f addMenuButton("Main menu") { worldScreen.game.setScreen(MainMenuScreen()) }
val height = 30f addMenuButton("Civilopedia") { worldScreen.game.setScreen(CivilopediaScreen(worldScreen.gameInfo.ruleSet)) }
addSquareButton("Main menu".tr()){ addMenuButton("Save game") { worldScreen.game.setScreen(SaveGameScreen()) }
worldScreen.game.setScreen(MainMenuScreen()) addMenuButton("Load game") { worldScreen.game.setScreen(LoadGameScreen(worldScreen)) }
close()
}.size(width,height)
addSeparator()
addSquareButton("Civilopedia".tr()){ addMenuButton("Start new game") {
worldScreen.game.setScreen(CivilopediaScreen(worldScreen.gameInfo.ruleSet)) val newGameScreen = NewGameScreen(worldScreen, GameSetupInfo(worldScreen.gameInfo))
close() worldScreen.game.setScreen(newGameScreen)
}.size(width,height) }
addSeparator()
addSquareButton("Save game".tr()){ addMenuButton("Victory status") { worldScreen.game.setScreen(VictoryScreen(worldScreen)) }
worldScreen.game.setScreen(SaveGameScreen()) addMenuButton("Options") { OptionsPopup(worldScreen).open(force = true) }
close() addMenuButton("Community") { WorldScreenCommunityPopup(worldScreen).open(force = true) }
}.size(width,height)
addSeparator()
addSquareButton("Load game".tr()){ addSquareButton(Constants.close) {
worldScreen.game.setScreen(LoadGameScreen(worldScreen))
close() close()
}.size(width,height) }.size(buttonWidth, buttonHeight)
addSeparator() }
addSquareButton("Start new game".tr()){ fun addMenuButton(text: String, action: () -> Unit) {
worldScreen.game.setScreen(NewGameScreen(worldScreen, GameSetupInfo(worldScreen.gameInfo))) addSquareButton(text) {
action()
close() close()
}.size(width,height) }.size(buttonWidth, buttonHeight)
addSeparator() innerTable.addSeparator()
addSquareButton("Victory status".tr()){
worldScreen.game.setScreen(VictoryScreen(worldScreen))
close()
}.size(width,height)
addSeparator()
addSquareButton("Options".tr()){
OptionsPopup(worldScreen).open(force = true)
close()
}.size(width,height)
addSeparator()
addSquareButton("Community"){
WorldScreenCommunityPopup(worldScreen).open(force = true)
close()
}.size(width,height)
addSeparator()
addSquareButton(Constants.close){
close()
}.size(width,height)
} }
fun addSquareButton(text: String, action: () -> Unit): Cell<Table> {
val button = Table()
button.add(text.toLabel())
button.onClick(action)
button.touchable = Touchable.enabled
return add(button).apply { row() }
}
} }
class WorldScreenCommunityPopup(val worldScreen: WorldScreen) : Popup(worldScreen) { class WorldScreenCommunityPopup(val worldScreen: WorldScreen) : Popup(worldScreen) {