From 27a783104c6e712c005d42728e4d7d05742dff64 Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Mon, 13 Nov 2023 21:17:58 +0100 Subject: [PATCH] (UX, QOL) World screen top stats row scales down to squeeze into available width (#10473) * Let WorldScreenTopBarStats scale itself down when content exceeds available width * Refactor scale mapper functionality into reusable ScalingTableWrapper * Use ScalingTableWrapper on WorldScreenTopBarResources too * Oops, they're now equal, but technically it should be prefWidth now, and the comment is moot --- .../components/widgets/ScalingTableWrapper.kt | 72 +++++++++++++++++++ .../worldscreen/topbar/WorldScreenTopBar.kt | 4 +- .../topbar/WorldScreenTopBarResources.kt | 9 ++- .../topbar/WorldScreenTopBarStats.kt | 15 ++-- 4 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 core/src/com/unciv/ui/components/widgets/ScalingTableWrapper.kt diff --git a/core/src/com/unciv/ui/components/widgets/ScalingTableWrapper.kt b/core/src/com/unciv/ui/components/widgets/ScalingTableWrapper.kt new file mode 100644 index 0000000000..a36ee72c40 --- /dev/null +++ b/core/src/com/unciv/ui/components/widgets/ScalingTableWrapper.kt @@ -0,0 +1,72 @@ +package com.unciv.ui.components.widgets + +import com.badlogic.gdx.scenes.scene2d.Actor +import com.badlogic.gdx.scenes.scene2d.ui.Cell +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup +import com.badlogic.gdx.scenes.scene2d.utils.Drawable + +/** Allows inserting a scaled Table into another, such that the outer Table "sees" correct inner Table dimensions. + * + * Note: Delegates only the basic Table API: [background], [columns], [rows], [add], [row] and [defaults]. + * Add to these as needed. + */ +open class ScalingTableWrapper( + private val minScale: Float = 0.5f +) : WidgetGroup() { + private val innerTable = Table() + + init { + isTransform = false + super.addActor(innerTable) + } + + //region WidgetGroup overrides + + // I'd like to report "we could, if needed, shrink to innerTable.minWidth * minScale" + // - but then we get overall a glitch during resizes where the outer Table sets our height + // to minHeight despite there being room (as far as WorldScreenTopBar is concened) and scale > minScale... + override fun getMinWidth() = innerTable.minWidth * innerTable.scaleX + + override fun getPrefWidth() = innerTable.prefWidth * innerTable.scaleX + override fun getMaxWidth() = innerTable.prefWidth + + override fun getMinHeight() = innerTable.minHeight * innerTable.scaleY + override fun getPrefHeight() = innerTable.prefHeight * innerTable.scaleY + override fun getMaxHeight() = innerTable.prefHeight + + override fun layout() { + innerTable.setBounds(0f, 0f, width / innerTable.scaleX, height / innerTable.scaleY) + } + //endregion + + //region Table API delegates + var background: Drawable? + get() = innerTable.background + set(value) { innerTable.background = value } + val columns: Int get() = innerTable.columns + val rows: Int get() = innerTable.rows + + fun defaults(): Cell = innerTable.defaults() + fun add(actor: Actor): Cell = innerTable.add(actor) + fun add(): Cell = innerTable.add() + fun row(): Cell = innerTable.row() + //endregion + + fun resetScale() { + innerTable.setScale(1f) + innerTable.isTransform = false + } + + fun scaleTo(maxWidth: Float) { + innerTable.pack() + val scale = (maxWidth / innerTable.prefWidth).coerceIn(minScale, 1f) + if (scale >= 1f) return + innerTable.isTransform = true + innerTable.setScale(scale) + if (!innerTable.needsLayout()) { + innerTable.invalidate() + invalidate() + } + } +} diff --git a/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBar.kt b/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBar.kt index ba27a6fbbc..fc73511d2e 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBar.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBar.kt @@ -104,8 +104,8 @@ class WorldScreenTopBar(internal val worldScreen: WorldScreen) : Table() { /** Performs the layout tricks mentioned in the class Kdoc */ private fun updateLayout() { val targetWidth = stage.width - val statsWidth = statsTable.minWidth - val resourceWidth = resourceTable.minWidth + val statsWidth = statsTable.prefWidth + val resourceWidth = resourceTable.prefWidth val overviewWidth = overviewButton.minWidth val overviewHeight = overviewButton.minHeight val selectedCivWidth = selectedCivTable.minWidth diff --git a/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarResources.kt b/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarResources.kt index a3332e5133..9f30a63a52 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarResources.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarResources.kt @@ -14,15 +14,17 @@ import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toStringSigned import com.unciv.ui.components.fonts.Fonts import com.unciv.ui.components.input.onClick +import com.unciv.ui.components.widgets.ScalingTableWrapper import com.unciv.ui.images.ImageGetter import com.unciv.ui.screens.overviewscreen.EmpireOverviewCategories import com.unciv.ui.screens.victoryscreen.VictoryScreen -internal class WorldScreenTopBarResources(topbar: WorldScreenTopBar) : Table() { +internal class WorldScreenTopBarResources(topbar: WorldScreenTopBar) : ScalingTableWrapper() { private val turnsLabel = "Turns: 0/400".toLabel() private data class ResourceActors(val resource: TileResource, val label: Label, val icon: Group) private val resourceActors = ArrayList(12) private val resourcesWrapper = Table() + val worldScreen = topbar.worldScreen // Note: For a proper functioning of the "shift floating buttons down when cramped" feature, it is // important that this entire Widget has only the bare minimum padding to its left and right. @@ -46,7 +48,6 @@ internal class WorldScreenTopBarResources(topbar: WorldScreenTopBar) : Table() { resourcesWrapper.defaults().space(defaultPad) resourcesWrapper.touchable = Touchable.enabled - val worldScreen = topbar.worldScreen turnsLabel.onClick { if (worldScreen.selectedCiv.isLongCountDisplay()) { val gameInfo = worldScreen.selectedCiv.gameInfo @@ -77,6 +78,8 @@ internal class WorldScreenTopBarResources(topbar: WorldScreenTopBar) : Table() { } fun update(civInfo: Civilization) { + resetScale() + val yearText = YearTextUtil.toYearText( civInfo.gameInfo.getYear(), civInfo.isLongCountDisplay() ) @@ -108,6 +111,6 @@ internal class WorldScreenTopBarResources(topbar: WorldScreenTopBar) : Table() { resourcesWrapper.add(label).padTop(resourceAmountDescentTweak) // digits don't have descenders, so push them down a little } - pack() + scaleTo(worldScreen.stage.width) } } diff --git a/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarStats.kt b/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarStats.kt index 88d5985c0c..c00fb838c5 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarStats.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/topbar/WorldScreenTopBarStats.kt @@ -2,7 +2,6 @@ package com.unciv.ui.screens.worldscreen.topbar import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.scenes.scene2d.ui.Label -import com.badlogic.gdx.scenes.scene2d.ui.Table import com.unciv.logic.civilization.Civilization import com.unciv.models.stats.Stats import com.unciv.models.translations.tr @@ -11,6 +10,7 @@ import com.unciv.ui.components.extensions.setFontColor import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toStringSigned import com.unciv.ui.components.input.onClick +import com.unciv.ui.components.widgets.ScalingTableWrapper import com.unciv.ui.images.ImageGetter import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.overviewscreen.EmpireOverviewCategories @@ -20,7 +20,7 @@ import com.unciv.ui.screens.pickerscreens.TechPickerScreen import kotlin.math.ceil import kotlin.math.roundToInt -internal class WorldScreenTopBarStats(topbar: WorldScreenTopBar) : Table() { +internal class WorldScreenTopBarStats(topbar: WorldScreenTopBar) : ScalingTableWrapper() { private val goldLabel = "0".toLabel(colorFromRGB(225, 217, 71)) private val scienceLabel = "0".toLabel(colorFromRGB(78, 140, 151)) private val happinessLabel = "0".toLabel() @@ -48,6 +48,8 @@ internal class WorldScreenTopBarStats(topbar: WorldScreenTopBar) : Table() { } init { + isTransform = false + fun addStat(label: Label, icon: String, isLast: Boolean = false, screenFactory: ()-> BaseScreen) { val image = ImageGetter.getStatIcon(icon) @@ -83,14 +85,13 @@ internal class WorldScreenTopBarStats(topbar: WorldScreenTopBar) : Table() { } else { add("Religion: Off".toLabel()) } - - //saveDimensions() } + private fun rateLabel(value: Float) = value.roundToInt().toStringSigned() fun update(civInfo: Civilization) { - //resetChildrenSizes() + resetScale() val nextTurnStats = civInfo.stats.statsForNextTurn val goldPerTurn = " (" + rateLabel(nextTurnStats.gold) + ")" @@ -113,8 +114,8 @@ internal class WorldScreenTopBarStats(topbar: WorldScreenTopBar) : Table() { cultureLabel.setText(getCultureText(civInfo, nextTurnStats)) faithLabel.setText(civInfo.religionManager.storedFaith.toString() + " (" + rateLabel(nextTurnStats.faith) + ")") - //scaleToMaxWidth(worldScreen.stage.width) - pack() + + scaleTo(worldScreen.stage.width) } private fun getCultureText(civInfo: Civilization, nextTurnStats: Stats): String {