mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-09 15:29:32 +07:00
City detailed stats popup - fixed header (#8959)
* DetailedStatsPopup fixed header * DetailedStatsPopup formatters optimized * DetailedStatsPopup keyboard
This commit is contained in:
@ -59,7 +59,7 @@ private class ConstructionButtonDTO(
|
||||
|
||||
/**
|
||||
* Manager to hold and coordinate two widgets for the city screen left side:
|
||||
* - Construction queue with switch to [ConstructionInfoTable] button and the enqueue / buy buttons.
|
||||
* - Construction queue with the enqueue / buy buttons.
|
||||
* The queue is scrollable, limited to one third of the stage height.
|
||||
* - Available constructions display, scrolling, grouped with expanders and therefore of dynamic height.
|
||||
*/
|
||||
|
@ -59,15 +59,15 @@ class CityScreen(
|
||||
// Clockwise from the top-left
|
||||
|
||||
/** Displays current production, production queue and available productions list
|
||||
* Not a widget, but manages two: construction queue, info toggle button, buy buttons
|
||||
* in a Table holder on upper LEFT, and available constructions in a ScrollPane lower LEFT.
|
||||
* Not a widget, but manages two: construction queue + buy buttons
|
||||
* in a Table holder on TOP LEFT, and available constructions in a ScrollPane BOTTOM LEFT.
|
||||
*/
|
||||
private var constructionsTable = CityConstructionsTable(this)
|
||||
|
||||
/** Displays raze city button - sits on TOP CENTER */
|
||||
private var razeCityButtonHolder = Table()
|
||||
|
||||
/** Displays city stats info */
|
||||
/** Displays city stats, population management, religion, built buildings info - TOP RIGHT */
|
||||
private var cityStatsTable = CityStatsTable(this)
|
||||
|
||||
/** Displays tile info, alternate with selectedConstructionTable - sits on BOTTOM RIGHT */
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.unciv.ui.screens.cityscreen
|
||||
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Stage
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
@ -9,25 +9,28 @@ import com.unciv.logic.city.StatTreeNode
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.images.IconCircleGroup
|
||||
import com.unciv.ui.popups.Popup
|
||||
import com.unciv.ui.components.AutoScrollPane
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.components.KeyCharAndCode
|
||||
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.keyShortcuts
|
||||
import com.unciv.ui.components.extensions.onActivation
|
||||
import com.unciv.ui.components.extensions.onClick
|
||||
import com.unciv.ui.components.extensions.packIfNeeded
|
||||
import com.unciv.ui.components.extensions.pad
|
||||
import com.unciv.ui.components.extensions.surroundWithCircle
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.images.IconCircleGroup
|
||||
import com.unciv.ui.popups.Popup
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.max
|
||||
|
||||
class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Popup(
|
||||
stageToShowOn = stageToShowOn,
|
||||
scrollable = false) {
|
||||
|
||||
constructor(screen: CityScreen) : this(screen, screen.stage)
|
||||
|
||||
class DetailedStatsPopup(
|
||||
private val cityScreen: CityScreen
|
||||
) : Popup(stageToShowOn = cityScreen.stage, scrollable = false) {
|
||||
private val headerTable = Table()
|
||||
private val totalTable = Table()
|
||||
|
||||
private var sourceHighlighted: String? = null
|
||||
@ -37,20 +40,27 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
private val colorTotal: Color = Color.BLUE.brighten(0.5f)
|
||||
private val colorSelector: Color = Color.GREEN.darken(0.5f)
|
||||
|
||||
private val percentFormatter = DecimalFormat("0.#%").apply { positivePrefix = "+"; multiplier = 1 }
|
||||
private val decimalFormatter = DecimalFormat("0.#")
|
||||
|
||||
init {
|
||||
headerTable.defaults().pad(3f, 0f)
|
||||
add(headerTable).padBottom(0f).row()
|
||||
|
||||
totalTable.defaults().pad(3f, 0f)
|
||||
|
||||
val scrollPane = AutoScrollPane(totalTable)
|
||||
scrollPane.setOverscroll(false, false)
|
||||
val scrollPaneCell = add(scrollPane)
|
||||
val scrollPaneCell = add(scrollPane).padTop(0f)
|
||||
scrollPaneCell.maxHeight(cityScreen.stage.height *3 / 4)
|
||||
|
||||
row()
|
||||
addCloseButton("Cancel", KeyCharAndCode('n'))
|
||||
addCloseButton(additionalKey = KeyCharAndCode.SPACE)
|
||||
update()
|
||||
}
|
||||
|
||||
private fun update() {
|
||||
|
||||
headerTable.clear()
|
||||
totalTable.clear()
|
||||
|
||||
val cityStats = cityScreen.city.cityStats
|
||||
@ -61,12 +71,10 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
!showFaith -> Stat.values().filter { it != Stat.Faith }
|
||||
else -> Stat.values().toList()
|
||||
}
|
||||
val columnCount = stats.size + 1
|
||||
val statColMinWidth = if (onlyWithStat != null) 150f else 110f
|
||||
|
||||
totalTable.defaults().pad(3f).padLeft(0f).padRight(0f)
|
||||
|
||||
totalTable.add(getToggleButton(isDetailed).onClick {
|
||||
isDetailed = !isDetailed
|
||||
update() }).minWidth(150f).grow()
|
||||
headerTable.add(getToggleButton(isDetailed)).minWidth(150f).grow()
|
||||
|
||||
for (stat in stats) {
|
||||
val label = stat.name.toLabel()
|
||||
@ -74,26 +82,26 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
onlyWithStat = if (onlyWithStat == null) stat else null
|
||||
update()
|
||||
}
|
||||
totalTable.add(wrapInTable(label, if (onlyWithStat == stat) colorSelector else null))
|
||||
.minWidth(if (onlyWithStat == stat) 150f else 110f).grow()
|
||||
headerTable.add(wrapInTable(label, if (onlyWithStat == stat) colorSelector else null))
|
||||
.minWidth(statColMinWidth).grow()
|
||||
}
|
||||
totalTable.row()
|
||||
headerTable.row()
|
||||
headerTable.addSeparator().padBottom(2f)
|
||||
|
||||
totalTable.addSeparator().padBottom(2f)
|
||||
totalTable.add("Base values".toLabel().apply { setAlignment(Align.center) })
|
||||
.colspan(totalTable.columns).padLeft(0f).padRight(0f).growX().row()
|
||||
totalTable.addSeparator().padTop(2f)
|
||||
.colspan(columnCount).growX().row()
|
||||
totalTable.addSeparator(colSpan = columnCount).padTop(2f)
|
||||
traverseTree(totalTable, stats, cityStats.baseStatTree, mergeHappiness = true, percentage = false)
|
||||
|
||||
totalTable.addSeparator().padBottom(2f)
|
||||
totalTable.add("Bonuses".toLabel().apply { setAlignment(Align.center) })
|
||||
.colspan(totalTable.columns).padLeft(0f).padRight(0f).growX().row()
|
||||
.colspan(columnCount).growX().row()
|
||||
totalTable.addSeparator().padTop(2f)
|
||||
traverseTree(totalTable, stats, cityStats.statPercentBonusTree, percentage = true)
|
||||
|
||||
totalTable.addSeparator().padBottom(2f)
|
||||
totalTable.add("Final".toLabel().apply { setAlignment(Align.center) })
|
||||
.colspan(totalTable.columns).padLeft(0f).padRight(0f).growX().row()
|
||||
.colspan(columnCount).growX().row()
|
||||
totalTable.addSeparator().padTop(2f)
|
||||
|
||||
val final = LinkedHashMap<Stat, Float>()
|
||||
@ -101,8 +109,7 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
|
||||
for ((key, value) in cityScreen.city.cityStats.happinessList) {
|
||||
if (!map.containsKey(key)) {
|
||||
map[key] = Stats()
|
||||
map[key]!![Stat.Happiness] = value
|
||||
map[key] = Stats(happiness = value)
|
||||
} else if (map[key]!![Stat.Happiness] == 0f) {
|
||||
map[key]!![Stat.Happiness] = value
|
||||
}
|
||||
@ -110,7 +117,7 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
|
||||
for ((source, finalStats) in map) {
|
||||
|
||||
if (finalStats.all { it.value == 0f })
|
||||
if (finalStats.isEmpty())
|
||||
continue
|
||||
|
||||
if (onlyWithStat != null && finalStats[onlyWithStat!!] == 0f)
|
||||
@ -124,11 +131,7 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
}
|
||||
}
|
||||
|
||||
var color: Color? = null
|
||||
|
||||
if (sourceHighlighted == source)
|
||||
color = colorSelector
|
||||
|
||||
val color = colorSelector.takeIf { sourceHighlighted == source }
|
||||
totalTable.add(wrapInTable(label, color, Align.left)).grow()
|
||||
|
||||
for (stat in stats) {
|
||||
@ -152,17 +155,36 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
|
||||
totalTable.add(wrapInTable("Total".toLabel(), colorTotal)).grow()
|
||||
for (stat in stats) {
|
||||
totalTable.add(wrapInTable(final[stat]?.toOneDecimalLabel(), colorTotal)).grow()
|
||||
totalTable.add(wrapInTable(final[stat]?.toOneDecimalLabel(), colorTotal))
|
||||
.minWidth(statColMinWidth).grow()
|
||||
}
|
||||
totalTable.row()
|
||||
|
||||
// Mini version of EmpireOverviewTab.equalizeColumns - the number columns work thanks to statColMinWidth
|
||||
headerTable.packIfNeeded()
|
||||
totalTable.packIfNeeded()
|
||||
val firstColumnWidth = max(totalTable.getColumnWidth(0), headerTable.getColumnWidth(0))
|
||||
headerTable.cells.first().minWidth(firstColumnWidth)
|
||||
totalTable.cells.first().minWidth(firstColumnWidth)
|
||||
headerTable.invalidate()
|
||||
totalTable.invalidate()
|
||||
}
|
||||
|
||||
private fun getToggleButton(showDetails: Boolean): IconCircleGroup {
|
||||
val label = (if (showDetails) "-" else "+").toLabel()
|
||||
label.setAlignment(Align.center)
|
||||
return label
|
||||
val button = label
|
||||
.surroundWithCircle(25f, color = BaseScreen.skinStrings.skinConfig.baseColor)
|
||||
.surroundWithCircle(27f, false)
|
||||
button.keyShortcuts.run {
|
||||
add(Input.Keys.PLUS)
|
||||
add(Input.Keys.NUMPAD_ADD)
|
||||
}
|
||||
button.onActivation {
|
||||
isDetailed = !isDetailed
|
||||
update()
|
||||
}
|
||||
return button
|
||||
}
|
||||
|
||||
private fun traverseTree(
|
||||
@ -263,11 +285,6 @@ class DetailedStatsPopup(val cityScreen: CityScreen, stageToShowOn: Stage) : Pop
|
||||
return tbl
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun Float.toPercentLabel() =
|
||||
"${if (this>0f) "+" else ""}${DecimalFormat("0.#").format(this)}%".toLabel()
|
||||
private fun Float.toOneDecimalLabel() =
|
||||
DecimalFormat("0.#").format(this).toLabel()
|
||||
}
|
||||
private fun Float.toPercentLabel() = percentFormatter.format(this).toLabel()
|
||||
private fun Float.toOneDecimalLabel() = decimalFormatter.format(this).toLabel()
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user