mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-22 22:00:24 +07:00
Redesign EmpireOverviewScreen as TabbedPager (#6364)
This commit is contained in:
@ -18,7 +18,18 @@ import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class CityOverviewTable(private val viewingPlayer: CivilizationInfo, private val overviewScreen: EmpireOverviewScreen): Table() {
|
||||
class CityOverviewTab(
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen,
|
||||
persistedData: EmpireOverviewTabPersistableData? = null
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
class CityTabPersistableData(
|
||||
var sortedBy: String = "City"
|
||||
) : EmpireOverviewTabPersistableData() {
|
||||
override fun isEmpty() = sortedBy == "City"
|
||||
}
|
||||
|
||||
override val persistableData = (persistedData as? CityTabPersistableData) ?: CityTabPersistableData()
|
||||
|
||||
companion object {
|
||||
const val iconSize = 50f //if you set this too low, there is a chance that the tables will be misaligned
|
||||
@ -31,20 +42,19 @@ class CityOverviewTable(private val viewingPlayer: CivilizationInfo, private val
|
||||
|
||||
init {
|
||||
val numHeaderCells = columnsNames.size + 2 // +1 City +1 Filler
|
||||
var sortedBy = "City"
|
||||
|
||||
val cityInfoTableIcons = Table(skin)
|
||||
val cityInfoTableDetails = Table(skin)
|
||||
val cityInfoTableTotal = Table(skin)
|
||||
|
||||
fun sortOnClick(iconName: String) {
|
||||
val descending = sortedBy == iconName
|
||||
sortedBy = iconName
|
||||
val descending = persistableData.sortedBy == iconName
|
||||
persistableData.sortedBy = iconName
|
||||
// sort the table: clear and fill with sorted data
|
||||
cityInfoTableDetails.clear()
|
||||
fillCitiesTable(cityInfoTableDetails, iconName, descending)
|
||||
// reset to return back for ascending next time
|
||||
if (descending) sortedBy = ""
|
||||
if (descending) persistableData.sortedBy = ""
|
||||
}
|
||||
|
||||
fun addSortIcon(iconName: String, iconParam: Actor? = null) {
|
||||
@ -77,7 +87,7 @@ class CityOverviewTable(private val viewingPlayer: CivilizationInfo, private val
|
||||
.minWidth(iconSize) //we need the min width so we can align the different tables
|
||||
.align(Align.left)
|
||||
|
||||
fillCitiesTable(cityInfoTableDetails, sortedBy, false)
|
||||
fillCitiesTable(cityInfoTableDetails, persistableData.sortedBy, false)
|
||||
|
||||
val cityInfoScrollPane = AutoScrollPane(cityInfoTableDetails)
|
||||
cityInfoScrollPane.pack()
|
||||
|
@ -12,12 +12,17 @@ import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.ui.trade.DiplomacyScreen
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
class DiplomacyOverviewTable (
|
||||
private val viewingPlayer: CivilizationInfo,
|
||||
private val overviewScreen: EmpireOverviewScreen
|
||||
): Table() {
|
||||
|
||||
private var includeCityStates = false
|
||||
class DiplomacyOverviewTab (
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen,
|
||||
persistedData: EmpireOverviewTabPersistableData? = null
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
class DiplomacyTabPersistableData(
|
||||
var includeCityStates: Boolean = false
|
||||
) : EmpireOverviewTabPersistableData() {
|
||||
override fun isEmpty() = !includeCityStates
|
||||
}
|
||||
override val persistableData = (persistedData as? DiplomacyTabPersistableData) ?: DiplomacyTabPersistableData()
|
||||
|
||||
init {
|
||||
update()
|
||||
@ -25,9 +30,9 @@ class DiplomacyOverviewTable (
|
||||
|
||||
fun update() {
|
||||
clear()
|
||||
val relevantCivs = viewingPlayer.gameInfo.civilizations
|
||||
.filter { !it.isBarbarian() && !it.isSpectator() && (includeCityStates || !it.isCityState()) }
|
||||
val diplomacyGroup = DiplomacyGroup(viewingPlayer, overviewScreen.centerAreaHeight, includeCityStates)
|
||||
val relevantCivs = gameInfo.civilizations
|
||||
.filter { !it.isBarbarian() && !it.isSpectator() && (persistableData.includeCityStates || !it.isCityState()) }
|
||||
val diplomacyGroup = DiplomacyGroup(viewingPlayer, overviewScreen.centerAreaHeight, persistableData.includeCityStates)
|
||||
val playerKnowsAndUndefeatedCivs = relevantCivs.filter { diplomacyGroup.playerKnows(it) && !it.isDefeated() }
|
||||
val playerKnowsAndDefeatedCivs = relevantCivs.filter { diplomacyGroup.playerKnows(it) && it.isDefeated() }
|
||||
if (playerKnowsAndUndefeatedCivs.size > 1)
|
||||
@ -39,12 +44,14 @@ class DiplomacyOverviewTable (
|
||||
titleTable.add(viewingPlayer.civName.toLabel()).left().padRight(10f)
|
||||
titleTable.add(viewingPlayer.calculateScoreBreakdown().values.sum().toInt().toLabel()).row()
|
||||
|
||||
|
||||
val civTableScrollPane = getCivTableScroll(relevantCivs, titleTable, playerKnowsAndUndefeatedCivs, playerKnowsAndDefeatedCivs)
|
||||
|
||||
val toggleCityStatesButton = "City-States".toTextButton()
|
||||
toggleCityStatesButton.color = if(includeCityStates) Color.RED else Color.GREEN
|
||||
toggleCityStatesButton.onClick { includeCityStates = !includeCityStates; update() }
|
||||
toggleCityStatesButton.color = if (persistableData.includeCityStates) Color.RED else Color.GREEN
|
||||
toggleCityStatesButton.onClick {
|
||||
persistableData.includeCityStates = !persistableData.includeCityStates
|
||||
update()
|
||||
}
|
||||
|
||||
val floatingTable = Table()
|
||||
floatingTable.add(toggleCityStatesButton).row()
|
||||
|
@ -0,0 +1,65 @@
|
||||
package com.unciv.ui.overviewscreen
|
||||
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.ui.utils.KeyCharAndCode
|
||||
import com.unciv.ui.overviewscreen.EmpireOverviewTab.EmpireOverviewTabPersistableData
|
||||
|
||||
private typealias FactoryType = (CivilizationInfo, EmpireOverviewScreen, EmpireOverviewTabPersistableData?) -> EmpireOverviewTab
|
||||
|
||||
enum class EmpireOverviewTabState { Normal, Disabled, Hidden }
|
||||
private typealias StateTesterType = (CivilizationInfo) -> EmpireOverviewTabState
|
||||
private fun Boolean.toState(): EmpireOverviewTabState = if (this) EmpireOverviewTabState.Disabled else EmpireOverviewTabState.Normal
|
||||
|
||||
/** This controls which Tabs for the [EmpireOverviewScreen] exist and their order.
|
||||
*
|
||||
* To add a Tab, build a new [EmpireOverviewTab] subclass and fill out a new entry here, that's all.
|
||||
* Note the enum value's name is used as Tab caption, so if you ever need a non-alphanumeric caption please redesign to include a property for the caption.
|
||||
*/
|
||||
enum class EmpireOverviewCategories(
|
||||
val iconName: String,
|
||||
val shortcutKey: KeyCharAndCode,
|
||||
val factory: FactoryType,
|
||||
val stateTester: StateTesterType
|
||||
) {
|
||||
Cities("OtherIcons/Cities", 'C',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)
|
||||
= CityOverviewTab(viewingPlayer, overviewScreen, persistedData),
|
||||
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.cities.isEmpty().toState()),
|
||||
Stats("StatIcons/Gold", 'S',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?)
|
||||
= StatsOverviewTab(viewingPlayer, overviewScreen),
|
||||
fun (_: CivilizationInfo) = EmpireOverviewTabState.Normal),
|
||||
Trades("StatIcons/Acquire", 'T',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?)
|
||||
= TradesOverviewTab(viewingPlayer, overviewScreen),
|
||||
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.diplomacy.values.all { it.trades.isEmpty() }.toState()),
|
||||
Units("OtherIcons/Shield", 'U',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?)
|
||||
= UnitOverviewTab(viewingPlayer, overviewScreen),
|
||||
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.getCivUnits().none().toState()),
|
||||
Diplomacy("OtherIcons/DiplomacyW", 'D',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)
|
||||
= DiplomacyOverviewTab(viewingPlayer, overviewScreen, persistedData),
|
||||
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.diplomacy.isEmpty().toState()),
|
||||
Resources("StatIcons/Happiness", 'R',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?)
|
||||
= ResourcesOverviewTab(viewingPlayer, overviewScreen),
|
||||
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.detailedCivResources.isEmpty().toState()),
|
||||
Religion("StatIcons/Faith", 'F',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)
|
||||
= ReligionOverviewTab(viewingPlayer, overviewScreen, persistedData),
|
||||
fun (viewingPlayer: CivilizationInfo) = when {
|
||||
!viewingPlayer.gameInfo.isReligionEnabled() -> EmpireOverviewTabState.Hidden
|
||||
viewingPlayer.gameInfo.religions.isEmpty() -> EmpireOverviewTabState.Disabled
|
||||
else -> EmpireOverviewTabState.Normal
|
||||
}),
|
||||
Wonders("OtherIcons/Wonders", 'W',
|
||||
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?)
|
||||
= WonderOverviewTab(viewingPlayer, overviewScreen),
|
||||
fun (viewingPlayer: CivilizationInfo) = (viewingPlayer.naturalWonders.isEmpty() && viewingPlayer.cities.isEmpty()).toState()),
|
||||
;
|
||||
|
||||
constructor(iconName: String, shortcutChar: Char, factory: FactoryType, stateTester: StateTesterType = { _ -> EmpireOverviewTabState.Normal })
|
||||
: this(iconName, KeyCharAndCode(shortcutChar), factory, stateTester)
|
||||
}
|
||||
|
@ -1,73 +1,37 @@
|
||||
package com.unciv.ui.overviewscreen
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Button
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.utils.KeyPressDispatcher.Companion.keyboardAvailable
|
||||
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
||||
import com.unciv.ui.overviewscreen.EmpireOverviewTab.EmpireOverviewTabPersistableData
|
||||
import com.unciv.ui.utils.BaseScreen
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.utils.TabbedPager
|
||||
|
||||
class EmpireOverviewScreen(
|
||||
private var viewingPlayer: CivilizationInfo,
|
||||
defaultPage: String = ""
|
||||
) : BaseScreen() {
|
||||
private val topTable = Table().apply { defaults().pad(10f) }
|
||||
private val centerTable = Table().apply { defaults().pad(5f) }
|
||||
|
||||
internal val setCategoryActions = HashMap<String, () -> Unit>()
|
||||
private val categoryButtons = HashMap<String, Button>()
|
||||
|
||||
// 50 normal button height + 2*10 topTable padding + 2 Separator + 2*5 centerTable padding
|
||||
// Since a resize recreates this screen this should be fine as a val
|
||||
internal val centerAreaHeight = stage.height - 82f
|
||||
|
||||
private object ButtonDecorations {
|
||||
data class IconAndKey (val icon: String, val key: Char = Char.MIN_VALUE)
|
||||
val keyIconMap: HashMap<String,IconAndKey> = hashMapOf(
|
||||
Pair("Cities", IconAndKey("OtherIcons/Cities", 'C')),
|
||||
Pair("Stats", IconAndKey("StatIcons/Gold", 'S')),
|
||||
Pair("Trades", IconAndKey("StatIcons/Acquire", 'T')),
|
||||
Pair("Units", IconAndKey("OtherIcons/Shield", 'U')),
|
||||
Pair("Diplomacy", IconAndKey("OtherIcons/DiplomacyW", 'D')),
|
||||
Pair("Resources", IconAndKey("StatIcons/Happiness", 'R')),
|
||||
Pair("Religion", IconAndKey("StatIcons/Faith", 'F')),
|
||||
Pair("Wonders", IconAndKey("OtherIcons/Wonders", 'W'))
|
||||
)
|
||||
private val tabbedPager: TabbedPager
|
||||
private val pageObjects = HashMap<EmpireOverviewCategories, EmpireOverviewTab>()
|
||||
|
||||
companion object {
|
||||
// This is what keeps per-tab states between overview invocations
|
||||
var persistState: Map<EmpireOverviewCategories, EmpireOverviewTabPersistableData>? = null
|
||||
|
||||
private fun updatePersistState(pageObjects: HashMap<EmpireOverviewCategories, EmpireOverviewTab>) {
|
||||
persistState = pageObjects.mapValues { it.value.persistableData }.filterNot { it.value.isEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCategory(name: String, table: Table, disabled: Boolean = false) {
|
||||
// Buttons now hold their old label plus optionally an indicator for the shortcut key.
|
||||
// Implement this templated on UnitActionsTable.getUnitActionButton()
|
||||
val iconAndKey = ButtonDecorations.keyIconMap[name] ?: return // category without decoration entry disappears
|
||||
val setCategoryAction = {
|
||||
centerTable.clear()
|
||||
centerTable.add(ScrollPane(table).apply { setOverscroll(false, false) })
|
||||
.height(centerAreaHeight)
|
||||
.width(stage.width)
|
||||
centerTable.pack()
|
||||
for ((key, categoryButton) in categoryButtons.filterNot { it.value.touchable == Touchable.disabled })
|
||||
categoryButton.color = if (key == name) Color.BLUE else Color.WHITE
|
||||
if (name == "Stats")
|
||||
game.settings.addCompletedTutorialTask("See your stats breakdown")
|
||||
game.settings.lastOverviewPage = name
|
||||
}
|
||||
val icon = if (iconAndKey.icon != "") ImageGetter.getImage(iconAndKey.icon) else null
|
||||
val button = IconTextButton(name, icon)
|
||||
if (!disabled && keyboardAvailable && iconAndKey.key != Char.MIN_VALUE) {
|
||||
button.addTooltip(iconAndKey.key)
|
||||
keyPressDispatcher[iconAndKey.key] = setCategoryAction
|
||||
}
|
||||
setCategoryActions[name] = setCategoryAction
|
||||
categoryButtons[name] = button
|
||||
button.onClick(setCategoryAction)
|
||||
if (disabled) button.disable()
|
||||
topTable.add(button)
|
||||
override fun dispose() {
|
||||
tabbedPager.selectPage(-1)
|
||||
updatePersistState(pageObjects)
|
||||
super.dispose()
|
||||
}
|
||||
|
||||
init {
|
||||
@ -77,76 +41,59 @@ class EmpireOverviewScreen(
|
||||
defaultPage
|
||||
}
|
||||
else game.settings.lastOverviewPage
|
||||
val iconSize = Constants.defaultFontSize.toFloat()
|
||||
|
||||
onBackButtonClicked { game.setWorldScreen() }
|
||||
|
||||
addCategory("Cities", CityOverviewTable(viewingPlayer, this), viewingPlayer.cities.isEmpty())
|
||||
addCategory("Stats", StatsOverviewTable(viewingPlayer, this))
|
||||
addCategory("Trades", TradesOverviewTable(viewingPlayer, this), viewingPlayer.diplomacy.values.all { it.trades.isEmpty() })
|
||||
addCategory("Units", UnitOverviewTable(viewingPlayer, this), viewingPlayer.getCivUnits().none())
|
||||
addCategory("Diplomacy", DiplomacyOverviewTable(viewingPlayer, this), viewingPlayer.diplomacy.isEmpty())
|
||||
addCategory("Resources", ResourcesOverviewTable(viewingPlayer, this), viewingPlayer.detailedCivResources.isEmpty())
|
||||
if (viewingPlayer.gameInfo.isReligionEnabled())
|
||||
addCategory("Religion", ReligionOverviewTable(viewingPlayer, this), viewingPlayer.gameInfo.religions.isEmpty())
|
||||
addCategory("Wonders", WonderOverviewTable(viewingPlayer, this), viewingPlayer.naturalWonders.isEmpty() && viewingPlayer.cities.isEmpty())
|
||||
tabbedPager = TabbedPager(
|
||||
stage.width, stage.width,
|
||||
centerAreaHeight, centerAreaHeight,
|
||||
separatorColor = Color.WHITE,
|
||||
keyPressDispatcher = keyPressDispatcher,
|
||||
capacity = EmpireOverviewCategories.values().size)
|
||||
|
||||
val closeButton = Constants.close.toTextButton().apply {
|
||||
setColor(0.75f, 0.1f, 0.1f, 1f)
|
||||
tabbedPager.addPage(Constants.close) {
|
||||
_, _ -> game.setWorldScreen()
|
||||
}
|
||||
closeButton.onClick { game.setWorldScreen() }
|
||||
closeButton.y = stage.height - closeButton.height - 5
|
||||
topTable.add(closeButton)
|
||||
tabbedPager.getPageButton(0).setColor(0.75f, 0.1f, 0.1f, 1f)
|
||||
|
||||
topTable.pack()
|
||||
val topScroll = ScrollPane(topTable).apply { setScrollingDisabled(false, true) }
|
||||
for (category in EmpireOverviewCategories.values()) {
|
||||
val tabState = category.stateTester(viewingPlayer)
|
||||
if (tabState == EmpireOverviewTabState.Hidden) continue
|
||||
val icon = if (category.iconName.isEmpty()) null else ImageGetter.getImage(category.iconName)
|
||||
val pageObject = category.factory(viewingPlayer, this, persistState?.get(category))
|
||||
pageObject.pad(10f, 0f, 10f, 0f)
|
||||
pageObjects[category] = pageObject
|
||||
val index = tabbedPager.addPage(
|
||||
caption = category.name,
|
||||
content = pageObject,
|
||||
icon, iconSize,
|
||||
disabled = tabState != EmpireOverviewTabState.Normal,
|
||||
shortcutKey = category.shortcutKey,
|
||||
fixedContent = pageObject.getFixedContent(),
|
||||
onDeactivation = { _, _, scrollY -> pageObject.deactivated(scrollY) }
|
||||
) {
|
||||
index, name ->
|
||||
val scrollY = pageObject.activated()
|
||||
if (scrollY != null) tabbedPager.setPageScrollY(index, scrollY)
|
||||
if (name == "Stats")
|
||||
game.settings.addCompletedTutorialTask("See your stats breakdown")
|
||||
game.settings.lastOverviewPage = name
|
||||
}
|
||||
if (category.name == page)
|
||||
tabbedPager.selectPage(index)
|
||||
}
|
||||
|
||||
setCategoryActions[page]?.invoke()
|
||||
|
||||
val table = Table()
|
||||
table.add(topScroll).row()
|
||||
table.addSeparator()
|
||||
table.add(centerTable).height(stage.height - topTable.height).expand().row()
|
||||
table.setFillParent(true)
|
||||
stage.addActor(table)
|
||||
}
|
||||
tabbedPager.setFillParent(true)
|
||||
stage.addActor(tabbedPager)
|
||||
}
|
||||
|
||||
override fun resize(width: Int, height: Int) {
|
||||
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
|
||||
updatePersistState(pageObjects)
|
||||
game.setScreen(EmpireOverviewScreen(viewingPlayer, game.settings.lastOverviewPage))
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
//todo this belongs in VictoryScreen as it's only ever used there
|
||||
companion object {
|
||||
fun getCivGroup(civ: CivilizationInfo, afterCivNameText:String, currentPlayer:CivilizationInfo): Table {
|
||||
val civGroup = Table()
|
||||
|
||||
var labelText = civ.civName.tr()+afterCivNameText
|
||||
var labelColor = Color.WHITE
|
||||
val backgroundColor: Color
|
||||
|
||||
if (civ.isDefeated()) {
|
||||
civGroup.add(ImageGetter.getImage("OtherIcons/DisbandUnit")).size(30f)
|
||||
backgroundColor = Color.LIGHT_GRAY
|
||||
labelColor = Color.BLACK
|
||||
} else if (currentPlayer == civ // || game.viewEntireMapForDebug
|
||||
|| currentPlayer.knows(civ) || currentPlayer.isDefeated() || currentPlayer.victoryManager.hasWon()) {
|
||||
civGroup.add(ImageGetter.getNationIndicator(civ.nation, 30f))
|
||||
backgroundColor = civ.nation.getOuterColor()
|
||||
labelColor = civ.nation.getInnerColor()
|
||||
} else {
|
||||
civGroup.add(ImageGetter.getRandomNationIndicator(30f))
|
||||
backgroundColor = Color.DARK_GRAY
|
||||
labelText = Constants.unknownNationName
|
||||
}
|
||||
|
||||
civGroup.background = ImageGetter.getRoundedEdgeRectangle(backgroundColor)
|
||||
val label = labelText.toLabel(labelColor)
|
||||
label.setAlignment(Align.center)
|
||||
|
||||
civGroup.add(label).padLeft(10f)
|
||||
civGroup.pack()
|
||||
return civGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
|
40
core/src/com/unciv/ui/overviewscreen/EmpireOverviewTab.kt
Normal file
40
core/src/com/unciv/ui/overviewscreen/EmpireOverviewTab.kt
Normal file
@ -0,0 +1,40 @@
|
||||
package com.unciv.ui.overviewscreen
|
||||
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.ui.utils.BaseScreen
|
||||
|
||||
abstract class EmpireOverviewTab (
|
||||
val viewingPlayer: CivilizationInfo,
|
||||
val overviewScreen: EmpireOverviewScreen,
|
||||
persistedData: EmpireOverviewTabPersistableData? = null
|
||||
) : Table(BaseScreen.skin) {
|
||||
open class EmpireOverviewTabPersistableData {
|
||||
open fun isEmpty() = true
|
||||
}
|
||||
open val persistableData = persistedData ?: EmpireOverviewTabPersistableData()
|
||||
/** Override if your Tab needs to do stuff on activation. @return non-null to scroll the Tab vertically within the TabbedPager. */
|
||||
open fun activated(): Float? = null
|
||||
/** Override if your Tab needs to do housekeeping when it loses focus. [scrollY] is the Tab's current vertical scroll position. */
|
||||
open fun deactivated(scrollY: Float) {}
|
||||
/** Override to supply content not participating in scrolling */
|
||||
open fun getFixedContent(): WidgetGroup? = null
|
||||
|
||||
val gameInfo = viewingPlayer.gameInfo
|
||||
|
||||
protected fun equalizeColumns(vararg tables: Table) {
|
||||
val columns = tables.first().columns
|
||||
val widths = (0 until columns)
|
||||
.mapTo(ArrayList(columns)) { column ->
|
||||
tables.maxOf { it.getColumnWidth(column) }
|
||||
}
|
||||
for (table in tables) {
|
||||
for (column in 0 until columns)
|
||||
table.cells[column].run {
|
||||
minWidth(widths[column] - padLeft - padRight)
|
||||
}
|
||||
table.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
@ -17,20 +17,23 @@ import com.unciv.ui.civilopedia.MarkupRenderer
|
||||
import com.unciv.ui.utils.*
|
||||
import kotlin.math.max
|
||||
|
||||
class ReligionOverviewTable(
|
||||
private val viewingPlayer: CivilizationInfo,
|
||||
private val overviewScreen: EmpireOverviewScreen
|
||||
): Table() {
|
||||
class ReligionOverviewTab(
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen,
|
||||
persistedData: EmpireOverviewTabPersistableData? = null
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
class ReligionTabPersistableData(
|
||||
var selectedReligion: String? = null
|
||||
) : EmpireOverviewTabPersistableData() {
|
||||
override fun isEmpty() = selectedReligion == null
|
||||
}
|
||||
override val persistableData = (persistedData as? ReligionTabPersistableData) ?: ReligionTabPersistableData()
|
||||
|
||||
val gameInfo = viewingPlayer.gameInfo
|
||||
|
||||
private val civStatsTable = Table(BaseScreen.skin)
|
||||
private val religionButtons = Table(BaseScreen.skin)
|
||||
private val civStatsTable = Table()
|
||||
private val religionButtons = Table()
|
||||
private val religionButtonLabel = "Click an icon to see the stats of this religion".toLabel()
|
||||
private val statsTable = Table(BaseScreen.skin)
|
||||
private val beliefsTable = Table(BaseScreen.skin)
|
||||
|
||||
private var selectedReligion: String? = null
|
||||
private val statsTable = Table()
|
||||
private val beliefsTable = Table()
|
||||
|
||||
init {
|
||||
defaults().pad(5f)
|
||||
@ -38,14 +41,15 @@ class ReligionOverviewTable(
|
||||
loadReligionButtons()
|
||||
|
||||
civStatsTable.defaults().left().pad(5f)
|
||||
statsTable.defaults().left().pad(5f)
|
||||
beliefsTable.defaults().padBottom(20f)
|
||||
civStatsTable.addCivSpecificStats()
|
||||
add(civStatsTable).row()
|
||||
add(religionButtons).row()
|
||||
add(religionButtonLabel)
|
||||
addSeparator()
|
||||
statsTable.defaults().left().pad(5f)
|
||||
loadReligion(persistableData.selectedReligion)
|
||||
add(statsTable).row()
|
||||
beliefsTable.defaults().padBottom(20f)
|
||||
add(beliefsTable).pad(20f)
|
||||
}
|
||||
|
||||
@ -85,18 +89,23 @@ class ReligionOverviewTable(
|
||||
val button = Button(image, BaseScreen.skin)
|
||||
|
||||
button.onClick {
|
||||
selectedReligion = religion.name
|
||||
persistableData.selectedReligion = religion.name
|
||||
loadReligionButtons()
|
||||
loadReligion(religion)
|
||||
}
|
||||
|
||||
if (selectedReligion == religion.name)
|
||||
if (persistableData.selectedReligion == religion.name)
|
||||
button.disable()
|
||||
|
||||
religionButtons.add(button).pad(5f)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadReligion(religionName: String?) {
|
||||
if (religionName == null) return
|
||||
val religion = gameInfo.religions[religionName] ?: return
|
||||
loadReligion(religion)
|
||||
}
|
||||
private fun loadReligion(religion: Religion) {
|
||||
statsTable.clear()
|
||||
beliefsTable.clear()
|
||||
|
@ -11,11 +11,10 @@ import com.unciv.ui.utils.addSeparator
|
||||
import com.unciv.ui.utils.onClick
|
||||
import com.unciv.ui.utils.toLabel
|
||||
|
||||
@Suppress("UNUSED_PARAMETER") // Keep all OverviewScreen Pages compatible
|
||||
class ResourcesOverviewTable (
|
||||
class ResourcesOverviewTab(
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen
|
||||
) : Table() {
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
|
||||
init {
|
||||
defaults().pad(10f)
|
||||
|
@ -6,145 +6,150 @@ import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.StatMap
|
||||
import com.unciv.ui.utils.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class StatsOverviewTable (
|
||||
private val viewingPlayer: CivilizationInfo,
|
||||
private val overviewScreen: EmpireOverviewScreen
|
||||
) : Table() {
|
||||
class StatsOverviewTab(
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
private val happinessTable = Table()
|
||||
private val goldAndSliderTable = Table()
|
||||
private val goldTable = Table()
|
||||
private val scienceTable = Table()
|
||||
private val greatPeopleTable = Table()
|
||||
private val scoreTable = Table()
|
||||
|
||||
init {
|
||||
defaults().pad(40f)
|
||||
add(getHappinessTable()).top()
|
||||
add(getGoldTable()).top()
|
||||
add(getScienceTable()).top()
|
||||
add(getGreatPeopleTable()).top()
|
||||
add(getScoreTable()).top()
|
||||
}
|
||||
val tablePadding = 30f // Padding around each of the stat tables
|
||||
defaults().pad(tablePadding).top()
|
||||
|
||||
private fun getHappinessTable(): Table {
|
||||
val happinessTable = Table(BaseScreen.skin)
|
||||
happinessTable.defaults().pad(5f)
|
||||
happinessTable.add("Happiness".toLabel(fontSize = Constants.headingFontSize)).colspan(2).row()
|
||||
happinessTable.addSeparator()
|
||||
|
||||
val happinessBreakdown = viewingPlayer.stats().getHappinessBreakdown()
|
||||
|
||||
for (entry in happinessBreakdown.filterNot { it.value.roundToInt()==0 }) {
|
||||
happinessTable.add(entry.key.toLabel())
|
||||
happinessTable.add(entry.value.roundToInt().toString()).right().row()
|
||||
}
|
||||
happinessTable.add("Total".toLabel())
|
||||
happinessTable.add(happinessBreakdown.values.sum().roundToInt().toString()).right()
|
||||
happinessTable.pack()
|
||||
return happinessTable
|
||||
}
|
||||
|
||||
private fun getGoldTable(): Table {
|
||||
val goldTable = Table(BaseScreen.skin)
|
||||
goldTable.defaults().pad(5f)
|
||||
goldTable.add("Gold".toLabel(fontSize = Constants.headingFontSize)).colspan(2).row()
|
||||
goldTable.addSeparator()
|
||||
var total = 0f
|
||||
for (entry in viewingPlayer.stats().getStatMapForNextTurn()) {
|
||||
if (entry.value.gold == 0f) continue
|
||||
goldTable.add(entry.key.toLabel())
|
||||
goldTable.add(entry.value.gold.roundToInt().toString()).right().row()
|
||||
total += entry.value.gold
|
||||
}
|
||||
goldTable.add("Total".toLabel())
|
||||
goldTable.add(total.roundToInt().toString()).right()
|
||||
|
||||
if (viewingPlayer.gameInfo.ruleSet.modOptions.uniques.contains(ModOptionsConstants.convertGoldToScience)) {
|
||||
goldTable.addSeparator()
|
||||
val sliderTable = Table()
|
||||
sliderTable.add("Convert gold to science".toLabel()).row()
|
||||
|
||||
val slider = UncivSlider(0f, 1f, 0.1f,
|
||||
initial = viewingPlayer.tech.goldPercentConvertedToScience,
|
||||
getTipText = UncivSlider::formatPercent
|
||||
) {
|
||||
viewingPlayer.tech.goldPercentConvertedToScience = it
|
||||
for (city in viewingPlayer.cities) { city.cityStats.update() }
|
||||
overviewScreen.setCategoryActions["Stats"]!!() // ? will probably steal focus and so prevent dragging the slider
|
||||
}
|
||||
slider.isDisabled = !UncivGame.Current.worldScreen.canChangeState
|
||||
|
||||
sliderTable.add(slider).padTop(15f)
|
||||
goldTable.add(sliderTable).colspan(2)
|
||||
}
|
||||
|
||||
goldTable.pack()
|
||||
return goldTable
|
||||
}
|
||||
|
||||
private fun getScienceTable(): Table {
|
||||
val scienceTable = Table(BaseScreen.skin)
|
||||
scienceTable.defaults().pad(5f)
|
||||
scienceTable.add("Science".toLabel(fontSize = Constants.headingFontSize)).colspan(2).row()
|
||||
scienceTable.addSeparator()
|
||||
val scienceStats = viewingPlayer.stats().getStatMapForNextTurn()
|
||||
.filter { it.value.science != 0f }
|
||||
for (entry in scienceStats) {
|
||||
scienceTable.add(entry.key.toLabel())
|
||||
scienceTable.add(entry.value.science.roundToInt().toString()).right().row()
|
||||
}
|
||||
scienceTable.add("Total".toLabel())
|
||||
scienceTable.add(scienceStats.map { it.value.science }.sum().roundToInt().toString()).right()
|
||||
scienceTable.pack()
|
||||
return scienceTable
|
||||
greatPeopleTable.defaults().pad(5f)
|
||||
scoreTable.defaults().pad(5f)
|
||||
|
||||
goldAndSliderTable.add(goldTable).row()
|
||||
if (gameInfo.ruleSet.modOptions.uniques.contains(ModOptionsConstants.convertGoldToScience))
|
||||
goldAndSliderTable.addGoldSlider()
|
||||
|
||||
update()
|
||||
|
||||
add(happinessTable)
|
||||
add(goldAndSliderTable)
|
||||
add(scienceTable)
|
||||
add(greatPeopleTable)
|
||||
add(scoreTable)
|
||||
}
|
||||
|
||||
private fun getGreatPeopleTable(): Table {
|
||||
val greatPeopleTable = Table(BaseScreen.skin)
|
||||
fun update() {
|
||||
val statMap = viewingPlayer.stats().getStatMapForNextTurn()
|
||||
updateHappinessTable()
|
||||
goldTable.updateStatTable(Stat.Gold, statMap)
|
||||
scienceTable.updateStatTable(Stat.Science, statMap)
|
||||
updateGreatPeopleTable()
|
||||
updateScoreTable()
|
||||
}
|
||||
|
||||
greatPeopleTable.defaults().pad(5f)
|
||||
val greatPeopleHeader = Table(BaseScreen.skin)
|
||||
private fun Table.addHeading(label: String) {
|
||||
clear()
|
||||
add(label.toLabel(fontSize = Constants.headingFontSize)).colspan(2).row()
|
||||
addSeparator()
|
||||
}
|
||||
private fun Table.addLabeledValue(label: String, value: Float) {
|
||||
val roundedValue = value.roundToInt()
|
||||
if (roundedValue == 0) return
|
||||
add(label.toLabel()).left()
|
||||
add(roundedValue.toLabel()).right().row()
|
||||
}
|
||||
private fun Table.addTotal(value: Float) {
|
||||
add("Total".toLabel()).left()
|
||||
add(value.roundToInt().toLabel()).right()
|
||||
pack()
|
||||
}
|
||||
|
||||
private fun updateHappinessTable() = happinessTable.apply {
|
||||
addHeading("Happiness")
|
||||
val happinessBreakdown = viewingPlayer.stats().getHappinessBreakdown()
|
||||
for ((key, value) in happinessBreakdown)
|
||||
addLabeledValue(key, value)
|
||||
addTotal(happinessBreakdown.values.sum())
|
||||
}
|
||||
|
||||
private fun Table.updateStatTable(stat: Stat, statMap: StatMap) {
|
||||
addHeading(stat.name)
|
||||
var total = 0f
|
||||
for ((source, stats) in statMap) {
|
||||
addLabeledValue(source, stats[stat])
|
||||
total += stats[stat]
|
||||
}
|
||||
addTotal(total)
|
||||
}
|
||||
|
||||
private fun Table.addGoldSlider() {
|
||||
addSeparator()
|
||||
val sliderTable = Table()
|
||||
sliderTable.add("Convert gold to science".toLabel()).row()
|
||||
|
||||
val slider = UncivSlider(0f, 1f, 0.1f,
|
||||
initial = viewingPlayer.tech.goldPercentConvertedToScience,
|
||||
getTipText = UncivSlider::formatPercent
|
||||
) {
|
||||
viewingPlayer.tech.goldPercentConvertedToScience = it
|
||||
for (city in viewingPlayer.cities) { city.cityStats.update() }
|
||||
update()
|
||||
}
|
||||
slider.isDisabled = !UncivGame.Current.worldScreen.canChangeState
|
||||
|
||||
sliderTable.add(slider).padTop(15f)
|
||||
add(sliderTable).colspan(2)
|
||||
}
|
||||
|
||||
private fun updateGreatPeopleTable() = greatPeopleTable.apply {
|
||||
clear()
|
||||
val greatPeopleHeader = Table()
|
||||
val greatPeopleIcon = ImageGetter.getStatIcon("Specialist")
|
||||
greatPeopleIcon.color = Color.ROYAL
|
||||
greatPeopleHeader.add(greatPeopleIcon).padRight(12f).size(30f)
|
||||
greatPeopleHeader.add("Great person points".toLabel(fontSize = Constants.headingFontSize)).padTop(5f)
|
||||
greatPeopleTable.add(greatPeopleHeader).colspan(3).row()
|
||||
greatPeopleTable.addSeparator()
|
||||
greatPeopleTable.add()
|
||||
greatPeopleTable.add("Current points".toLabel())
|
||||
greatPeopleTable.add("Points per turn".toLabel()).row()
|
||||
greatPeopleHeader.add(greatPeopleIcon).padRight(1f).size(Constants.headingFontSize.toFloat())
|
||||
greatPeopleHeader.add("Great person points".toLabel(fontSize = Constants.headingFontSize))
|
||||
add(greatPeopleHeader).colspan(3).row()
|
||||
addSeparator()
|
||||
add()
|
||||
add("Current points".toLabel())
|
||||
add("Points per turn".toLabel()).row()
|
||||
|
||||
val greatPersonPoints = viewingPlayer.greatPeople.greatPersonPointsCounter
|
||||
val greatPersonPointsPerTurn = viewingPlayer.getGreatPersonPointsForNextTurn()
|
||||
val pointsToGreatPerson = viewingPlayer.greatPeople.pointsForNextGreatPerson
|
||||
|
||||
for((greatPerson, points) in greatPersonPoints) {
|
||||
greatPeopleTable.add(greatPerson.toLabel())
|
||||
greatPeopleTable.add("$points/$pointsToGreatPerson")
|
||||
greatPeopleTable.add(greatPersonPointsPerTurn[greatPerson].toString()).row()
|
||||
for ((greatPerson, points) in greatPersonPoints) {
|
||||
add(greatPerson.toLabel()).left()
|
||||
add("$points/$pointsToGreatPerson".toLabel())
|
||||
add(greatPersonPointsPerTurn[greatPerson]!!.toLabel()).right().row()
|
||||
}
|
||||
|
||||
val pointsForGreatGeneral = viewingPlayer.greatPeople.greatGeneralPoints
|
||||
val pointsForNextGreatGeneral = viewingPlayer.greatPeople.pointsForNextGreatGeneral
|
||||
greatPeopleTable.add("Great General".toLabel())
|
||||
greatPeopleTable.add("$pointsForGreatGeneral/$pointsForNextGreatGeneral").row()
|
||||
greatPeopleTable.pack()
|
||||
return greatPeopleTable
|
||||
add("Great General".toLabel()).left()
|
||||
add("$pointsForGreatGeneral/$pointsForNextGreatGeneral".toLabel())
|
||||
pack()
|
||||
}
|
||||
|
||||
private fun getScoreTable(): Table {
|
||||
val scoreTableHeader = Table(BaseScreen.skin)
|
||||
scoreTableHeader.add("Score".toLabel(fontSize = Constants.headingFontSize)).padBottom(6f)
|
||||
|
||||
val scoreTable = Table(BaseScreen.skin)
|
||||
scoreTable.defaults().pad(5f)
|
||||
scoreTable.add(scoreTableHeader).colspan(2).row()
|
||||
scoreTable.addSeparator()
|
||||
|
||||
val scoreBreakdown = viewingPlayer.calculateScoreBreakdown().filter { it.value != 0.0 }
|
||||
for ((label, value) in scoreBreakdown) {
|
||||
scoreTable.add(label.toLabel())
|
||||
scoreTable.add(value.toInt().toLabel()).row()
|
||||
}
|
||||
|
||||
scoreTable.add("Total".toLabel())
|
||||
scoreTable.add(scoreBreakdown.values.sum().toInt().toLabel())
|
||||
return scoreTable
|
||||
|
||||
private fun updateScoreTable() = scoreTable.apply {
|
||||
clear()
|
||||
val scoreHeader = Table()
|
||||
val scoreIcon = ImageGetter.getImage("OtherIcons/Cultured")
|
||||
scoreIcon.color = Color.FIREBRICK
|
||||
scoreHeader.add(scoreIcon).padRight(1f).size(Constants.headingFontSize.toFloat())
|
||||
scoreHeader.add("Score".toLabel(fontSize = Constants.headingFontSize))
|
||||
add(scoreHeader).colspan(2).row()
|
||||
addSeparator()
|
||||
|
||||
val scoreBreakdown = viewingPlayer.calculateScoreBreakdown()
|
||||
for ((label, value) in scoreBreakdown)
|
||||
addLabeledValue(label, value.toFloat())
|
||||
addTotal(scoreBreakdown.values.sum().toFloat())
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,14 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.trade.Trade
|
||||
import com.unciv.logic.trade.TradeOffersList
|
||||
import com.unciv.ui.utils.BaseScreen
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.utils.addSeparator
|
||||
import com.unciv.ui.utils.toLabel
|
||||
|
||||
class TradesOverviewTable (
|
||||
private val viewingPlayer: CivilizationInfo,
|
||||
private val overviewScreen: EmpireOverviewScreen
|
||||
) : Table() {
|
||||
class TradesOverviewTab(
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
|
||||
init {
|
||||
defaults().pad(10f)
|
||||
@ -28,14 +27,14 @@ class TradesOverviewTable (
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
for(diplomacy in diplomacies) {
|
||||
for (diplomacy in diplomacies) {
|
||||
for (trade in diplomacy.trades)
|
||||
add(createTradeTable(trade, diplomacy.otherCiv())).row()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTradeTable(trade: Trade, otherCiv: CivilizationInfo): Table {
|
||||
val generalTable = Table(BaseScreen.skin)
|
||||
val generalTable = Table()
|
||||
generalTable.add(createOffersTable(viewingPlayer, trade.ourOffers, trade.theirOffers.size)).minWidth(overviewScreen.stage.width/4).fillY()
|
||||
generalTable.add(createOffersTable(otherCiv, trade.theirOffers, trade.ourOffers.size)).minWidth(overviewScreen.stage.width/4).fillY()
|
||||
return generalTable
|
||||
@ -47,12 +46,12 @@ class TradesOverviewTable (
|
||||
table.background = ImageGetter.getBackground(civ.nation.getOuterColor())
|
||||
table.add(civ.civName.toLabel(civ.nation.getInnerColor())).row()
|
||||
table.addSeparator()
|
||||
for(offer in offersList){
|
||||
for (offer in offersList) {
|
||||
var offerText = offer.getOfferText()
|
||||
if(!offerText.contains("\n")) offerText+="\n"
|
||||
if (!offerText.contains("\n")) offerText += "\n"
|
||||
table.add(offerText.toLabel(civ.nation.getInnerColor())).row()
|
||||
}
|
||||
for(i in 1..numberOfOtherSidesOffers - offersList.size)
|
||||
for (i in 1..numberOfOtherSidesOffers - offersList.size)
|
||||
table.add("\n".toLabel()).row() // we want both sides of the general table to have the same number of rows
|
||||
return table
|
||||
}
|
||||
|
@ -13,10 +13,10 @@ import kotlin.math.abs
|
||||
/**
|
||||
* Supplies the Unit sub-table for the Empire Overview
|
||||
*/
|
||||
class UnitOverviewTable(
|
||||
private val viewingPlayer: CivilizationInfo,
|
||||
private val overviewScreen: EmpireOverviewScreen
|
||||
) : Table(BaseScreen.skin) {
|
||||
class UnitOverviewTab(
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
|
||||
init {
|
||||
add(getUnitSupplyTable()).top().padRight(25f)
|
||||
|
@ -20,11 +20,10 @@ import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.utils.onClick
|
||||
import com.unciv.ui.utils.toLabel
|
||||
|
||||
class WonderOverviewTable(
|
||||
private val viewingPlayer: CivilizationInfo,
|
||||
@Suppress("unused") private val overviewScreen: EmpireOverviewScreen
|
||||
): Table() {
|
||||
val gameInfo = viewingPlayer.gameInfo
|
||||
class WonderOverviewTab(
|
||||
viewingPlayer: CivilizationInfo,
|
||||
overviewScreen: EmpireOverviewScreen
|
||||
) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
|
||||
val ruleSet = gameInfo.ruleSet
|
||||
|
||||
private val hideReligionItems = !gameInfo.isReligionEnabled()
|
||||
|
@ -1,17 +1,18 @@
|
||||
package com.unciv.ui.utils
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g3d.model.Animation
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.Group
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.*
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||
import kotlin.math.min
|
||||
|
||||
/*
|
||||
Unimplemented ideas:
|
||||
Allow "fixed header" content that does not participate in scrolling
|
||||
(OptionsPopup mod check tab)
|
||||
Use fixedContent for OptionsPopup mod check tab
|
||||
`scrollAlign: Align` property controls initial content scroll position (currently it's Align.top)
|
||||
*/
|
||||
|
||||
@ -30,6 +31,8 @@ import kotlin.math.min
|
||||
* area of added pages and set the reported pref-W/H to their maximum within these bounds. But, if a
|
||||
* maximum is not specified, that coordinate will grow with content unlimited, and layout max-W/H will
|
||||
* always report the same as pref-W/H.
|
||||
*
|
||||
* [keyPressDispatcher] is optional and works with the `shortcutKey` parameter of [addPage] to support key bindings with tooltips.
|
||||
*/
|
||||
//region Fields and initialization
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused") // All member are part of our API
|
||||
@ -43,19 +46,23 @@ class TabbedPager(
|
||||
private val highlightColor: Color = Color.BLUE,
|
||||
backgroundColor: Color = ImageGetter.getBlue().darken(0.5f),
|
||||
private val headerPadding: Float = 10f,
|
||||
separatorColor: Color = Color.CLEAR,
|
||||
private val keyPressDispatcher: KeyPressDispatcher? = null,
|
||||
capacity: Int = 4
|
||||
) : Table() {
|
||||
|
||||
private class PageState(
|
||||
caption: String,
|
||||
var content: Actor,
|
||||
var fixedContent: WidgetGroup? = null,
|
||||
var disabled: Boolean = false,
|
||||
val onActivation: ((Int, String)->Unit)? = null,
|
||||
val onActivation: ((Int, String) -> Unit)? = null,
|
||||
val onDeactivation: ((Int, String, Float) -> Unit)? = null,
|
||||
icon: Actor? = null,
|
||||
iconSize: Float = 0f,
|
||||
val shortcutKey: KeyCharAndCode = KeyCharAndCode.UNKNOWN,
|
||||
pager: TabbedPager
|
||||
) {
|
||||
|
||||
var scrollX = 0f
|
||||
var scrollY = 0f
|
||||
|
||||
@ -90,6 +97,7 @@ class TabbedPager(
|
||||
private var headerHeight = 0f
|
||||
|
||||
private val contentScroll = AutoScrollPane(null)
|
||||
private val fixedContentWrapper = Container<WidgetGroup>()
|
||||
|
||||
private val deferredSecretPages = ArrayDeque<PageState>(0)
|
||||
private var askPasswordLock = false
|
||||
@ -102,6 +110,9 @@ class TabbedPager(
|
||||
// Measure header height, most likely its final value
|
||||
removePage(addPage("Dummy"))
|
||||
add(headerScroll).growX().minHeight(headerHeight).row()
|
||||
if (separatorColor != Color.CLEAR)
|
||||
addSeparator(separatorColor)
|
||||
add(fixedContentWrapper).growX().row()
|
||||
add(contentScroll).grow().row()
|
||||
}
|
||||
|
||||
@ -145,7 +156,9 @@ class TabbedPager(
|
||||
if (index >= 0 && pages[index].disabled) return false
|
||||
if (activePage != -1) {
|
||||
pages[activePage].apply {
|
||||
onDeactivation?.invoke(activePage, button.name, contentScroll.scrollY)
|
||||
button.color = Color.WHITE
|
||||
fixedContentWrapper.actor = null
|
||||
scrollX = contentScroll.scrollX
|
||||
scrollY = contentScroll.scrollY
|
||||
contentScroll.removeActor(content)
|
||||
@ -155,6 +168,7 @@ class TabbedPager(
|
||||
if (index != -1) {
|
||||
pages[index].apply {
|
||||
button.color = highlightColor
|
||||
fixedContentWrapper.actor = fixedContent
|
||||
contentScroll.actor = content
|
||||
contentScroll.layout()
|
||||
if (scrollX < 0f) // was marked to center on first show
|
||||
@ -196,6 +210,19 @@ class TabbedPager(
|
||||
*/
|
||||
fun setPageDisabled(caption: String, disabled: Boolean) = setPageDisabled(getPageIndex(caption), disabled)
|
||||
|
||||
/** Access a page's header button e.g. for unusual formatting */
|
||||
fun getPageButton(index: Int) = pages[index].button
|
||||
|
||||
/** Change the vertical scroll position af a page's contents */
|
||||
fun setPageScrollY(index: Int, scrollY: Float, animation: Boolean = false) {
|
||||
if (index !in 0 until pages.size) return
|
||||
val page = pages[index]
|
||||
page.scrollY = scrollY
|
||||
if (index != activePage) return
|
||||
contentScroll.scrollY = scrollY
|
||||
if (!animation) contentScroll.updateVisualScroll()
|
||||
}
|
||||
|
||||
/** Remove a page by its index.
|
||||
* @return `true` if page successfully removed */
|
||||
fun removePage(index: Int): Boolean {
|
||||
@ -211,7 +238,7 @@ class TabbedPager(
|
||||
* @return `true` if page successfully removed */
|
||||
fun removePage(caption: String) = removePage(getPageIndex(caption))
|
||||
|
||||
/** Replace a page's content by its index. */
|
||||
/** Replace a page's [content] by its [index]. */
|
||||
fun replacePage(index: Int, content: Actor) {
|
||||
if (index !in 0 until pages.size) return
|
||||
val isActive = index == activePage
|
||||
@ -219,9 +246,20 @@ class TabbedPager(
|
||||
pages[index].content = content
|
||||
if (isActive) selectPage(index)
|
||||
}
|
||||
/** Replace a page's [content] and [fixedContent] by its [index]. */
|
||||
fun replacePage(index: Int, content: Actor, fixedContent: WidgetGroup?) {
|
||||
if (index !in 0 until pages.size) return
|
||||
val isActive = index == activePage
|
||||
if (isActive) selectPage(-1)
|
||||
pages[index].content = content
|
||||
pages[index].fixedContent = fixedContent
|
||||
if (isActive) selectPage(index)
|
||||
}
|
||||
|
||||
/** Replace a page's content by its caption. */
|
||||
/** Replace a page's [content] by its [caption]. */
|
||||
fun replacePage(caption: String, content: Actor) = replacePage(getPageIndex(caption), content)
|
||||
/** Replace a page's [content] and [fixedContent] by its [caption]. */
|
||||
fun replacePage(caption: String, content: Actor, fixedContent: WidgetGroup?) = replacePage(getPageIndex(caption), content, fixedContent)
|
||||
|
||||
/** Add a page!
|
||||
* @param caption Text to be shown on the header button (automatically translated), can later be used to reference the page in other calls.
|
||||
@ -231,6 +269,9 @@ class TabbedPager(
|
||||
* @param insertBefore -1 to add at the end or index of existing page to insert this before
|
||||
* @param secret Marks page as 'secret'. A password is asked once per [TabbedPager] and if it does not match the has passed in the constructor the page and all subsequent secret pages are dropped.
|
||||
* @param disabled Initial disabled state. Disabled pages cannot be selected even with [selectPage], their button is dimmed.
|
||||
* @param shortcutKey Optional keyboard key to associate - goes to the [KeyPressDispatcher] passed in the constructor.
|
||||
* @param fixedContent Optional second content [WidgetGroup], will be placed outside the tab's [ScrollPane] between header and [content].
|
||||
* @param onDeactivation _Optional_ callback called when this page is hidden. Lambda arguments are page index and caption, and scrollY of the tab's [ScrollPane].
|
||||
* @param onActivation _Optional_ callback called when this page is shown (per actual change to this page, not per header click). Lambda arguments are page index and caption.
|
||||
* @return The new page's index or -1 if it could not be immediately added (secret).
|
||||
*/
|
||||
@ -242,16 +283,22 @@ class TabbedPager(
|
||||
insertBefore: Int = -1,
|
||||
secret: Boolean = false,
|
||||
disabled: Boolean = false,
|
||||
onActivation: ((Int, String)->Unit)? = null
|
||||
shortcutKey: KeyCharAndCode = KeyCharAndCode.UNKNOWN,
|
||||
fixedContent: WidgetGroup? = null,
|
||||
onDeactivation: ((Int, String, Float) -> Unit)? = null,
|
||||
onActivation: ((Int, String) -> Unit)? = null
|
||||
): Int {
|
||||
// Build page descriptor and header button
|
||||
val page = PageState(
|
||||
caption = caption,
|
||||
content = content ?: Group(),
|
||||
fixedContent = fixedContent,
|
||||
disabled = disabled,
|
||||
onActivation = onActivation,
|
||||
onDeactivation = onDeactivation,
|
||||
icon = icon,
|
||||
iconSize = iconSize,
|
||||
shortcutKey = shortcutKey,
|
||||
pager = this
|
||||
)
|
||||
page.button.apply {
|
||||
@ -260,6 +307,7 @@ class TabbedPager(
|
||||
onClick {
|
||||
selectPage(page)
|
||||
}
|
||||
addTooltip(shortcutKey, if (iconSize > 0f) iconSize else 18f)
|
||||
pack()
|
||||
if (height + 2 * headerPadding > headerHeight) {
|
||||
headerHeight = height + 2 * headerPadding
|
||||
@ -310,6 +358,22 @@ class TabbedPager(
|
||||
|
||||
private fun getPageIndex(page: PageState) = pages.indexOf(page)
|
||||
|
||||
private fun measureContent(group: WidgetGroup) {
|
||||
group.packIfNeeded()
|
||||
var needLayout = false
|
||||
val contentWidth = min(group.width, limitWidth)
|
||||
if (contentWidth > preferredWidth) {
|
||||
preferredWidth = contentWidth
|
||||
needLayout = true
|
||||
}
|
||||
val contentHeight = min(group.height, limitHeight)
|
||||
if (contentHeight > preferredHeight) {
|
||||
preferredHeight = contentHeight
|
||||
needLayout = true
|
||||
}
|
||||
if (needLayout && activePage >= 0) invalidateHierarchy()
|
||||
}
|
||||
|
||||
private fun addAndShowPage(page: PageState, insertBefore: Int): Int {
|
||||
// Update pages array and header table
|
||||
val newIndex: Int
|
||||
@ -330,23 +394,16 @@ class TabbedPager(
|
||||
pages[i].buttonX += page.buttonW
|
||||
|
||||
// Content Sizing
|
||||
if (page.content is WidgetGroup) {
|
||||
(page.content as WidgetGroup).packIfNeeded()
|
||||
val contentWidth = min(page.content.width, limitWidth)
|
||||
if (contentWidth > preferredWidth) {
|
||||
preferredWidth = contentWidth
|
||||
if (activePage >= 0) invalidateHierarchy()
|
||||
}
|
||||
val contentHeight = min(page.content.height, limitHeight)
|
||||
if (contentHeight > preferredHeight) {
|
||||
preferredHeight = contentHeight
|
||||
if (activePage >= 0) invalidateHierarchy()
|
||||
}
|
||||
if (page.fixedContent != null) measureContent(page.fixedContent!!)
|
||||
(page.content as? WidgetGroup)?.let {
|
||||
measureContent(it)
|
||||
page.scrollX = -1f // mark to center later when all pages are measured
|
||||
}
|
||||
if (growMaxWidth) maximumWidth = minimumWidth
|
||||
if (growMaxHeight) maximumHeight = minimumHeight
|
||||
|
||||
keyPressDispatcher?.set(page.shortcutKey) { selectPage(newIndex) }
|
||||
|
||||
return newIndex
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,12 @@ import com.unciv.models.translations.tr
|
||||
import com.unciv.models.metadata.GameSetupInfo
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.newgamescreen.NewGameScreen
|
||||
import com.unciv.ui.overviewscreen.EmpireOverviewScreen
|
||||
import com.unciv.ui.pickerscreens.PickerScreen
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.worldscreen.WorldScreen
|
||||
|
||||
class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
||||
|
||||
|
||||
val gameInfo = worldScreen.gameInfo
|
||||
private val playerCivInfo = worldScreen.viewingCiv
|
||||
val victoryTypes = gameInfo.gameParameters.victoryTypes
|
||||
@ -27,7 +26,6 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
||||
private val culturalVictoryEnabled = victoryTypes.contains(VictoryType.Cultural)
|
||||
private val dominationVictoryEnabled = victoryTypes.contains(VictoryType.Domination)
|
||||
|
||||
|
||||
private val contentsTable = Table()
|
||||
|
||||
init {
|
||||
@ -193,10 +191,10 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
||||
dominationVictoryColumn.addSeparator()
|
||||
|
||||
for (civ in majorCivs.filter { !it.isDefeated() })
|
||||
dominationVictoryColumn.add(EmpireOverviewScreen.getCivGroup(civ, "", playerCivInfo)).fillX().row()
|
||||
dominationVictoryColumn.add(getCivGroup(civ, "", playerCivInfo)).fillX().row()
|
||||
|
||||
for (civ in majorCivs.filter { it.isDefeated() })
|
||||
dominationVictoryColumn.add(EmpireOverviewScreen.getCivGroup(civ, "", playerCivInfo)).fillX().row()
|
||||
dominationVictoryColumn.add(getCivGroup(civ, "", playerCivInfo)).fillX().row()
|
||||
|
||||
return dominationVictoryColumn
|
||||
}
|
||||
@ -213,7 +211,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
||||
}.sortedByDescending { it.branchesCompleted }
|
||||
|
||||
for (entry in civsToBranchesCompleted) {
|
||||
val civToBranchesHaveCompleted = EmpireOverviewScreen.getCivGroup(entry.civ, " - " + entry.branchesCompleted, playerCivInfo)
|
||||
val civToBranchesHaveCompleted = getCivGroup(entry.civ, " - " + entry.branchesCompleted, playerCivInfo)
|
||||
policyVictoryColumn.add(civToBranchesHaveCompleted).fillX().row()
|
||||
}
|
||||
return policyVictoryColumn
|
||||
@ -232,7 +230,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
||||
}
|
||||
|
||||
for (entry in civsToPartsRemaining) {
|
||||
val civToPartsBeRemaining = (EmpireOverviewScreen.getCivGroup(entry.civ, " - " + entry.partsRemaining, playerCivInfo))
|
||||
val civToPartsBeRemaining = (getCivGroup(entry.civ, " - " + entry.partsRemaining, playerCivInfo))
|
||||
scientificVictoryColumn.add(civToPartsBeRemaining).fillX().row()
|
||||
}
|
||||
return scientificVictoryColumn
|
||||
@ -248,7 +246,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
||||
column.addSeparator()
|
||||
|
||||
for (civ in majorCivs.sortedByDescending { it.getStatForRanking(category) }) {
|
||||
column.add(EmpireOverviewScreen.getCivGroup(civ, ": " + civ.getStatForRanking(category).toString(), playerCivInfo)).fillX().row()
|
||||
column.add(getCivGroup(civ, ": " + civ.getStatForRanking(category).toString(), playerCivInfo)).fillX().row()
|
||||
}
|
||||
|
||||
civRankingsTable.add(column)
|
||||
@ -258,4 +256,36 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
||||
contentsTable.add(civRankingsTable)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getCivGroup(civ: CivilizationInfo, afterCivNameText:String, currentPlayer:CivilizationInfo): Table {
|
||||
val civGroup = Table()
|
||||
|
||||
var labelText = civ.civName.tr()+afterCivNameText
|
||||
var labelColor = Color.WHITE
|
||||
val backgroundColor: Color
|
||||
|
||||
if (civ.isDefeated()) {
|
||||
civGroup.add(ImageGetter.getImage("OtherIcons/DisbandUnit")).size(30f)
|
||||
backgroundColor = Color.LIGHT_GRAY
|
||||
labelColor = Color.BLACK
|
||||
} else if (currentPlayer == civ // || game.viewEntireMapForDebug
|
||||
|| currentPlayer.knows(civ) || currentPlayer.isDefeated() || currentPlayer.victoryManager.hasWon()) {
|
||||
civGroup.add(ImageGetter.getNationIndicator(civ.nation, 30f))
|
||||
backgroundColor = civ.nation.getOuterColor()
|
||||
labelColor = civ.nation.getInnerColor()
|
||||
} else {
|
||||
civGroup.add(ImageGetter.getRandomNationIndicator(30f))
|
||||
backgroundColor = Color.DARK_GRAY
|
||||
labelText = Constants.unknownNationName
|
||||
}
|
||||
|
||||
civGroup.background = ImageGetter.getRoundedEdgeRectangle(backgroundColor)
|
||||
val label = labelText.toLabel(labelColor)
|
||||
label.setAlignment(Align.center)
|
||||
|
||||
civGroup.add(label).padLeft(10f)
|
||||
civGroup.pack()
|
||||
return civGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user