Improve handling of Religion PickerScreen (#6287)

* Improve handling of Religion PickerScreen with long lists

* Fix mouseover graying Labels bug
This commit is contained in:
SomeTroglodyte
2022-03-08 13:24:44 +01:00
committed by GitHub
parent 12428835b3
commit e97c95b7aa

View File

@ -1,7 +1,9 @@
package com.unciv.ui.pickerscreens package com.unciv.ui.pickerscreens
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.* import com.badlogic.gdx.scenes.scene2d.ui.*
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
@ -18,14 +20,16 @@ import com.unciv.ui.utils.*
class ReligiousBeliefsPickerScreen ( class ReligiousBeliefsPickerScreen (
private val choosingCiv: CivilizationInfo, private val choosingCiv: CivilizationInfo,
private val gameInfo: GameInfo, private val gameInfo: GameInfo,
private val beliefsToChoose: Counter<BeliefType>, newBeliefsToChoose: Counter<BeliefType>,
private val pickIconAndName: Boolean private val pickIconAndName: Boolean
): PickerScreen(disableScroll = true) { ): PickerScreen(disableScroll = true) {
// Roughly follows the layout of the original (although I am not very good at UI designing, so please improve this) // Roughly follows the layout of the original (although I am not very good at UI designing, so please improve this)
private val topReligionIcons = Table() // Top of the layout, contains icons for religions private val topReligionIcons = Table() // Top of the layout, contains icons for religions
private val leftChosenBeliefs = Table() // Left middle part, contains buttons to select the types of beliefs to choose private val leftChosenBeliefs = Table() // Left middle part, contains buttons to select the types of beliefs to choose
private val leftScrollPane = AutoScrollPane(leftChosenBeliefs)
private val rightBeliefsToChoose = Table() // Right middle part, contains the beliefs to choose private val rightBeliefsToChoose = Table() // Right middle part, contains the beliefs to choose
private val rightScrollPane = AutoScrollPane(rightBeliefsToChoose)
private val middlePanes = Table() private val middlePanes = Table()
@ -33,9 +37,19 @@ class ReligiousBeliefsPickerScreen (
private var displayName: String? = null private var displayName: String? = null
private var religionName: String? = null private var religionName: String? = null
private val chosenBeliefs: Array<Belief?> = Array(beliefsToChoose.values.sum()) { null } // One entry per new Belief to choose - the left side will offer these below the choices from earlier in the game
class BeliefToChoose(val type: BeliefType, var belief: Belief? = null)
private val beliefsToChoose: Array<BeliefToChoose> =
newBeliefsToChoose.flatMap { entry -> (0 until entry.value).map { BeliefToChoose(entry.key) } }.toTypedArray()
private var leftSelectedButton: Button? = null
private var leftSelectedIndex = -1
private var rightSelectedButton: Button? = null
init { init {
leftChosenBeliefs.defaults().right().pad(10f).fillX()
rightBeliefsToChoose.defaults().left().pad(10f).fillX()
closeButton.isVisible = true closeButton.isVisible = true
setDefaultCloseAction() setDefaultCloseAction()
@ -44,33 +58,35 @@ class ReligiousBeliefsPickerScreen (
updateLeftTable() updateLeftTable()
middlePanes.add(ScrollPane(leftChosenBeliefs)) middlePanes.add(leftScrollPane)
middlePanes.addSeparatorVertical() middlePanes.addSeparatorVertical()
middlePanes.add(ScrollPane(rightBeliefsToChoose)) middlePanes.add(rightScrollPane)
topTable.add(topReligionIcons).row() topTable.add(topReligionIcons).minHeight(topReligionIcons.prefHeight).row()
topTable.addSeparator() topTable.addSeparator()
topTable.add(middlePanes) topTable.add(middlePanes)
if (pickIconAndName) rightSideButton.label = "Choose a Religion".toLabel() val rightSideButtonText = if (pickIconAndName)
else rightSideButton.label = "Enhance [${choosingCiv.religionManager.religion!!.getReligionDisplayName()}]".toLabel() "Choose a Religion"
else "Enhance [${choosingCiv.religionManager.religion!!.getReligionDisplayName()}]"
// This forces this label to use an own clone of the default style. If not, hovering over the button
// will gray out all the default-styled Labels on the screen - exact cause and why this does not affect other buttons unknown
rightSideButton.label = Label(rightSideButtonText.tr(), LabelStyle(skin[LabelStyle::class.java]))
rightSideButton.onClick(UncivSound.Choir) { rightSideButton.onClick(UncivSound.Choir) {
choosingCiv.religionManager.chooseBeliefs(displayName, religionName, chosenBeliefs.map { it!! }) choosingCiv.religionManager.chooseBeliefs(displayName, religionName, beliefsToChoose.map { it.belief!! })
UncivGame.Current.setWorldScreen() UncivGame.Current.setWorldScreen()
dispose()
} }
} }
private fun checkAndEnableRightSideButton() { private fun checkAndEnableRightSideButton() {
if (pickIconAndName && (religionName == null || displayName == null)) return if (pickIconAndName && (religionName == null || displayName == null)) return
if (chosenBeliefs.any { it == null }) return if (beliefsToChoose.any { it.belief == null }) return
rightSideButton.enable() rightSideButton.enable()
} }
private fun setupChoosableReligionIcons() { private fun setupChoosableReligionIcons() {
topReligionIcons.clear()
// This should later be replaced with a user-modifiable text field, but not in this PR
// Note that this would require replacing 'religion.name' with 'religion.iconName' at many spots
val descriptionLabel = "Choose an Icon and name for your Religion".toLabel() val descriptionLabel = "Choose an Icon and name for your Religion".toLabel()
fun changeDisplayedReligionName(newReligionName: String) { fun changeDisplayedReligionName(newReligionName: String) {
@ -84,17 +100,9 @@ class ReligiousBeliefsPickerScreen (
skin skin
) )
val iconsTable = Table() addIconsScroll { button, religionName ->
iconsTable.align(Align.center)
for (religionName in gameInfo.ruleSet.religions) {
val button = Button(
ImageGetter.getCircledReligionIcon(religionName, 60f),
skin
)
button.onClick { button.onClick {
if (previouslySelectedIcon != null) { previouslySelectedIcon?.enable()
previouslySelectedIcon!!.enable()
}
previouslySelectedIcon = button previouslySelectedIcon = button
button.disable() button.disable()
@ -104,17 +112,13 @@ class ReligiousBeliefsPickerScreen (
checkAndEnableRightSideButton() checkAndEnableRightSideButton()
} }
if (religionName == this.religionName || gameInfo.religions.keys.any { it == religionName }) button.disable()
iconsTable.add(button).pad(5f)
} }
iconsTable.row()
topReligionIcons.add(iconsTable).pad(5f).row()
val labelTable = Table() val labelTable = Table()
labelTable.add(descriptionLabel).pad(5f) labelTable.add(descriptionLabel).pad(5f)
labelTable.add(changeReligionNameButton).pad(5f).row() labelTable.add(changeReligionNameButton).pad(5f).row()
topReligionIcons.add(labelTable).center().pad(5f).row() topReligionIcons.add(labelTable).center().pad(5f).row()
changeReligionNameButton.onClick { changeReligionNameButton.onClick {
AskTextPopup( AskTextPopup(
this, this,
@ -134,108 +138,154 @@ class ReligiousBeliefsPickerScreen (
private fun setupVisibleReligionIcons() { private fun setupVisibleReligionIcons() {
topReligionIcons.clear() topReligionIcons.clear()
religionName = choosingCiv.religionManager.religion!!.name
val descriptionLabel = choosingCiv.religionManager.religion!!.getReligionDisplayName().toLabel() val descriptionLabel = choosingCiv.religionManager.religion!!.getReligionDisplayName().toLabel()
addIconsScroll { button, _ ->
button.disable()
}
topReligionIcons.add(descriptionLabel).center().padBottom(15f)
}
private fun addIconsScroll(buttonSetup: (Button, String)->Unit) {
var scrollTo = 0f
val iconsTable = Table() val iconsTable = Table()
iconsTable.align(Align.center)
for (religionName in gameInfo.ruleSet.religions) { for (religionName in gameInfo.ruleSet.religions) {
val button = Button( if (religionName == this.religionName)
ImageGetter.getCircledReligionIcon(religionName, 60f), scrollTo = iconsTable.packIfNeeded().prefWidth
skin val button = Button(ImageGetter.getCircledReligionIcon(religionName, 60f), skin)
) buttonSetup(button, religionName)
button.disable() if (religionName == this.religionName) button.disable(Color(0x007f00ff))
else if (gameInfo.religions.keys.any { it == religionName }) button.disable(Color(0x7f0000ff))
iconsTable.add(button).pad(5f) iconsTable.add(button).pad(5f)
} }
topReligionIcons.add(iconsTable).padBottom(10f).row() iconsTable.row()
topReligionIcons.add(descriptionLabel).center().padBottom(5f)
AutoScrollPane(iconsTable, skin).apply {
setScrollingDisabled(false, true)
setupFadeScrollBars(0f, 0f) // only way to "remove" scrollbar
setScrollbarsOnTop(true) // don't waste space on scrollbar
topReligionIcons.add(this).padBottom(10f).row()
layout()
scrollX = scrollTo - (width - 70f) / 2 // 70 = button width incl pad
}
} }
private fun updateLeftTable() { private fun updateLeftTable() {
leftChosenBeliefs.clear() leftChosenBeliefs.clear()
leftSelectedButton = null
val currentReligion = choosingCiv.religionManager.religion ?: Religion("None", gameInfo, choosingCiv.civName) val currentReligion = choosingCiv.religionManager.religion ?: Religion("None", gameInfo, choosingCiv.civName)
for (belief in currentReligion.getAllBeliefsOrdered()) { for (belief in currentReligion.getAllBeliefsOrdered()) {
val beliefButton = convertBeliefToButton(belief) val beliefButton = getBeliefButton(belief)
leftChosenBeliefs.add(beliefButton).pad(10f).row() leftChosenBeliefs.add(beliefButton).row()
beliefButton.disable() beliefButton.disable(Color.GREEN)
} }
for (newBelief in chosenBeliefs.withIndex()) { for ((index, entry) in beliefsToChoose.withIndex()) {
addChoosableBeliefButton(newBelief, getBeliefTypeFromIndex(newBelief.index)) addChoosableBeliefButton(entry.belief, entry.type, index)
} }
equalizeAllButtons(leftChosenBeliefs) equalizeAllButtons(leftChosenBeliefs)
} }
private fun loadRightTable(beliefType: BeliefType, leftButtonIndex: Int) { private fun loadRightTable(beliefType: BeliefType, leftButtonIndex: Int) {
var selectedButtonY = 0f
var selectedButtonHeight = 0f
rightBeliefsToChoose.clear() rightBeliefsToChoose.clear()
rightSelectedButton = null
val availableBeliefs = gameInfo.ruleSet.beliefs.values val availableBeliefs = gameInfo.ruleSet.beliefs.values
.filter { .filter { (it.type == beliefType || beliefType == BeliefType.Any) }
(it.type == beliefType || beliefType == BeliefType.Any)
&& gameInfo.religions.values.none {
religion -> religion.hasBelief(it.name)
}
&& (it !in chosenBeliefs)
}
for (belief in availableBeliefs) { for (belief in availableBeliefs) {
val beliefButton = convertBeliefToButton(belief) val beliefButton = getBeliefButton(belief)
beliefButton.onClick { beliefButton.onClick {
chosenBeliefs[leftButtonIndex] = belief rightSelectedButton?.enable()
rightSelectedButton = beliefButton
beliefButton.disable()
beliefsToChoose[leftButtonIndex].belief = belief
updateLeftTable() updateLeftTable()
checkAndEnableRightSideButton() checkAndEnableRightSideButton()
} }
rightBeliefsToChoose.add(beliefButton).left().pad(10f).row() when {
beliefsToChoose[leftButtonIndex].belief == belief -> {
selectedButtonY = rightBeliefsToChoose.packIfNeeded().prefHeight
selectedButtonHeight = beliefButton.packIfNeeded().prefHeight + 20f
rightSelectedButton = beliefButton
beliefButton.disable()
}
beliefsToChoose.any { it.belief == belief } ||
choosingCiv.religionManager.religion!!.hasBelief(belief.name) -> {
// The Belief button should be disabled because you already have it selected
beliefButton.disable(Color(0x007f00ff))
}
gameInfo.religions.values.any { it.hasBelief(belief.name) } -> {
// The Belief is not available because someone already has it
beliefButton.disable(Color(0x7f0000ff))
}
}
rightBeliefsToChoose.add(beliefButton).row()
} }
equalizeAllButtons(rightBeliefsToChoose) equalizeAllButtons(rightBeliefsToChoose)
if (rightSelectedButton == null) return
rightScrollPane.layout()
rightScrollPane.scrollY = selectedButtonY - (rightScrollPane.height - selectedButtonHeight) / 2
} }
private fun equalizeAllButtons(table: Table) { private fun equalizeAllButtons(table: Table) {
val minWidth = table.cells val minWidth = table.cells.maxOfOrNull { it.prefWidth }
.filter { it.actor is Button }
.maxOfOrNull { it.actor.width }
?: return ?: return
for (button in table.cells) { for (buttonCell in table.cells) {
if (button.actor is Button) if (buttonCell.actor is Button)
button.minWidth(minWidth) buttonCell.minWidth(minWidth)
} }
} }
private fun addChoosableBeliefButton(belief: IndexedValue<Belief?>, beliefType: BeliefType) { private fun addChoosableBeliefButton(belief: Belief?, beliefType: BeliefType, index: Int) {
val newBeliefButton = val newBeliefButton = getBeliefButton(belief, beliefType)
if (belief.value == null) emptyBeliefButton(beliefType)
else convertBeliefToButton(belief.value!!) if (index == leftSelectedIndex) {
newBeliefButton.disable()
leftSelectedButton = newBeliefButton
leftScrollPane.scrollY = leftChosenBeliefs.packIfNeeded().prefHeight -
(leftScrollPane.height - (newBeliefButton.prefHeight + 20f)) / 2
leftScrollPane.updateVisualScroll()
}
leftChosenBeliefs.add(newBeliefButton).row()
leftChosenBeliefs.add(newBeliefButton).pad(10f).row()
newBeliefButton.onClick { newBeliefButton.onClick {
loadRightTable(beliefType, belief.index) leftSelectedButton?.enable()
leftSelectedButton = newBeliefButton
leftSelectedIndex = index
newBeliefButton.disable()
loadRightTable(beliefType, index)
} }
} }
private fun convertBeliefToButton(belief: Belief): Button { private fun getBeliefButton(belief: Belief? = null, beliefType: BeliefType? = null): Button {
val contentsTable = Table() val labelWidth = stage.width * 0.5f - 52f // 32f empirically measured padding inside button, 20f outside padding
contentsTable.add(belief.type.name.toLabel(fontColor = Color.valueOf(belief.type.color))).row() return Button(skin).apply {
contentsTable.add(belief.name.toLabel(fontSize = Constants.headingFontSize)).row() when {
contentsTable.add(belief.uniques.joinToString("\n") { it.tr() }.toLabel()) belief != null -> {
return Button(contentsTable, skin) add(belief.type.name.toLabel(fontColor = Color.valueOf(belief.type.color))).row()
} val nameLabel = WrappableLabel(belief.name, labelWidth, fontSize = Constants.headingFontSize)
add(nameLabel.apply { wrap = true }).row()
private fun emptyBeliefButton(beliefType: BeliefType): Button { val effectLabel = WrappableLabel(belief.uniques.joinToString("\n") { it.tr() }, labelWidth)
val contentsTable = Table() add(effectLabel.apply { wrap = true })
if (beliefType != BeliefType.Any) }
contentsTable.add("Choose a [${beliefType.name}] belief!".toLabel()) beliefType == BeliefType.Any ->
else add("Choose any belief!".toLabel())
contentsTable.add("Choose any belief!".toLabel()) beliefType != null ->
return Button(contentsTable, skin) add("Choose a [${beliefType.name}] belief!".toLabel())
} else -> throw(IllegalArgumentException("getBeliefButton must have one non-null parameter"))
}
private fun getBeliefTypeFromIndex(index: Int): BeliefType {
return when {
index < beliefsToChoose.filter { it.key <= BeliefType.Pantheon }.values.sum() -> BeliefType.Pantheon
index < beliefsToChoose.filter { it.key <= BeliefType.Founder }.values.sum() -> BeliefType.Founder
index < beliefsToChoose.filter { it.key <= BeliefType.Follower }.values.sum() -> BeliefType.Follower
index < beliefsToChoose.filter { it.key <= BeliefType.Enhancer }.values.sum() -> BeliefType.Enhancer
else -> BeliefType.Any
} }
} }
private fun Button.disable(color: Color) {
touchable = Touchable.disabled
this.color = color
}
} }