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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,7 +1,9 @@
package com.unciv.ui.pickerscreens
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.Label.LabelStyle
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
@ -18,59 +20,73 @@ import com.unciv.ui.utils.*
class ReligiousBeliefsPickerScreen (
private val choosingCiv: CivilizationInfo,
private val gameInfo: GameInfo,
private val beliefsToChoose: Counter<BeliefType>,
newBeliefsToChoose: Counter<BeliefType>,
private val pickIconAndName: Boolean
): PickerScreen(disableScroll = true) {
// 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 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 rightScrollPane = AutoScrollPane(rightBeliefsToChoose)
private val middlePanes = Table()
private var previouslySelectedIcon: Button? = null
private var displayName: 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 {
leftChosenBeliefs.defaults().right().pad(10f).fillX()
rightBeliefsToChoose.defaults().left().pad(10f).fillX()
closeButton.isVisible = true
setDefaultCloseAction()
if (pickIconAndName) setupChoosableReligionIcons()
else setupVisibleReligionIcons()
updateLeftTable()
middlePanes.add(ScrollPane(leftChosenBeliefs))
middlePanes.add(leftScrollPane)
middlePanes.addSeparatorVertical()
middlePanes.add(ScrollPane(rightBeliefsToChoose))
topTable.add(topReligionIcons).row()
middlePanes.add(rightScrollPane)
topTable.add(topReligionIcons).minHeight(topReligionIcons.prefHeight).row()
topTable.addSeparator()
topTable.add(middlePanes)
if (pickIconAndName) rightSideButton.label = "Choose a Religion".toLabel()
else rightSideButton.label = "Enhance [${choosingCiv.religionManager.religion!!.getReligionDisplayName()}]".toLabel()
val rightSideButtonText = if (pickIconAndName)
"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) {
choosingCiv.religionManager.chooseBeliefs(displayName, religionName, chosenBeliefs.map { it!! })
choosingCiv.religionManager.chooseBeliefs(displayName, religionName, beliefsToChoose.map { it.belief!! })
UncivGame.Current.setWorldScreen()
dispose()
}
}
private fun checkAndEnableRightSideButton() {
if (pickIconAndName && (religionName == null || displayName == null)) return
if (chosenBeliefs.any { it == null }) return
if (beliefsToChoose.any { it.belief == null }) return
rightSideButton.enable()
}
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()
fun changeDisplayedReligionName(newReligionName: String) {
@ -83,38 +99,26 @@ class ReligiousBeliefsPickerScreen (
ImageGetter.getImage("OtherIcons/Pencil").apply { this.color = Color.BLACK }.surroundWithCircle(30f),
skin
)
val iconsTable = Table()
iconsTable.align(Align.center)
for (religionName in gameInfo.ruleSet.religions) {
val button = Button(
ImageGetter.getCircledReligionIcon(religionName, 60f),
skin
)
addIconsScroll { button, religionName ->
button.onClick {
if (previouslySelectedIcon != null) {
previouslySelectedIcon!!.enable()
}
previouslySelectedIcon?.enable()
previouslySelectedIcon = button
button.disable()
changeDisplayedReligionName(religionName)
this.religionName = religionName
changeReligionNameButton.enable()
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()
labelTable.add(descriptionLabel).pad(5f)
labelTable.add(changeReligionNameButton).pad(5f).row()
topReligionIcons.add(labelTable).center().pad(5f).row()
changeReligionNameButton.onClick {
AskTextPopup(
this,
@ -134,108 +138,154 @@ class ReligiousBeliefsPickerScreen (
private fun setupVisibleReligionIcons() {
topReligionIcons.clear()
religionName = choosingCiv.religionManager.religion!!.name
val descriptionLabel = choosingCiv.religionManager.religion!!.getReligionDisplayName().toLabel()
val iconsTable = Table()
for (religionName in gameInfo.ruleSet.religions) {
val button = Button(
ImageGetter.getCircledReligionIcon(religionName, 60f),
skin
)
addIconsScroll { button, _ ->
button.disable()
iconsTable.add(button).pad(5f)
}
topReligionIcons.add(iconsTable).padBottom(10f).row()
topReligionIcons.add(descriptionLabel).center().padBottom(5f)
topReligionIcons.add(descriptionLabel).center().padBottom(15f)
}
private fun addIconsScroll(buttonSetup: (Button, String)->Unit) {
var scrollTo = 0f
val iconsTable = Table()
iconsTable.align(Align.center)
for (religionName in gameInfo.ruleSet.religions) {
if (religionName == this.religionName)
scrollTo = iconsTable.packIfNeeded().prefWidth
val button = Button(ImageGetter.getCircledReligionIcon(religionName, 60f), skin)
buttonSetup(button, religionName)
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.row()
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() {
leftChosenBeliefs.clear()
leftSelectedButton = null
val currentReligion = choosingCiv.religionManager.religion ?: Religion("None", gameInfo, choosingCiv.civName)
for (belief in currentReligion.getAllBeliefsOrdered()) {
val beliefButton = convertBeliefToButton(belief)
leftChosenBeliefs.add(beliefButton).pad(10f).row()
beliefButton.disable()
val beliefButton = getBeliefButton(belief)
leftChosenBeliefs.add(beliefButton).row()
beliefButton.disable(Color.GREEN)
}
for (newBelief in chosenBeliefs.withIndex()) {
addChoosableBeliefButton(newBelief, getBeliefTypeFromIndex(newBelief.index))
for ((index, entry) in beliefsToChoose.withIndex()) {
addChoosableBeliefButton(entry.belief, entry.type, index)
}
equalizeAllButtons(leftChosenBeliefs)
}
private fun loadRightTable(beliefType: BeliefType, leftButtonIndex: Int) {
var selectedButtonY = 0f
var selectedButtonHeight = 0f
rightBeliefsToChoose.clear()
rightSelectedButton = null
val availableBeliefs = gameInfo.ruleSet.beliefs.values
.filter {
(it.type == beliefType || beliefType == BeliefType.Any)
&& gameInfo.religions.values.none {
religion -> religion.hasBelief(it.name)
}
&& (it !in chosenBeliefs)
}
.filter { (it.type == beliefType || beliefType == BeliefType.Any) }
for (belief in availableBeliefs) {
val beliefButton = convertBeliefToButton(belief)
val beliefButton = getBeliefButton(belief)
beliefButton.onClick {
chosenBeliefs[leftButtonIndex] = belief
rightSelectedButton?.enable()
rightSelectedButton = beliefButton
beliefButton.disable()
beliefsToChoose[leftButtonIndex].belief = belief
updateLeftTable()
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)
}
private fun equalizeAllButtons(table: Table) {
val minWidth = table.cells
.filter { it.actor is Button }
.maxOfOrNull { it.actor.width }
?: return
for (button in table.cells) {
if (button.actor is Button)
button.minWidth(minWidth)
}
}
private fun addChoosableBeliefButton(belief: IndexedValue<Belief?>, beliefType: BeliefType) {
val newBeliefButton =
if (belief.value == null) emptyBeliefButton(beliefType)
else convertBeliefToButton(belief.value!!)
leftChosenBeliefs.add(newBeliefButton).pad(10f).row()
if (rightSelectedButton == null) return
rightScrollPane.layout()
rightScrollPane.scrollY = selectedButtonY - (rightScrollPane.height - selectedButtonHeight) / 2
}
private fun equalizeAllButtons(table: Table) {
val minWidth = table.cells.maxOfOrNull { it.prefWidth }
?: return
for (buttonCell in table.cells) {
if (buttonCell.actor is Button)
buttonCell.minWidth(minWidth)
}
}
private fun addChoosableBeliefButton(belief: Belief?, beliefType: BeliefType, index: Int) {
val newBeliefButton = getBeliefButton(belief, beliefType)
if (index == leftSelectedIndex) {
newBeliefButton.disable()
leftSelectedButton = newBeliefButton
leftScrollPane.scrollY = leftChosenBeliefs.packIfNeeded().prefHeight -
(leftScrollPane.height - (newBeliefButton.prefHeight + 20f)) / 2
leftScrollPane.updateVisualScroll()
}
leftChosenBeliefs.add(newBeliefButton).row()
newBeliefButton.onClick {
loadRightTable(beliefType, belief.index)
leftSelectedButton?.enable()
leftSelectedButton = newBeliefButton
leftSelectedIndex = index
newBeliefButton.disable()
loadRightTable(beliefType, index)
}
}
private fun convertBeliefToButton(belief: Belief): Button {
val contentsTable = Table()
contentsTable.add(belief.type.name.toLabel(fontColor = Color.valueOf(belief.type.color))).row()
contentsTable.add(belief.name.toLabel(fontSize = Constants.headingFontSize)).row()
contentsTable.add(belief.uniques.joinToString("\n") { it.tr() }.toLabel())
return Button(contentsTable, skin)
}
private fun emptyBeliefButton(beliefType: BeliefType): Button {
val contentsTable = Table()
if (beliefType != BeliefType.Any)
contentsTable.add("Choose a [${beliefType.name}] belief!".toLabel())
else
contentsTable.add("Choose any belief!".toLabel())
return Button(contentsTable, skin)
}
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 getBeliefButton(belief: Belief? = null, beliefType: BeliefType? = null): Button {
val labelWidth = stage.width * 0.5f - 52f // 32f empirically measured padding inside button, 20f outside padding
return Button(skin).apply {
when {
belief != null -> {
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()
val effectLabel = WrappableLabel(belief.uniques.joinToString("\n") { it.tr() }, labelWidth)
add(effectLabel.apply { wrap = true })
}
beliefType == BeliefType.Any ->
add("Choose any belief!".toLabel())
beliefType != null ->
add("Choose a [${beliefType.name}] belief!".toLabel())
else -> throw(IllegalArgumentException("getBeliefButton must have one non-null parameter"))
}
}
}
private fun Button.disable(color: Color) {
touchable = Touchable.disabled
this.color = color
}
}