ExpanderTab revisited - move and apply to city screen (#4401)

This commit is contained in:
SomeTroglodyte 2021-07-07 17:21:51 +02:00 committed by GitHub
parent 9e37cc8f16
commit 1d080ac54d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 112 deletions

View File

@ -18,6 +18,7 @@ import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr
import com.unciv.ui.utils.*
import kotlin.concurrent.thread
import kotlin.math.max
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
class CityConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScreen.skin) {
@ -53,6 +54,7 @@ class CityConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBase
add(showCityInfoTableButton).left().padLeft(pad).padBottom(pad).row()
add(constructionsQueueScrollPane).left().padBottom(pad).row()
add().expandY().row() // allow the bottom() below to open up the unneeded space
add(buttons).left().bottom().padBottom(pad).row()
add(availableConstructionsScrollPane).left().bottom().row()
}
@ -81,7 +83,7 @@ class CityConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBase
val queue = cityConstructions.constructionQueue
constructionsQueueTable.defaults().pad(0f)
constructionsQueueTable.add(getHeader("Current construction".tr())).fillX()
constructionsQueueTable.add(getHeader("Current construction")).fillX()
constructionsQueueTable.addSeparator()
@ -92,21 +94,20 @@ class CityConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBase
constructionsQueueTable.add("Pick a construction".toLabel()).pad(2f).row()
constructionsQueueTable.addSeparator()
constructionsQueueTable.add(getHeader("Construction queue".tr())).fillX()
constructionsQueueTable.addSeparator()
if (queue.isNotEmpty()) {
if (queue.size > 1) {
constructionsQueueTable.add(getHeader("Construction queue")).fillX()
constructionsQueueTable.addSeparator()
queue.forEachIndexed { i, constructionName ->
if (i != 0) // This is already displayed as "Current construction"
// The first entry is already displayed as "Current construction"
if (i != 0) {
constructionsQueueTable.add(getQueueEntry(i, constructionName))
.expandX().fillX().row()
if (i != queue.size - 1)
constructionsQueueTable.addSeparator()
.expandX().fillX().row()
if (i != queue.size - 1)
constructionsQueueTable.addSeparator()
}
}
} else
constructionsQueueTable.add("Queue empty".toLabel()).pad(2f).row()
}
constructionsQueueScrollPane.layout()
constructionsQueueScrollPane.scrollY = queueScrollY
@ -171,6 +172,7 @@ class CityConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBase
val constructionButtonDTOList = getConstructionButtonDTOs()
Gdx.app.postRunnable {
availableConstructionsTable.clear()
var maxWidth = constructionsQueueTable.width
for (dto in constructionButtonDTOList) {
val constructionButton = getConstructionButton(dto)
when (dto.construction) {
@ -184,14 +186,15 @@ class CityConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBase
}
is PerpetualConstruction -> specialConstructions.add(constructionButton)
}
if (constructionButton.needsLayout()) constructionButton.pack()
maxWidth = max(maxWidth, constructionButton.width)
}
availableConstructionsTable.addCategory("Units", units, constructionsQueueTable.width)
availableConstructionsTable.addCategory("Wonders", buildableWonders, constructionsQueueTable.width)
availableConstructionsTable.addCategory("National Wonders", buildableNationalWonders, constructionsQueueTable.width)
availableConstructionsTable.addCategory("Buildings", buildableBuildings, constructionsQueueTable.width)
availableConstructionsTable.addCategory("Other", specialConstructions, constructionsQueueTable.width)
availableConstructionsTable.addCategory("Units", units, maxWidth)
availableConstructionsTable.addCategory("Wonders", buildableWonders, maxWidth)
availableConstructionsTable.addCategory("National Wonders", buildableNationalWonders, maxWidth)
availableConstructionsTable.addCategory("Buildings", buildableBuildings, maxWidth)
availableConstructionsTable.addCategory("Other", specialConstructions, maxWidth)
availableConstructionsScrollPane.layout()
availableConstructionsScrollPane.scrollY = constrScrollY
@ -489,13 +492,13 @@ class CityConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBase
private fun Table.addCategory(title: String, list: ArrayList<Table>, prefWidth: Float) {
if (list.isEmpty()) return
addSeparator()
add(getHeader(title)).prefWidth(prefWidth).fill().row()
addSeparator()
for (table in list) {
add(table).fill().left().row()
if (table != list.last()) addSeparator()
if (rows > 0) addSeparator()
val expander = ExpanderTab(title, defaultPad = 0f, expanderWidth = prefWidth) {
for (table in list) {
it.addSeparator(colSpan = 1)
it.add(table).left().row()
}
}
add(expander).prefWidth(prefWidth).growX().row()
}
}
}

View File

@ -54,74 +54,49 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS
}
private fun Table.addCategory(str: String, showHideTable: Table) {
val width = cityScreen.stage.width / 4
val showHideTableWrapper = Table()
.add(showHideTable)
.minWidth(width)
.table
val titleTable = Table()
.background(ImageGetter.getBackground(ImageGetter.getBlue()))
.pad(4f)
.addCell(str.toLabel(fontSize = 24))
.onClick {
if (showHideTableWrapper.hasChildren()) {
showHideTableWrapper.clear()
} else {
showHideTableWrapper.add(showHideTable).minWidth(width)
}
}
val categoryWidth = cityScreen.stage.width / 4
val expander = ExpanderTab(str) {
it.add(showHideTable).minWidth(categoryWidth)
}
addSeparator()
add(titleTable).minWidth(width).row()
add(showHideTableWrapper).row()
add(expander).minWidth(categoryWidth).expandX().fillX().row()
}
private fun addBuildingInfo(building: Building, wondersTable: Table) {
val wonderNameAndIconTable = Table()
wonderNameAndIconTable.touchable = Touchable.enabled
wonderNameAndIconTable.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(30f))
wonderNameAndIconTable.add(building.name.toLabel()).pad(5f)
wondersTable.add(wonderNameAndIconTable).pad(5f).fillX().row()
private fun addBuildingInfo(building: Building, destinationTable: Table) {
val icon = ImageGetter.getConstructionImage(building.name).surroundWithCircle(30f)
val buildingNameAndIconTable = ExpanderTab(building.name, 18, icon, false, 5f) {
//todo: getDescription signature changes with civilopedia phase 5
val detailsString = building.getDescription(true,
cityScreen.city, cityScreen.city.civInfo.gameInfo.ruleSet)
it.add(detailsString.toLabel().apply { wrap = true })
.width(cityScreen.stage.width / 4 - 2 * pad).row() // when you set wrap, then you need to manually set the size of the label
if (building.isSellable()) {
val sellAmount = cityScreen.city.getGoldForSellingBuilding(building.name)
val sellBuildingButton = "Sell for [$sellAmount] gold".toTextButton()
it.add(sellBuildingButton).pad(5f).row()
val wonderDetailsTable = Table()
wondersTable.add(wonderDetailsTable).pad(5f).align(Align.left).row()
sellBuildingButton.onClick {
sellBuildingButton.disable()
cityScreen.closeAllPopups()
wonderNameAndIconTable.onClick {
if (wonderDetailsTable.hasChildren())
wonderDetailsTable.clear()
else {
val detailsString = building.getDescription(true,
cityScreen.city, cityScreen.city.civInfo.gameInfo.ruleSet)
wonderDetailsTable.add(detailsString.toLabel().apply { wrap = true })
.width(cityScreen.stage.width / 4 - 2 * pad).row() // when you set wrap, then you need to manually set the size of the label
if (building.isSellable()) {
val sellAmount = cityScreen.city.getGoldForSellingBuilding(building.name)
val sellBuildingButton = "Sell for [$sellAmount] gold".toTextButton()
wonderDetailsTable.add(sellBuildingButton).pad(5f).row()
sellBuildingButton.onClick {
sellBuildingButton.disable()
cityScreen.closeAllPopups()
YesNoPopup("Are you sure you want to sell this [${building.name}]?".tr(),
{
cityScreen.city.sellBuilding(building.name)
cityScreen.city.cityStats.update()
cityScreen.update()
}, cityScreen,
{
cityScreen.update()
}).open()
}
if (cityScreen.city.hasSoldBuildingThisTurn && !cityScreen.city.civInfo.gameInfo.gameParameters.godMode
|| cityScreen.city.isPuppet
|| !UncivGame.Current.worldScreen.isPlayersTurn || !cityScreen.canChangeState)
sellBuildingButton.disable()
YesNoPopup("Are you sure you want to sell this [${building.name}]?".tr(),
{
cityScreen.city.sellBuilding(building.name)
cityScreen.city.cityStats.update()
cityScreen.update()
}, cityScreen,
{
cityScreen.update()
}).open()
}
wonderDetailsTable.addSeparator()
if (cityScreen.city.hasSoldBuildingThisTurn && !cityScreen.city.civInfo.gameInfo.gameParameters.godMode
|| cityScreen.city.isPuppet
|| !UncivGame.Current.worldScreen.isPlayersTurn || !cityScreen.canChangeState)
sellBuildingButton.disable()
}
it.addSeparator()
}
destinationTable.add(buildingNameAndIconTable).pad(5f).fillX().row()
}
private fun Table.addBuildingsInfo(cityInfo: CityInfo) {
@ -153,7 +128,7 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS
specialistIcons.row().size(20f).pad(5f)
for ((specialistName, amount) in building.newSpecialists()) {
val specialist = cityInfo.getRuleset().specialists[specialistName]
if (specialist == null) continue // probably a mod that doesn't have the specialist defined yet
?: continue // probably a mod that doesn't have the specialist defined yet
for (i in 0 until amount)
specialistIcons.add(ImageGetter.getSpecialistIcon(specialist.colorObject)).size(20f)
}
@ -194,11 +169,11 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS
val specificStatValue = entry.value
sumOfAllBaseValues += specificStatValue
statValuesTable.add(entry.key.toLabel())
statValuesTable.add(DecimalFormat("0.#").format(specificStatValue).toLabel()).row()
statValuesTable.add(specificStatValue.toOneDecimalLabel()).row()
}
statValuesTable.addSeparator()
statValuesTable.add("Total".toLabel())
statValuesTable.add(DecimalFormat("0.#").format(sumOfAllBaseValues).toLabel()).row()
statValuesTable.add(sumOfAllBaseValues.toOneDecimalLabel()).row()
val relevantBonuses = cityStats.statPercentBonusList.filter { it.value.get(stat) != 0f }
if (relevantBonuses.isNotEmpty()) {
@ -208,15 +183,11 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS
val specificStatValue = entry.value.get(stat)
sumOfBonuses += specificStatValue
statValuesTable.add(entry.key.toLabel())
val decimal = DecimalFormat("0.#").format(specificStatValue)
if (specificStatValue > 0) statValuesTable.add("+$decimal%".toLabel()).row()
else statValuesTable.add("$decimal%".toLabel()).row() // negative bonus
statValuesTable.add(specificStatValue.toPercentLabel()).row() // negative bonus
}
statValuesTable.addSeparator()
statValuesTable.add("Total".toLabel())
val decimal = DecimalFormat("0.#").format(sumOfBonuses)
if (sumOfBonuses > 0) statValuesTable.add("+$decimal%".toLabel()).row()
else statValuesTable.add("$decimal%".toLabel()).row() // negative bonus
statValuesTable.add(sumOfBonuses.toPercentLabel()).row() // negative bonus
}
if (stat != Stat.Happiness) {
@ -227,11 +198,11 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS
finalTotal += specificStatValue
if (specificStatValue == 0f) continue
statValuesTable.add(entry.key.toLabel())
statValuesTable.add(DecimalFormat("0.#").format(specificStatValue).toLabel()).row()
statValuesTable.add(specificStatValue.toOneDecimalLabel()).row()
}
statValuesTable.addSeparator()
statValuesTable.add("Total".toLabel())
statValuesTable.add(DecimalFormat("0.#").format(finalTotal).toLabel()).row()
statValuesTable.add(finalTotal.toOneDecimalLabel()).row()
}
statValuesTable.padBottom(4f)
@ -252,13 +223,18 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS
val value = entry.value.toHashMap()[stat]!!
if (value == 0f) continue
greatPersonTable.add(entry.key.toLabel()).padRight(10f)
greatPersonTable.add(DecimalFormat("0.#").format(value).toLabel()).row()
greatPersonTable.add(value.toOneDecimalLabel()).row()
}
}
}
companion object {
private const val FONT_SIZE_STAT_INFO_HEADER = 22
private fun Float.toPercentLabel() =
"${if (this>0f) "+" else ""}${DecimalFormat("0.#").format(this)}%".toLabel()
private fun Float.toOneDecimalLabel() =
DecimalFormat("0.#").format(this).toLabel()
}
}
}

View File

@ -9,10 +9,7 @@ import com.unciv.logic.trade.TradeOffersList
import com.unciv.logic.trade.TradeType
import com.unciv.logic.trade.TradeType.*
import com.unciv.models.translations.tr
import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.disable
import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.toTextButton
import com.unciv.ui.utils.*
import kotlin.math.min
import com.unciv.ui.utils.AutoScrollPane as ScrollPane

View File

@ -1,32 +1,34 @@
package com.unciv.ui.trade
package com.unciv.ui.utils
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Interpolation
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.actions.FloatAction
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame
import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.toLabel
/**
* A widget with a header that when clicked shows/hides a sub-Table.
*
* @param title The header text, automatically translated.
* @param fontSize Size applied to header text (only)
* @param icon Optional icon - please use [Image][com.badlogic.gdx.scenes.scene2d.ui.Image] or [IconCircleGroup]
* @param defaultPad Padding between content and wrapper. Header padding is currently not modifiable.
* @param expanderWidth If set initializes header width
* @param initContent Optional lambda with [innerTable] as parameter, to help initialize content.
*/
class ExpanderTab(
title: String,
fontSize: Int = 24,
icon: Actor? = null,
startsOutOpened: Boolean = true,
defaultPad: Float = 10f,
expanderWidth: Float = 0f,
initContent: ((Table) -> Unit)? = null
): Table(CameraStageBaseScreen.skin) {
private companion object {
const val fontSize = 24
const val arrowSize = 18f
const val arrowImage = "OtherIcons/BackArrow"
val arrowColor = Color(1f,0.96f,0.75f,1f)
@ -56,14 +58,25 @@ class ExpanderTab(
headerIcon.rotation = 180f
headerIcon.color = arrowColor
header.background(ImageGetter.getBackground(ImageGetter.getBlue()))
if (icon != null) header.add(icon)
header.add(headerLabel)
header.add(headerIcon).size(arrowSize).align(Align.center)
header.touchable= Touchable.enabled
header.onClick { toggle() }
add(header).expandX().fill().row()
contentWrapper.defaults().pad(defaultPad)
add(contentWrapper).expandX()
if (expanderWidth != 0f)
defaults().minWidth(expanderWidth)
defaults().growX()
contentWrapper.defaults().growX().pad(defaultPad)
innerTable.defaults().growX()
add(header).fillY().row()
add(contentWrapper)
contentWrapper.add(innerTable) // update will revert this
initContent?.invoke(innerTable)
if (expanderWidth == 0f) {
// Measure content width incl. pad, set header to same width
if (innerTable.needsLayout()) contentWrapper.pack()
getCell(header).minWidth(contentWrapper.width)
}
update(noAnimation = true)
}