diff --git a/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt b/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt
index 3cf8d7b12e..2f38026441 100644
--- a/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt
+++ b/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt
@@ -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
, 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()
}
-}
\ No newline at end of file
+}
diff --git a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt
index 1170640708..ad7ba5be33 100644
--- a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt
+++ b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt
@@ -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()
}
-}
\ No newline at end of file
+}
diff --git a/core/src/com/unciv/ui/trade/OffersListScroll.kt b/core/src/com/unciv/ui/trade/OffersListScroll.kt
index f6d757dfa6..c402355a6f 100644
--- a/core/src/com/unciv/ui/trade/OffersListScroll.kt
+++ b/core/src/com/unciv/ui/trade/OffersListScroll.kt
@@ -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
diff --git a/core/src/com/unciv/ui/trade/ExpanderTab.kt b/core/src/com/unciv/ui/utils/ExpanderTab.kt
similarity index 76%
rename from core/src/com/unciv/ui/trade/ExpanderTab.kt
rename to core/src/com/unciv/ui/utils/ExpanderTab.kt
index 195edd9478..83d5a43954 100644
--- a/core/src/com/unciv/ui/trade/ExpanderTab.kt
+++ b/core/src/com/unciv/ui/utils/ExpanderTab.kt
@@ -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)
}