optimized city screen for smart phone (#12315)

* optimized city screen for smart phone
- moved "buy" button to city info table
- removed "add to queue" button
- expand icon changed to android defaults
- CityStatsTable: big scrollable area, expandable

* made CityStatsTable collapsible

* Extracted BuyButtonFactory and re-added buy button close to construction queue

---------

Co-authored-by: M. Rittweger <m.rittweger@mvolution.de>
Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
sulai 2024-10-24 19:38:26 +02:00 committed by GitHub
parent 32247fe437
commit 835ca58b4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 272 additions and 221 deletions

View File

@ -74,7 +74,7 @@ class ExpanderTab(
header.defaults().pad(headerPad)
headerIcon.setSize(arrowSize, arrowSize)
headerIcon.setOrigin(Align.center)
headerIcon.rotation = 180f
headerIcon.rotation = 0f
headerIcon.color = arrowColor
header.background(
BaseScreen.skinStrings.getUiBackground(
@ -83,7 +83,7 @@ class ExpanderTab(
)
)
if (icon != null) header.add(icon)
header.add(headerLabel)
header.add(headerLabel).expandX()
header.add(headerIcon).size(arrowSize).align(Align.center)
header.touchable= Touchable.enabled
header.onActivation { toggle() }
@ -111,11 +111,11 @@ class ExpanderTab(
if (noAnimation || !UncivGame.Current.settings.continuousRendering) {
contentWrapper.clear()
if (isOpen) contentWrapper.add(innerTable)
headerIcon.rotation = if (isOpen) 90f else 180f
headerIcon.rotation = if (isOpen) 90f else 0f
if (!noAnimation) onChange?.invoke()
return
}
val action = object: FloatAction ( 90f, 180f, animationDuration, Interpolation.linear) {
val action = object: FloatAction ( 90f, 0f, animationDuration, Interpolation.linear) {
override fun update(percent: Float) {
super.update(percent)
headerIcon.rotation = this.value

View File

@ -0,0 +1,200 @@
package com.unciv.ui.screens.cityscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.Constants
import com.unciv.logic.city.CityConstructions
import com.unciv.logic.map.tile.Tile
import com.unciv.models.Religion
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.IConstruction
import com.unciv.models.ruleset.INonPerpetualConstruction
import com.unciv.models.ruleset.PerpetualConstruction
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr
import com.unciv.ui.audio.SoundPlayer
import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.extensions.isEnabled
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.input.onActivation
import com.unciv.ui.popups.Popup
import com.unciv.ui.popups.closeAllPopups
import com.unciv.ui.screens.basescreen.BaseScreen
/**
* Use [addBuyButtons] to add buy buttons to a table.
* This class handles everything related to buying constructions. This includes
* showing and handling [ConfirmBuyPopup] and the actual purchase in [purchaseConstruction].
*/
class BuyButtonFactory(val cityScreen: CityScreen) {
private var preferredBuyStat = Stat.Gold // Used for keyboard buy
fun addBuyButtons(table: Table, construction: IConstruction?, onButtonAdded: (Cell<TextButton>) -> Unit) {
for (button in getBuyButtons(construction)) {
onButtonAdded(table.add(button))
}
}
fun hasBuyButtons(construction: IConstruction?): Boolean {
return getBuyButtons(construction).isNotEmpty()
}
private fun getBuyButtons(construction: IConstruction?): List<TextButton> {
val selection = cityScreen.selectedConstruction!=null || cityScreen.selectedQueueEntry >= 0
if (selection && construction != null && construction !is PerpetualConstruction)
return Stat.statsUsableToBuy.mapNotNull {
getBuyButton(construction as INonPerpetualConstruction, it)
}
return emptyList()
}
private fun getBuyButton(construction: INonPerpetualConstruction?, stat: Stat = Stat.Gold): TextButton? {
if (stat !in Stat.statsUsableToBuy || construction == null)
return null
val city = cityScreen.city
val button = "".toTextButton()
if (!isConstructionPurchaseShown(construction, stat)) {
// This can't ever be bought with the given currency.
// We want one disabled "buy" button without a price for "priceless" buildings such as wonders
// We don't want such a button when the construction can be bought using a different currency
if (stat != Stat.Gold || construction.canBePurchasedWithAnyStat(city))
return null
button.setText("Buy".tr())
button.disable()
} else {
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
button.setText("Buy".tr() + " " + constructionBuyCost.tr() + stat.character)
button.onActivation(binding = KeyboardBinding.BuyConstruction) {
button.disable()
buyButtonOnClick(construction, stat)
}
button.isEnabled = cityScreen.canCityBeChanged() &&
city.cityConstructions.isConstructionPurchaseAllowed(construction, stat, constructionBuyCost)
preferredBuyStat = stat // Not very intelligent, but the least common currency "wins"
}
button.labelCell.pad(5f)
return button
}
private fun buyButtonOnClick(construction: INonPerpetualConstruction, stat: Stat = preferredBuyStat) {
if (construction !is Building || !construction.hasCreateOneImprovementUnique())
return askToBuyConstruction(construction, stat)
if (cityScreen.selectedQueueEntry < 0)
return cityScreen.startPickTileForCreatesOneImprovement(construction, stat, true)
// Buying a UniqueType.CreatesOneImprovement building from queue must pass down
// the already selected tile, otherwise a new one is chosen from Automation code.
val improvement = construction.getImprovementToCreate(
cityScreen.city.getRuleset(), cityScreen.city.civ)!!
val tileForImprovement = cityScreen.city.cityConstructions.getTileForImprovement(improvement.name)
askToBuyConstruction(construction, stat, tileForImprovement)
}
/** Ask whether user wants to buy [construction] for [stat].
*
* Used from onClick and keyboard dispatch, thus only minimal parameters are passed,
* and it needs to do all checks and the sound as appropriate.
*/
fun askToBuyConstruction(
construction: INonPerpetualConstruction,
stat: Stat = preferredBuyStat,
tile: Tile? = null
) {
if (!isConstructionPurchaseShown(construction, stat)) return
val city = cityScreen.city
val constructionStatBuyCost = construction.getStatBuyCost(city, stat)!!
if (!city.cityConstructions.isConstructionPurchaseAllowed(construction, stat, constructionStatBuyCost)) return
cityScreen.closeAllPopups()
ConfirmBuyPopup(construction, stat,constructionStatBuyCost, tile)
}
private inner class ConfirmBuyPopup(
construction: INonPerpetualConstruction,
stat: Stat,
constructionStatBuyCost: Int,
tile: Tile?
) : Popup(cityScreen.stage) {
init {
val city = cityScreen.city
val balance = city.getStatReserve(stat)
val majorityReligion = city.religion.getMajorityReligion()
val yourReligion = city.civ.religionManager.religion
val isBuyingWithFaithForForeignReligion = construction.hasUnique(UniqueType.ReligiousUnit)
&& !construction.hasUnique(UniqueType.TakeReligionOverBirthCity)
&& majorityReligion != yourReligion
addGoodSizedLabel("Currently you have [$balance] [${stat.name}].").padBottom(10f).row()
if (isBuyingWithFaithForForeignReligion) {
// Earlier tests should forbid this Popup unless both religions are non-null, but to be safe:
fun Religion?.getName() = this?.getReligionDisplayName() ?: Constants.unknownCityName
addGoodSizedLabel("You are buying a religious unit in a city that doesn't follow the religion you founded ([${yourReligion.getName()}]). " +
"This means that the unit is tied to that foreign religion ([${majorityReligion.getName()}]) and will be less useful.").row()
addGoodSizedLabel("Are you really sure you want to purchase this unit?", Constants.headingFontSize).run {
actor.color = Color.FIREBRICK
padBottom(10f)
row()
}
}
addGoodSizedLabel("Would you like to purchase [${construction.name}] for [$constructionStatBuyCost] [${stat.character}]?").row()
addCloseButton(Constants.cancel, KeyboardBinding.Cancel) { cityScreen.update() }
val confirmStyle = BaseScreen.skin.get("positive", TextButton.TextButtonStyle::class.java)
addOKButton("Purchase", KeyboardBinding.Confirm, confirmStyle) {
purchaseConstruction(construction, stat, tile)
}
equalizeLastTwoButtonWidths()
open(true)
}
}
/** This tests whether the buy button should be _shown_ */
private fun isConstructionPurchaseShown(construction: INonPerpetualConstruction, stat: Stat): Boolean {
val city = cityScreen.city
return construction.canBePurchasedWithStat(city, stat)
}
/** Called only by askToBuyConstruction's Yes answer - not to be confused with [CityConstructions.purchaseConstruction]
* @param tile supports [UniqueType.CreatesOneImprovement]
*/
private fun purchaseConstruction(
construction: INonPerpetualConstruction,
stat: Stat = Stat.Gold,
tile: Tile? = null
) {
SoundPlayer.play(stat.purchaseSound)
val city = cityScreen.city
if (!city.cityConstructions.purchaseConstruction(construction, cityScreen.selectedQueueEntry, false, stat, tile)) {
Popup(cityScreen).apply {
add("No space available to place [${construction.name}] near [${city.name}]".tr()).row()
addCloseButton()
open()
}
return
}
if (cityScreen.selectedQueueEntry>=0 || cityScreen.selectedConstruction?.isBuildable(city.cityConstructions) != true) {
cityScreen.selectedQueueEntry = -1
cityScreen.clearSelection()
// Allow buying next queued or auto-assigned construction right away
city.cityConstructions.chooseNextConstruction()
if (city.cityConstructions.currentConstructionFromQueue.isNotEmpty()) {
val newConstruction = city.cityConstructions.getCurrentConstruction()
if (newConstruction is INonPerpetualConstruction)
cityScreen.selectConstruction(newConstruction)
}
}
cityScreen.city.reassignPopulation()
cityScreen.update()
}
}

View File

@ -5,14 +5,11 @@ import com.badlogic.gdx.scenes.scene2d.Group
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.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.GUI
import com.unciv.logic.city.City
import com.unciv.logic.city.CityConstructions
import com.unciv.logic.map.tile.Tile
import com.unciv.models.Religion
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.IConstruction
@ -32,13 +29,10 @@ import com.unciv.ui.components.extensions.addCell
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.brighten
import com.unciv.ui.components.extensions.darken
import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.extensions.getConsumesAmountString
import com.unciv.ui.components.extensions.isEnabled
import com.unciv.ui.components.extensions.packIfNeeded
import com.unciv.ui.components.extensions.surroundWithCircle
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.components.fonts.Fonts
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.input.keyShortcuts
@ -49,8 +43,6 @@ import com.unciv.ui.components.widgets.ColorMarkupLabel
import com.unciv.ui.components.widgets.ExpanderTab
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.CityScreenConstructionMenu
import com.unciv.ui.popups.Popup
import com.unciv.ui.popups.closeAllPopups
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.utils.Concurrency
import com.unciv.utils.launchOnGLThread
@ -66,19 +58,19 @@ private class ConstructionButtonDTO(
/**
* Manager to hold and coordinate two widgets for the city screen left side:
* - Construction queue with the enqueue / buy buttons.
* - Construction queue with the buy button.
* The queue is scrollable, limited to one third of the stage height.
* - Available constructions display, scrolling, grouped with expanders and therefore of dynamic height.
*/
class CityConstructionsTable(private val cityScreen: CityScreen) {
/* -1 = Nothing, >= 0 queue entry (0 = current construction) */
private var selectedQueueEntry = -1 // None
private var preferredBuyStat = Stat.Gold // Used for keyboard buy
var selectedQueueEntry = -1 // None
private val upperTable = Table(BaseScreen.skin)
private val constructionsQueueScrollPane: ScrollPane
private val constructionsQueueTable = Table()
private val buyButtonsTable = Table()
private val buttonsTable = Table()
private val buyButtonFactory = BuyButtonFactory(cityScreen)
private val lowerTable = Table()
private val availableConstructionsScrollPane: ScrollPane
@ -109,7 +101,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
upperTable.add(constructionsQueueScrollPane)
.maxHeight(stageHeight / 3 - 10f)
.padBottom(pad).row()
upperTable.add(buyButtonsTable).padBottom(pad).row()
upperTable.add(buttonsTable).padBottom(pad).row()
availableConstructionsScrollPane = ScrollPane(availableConstructionsTable.addBorder(2f, Color.WHITE))
availableConstructionsScrollPane.setOverscroll(false, false)
@ -149,14 +141,13 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
}
private fun updateButtons(construction: IConstruction?) {
buyButtonsTable.clear()
if (!cityScreen.canChangeState) return
/** [UniqueType.MayBuyConstructionsInPuppets] support - we need a buy button for civs that could buy items in puppets */
if (cityScreen.city.isPuppet && !cityScreen.city.getMatchingUniques(UniqueType.MayBuyConstructionsInPuppets).any()) return
buyButtonsTable.add(getQueueButton(construction)).padRight(5f)
if (construction != null && construction !is PerpetualConstruction)
for (button in getBuyButtons(construction as INonPerpetualConstruction))
buyButtonsTable.add(button).padRight(5f)
buttonsTable.clear()
buyButtonFactory.addBuyButtons(buttonsTable, construction) {
it.padRight(5f)
}
}
private fun updateConstructionQueue() {
@ -553,38 +544,6 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|| city.isPuppet
}
private fun getQueueButton(construction: IConstruction?): TextButton {
val city = cityScreen.city
val cityConstructions = city.cityConstructions
val button: TextButton
if (isSelectedQueueEntry()) {
button = "Remove from queue".toTextButton()
button.onActivation(binding = KeyboardBinding.AddConstruction) {
cityConstructions.removeFromQueue(selectedQueueEntry, false)
cityScreen.clearSelection()
selectedQueueEntry = -1
cityScreen.city.reassignPopulation()
cityScreen.update()
}
if (city.isPuppet)
button.disable()
} else {
button = "Add to queue".toTextButton()
if (construction == null
|| cannotAddConstructionToQueue(construction, city, cityConstructions)) {
button.disable()
} else {
button.onActivation(binding = KeyboardBinding.AddConstruction, sound = UncivSound.Silent) {
addConstructionToQueue(construction, cityConstructions)
}
}
}
button.labelCell.pad(5f)
return button
}
private fun addConstructionToQueue(construction: IConstruction, cityConstructions: CityConstructions) {
// Some evil person decided to double tap real fast - #4977
if (cannotAddConstructionToQueue(construction, cityScreen.city, cityConstructions))
@ -618,155 +577,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
else -> UncivSound.Click
}
}
private fun getBuyButtons(construction: INonPerpetualConstruction?): List<TextButton> {
return Stat.statsUsableToBuy.mapNotNull { getBuyButton(construction, it) }
}
private fun getBuyButton(construction: INonPerpetualConstruction?, stat: Stat = Stat.Gold): TextButton? {
if (stat !in Stat.statsUsableToBuy || construction == null)
return null
val city = cityScreen.city
val button = "".toTextButton()
if (!isConstructionPurchaseShown(construction, stat)) {
// This can't ever be bought with the given currency.
// We want one disabled "buy" button without a price for "priceless" buildings such as wonders
// We don't want such a button when the construction can be bought using a different currency
if (stat != Stat.Gold || construction.canBePurchasedWithAnyStat(city))
return null
button.setText("Buy".tr())
button.disable()
} else {
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
button.setText("Buy".tr() + " " + constructionBuyCost.tr() + stat.character)
button.onActivation(binding = KeyboardBinding.BuyConstruction) {
button.disable()
buyButtonOnClick(construction, stat)
}
button.isEnabled = cityScreen.canCityBeChanged() &&
city.cityConstructions.isConstructionPurchaseAllowed(construction, stat, constructionBuyCost)
preferredBuyStat = stat // Not very intelligent, but the least common currency "wins"
}
button.labelCell.pad(5f)
return button
}
private fun buyButtonOnClick(construction: INonPerpetualConstruction, stat: Stat = preferredBuyStat) {
if (construction !is Building || !construction.hasCreateOneImprovementUnique())
return askToBuyConstruction(construction, stat)
if (selectedQueueEntry < 0)
return cityScreen.startPickTileForCreatesOneImprovement(construction, stat, true)
// Buying a UniqueType.CreatesOneImprovement building from queue must pass down
// the already selected tile, otherwise a new one is chosen from Automation code.
val improvement = construction.getImprovementToCreate(
cityScreen.city.getRuleset(), cityScreen.city.civ)!!
val tileForImprovement = cityScreen.city.cityConstructions.getTileForImprovement(improvement.name)
askToBuyConstruction(construction, stat, tileForImprovement)
}
/** Ask whether user wants to buy [construction] for [stat].
*
* Used from onClick and keyboard dispatch, thus only minimal parameters are passed,
* and it needs to do all checks and the sound as appropriate.
*/
fun askToBuyConstruction(
construction: INonPerpetualConstruction,
stat: Stat = preferredBuyStat,
tile: Tile? = null
) {
if (!isConstructionPurchaseShown(construction, stat)) return
val city = cityScreen.city
val constructionStatBuyCost = construction.getStatBuyCost(city, stat)!!
if (!city.cityConstructions.isConstructionPurchaseAllowed(construction, stat, constructionStatBuyCost)) return
cityScreen.closeAllPopups()
ConfirmBuyPopup(construction, stat,constructionStatBuyCost, tile)
}
private inner class ConfirmBuyPopup(
construction: INonPerpetualConstruction,
stat: Stat,
constructionStatBuyCost: Int,
tile: Tile?
) : Popup(cityScreen.stage) {
init {
val city = cityScreen.city
val balance = city.getStatReserve(stat)
val majorityReligion = city.religion.getMajorityReligion()
val yourReligion = city.civ.religionManager.religion
val isBuyingWithFaithForForeignReligion = construction.hasUnique(UniqueType.ReligiousUnit)
&& !construction.hasUnique(UniqueType.TakeReligionOverBirthCity)
&& majorityReligion != yourReligion
addGoodSizedLabel("Currently you have [$balance] [${stat.name}].").padBottom(10f).row()
if (isBuyingWithFaithForForeignReligion) {
// Earlier tests should forbid this Popup unless both religions are non-null, but to be safe:
fun Religion?.getName() = this?.getReligionDisplayName() ?: Constants.unknownCityName
addGoodSizedLabel("You are buying a religious unit in a city that doesn't follow the religion you founded ([${yourReligion.getName()}]). " +
"This means that the unit is tied to that foreign religion ([${majorityReligion.getName()}]) and will be less useful.").row()
addGoodSizedLabel("Are you really sure you want to purchase this unit?", Constants.headingFontSize).run {
actor.color = Color.FIREBRICK
padBottom(10f)
row()
}
}
addGoodSizedLabel("Would you like to purchase [${construction.name}] for [$constructionStatBuyCost] [${stat.character}]?").row()
addCloseButton(Constants.cancel, KeyboardBinding.Cancel) { cityScreen.update() }
val confirmStyle = BaseScreen.skin.get("positive", TextButton.TextButtonStyle::class.java)
addOKButton("Purchase", KeyboardBinding.Confirm, confirmStyle) {
purchaseConstruction(construction, stat, tile)
}
equalizeLastTwoButtonWidths()
open(true)
}
}
/** This tests whether the buy button should be _shown_ */
private fun isConstructionPurchaseShown(construction: INonPerpetualConstruction, stat: Stat): Boolean {
val city = cityScreen.city
return construction.canBePurchasedWithStat(city, stat)
}
/** Called only by askToBuyConstruction's Yes answer - not to be confused with [CityConstructions.purchaseConstruction]
* @param tile supports [UniqueType.CreatesOneImprovement]
*/
private fun purchaseConstruction(
construction: INonPerpetualConstruction,
stat: Stat = Stat.Gold,
tile: Tile? = null
) {
SoundPlayer.play(stat.purchaseSound)
val city = cityScreen.city
if (!city.cityConstructions.purchaseConstruction(construction, selectedQueueEntry, false, stat, tile)) {
Popup(cityScreen).apply {
add("No space available to place [${construction.name}] near [${city.name}]".tr()).row()
addCloseButton()
open()
}
return
}
if (isSelectedQueueEntry() || cityScreen.selectedConstruction?.isBuildable(city.cityConstructions) != true) {
selectedQueueEntry = -1
cityScreen.clearSelection()
// Allow buying next queued or auto-assigned construction right away
city.cityConstructions.chooseNextConstruction()
if (city.cityConstructions.currentConstructionFromQueue.isNotEmpty()) {
val newConstruction = city.cityConstructions.getCurrentConstruction()
if (newConstruction is INonPerpetualConstruction)
cityScreen.selectConstruction(newConstruction)
}
}
cityScreen.city.reassignPopulation()
cityScreen.update()
}
private fun getMovePriorityButton(
arrowDirection: Int,
binding: KeyboardBinding,

View File

@ -133,6 +133,9 @@ class CityScreen(
var pickTileData: PickTileForImprovementData? = null
/** A [Building] with [UniqueType.CreatesOneImprovement] has been selected _in the queue_: show the tile it will place the improvement on */
private var selectedQueueEntryTargetTile: Tile? = null
var selectedQueueEntry
get() = constructionsTable.selectedQueueEntry
set(value) { constructionsTable.selectedQueueEntry = value }
/** Cached city.expansion.chooseNewTileToOwn() */
// val should be OK as buying tiles is what changes this, and that would re-create the whole CityScreen
private val nextTileToOwn = city.expansion.chooseNewTileToOwn()
@ -452,7 +455,7 @@ class CityScreen(
val improvement = pickTileData.improvement
if (tileInfo.improvementFunctions.canBuildImprovement(improvement, city.civ)) {
if (pickTileData.isBuying) {
constructionsTable.askToBuyConstruction(pickTileData.building, pickTileData.buyStat, tileInfo)
BuyButtonFactory(this).askToBuyConstruction(pickTileData.building, pickTileData.buyStat, tileInfo)
} else {
// This way to store where the improvement a CreatesOneImprovement Building will create goes
// might get a bit fragile if several buildings constructing the same improvement type

View File

@ -2,7 +2,7 @@ package com.unciv.ui.screens.cityscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
@ -32,8 +32,14 @@ class CityStatsTable(private val cityScreen: CityScreen) : Table() {
private val lowerTable = Table() // table that will be in the ScrollPane
private val lowerPane: ScrollPane
private val city = cityScreen.city
private val lowerCell: Cell<ScrollPane>
private val headerIcon = ImageGetter.getImage("OtherIcons/BackArrow").apply {
setSize(18f, 18f)
setOrigin(Align.center)
rotation = 90f
}
private var headerIconClickArea = Table()
private var isOpen = true
private val detailedStatsButton = "Stats".toTextButton().apply {
labelCell.pad(10f)
onActivation(binding = KeyboardBinding.ShowStats) {
@ -53,16 +59,22 @@ class CityStatsTable(private val cityScreen: CityScreen) : Table() {
"CityScreen/CityStatsTable/InnerTable",
tintColor = Color.BLACK.cpy().apply { a = 0.8f }
)
innerTable.add(upperTable).row()
upperTable.defaults().pad(2f)
lowerTable.defaults().pad(2f)
lowerPane = ScrollPane(lowerTable)
lowerPane.setOverscroll(false, false)
lowerPane.setScrollingDisabled(true, false)
lowerCell = innerTable.add(lowerPane)
add(innerTable)
add(innerTable).growX()
// collapse icon with larger click area
headerIconClickArea.add(headerIcon).size(headerIcon.width).pad(6f+2f, 12f, 6f, 2f )
headerIconClickArea.touchable = Touchable.enabled
headerIconClickArea.onClick {
isOpen = !isOpen
cityScreen.updateWithoutConstructionAndMap()
}
}
fun update(height: Float) {
@ -93,10 +105,10 @@ class CityStatsTable(private val cityScreen: CityScreen) : Table() {
val valueToDisplay = if (stat == Stat.Happiness) city.cityStats.happinessList.values.sum() else amount
miniStatsTable.add(round(valueToDisplay).toInt().toLabel()).padRight(5f)
}
upperTable.add(miniStatsTable)
upperTable.add(miniStatsTable).expandX()
upperTable.addSeparator()
upperTable.add(detailedStatsButton).row()
lowerTable.add(detailedStatsButton).row()
addText()
// begin lowerTable
@ -110,11 +122,20 @@ class CityStatsTable(private val cityScreen: CityScreen) : Table() {
addBuildingsInfo()
headerIcon.rotation = if(isOpen) 90f else 0f
innerTable.clear()
innerTable.add(upperTable).expandX()
innerTable.add(headerIconClickArea).row()
val lowerCell = if (isOpen) {
innerTable.add(lowerPane).colspan(2)
} else null
upperTable.pack()
lowerTable.pack()
lowerPane.layout()
lowerPane.updateVisualScroll()
lowerCell.maxHeight(height - upperTable.height - 8f) // 2 on each side of each cell in innerTable
lowerCell?.maxHeight(height - upperTable.height - 8f) // 2 on each side of each cell in innerTable
innerTable.pack() // update innerTable
pack() // update self last
@ -158,9 +179,9 @@ class CityStatsTable(private val cityScreen: CityScreen) : Table() {
}.tr()
turnsToPopString += " (${city.population.foodStored}${Fonts.food}/${city.population.getFoodToNextPopulation()}${Fonts.food})"
upperTable.add(unassignedPopLabel).row()
upperTable.add(turnsToExpansionString.toLabel()).row()
upperTable.add(turnsToPopString.toLabel()).row()
lowerTable.add(unassignedPopLabel).row()
lowerTable.add(turnsToExpansionString.toLabel()).row()
lowerTable.add(turnsToPopString.toLabel()).row()
val tableWithIcons = Table()
tableWithIcons.defaults().pad(2f)
@ -200,7 +221,7 @@ class CityStatsTable(private val cityScreen: CityScreen) : Table() {
tableWithIcons.add(wltkLabel).row()
}
upperTable.add(tableWithIcons).row()
lowerTable.add(tableWithIcons).row()
}
private fun addCitizenManagement() {

View File

@ -25,6 +25,7 @@ import com.unciv.ui.screens.basescreen.BaseScreen
class ConstructionInfoTable(val cityScreen: CityScreen) : Table() {
private val selectedConstructionTable = Table()
private val buyButtonFactory = BuyButtonFactory(cityScreen)
init {
selectedConstructionTable.background = BaseScreen.skinStrings.getUiBackground(
@ -89,9 +90,23 @@ class ConstructionInfoTable(val cityScreen: CityScreen) : Table() {
descriptionLabel.wrap = true
add(descriptionLabel).colspan(2).width(stage.width / 4)
// Show sell button if construction is a currently sellable building
if (construction is Building && cityConstructions.isBuilt(construction.name)
&& construction.isSellable()) {
if (cityConstructions.isBuilt(construction.name)) {
showSellButton(construction)
} else if (buyButtonFactory.hasBuyButtons(construction)) {
row()
buyButtonFactory.addBuyButtons(selectedConstructionTable, construction) {
it.padTop(5f).colspan(2).center()
}
}
}
}
// Show sell button if construction is a currently sellable building
private fun showSellButton(
construction: IConstruction
) {
if (construction is Building && construction.isSellable()) {
selectedConstructionTable.run {
val sellAmount = cityScreen.city.getGoldForSellingBuilding(construction.name)
val sellText = "{Sell} $sellAmount " + Fonts.gold
val sellBuildingButton = sellText.toTextButton()
@ -138,4 +153,5 @@ class ConstructionInfoTable(val cityScreen: CityScreen) : Table() {
cityScreen.clearSelection()
cityScreen.update()
}
}