diff --git a/core/src/com/unciv/ui/pickerscreens/ReligiousBeliefsPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/ReligiousBeliefsPickerScreen.kt index cfeb037457..f3cc7469ac 100644 --- a/core/src/com/unciv/ui/pickerscreens/ReligiousBeliefsPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/ReligiousBeliefsPickerScreen.kt @@ -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, + newBeliefsToChoose: Counter, 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 = 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 = + 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, 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 + } }