diff --git a/core/src/com/unciv/ui/overviewscreen/CityOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/CityOverviewTable.kt index d9c59c4474..590cf181d0 100644 --- a/core/src/com/unciv/ui/overviewscreen/CityOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/CityOverviewTable.kt @@ -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() diff --git a/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt index 28a8c473c7..6aec520585 100644 --- a/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt @@ -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() diff --git a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewCategories.kt b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewCategories.kt new file mode 100644 index 0000000000..8ad3ec9299 --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewCategories.kt @@ -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) +} + diff --git a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt index 7eb26f1e9e..fdd292d515 100644 --- a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt +++ b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt @@ -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 Unit>() - private val categoryButtons = HashMap() - // 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 = 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() + + companion object { + // This is what keeps per-tab states between overview invocations + var persistState: Map? = null + + private fun updatePersistState(pageObjects: HashMap) { + 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 - } - } } diff --git a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewTab.kt b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewTab.kt new file mode 100644 index 0000000000..26f714504a --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewTab.kt @@ -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() + } + } +} diff --git a/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt index fdba6e3aed..f5800d4afd 100644 --- a/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt @@ -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() diff --git a/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt index d1460dc821..424c099cf8 100644 --- a/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt @@ -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) diff --git a/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt index 50c5d288eb..751504d11e 100644 --- a/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt @@ -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()) } } diff --git a/core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt index 26b92c0e10..e790b60e1e 100644 --- a/core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt @@ -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 } diff --git a/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt index 33c10fff74..86a71b9a46 100644 --- a/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt @@ -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) diff --git a/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt index 93908aa30a..385963a4ba 100644 --- a/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt @@ -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() diff --git a/core/src/com/unciv/ui/utils/TabbedPager.kt b/core/src/com/unciv/ui/utils/TabbedPager.kt index c6385500ee..2be97e4a3b 100644 --- a/core/src/com/unciv/ui/utils/TabbedPager.kt +++ b/core/src/com/unciv/ui/utils/TabbedPager.kt @@ -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() private val deferredSecretPages = ArrayDeque(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 } diff --git a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt index 0c3087708d..d359668159 100644 --- a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt +++ b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt @@ -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 + } + } }