mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 05:46:43 -04:00
Key bindings CityScreen (#9828)
* CityScreen keyboard bindings - ExpanderTab update * CityScreen keyboard bindings - Linting * CityScreen keyboard bindings - Main Keys * CityScreen keyboard bindings - Queue * CityScreen keyboard bindings - Fix Expander scroll-to
This commit is contained in:
parent
a5e1f6d800
commit
52e756e9fb
@ -9,7 +9,7 @@ import com.unciv.logic.civilization.AlertType
|
|||||||
import com.unciv.logic.civilization.NotificationCategory
|
import com.unciv.logic.civilization.NotificationCategory
|
||||||
import com.unciv.logic.civilization.NotificationIcon
|
import com.unciv.logic.civilization.NotificationIcon
|
||||||
import com.unciv.logic.civilization.PopupAlert
|
import com.unciv.logic.civilization.PopupAlert
|
||||||
import com.unciv.logic.map.mapunit.MapUnit
|
import com.unciv.logic.map.mapunit.UnitTurnManager
|
||||||
import com.unciv.logic.map.tile.Tile
|
import com.unciv.logic.map.tile.Tile
|
||||||
import com.unciv.logic.multiplayer.isUsersTurn
|
import com.unciv.logic.multiplayer.isUsersTurn
|
||||||
import com.unciv.models.ruleset.Building
|
import com.unciv.models.ruleset.Building
|
||||||
@ -752,13 +752,15 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
} else true // we're just continuing the regular queue
|
} else true // we're just continuing the regular queue
|
||||||
}
|
}
|
||||||
|
|
||||||
fun raisePriority(constructionQueueIndex: Int) {
|
fun raisePriority(constructionQueueIndex: Int): Int {
|
||||||
constructionQueue.swap(constructionQueueIndex - 1, constructionQueueIndex)
|
constructionQueue.swap(constructionQueueIndex - 1, constructionQueueIndex)
|
||||||
|
return constructionQueueIndex - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lowering == Highering next element in queue
|
// Lowering == Highering next element in queue
|
||||||
fun lowerPriority(constructionQueueIndex: Int) {
|
fun lowerPriority(constructionQueueIndex: Int): Int {
|
||||||
raisePriority(constructionQueueIndex + 1)
|
raisePriority(constructionQueueIndex + 1)
|
||||||
|
return constructionQueueIndex + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableList<String>.swap(idx1: Int, idx2: Int) {
|
private fun MutableList<String>.swap(idx1: Int, idx2: Int) {
|
||||||
@ -778,7 +780,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
val tileForImprovement = getTileForImprovement(improvement.name) ?: return
|
val tileForImprovement = getTileForImprovement(improvement.name) ?: return
|
||||||
tileForImprovement.stopWorkingOnImprovement() // clears mark
|
tileForImprovement.stopWorkingOnImprovement() // clears mark
|
||||||
if (removeOnly) return
|
if (removeOnly) return
|
||||||
/**todo unify with [UnitActions.getImprovementConstructionActions] and [MapUnit.workOnImprovement] - this won't allow e.g. a building to place a road */
|
/**todo unify with [UnitActions.getImprovementConstructionActions] and [UnitTurnManager.workOnImprovement] - this won't allow e.g. a building to place a road */
|
||||||
tileForImprovement.changeImprovement(improvement.name)
|
tileForImprovement.changeImprovement(improvement.name)
|
||||||
city.civ.lastSeenImprovement[tileForImprovement.position] = improvement.name
|
city.civ.lastSeenImprovement[tileForImprovement.position] = improvement.name
|
||||||
city.cityStats.update()
|
city.cityStats.update()
|
||||||
@ -794,11 +796,11 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
*/
|
*/
|
||||||
fun removeCreateOneImprovementConstruction(improvement: String) {
|
fun removeCreateOneImprovementConstruction(improvement: String) {
|
||||||
val ruleset = city.getRuleset()
|
val ruleset = city.getRuleset()
|
||||||
val indexToRemove = constructionQueue.withIndex().mapNotNull {
|
val indexToRemove = constructionQueue.withIndex().firstNotNullOfOrNull {
|
||||||
val construction = getConstruction(it.value)
|
val construction = getConstruction(it.value)
|
||||||
val buildingImprovement = (construction as? Building)?.getImprovementToCreate(ruleset)?.name
|
val buildingImprovement = (construction as? Building)?.getImprovementToCreate(ruleset)?.name
|
||||||
it.index.takeIf { buildingImprovement == improvement }
|
it.index.takeIf { buildingImprovement == improvement }
|
||||||
}.firstOrNull() ?: return
|
} ?: return
|
||||||
|
|
||||||
constructionQueue.removeAt(indexToRemove)
|
constructionQueue.removeAt(indexToRemove)
|
||||||
|
|
||||||
|
@ -1,12 +1,29 @@
|
|||||||
package com.unciv.logic.city
|
package com.unciv.logic.city
|
||||||
|
|
||||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||||
|
import com.unciv.logic.automation.Automation
|
||||||
|
import com.unciv.logic.city.managers.CityPopulationManager
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
|
import com.unciv.ui.screens.cityscreen.CitizenManagementTable
|
||||||
|
|
||||||
// if tableEnabled == true, then Stat != null
|
/**
|
||||||
enum class CityFocus(val label: String, val tableEnabled: Boolean, val stat: Stat? = null) :
|
* Controls automatic worker-to-tile assignment
|
||||||
IsPartOfGameInfoSerialization {
|
* @param label Display label, formatted for tr()
|
||||||
|
* @param tableEnabled Whether to show or hide in CityScreen's [CitizenManagementTable]
|
||||||
|
* @param stat Which stat the default [getStatMultiplier] emphasizes - unused if that is overridden w/o calling super
|
||||||
|
* @param binding Bindable keyboard key in UI - this is an override, by default matching enum names in [KeyboardBinding] are assigned automatically
|
||||||
|
* @see CityPopulationManager.autoAssignPopulation
|
||||||
|
* @see Automation.rankStatsForCityWork
|
||||||
|
*/
|
||||||
|
enum class CityFocus(
|
||||||
|
val label: String,
|
||||||
|
val tableEnabled: Boolean,
|
||||||
|
val stat: Stat? = null,
|
||||||
|
binding: KeyboardBinding? = null
|
||||||
|
) : IsPartOfGameInfoSerialization {
|
||||||
|
// region Enum values
|
||||||
NoFocus("Default Focus", true, null) {
|
NoFocus("Default Focus", true, null) {
|
||||||
override fun getStatMultiplier(stat: Stat) = 1f // actually redundant, but that's two steps to see
|
override fun getStatMultiplier(stat: Stat) = 1f // actually redundant, but that's two steps to see
|
||||||
},
|
},
|
||||||
@ -28,8 +45,16 @@ enum class CityFocus(val label: String, val tableEnabled: Boolean, val stat: Sta
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
FaithFocus("[${Stat.Faith.name}] Focus", true, Stat.Faith),
|
FaithFocus("[${Stat.Faith.name}] Focus", true, Stat.Faith),
|
||||||
HappinessFocus("[${Stat.Happiness.name}] Focus", false, Stat.Happiness);
|
HappinessFocus("[${Stat.Happiness.name}] Focus", false, Stat.Happiness),
|
||||||
//GreatPersonFocus;
|
//GreatPersonFocus
|
||||||
|
|
||||||
|
;
|
||||||
|
// endregion Enum values
|
||||||
|
|
||||||
|
val binding: KeyboardBinding =
|
||||||
|
binding ?:
|
||||||
|
KeyboardBinding.values().firstOrNull { it.name == name } ?:
|
||||||
|
KeyboardBinding.None
|
||||||
|
|
||||||
open fun getStatMultiplier(stat: Stat) = when (this.stat) {
|
open fun getStatMultiplier(stat: Stat) = when (this.stat) {
|
||||||
stat -> 3f
|
stat -> 3f
|
||||||
@ -42,7 +67,9 @@ enum class CityFocus(val label: String, val tableEnabled: Boolean, val stat: Sta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun safeValueOf(stat: Stat): CityFocus {
|
companion object {
|
||||||
return values().firstOrNull { it.stat == stat } ?: NoFocus
|
fun safeValueOf(stat: Stat): CityFocus {
|
||||||
|
return values().firstOrNull { it.stat == stat } ?: NoFocus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,17 @@ import com.badlogic.gdx.math.Interpolation
|
|||||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||||
import com.badlogic.gdx.scenes.scene2d.actions.FloatAction
|
import com.badlogic.gdx.scenes.scene2d.actions.FloatAction
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
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
|
||||||
import com.unciv.ui.images.ImageGetter
|
|
||||||
import com.unciv.ui.components.input.onClick
|
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
|
import com.unciv.ui.components.input.keyShortcuts
|
||||||
|
import com.unciv.ui.components.input.onActivation
|
||||||
|
import com.unciv.ui.images.IconCircleGroup
|
||||||
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,6 +40,7 @@ class ExpanderTab(
|
|||||||
headerPad: Float = 10f,
|
headerPad: Float = 10f,
|
||||||
expanderWidth: Float = 0f,
|
expanderWidth: Float = 0f,
|
||||||
private val persistenceID: String? = null,
|
private val persistenceID: String? = null,
|
||||||
|
toggleKey: KeyboardBinding = KeyboardBinding.None,
|
||||||
private val onChange: (() -> Unit)? = null,
|
private val onChange: (() -> Unit)? = null,
|
||||||
initContent: ((Table) -> Unit)? = null
|
initContent: ((Table) -> Unit)? = null
|
||||||
): Table(BaseScreen.skin) {
|
): Table(BaseScreen.skin) {
|
||||||
@ -81,7 +86,8 @@ class ExpanderTab(
|
|||||||
header.add(headerLabel)
|
header.add(headerLabel)
|
||||||
header.add(headerIcon).size(arrowSize).align(Align.center)
|
header.add(headerIcon).size(arrowSize).align(Align.center)
|
||||||
header.touchable= Touchable.enabled
|
header.touchable= Touchable.enabled
|
||||||
header.onClick { toggle() }
|
header.onActivation { toggle() }
|
||||||
|
header.keyShortcuts.add(toggleKey) // Using the onActivation parameter adds a tooltip, which often does not look too good
|
||||||
if (expanderWidth != 0f)
|
if (expanderWidth != 0f)
|
||||||
defaults().minWidth(expanderWidth)
|
defaults().minWidth(expanderWidth)
|
||||||
defaults().growX()
|
defaults().growX()
|
||||||
@ -126,9 +132,44 @@ class ExpanderTab(
|
|||||||
/** Toggle [isOpen], animated */
|
/** Toggle [isOpen], animated */
|
||||||
fun toggle() {
|
fun toggle() {
|
||||||
isOpen = !isOpen
|
isOpen = !isOpen
|
||||||
|
|
||||||
|
// In the common case where the expander is hosted in a Table within a ScrollPane...
|
||||||
|
// try scrolling our header so it is visible (when toggled by keyboard)
|
||||||
|
if (parent is Table && parent.parent is ScrollPane)
|
||||||
|
tryAutoScroll(parent.parent as ScrollPane)
|
||||||
|
// But - our Actor.addBorder extension can ruin that, so cater for that special case too...
|
||||||
|
else if (testForBorderedTable())
|
||||||
|
tryAutoScroll(parent.parent.parent as ScrollPane)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Change header label text after initialization */
|
private fun testForBorderedTable(): Boolean {
|
||||||
|
if (parent !is Table) return false
|
||||||
|
val borderTable = parent.parent as? Table ?: return false
|
||||||
|
if (parent.parent.parent !is ScrollPane) return false
|
||||||
|
return borderTable.cells.size == 1 && borderTable.background != null && borderTable.padTop == 2f
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryAutoScroll(scrollPane: ScrollPane) {
|
||||||
|
if (scrollPane.isScrollingDisabledY) return
|
||||||
|
|
||||||
|
// As the "opening" is animated, and right now the animation has just started,
|
||||||
|
// a scroll-to-visible won't work, so limit it to showing the header for now.
|
||||||
|
val heightToShow = header.height
|
||||||
|
|
||||||
|
// Coords as seen by "this" expander relative to parent and as seen by scrollPane may differ by the border size
|
||||||
|
// Also make area to show relative to top
|
||||||
|
val yToShow = this.y + this.height - heightToShow +
|
||||||
|
(if (scrollPane.actor == this.parent) 0f else parent.y)
|
||||||
|
|
||||||
|
// If ever needed - how to check whether scrollTo would not need to scroll (without testing for heightToShow > scrollHeight)
|
||||||
|
// val relativeY = scrollPane.actor.height - yToShow - scrollPane.scrollY
|
||||||
|
// if (relativeY >= heightToShow && relativeY <= scrollPane.scrollHeight) return
|
||||||
|
|
||||||
|
// scrollTo does the y axis inversion for us, and also will do nothing if the requested area is already fully visible
|
||||||
|
scrollPane.scrollTo(0f, yToShow, header.width, heightToShow)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Change header label text after initialization (does not auto-translate) */
|
||||||
fun setText(text: String) {
|
fun setText(text: String) {
|
||||||
headerLabel.setText(text)
|
headerLabel.setText(text)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.unciv.ui.components.input
|
|||||||
|
|
||||||
import com.badlogic.gdx.Input
|
import com.badlogic.gdx.Input
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
|
import com.unciv.models.stats.Stat
|
||||||
|
|
||||||
|
|
||||||
private val unCamelCaseRegex = Regex("([A-Z])([A-Z])([a-z])|([a-z])([A-Z])")
|
private val unCamelCaseRegex = Regex("([A-Z])([A-Z])([a-z])|([a-z])([A-Z])")
|
||||||
@ -123,6 +124,38 @@ enum class KeyboardBinding(
|
|||||||
HideAdditionalActions(Category.UnitActions,"Back", Input.Keys.PAGE_UP),
|
HideAdditionalActions(Category.UnitActions,"Back", Input.Keys.PAGE_UP),
|
||||||
AddInCapital(Category.UnitActions, "Add in capital", 'g'),
|
AddInCapital(Category.UnitActions, "Add in capital", 'g'),
|
||||||
|
|
||||||
|
// City Screen
|
||||||
|
AddConstruction(Category.CityScreen, "Add to or remove from queue", KeyCharAndCode.RETURN),
|
||||||
|
RaisePriority(Category.CityScreen, "Raise queue priority", Input.Keys.UP),
|
||||||
|
LowerPriority(Category.CityScreen, "Lower queue priority", Input.Keys.DOWN),
|
||||||
|
BuyConstruction(Category.CityScreen, 'b'),
|
||||||
|
BuyTile(Category.CityScreen, 't'),
|
||||||
|
BuildUnits(Category.CityScreen, "Buildable Units", 'u'),
|
||||||
|
BuildBuildings(Category.CityScreen, "Buildable Buildings", 'l'),
|
||||||
|
BuildWonders(Category.CityScreen, "Buildable Wonders", 'w'),
|
||||||
|
BuildNationalWonders(Category.CityScreen, "Buildable National Wonders", 'n'),
|
||||||
|
BuildOther(Category.CityScreen, "Other Constructions", 'o'),
|
||||||
|
NextCity(Category.CityScreen, Input.Keys.RIGHT),
|
||||||
|
PreviousCity(Category.CityScreen, Input.Keys.LEFT),
|
||||||
|
ShowStats(Category.CityScreen, 's'),
|
||||||
|
ShowStatDetails(Category.CityScreen, "Toggle Stat Details", Input.Keys.NUMPAD_ADD),
|
||||||
|
CitizenManagement(Category.CityScreen, 'c'),
|
||||||
|
GreatPeopleDetail(Category.CityScreen, 'g'),
|
||||||
|
SpecialistDetail(Category.CityScreen, 'p'),
|
||||||
|
ReligionDetail(Category.CityScreen, 'r'),
|
||||||
|
BuildingsDetail(Category.CityScreen, 'd'),
|
||||||
|
ResetCitizens(Category.CityScreen, KeyCharAndCode.ctrl('r')),
|
||||||
|
AvoidGrowth(Category.CityScreen, KeyCharAndCode.ctrl('a')),
|
||||||
|
// The following are automatically matched by enum name to CityFocus entries - if necessary override there
|
||||||
|
// Note on label: copied from CityFocus to ensure same translatable is used - without we'd get "Food Focus", not the same as "[Food] Focus"
|
||||||
|
NoFocus(Category.CityScreen, "Default Focus", KeyCharAndCode.ctrl('d')),
|
||||||
|
FoodFocus(Category.CityScreen, "[${Stat.Food.name}] Focus", KeyCharAndCode.ctrl('f')),
|
||||||
|
ProductionFocus(Category.CityScreen, "[${Stat.Production.name}] Focus", KeyCharAndCode.ctrl('p')),
|
||||||
|
GoldFocus(Category.CityScreen, "[${Stat.Gold.name}] Focus", KeyCharAndCode.ctrl('g')),
|
||||||
|
ScienceFocus(Category.CityScreen, "[${Stat.Science.name}] Focus", KeyCharAndCode.ctrl('s')),
|
||||||
|
CultureFocus(Category.CityScreen, "[${Stat.Culture.name}] Focus", KeyCharAndCode.ctrl('c')),
|
||||||
|
FaithFocus(Category.CityScreen, "[${Stat.Faith.name}] Focus", KeyCharAndCode.UNKNOWN),
|
||||||
|
|
||||||
// Popups
|
// Popups
|
||||||
Confirm(Category.Popups, "Confirm Dialog", 'y'),
|
Confirm(Category.Popups, "Confirm Dialog", 'y'),
|
||||||
Cancel(Category.Popups, "Cancel Dialog", 'n'),
|
Cancel(Category.Popups, "Cancel Dialog", 'n'),
|
||||||
@ -144,6 +177,7 @@ enum class KeyboardBinding(
|
|||||||
// Conflict checking within group disabled, but any key assigned on WorldScreen is a problem
|
// Conflict checking within group disabled, but any key assigned on WorldScreen is a problem
|
||||||
override fun checkConflictsIn() = sequenceOf(WorldScreen)
|
override fun checkConflictsIn() = sequenceOf(WorldScreen)
|
||||||
},
|
},
|
||||||
|
CityScreen,
|
||||||
Popups
|
Popups
|
||||||
;
|
;
|
||||||
val label = unCamelCase(name)
|
val label = unCamelCase(name)
|
||||||
|
@ -4,10 +4,11 @@ import com.badlogic.gdx.scenes.scene2d.Touchable
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.logic.city.CityFocus
|
import com.unciv.logic.city.CityFocus
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
|
||||||
import com.unciv.ui.components.ExpanderTab
|
import com.unciv.ui.components.ExpanderTab
|
||||||
import com.unciv.ui.components.input.onClick
|
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
|
import com.unciv.ui.components.input.onActivation
|
||||||
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
|
|
||||||
class CitizenManagementTable(val cityScreen: CityScreen) : Table(BaseScreen.skin) {
|
class CitizenManagementTable(val cityScreen: CityScreen) : Table(BaseScreen.skin) {
|
||||||
val city = cityScreen.city
|
val city = cityScreen.city
|
||||||
@ -24,7 +25,7 @@ class CitizenManagementTable(val cityScreen: CityScreen) : Table(BaseScreen.skin
|
|||||||
resetCell.add(resetLabel).pad(5f)
|
resetCell.add(resetLabel).pad(5f)
|
||||||
if (cityScreen.canCityBeChanged()) {
|
if (cityScreen.canCityBeChanged()) {
|
||||||
resetCell.touchable = Touchable.enabled
|
resetCell.touchable = Touchable.enabled
|
||||||
resetCell.onClick {
|
resetCell.onActivation(binding = KeyboardBinding.ResetCitizens) {
|
||||||
city.reassignPopulation(true)
|
city.reassignPopulation(true)
|
||||||
cityScreen.update()
|
cityScreen.update()
|
||||||
}
|
}
|
||||||
@ -41,7 +42,7 @@ class CitizenManagementTable(val cityScreen: CityScreen) : Table(BaseScreen.skin
|
|||||||
avoidCell.add(avoidLabel).pad(5f)
|
avoidCell.add(avoidLabel).pad(5f)
|
||||||
if (cityScreen.canCityBeChanged()) {
|
if (cityScreen.canCityBeChanged()) {
|
||||||
avoidCell.touchable = Touchable.enabled
|
avoidCell.touchable = Touchable.enabled
|
||||||
avoidCell.onClick {
|
avoidCell.onActivation(binding = KeyboardBinding.AvoidGrowth) {
|
||||||
city.avoidGrowth = !city.avoidGrowth
|
city.avoidGrowth = !city.avoidGrowth
|
||||||
city.reassignPopulation()
|
city.reassignPopulation()
|
||||||
cityScreen.update()
|
cityScreen.update()
|
||||||
@ -63,7 +64,10 @@ class CitizenManagementTable(val cityScreen: CityScreen) : Table(BaseScreen.skin
|
|||||||
cell.add(label).pad(5f)
|
cell.add(label).pad(5f)
|
||||||
if (cityScreen.canCityBeChanged()) {
|
if (cityScreen.canCityBeChanged()) {
|
||||||
cell.touchable = Touchable.enabled
|
cell.touchable = Touchable.enabled
|
||||||
cell.onClick {
|
// Note the binding here only works when visible, so the main one is on CityStatsTable.miniStatsTable
|
||||||
|
// If we bind both, both are executed - so only add the one here that re-applies the current focus
|
||||||
|
val binding = if (city.cityAIFocus == focus) focus.binding else KeyboardBinding.None
|
||||||
|
cell.onActivation(binding = binding) {
|
||||||
city.cityAIFocus = focus
|
city.cityAIFocus = focus
|
||||||
city.reassignPopulation()
|
city.reassignPopulation()
|
||||||
cityScreen.update()
|
cityScreen.update()
|
||||||
@ -88,6 +92,7 @@ class CitizenManagementTable(val cityScreen: CityScreen) : Table(BaseScreen.skin
|
|||||||
fontSize = Constants.defaultFontSize,
|
fontSize = Constants.defaultFontSize,
|
||||||
persistenceID = "CityStatsTable.CitizenManagement",
|
persistenceID = "CityStatsTable.CitizenManagement",
|
||||||
startsOutOpened = false,
|
startsOutOpened = false,
|
||||||
|
toggleKey = KeyboardBinding.CitizenManagement,
|
||||||
onChange = onChange
|
onChange = onChange
|
||||||
) {
|
) {
|
||||||
it.add(this)
|
it.add(this)
|
||||||
|
@ -34,13 +34,14 @@ import com.unciv.ui.components.extensions.darken
|
|||||||
import com.unciv.ui.components.extensions.disable
|
import com.unciv.ui.components.extensions.disable
|
||||||
import com.unciv.ui.components.extensions.getConsumesAmountString
|
import com.unciv.ui.components.extensions.getConsumesAmountString
|
||||||
import com.unciv.ui.components.extensions.isEnabled
|
import com.unciv.ui.components.extensions.isEnabled
|
||||||
import com.unciv.ui.components.input.keyShortcuts
|
|
||||||
import com.unciv.ui.components.input.onActivation
|
import com.unciv.ui.components.input.onActivation
|
||||||
import com.unciv.ui.components.input.onClick
|
import com.unciv.ui.components.input.onClick
|
||||||
import com.unciv.ui.components.extensions.packIfNeeded
|
import com.unciv.ui.components.extensions.packIfNeeded
|
||||||
import com.unciv.ui.components.extensions.surroundWithCircle
|
import com.unciv.ui.components.extensions.surroundWithCircle
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
import com.unciv.ui.components.extensions.toTextButton
|
import com.unciv.ui.components.extensions.toTextButton
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
|
import com.unciv.ui.components.input.keyShortcuts
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.popups.ConfirmPopup
|
import com.unciv.ui.popups.ConfirmPopup
|
||||||
import com.unciv.ui.popups.Popup
|
import com.unciv.ui.popups.Popup
|
||||||
@ -129,13 +130,16 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun update(selectedConstruction: IConstruction?) {
|
fun update(selectedConstruction: IConstruction?) {
|
||||||
updateButtons(selectedConstruction)
|
updateQueueAndButtons(selectedConstruction)
|
||||||
|
updateAvailableConstructions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateQueueAndButtons(construction: IConstruction?) {
|
||||||
|
updateButtons(construction)
|
||||||
updateConstructionQueue()
|
updateConstructionQueue()
|
||||||
upperTable.pack()
|
upperTable.pack()
|
||||||
// This should work when set once only in addActorsToStage, but it doesn't (table invisible - why?)
|
// Need to reposition when height changes as setPosition's alignment does not persist, it's just a readability shortcut to calculate bottomLeft
|
||||||
upperTable.setPosition(posFromEdge, stageHeight - posFromEdge, Align.topLeft)
|
upperTable.setPosition(posFromEdge, stageHeight - posFromEdge, Align.topLeft)
|
||||||
|
|
||||||
updateAvailableConstructions()
|
|
||||||
lowerTableScrollCell.maxHeight(stageHeight - upperTable.height - 2 * posFromEdge)
|
lowerTableScrollCell.maxHeight(stageHeight - upperTable.height - 2 * posFromEdge)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,11 +283,11 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
availableConstructionsTable.apply {
|
availableConstructionsTable.apply {
|
||||||
clear()
|
clear()
|
||||||
defaults().left().bottom()
|
defaults().left().bottom()
|
||||||
addCategory("Units", units, maxButtonWidth)
|
addCategory("Units", units, maxButtonWidth, KeyboardBinding.BuildUnits)
|
||||||
addCategory("Buildings", buildableBuildings, maxButtonWidth)
|
addCategory("Buildings", buildableBuildings, maxButtonWidth, KeyboardBinding.BuildBuildings)
|
||||||
addCategory("Wonders", buildableWonders, maxButtonWidth)
|
addCategory("Wonders", buildableWonders, maxButtonWidth, KeyboardBinding.BuildWonders)
|
||||||
addCategory("National Wonders", buildableNationalWonders, maxButtonWidth)
|
addCategory("National Wonders", buildableNationalWonders, maxButtonWidth, KeyboardBinding.BuildNationalWonders)
|
||||||
addCategory("Other", specialConstructions, maxButtonWidth)
|
addCategory("Other", specialConstructions, maxButtonWidth, KeyboardBinding.BuildOther)
|
||||||
pack()
|
pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,6 +349,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
cityScreen.selectConstruction(constructionName)
|
cityScreen.selectConstruction(constructionName)
|
||||||
selectedQueueEntry = constructionQueueIndex
|
selectedQueueEntry = constructionQueueIndex
|
||||||
cityScreen.update()
|
cityScreen.update()
|
||||||
|
ensureQueueEntryVisible()
|
||||||
}
|
}
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
@ -464,7 +469,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
|
|
||||||
if (isSelectedQueueEntry()) {
|
if (isSelectedQueueEntry()) {
|
||||||
button = "Remove from queue".toTextButton()
|
button = "Remove from queue".toTextButton()
|
||||||
button.onClick {
|
button.onActivation(binding = KeyboardBinding.AddConstruction) {
|
||||||
cityConstructions.removeFromQueue(selectedQueueEntry, false)
|
cityConstructions.removeFromQueue(selectedQueueEntry, false)
|
||||||
cityScreen.clearSelection()
|
cityScreen.clearSelection()
|
||||||
selectedQueueEntry = -1
|
selectedQueueEntry = -1
|
||||||
@ -476,7 +481,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
|| cannotAddConstructionToQueue(construction, city, cityConstructions)) {
|
|| cannotAddConstructionToQueue(construction, city, cityConstructions)) {
|
||||||
button.disable()
|
button.disable()
|
||||||
} else {
|
} else {
|
||||||
button.onClick(UncivSound.Silent) {
|
button.onActivation(binding = KeyboardBinding.AddConstruction, sound = UncivSound.Silent) {
|
||||||
addConstructionToQueue(construction, cityConstructions)
|
addConstructionToQueue(construction, cityConstructions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,13 +547,11 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
|
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
|
||||||
button.setText("Buy".tr() + " " + constructionBuyCost + stat.character)
|
button.setText("Buy".tr() + " " + constructionBuyCost + stat.character)
|
||||||
|
|
||||||
button.onActivation {
|
button.onActivation(binding = KeyboardBinding.BuyConstruction) {
|
||||||
button.disable()
|
button.disable()
|
||||||
buyButtonOnClick(construction, stat)
|
buyButtonOnClick(construction, stat)
|
||||||
}
|
}
|
||||||
button.isEnabled = isConstructionPurchaseAllowed(construction, stat, constructionBuyCost)
|
button.isEnabled = isConstructionPurchaseAllowed(construction, stat, constructionBuyCost)
|
||||||
button.keyShortcuts.add('B')
|
|
||||||
button.addTooltip('B') // The key binding is done in CityScreen constructor
|
|
||||||
preferredBuyStat = stat // Not very intelligent, but the least common currency "wins"
|
preferredBuyStat = stat // Not very intelligent, but the least common currency "wins"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,34 +654,40 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
cityScreen.update()
|
cityScreen.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRaisePriorityButton(constructionQueueIndex: Int, name: String, city: City): Table {
|
private fun getMovePriorityButton(
|
||||||
val tab = Table()
|
arrowDirection: Int,
|
||||||
tab.add(ImageGetter.getArrowImage(Align.top).apply { color = Color.BLACK }.surroundWithCircle(40f))
|
binding: KeyboardBinding,
|
||||||
tab.touchable = Touchable.enabled
|
constructionQueueIndex: Int,
|
||||||
tab.onClick {
|
name: String,
|
||||||
tab.touchable = Touchable.disabled
|
movePriority: (Int) -> Int
|
||||||
city.cityConstructions.raisePriority(constructionQueueIndex)
|
): Table {
|
||||||
|
val button = Table()
|
||||||
|
button.add(ImageGetter.getArrowImage(arrowDirection).apply { color = Color.BLACK }.surroundWithCircle(40f))
|
||||||
|
button.touchable = Touchable.enabled
|
||||||
|
// Don't bind the queue reordering keys here - those should affect only the selected entry, not all of them
|
||||||
|
button.onActivation {
|
||||||
|
button.touchable = Touchable.disabled
|
||||||
|
selectedQueueEntry = movePriority(constructionQueueIndex)
|
||||||
|
// No need to call entire cityScreen.update() as reordering doesn't influence Stat or Map,
|
||||||
|
// nor does it need an expensive rebuild of the available constructions.
|
||||||
|
// Selection display may need to update as I can click the button of a non-selected entry.
|
||||||
cityScreen.selectConstruction(name)
|
cityScreen.selectConstruction(name)
|
||||||
selectedQueueEntry = constructionQueueIndex - 1
|
cityScreen.updateWithoutConstructionAndMap()
|
||||||
cityScreen.update()
|
updateQueueAndButtons(cityScreen.selectedConstruction)
|
||||||
|
ensureQueueEntryVisible() // Not passing current button info - already outdated, our parent is already removed from the stage hierarchy and replaced
|
||||||
}
|
}
|
||||||
return tab
|
if (selectedQueueEntry == constructionQueueIndex) {
|
||||||
|
button.keyShortcuts.add(binding) // This binds without automatic tooltip
|
||||||
|
button.addTooltip(binding)
|
||||||
|
}
|
||||||
|
return button
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLowerPriorityButton(constructionQueueIndex: Int, name: String, city: City): Table {
|
private fun getRaisePriorityButton(constructionQueueIndex: Int, name: String, city: City) =
|
||||||
val tab = Table()
|
getMovePriorityButton(Align.top, KeyboardBinding.RaisePriority, constructionQueueIndex, name, city.cityConstructions::raisePriority)
|
||||||
tab.add(ImageGetter.getArrowImage(Align.bottom).apply { color = Color.BLACK }.surroundWithCircle(40f))
|
|
||||||
tab.touchable = Touchable.enabled
|
|
||||||
tab.onClick {
|
|
||||||
tab.touchable = Touchable.disabled
|
|
||||||
city.cityConstructions.lowerPriority(constructionQueueIndex)
|
|
||||||
cityScreen.selectConstruction(name)
|
|
||||||
selectedQueueEntry = constructionQueueIndex + 1
|
|
||||||
cityScreen.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
return tab
|
private fun getLowerPriorityButton(constructionQueueIndex: Int, name: String, city: City) =
|
||||||
}
|
getMovePriorityButton(Align.bottom, KeyboardBinding.LowerPriority, constructionQueueIndex, name, city.cityConstructions::lowerPriority)
|
||||||
|
|
||||||
private fun getRemoveFromQueueButton(constructionQueueIndex: Int, city: City): Table {
|
private fun getRemoveFromQueueButton(constructionQueueIndex: Int, city: City): Table {
|
||||||
val tab = Table()
|
val tab = Table()
|
||||||
@ -705,12 +714,24 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
.pad(4f)
|
.pad(4f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ensureQueueEntryVisible() {
|
||||||
|
// Ensure the selected queue entry stays visible, and if moved to the "current" top slot, that the header is visible too
|
||||||
|
// This uses knowledge about how we build constructionsQueueTable without re-evaluating that stuff:
|
||||||
|
// Every odd row is a separator, cells have no padding, and there's one header on top and another between selectedQueueEntries 0 and 1
|
||||||
|
val button = constructionsQueueTable.cells[if (selectedQueueEntry == 0) 2 else 2 * selectedQueueEntry + 4].actor
|
||||||
|
val buttonOrHeader = if (selectedQueueEntry == 0) constructionsQueueTable.cells[0].actor else button
|
||||||
|
// The 4f includes the two separators on top/bottom of the entry/header (the y offset we'd need cancels out with constructionsQueueTable.y being 2f as well):
|
||||||
|
val height = buttonOrHeader.y + buttonOrHeader.height - button.y + 4f
|
||||||
|
// Alternatively, scrollTo(..., true, true) would keep the selection as centered as possible:
|
||||||
|
constructionsQueueScrollPane.scrollTo(2f, button.y, button.width, height)
|
||||||
|
}
|
||||||
|
|
||||||
private fun resizeAvailableConstructionsScrollPane() {
|
private fun resizeAvailableConstructionsScrollPane() {
|
||||||
availableConstructionsScrollPane.height = min(availableConstructionsTable.prefHeight, lowerTableScrollCell.maxHeight)
|
availableConstructionsScrollPane.height = min(availableConstructionsTable.prefHeight, lowerTableScrollCell.maxHeight)
|
||||||
lowerTable.pack()
|
lowerTable.pack()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Table.addCategory(title: String, list: ArrayList<Table>, prefWidth: Float) {
|
private fun Table.addCategory(title: String, list: ArrayList<Table>, prefWidth: Float, toggleKey: KeyboardBinding) {
|
||||||
if (list.isEmpty()) return
|
if (list.isEmpty()) return
|
||||||
|
|
||||||
if (rows > 0) addSeparator()
|
if (rows > 0) addSeparator()
|
||||||
@ -719,6 +740,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
defaultPad = 0f,
|
defaultPad = 0f,
|
||||||
expanderWidth = prefWidth,
|
expanderWidth = prefWidth,
|
||||||
persistenceID = "CityConstruction.$title",
|
persistenceID = "CityConstruction.$title",
|
||||||
|
toggleKey = toggleKey,
|
||||||
onChange = { resizeAvailableConstructionsScrollPane() }
|
onChange = { resizeAvailableConstructionsScrollPane() }
|
||||||
) {
|
) {
|
||||||
for (table in list) {
|
for (table in list) {
|
||||||
|
@ -19,6 +19,7 @@ import com.unciv.ui.components.extensions.addSeparator
|
|||||||
import com.unciv.ui.components.extensions.addSeparatorVertical
|
import com.unciv.ui.components.extensions.addSeparatorVertical
|
||||||
import com.unciv.ui.components.input.onClick
|
import com.unciv.ui.components.input.onClick
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
|
|
||||||
class CityReligionInfoTable(
|
class CityReligionInfoTable(
|
||||||
private val religionManager: CityReligionManager,
|
private val religionManager: CityReligionManager,
|
||||||
@ -103,6 +104,7 @@ class CityReligionInfoTable(
|
|||||||
defaultPad = 0f,
|
defaultPad = 0f,
|
||||||
persistenceID = "CityStatsTable.Religion",
|
persistenceID = "CityStatsTable.Religion",
|
||||||
startsOutOpened = false,
|
startsOutOpened = false,
|
||||||
|
toggleKey = KeyboardBinding.ReligionDetail,
|
||||||
onChange = onChange
|
onChange = onChange
|
||||||
) {
|
) {
|
||||||
defaults().center().pad(5f)
|
defaults().center().pad(5f)
|
||||||
|
@ -27,6 +27,7 @@ import com.unciv.ui.components.extensions.packIfNeeded
|
|||||||
import com.unciv.ui.components.extensions.toTextButton
|
import com.unciv.ui.components.extensions.toTextButton
|
||||||
import com.unciv.ui.components.input.KeyCharAndCode
|
import com.unciv.ui.components.input.KeyCharAndCode
|
||||||
import com.unciv.ui.components.input.KeyShortcutDispatcherVeto
|
import com.unciv.ui.components.input.KeyShortcutDispatcherVeto
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
import com.unciv.ui.components.input.keyShortcuts
|
import com.unciv.ui.components.input.keyShortcuts
|
||||||
import com.unciv.ui.components.input.onActivation
|
import com.unciv.ui.components.input.onActivation
|
||||||
import com.unciv.ui.components.input.onClick
|
import com.unciv.ui.components.input.onClick
|
||||||
@ -139,8 +140,8 @@ class CityScreen(
|
|||||||
stage.addActor(exitCityButton)
|
stage.addActor(exitCityButton)
|
||||||
update()
|
update()
|
||||||
|
|
||||||
globalShortcuts.add(Input.Keys.LEFT) { page(-1) }
|
globalShortcuts.add(KeyboardBinding.PreviousCity) { page(-1) }
|
||||||
globalShortcuts.add(Input.Keys.RIGHT) { page(1) }
|
globalShortcuts.add(KeyboardBinding.NextCity) { page(1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun update() {
|
internal fun update() {
|
||||||
@ -150,6 +151,19 @@ class CityScreen(
|
|||||||
constructionsTable.isVisible = true
|
constructionsTable.isVisible = true
|
||||||
constructionsTable.update(selectedConstruction)
|
constructionsTable.update(selectedConstruction)
|
||||||
|
|
||||||
|
updateWithoutConstructionAndMap()
|
||||||
|
|
||||||
|
// Rest of screen: Map of surroundings
|
||||||
|
updateTileGroups()
|
||||||
|
if (isPortrait()) mapScrollPane.apply {
|
||||||
|
// center scrolling so city center sits more to the bottom right
|
||||||
|
scrollX = (maxX - constructionsTable.getLowerWidth() - posFromEdge) / 2
|
||||||
|
scrollY = (maxY - cityStatsTable.packIfNeeded().height - posFromEdge + cityPickerTable.top) / 2
|
||||||
|
updateVisualScroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun updateWithoutConstructionAndMap() {
|
||||||
// Bottom right: Tile or selected construction info
|
// Bottom right: Tile or selected construction info
|
||||||
tileTable.update(selectedTile)
|
tileTable.update(selectedTile)
|
||||||
tileTable.setPosition(stage.width - posFromEdge, posFromEdge, Align.bottomRight)
|
tileTable.setPosition(stage.width - posFromEdge, posFromEdge, Align.bottomRight)
|
||||||
@ -185,15 +199,6 @@ class CityScreen(
|
|||||||
|
|
||||||
// Top center: Annex/Raze button
|
// Top center: Annex/Raze button
|
||||||
updateAnnexAndRazeCityButton()
|
updateAnnexAndRazeCityButton()
|
||||||
|
|
||||||
// Rest of screen: Map of surroundings
|
|
||||||
updateTileGroups()
|
|
||||||
if (isPortrait()) mapScrollPane.apply {
|
|
||||||
// center scrolling so city center sits more to the bottom right
|
|
||||||
scrollX = (maxX - constructionsTable.getLowerWidth() - posFromEdge) / 2
|
|
||||||
scrollY = (maxY - cityStatsTable.packIfNeeded().height - posFromEdge + cityPickerTable.top) / 2
|
|
||||||
updateVisualScroll()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canCityBeChanged(): Boolean {
|
fun canCityBeChanged(): Boolean {
|
||||||
|
@ -16,6 +16,7 @@ import com.unciv.ui.components.input.onActivation
|
|||||||
import com.unciv.ui.components.input.onClick
|
import com.unciv.ui.components.input.onClick
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
import com.unciv.ui.components.extensions.toTextButton
|
import com.unciv.ui.components.extensions.toTextButton
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
|
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
|
||||||
@ -57,13 +58,11 @@ class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
|
|||||||
if (city.expansion.canBuyTile(selectedTile)) {
|
if (city.expansion.canBuyTile(selectedTile)) {
|
||||||
val goldCostOfTile = city.expansion.getGoldCostOfTile(selectedTile)
|
val goldCostOfTile = city.expansion.getGoldCostOfTile(selectedTile)
|
||||||
val buyTileButton = "Buy for [$goldCostOfTile] gold".toTextButton()
|
val buyTileButton = "Buy for [$goldCostOfTile] gold".toTextButton()
|
||||||
buyTileButton.onActivation {
|
buyTileButton.onActivation(binding = KeyboardBinding.BuyTile) {
|
||||||
buyTileButton.disable()
|
buyTileButton.disable()
|
||||||
cityScreen.askToBuyTile(selectedTile)
|
cityScreen.askToBuyTile(selectedTile)
|
||||||
}
|
}
|
||||||
buyTileButton.keyShortcuts.add('T')
|
|
||||||
buyTileButton.isEnabled = cityScreen.canChangeState && city.civ.hasStatToBuy(Stat.Gold, goldCostOfTile)
|
buyTileButton.isEnabled = cityScreen.canChangeState && city.civ.hasStatToBuy(Stat.Gold, goldCostOfTile)
|
||||||
buyTileButton.addTooltip('T') // The key binding is done in CityScreen constructor
|
|
||||||
innerTable.add(buyTileButton).padTop(5f).row()
|
innerTable.add(buyTileButton).padTop(5f).row()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import com.unciv.ui.components.extensions.surroundWithCircle
|
|||||||
import com.unciv.ui.components.extensions.toGroup
|
import com.unciv.ui.components.extensions.toGroup
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
import com.unciv.ui.components.extensions.toTextButton
|
import com.unciv.ui.components.extensions.toTextButton
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
import com.unciv.ui.components.input.onActivation
|
import com.unciv.ui.components.input.onActivation
|
||||||
import com.unciv.ui.components.input.onClick
|
import com.unciv.ui.components.input.onClick
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
@ -45,7 +46,7 @@ class CityStatsTable(private val cityScreen: CityScreen): Table() {
|
|||||||
|
|
||||||
private val detailedStatsButton = "Stats".toTextButton().apply {
|
private val detailedStatsButton = "Stats".toTextButton().apply {
|
||||||
labelCell.pad(10f)
|
labelCell.pad(10f)
|
||||||
onActivation {
|
onActivation(binding = KeyboardBinding.ShowStats) {
|
||||||
DetailedStatsPopup(cityScreen).open()
|
DetailedStatsPopup(cityScreen).open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,21 +84,19 @@ class CityStatsTable(private val cityScreen: CityScreen): Table() {
|
|||||||
for ((stat, amount) in city.cityStats.currentCityStats) {
|
for ((stat, amount) in city.cityStats.currentCityStats) {
|
||||||
if (stat == Stat.Faith && !city.civ.gameInfo.isReligionEnabled()) continue
|
if (stat == Stat.Faith && !city.civ.gameInfo.isReligionEnabled()) continue
|
||||||
val icon = Table()
|
val icon = Table()
|
||||||
if (city.cityAIFocus.stat == stat) {
|
val focus = CityFocus.safeValueOf(stat)
|
||||||
|
val toggledFocus = if (focus == city.cityAIFocus) {
|
||||||
icon.add(ImageGetter.getStatIcon(stat.name).surroundWithCircle(27f, false, color = selected))
|
icon.add(ImageGetter.getStatIcon(stat.name).surroundWithCircle(27f, false, color = selected))
|
||||||
if (cityScreen.canCityBeChanged()) {
|
CityFocus.NoFocus
|
||||||
icon.onClick {
|
|
||||||
city.cityAIFocus = CityFocus.NoFocus
|
|
||||||
city.reassignPopulation(); cityScreen.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
icon.add(ImageGetter.getStatIcon(stat.name).surroundWithCircle(27f, false, color = Color.CLEAR))
|
icon.add(ImageGetter.getStatIcon(stat.name).surroundWithCircle(27f, false, color = Color.CLEAR))
|
||||||
if (cityScreen.canCityBeChanged()) {
|
focus
|
||||||
icon.onClick {
|
}
|
||||||
city.cityAIFocus = city.cityAIFocus.safeValueOf(stat)
|
if (cityScreen.canCityBeChanged()) {
|
||||||
city.reassignPopulation(); cityScreen.update()
|
icon.onActivation(binding = toggledFocus.binding) {
|
||||||
}
|
city.cityAIFocus = toggledFocus
|
||||||
|
city.reassignPopulation()
|
||||||
|
cityScreen.update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
miniStatsTable.add(icon).size(27f).padRight(3f)
|
miniStatsTable.add(icon).size(27f).padRight(3f)
|
||||||
@ -247,7 +246,7 @@ class CityStatsTable(private val cityScreen: CityScreen): Table() {
|
|||||||
otherBuildings.sortBy { it.name }
|
otherBuildings.sortBy { it.name }
|
||||||
|
|
||||||
val totalTable = Table()
|
val totalTable = Table()
|
||||||
lowerTable.addCategory("Buildings", totalTable, false)
|
lowerTable.addCategory("Buildings", totalTable, KeyboardBinding.BuildingsDetail, false)
|
||||||
|
|
||||||
if (specialistBuildings.isNotEmpty()) {
|
if (specialistBuildings.isNotEmpty()) {
|
||||||
val specialistBuildingsTable = Table()
|
val specialistBuildingsTable = Table()
|
||||||
@ -327,13 +326,18 @@ class CityStatsTable(private val cityScreen: CityScreen): Table() {
|
|||||||
destinationTable.add(button).pad(1f).padBottom(2f).padTop(2f).expandX().right().row()
|
destinationTable.add(button).pad(1f).padBottom(2f).padTop(2f).expandX().right().row()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Table.addCategory(category: String, showHideTable: Table, startsOpened: Boolean = true, innerPadding: Float = 10f) : ExpanderTab {
|
private fun Table.addCategory(
|
||||||
|
category: String,
|
||||||
|
showHideTable: Table,
|
||||||
|
toggleKey: KeyboardBinding,
|
||||||
|
startsOpened: Boolean = true
|
||||||
|
) : ExpanderTab {
|
||||||
val expanderTab = ExpanderTab(
|
val expanderTab = ExpanderTab(
|
||||||
title = category,
|
title = category,
|
||||||
fontSize = Constants.defaultFontSize,
|
fontSize = Constants.defaultFontSize,
|
||||||
persistenceID = "CityInfo.$category",
|
persistenceID = "CityInfo.$category",
|
||||||
startsOutOpened = startsOpened,
|
startsOutOpened = startsOpened,
|
||||||
defaultPad = innerPadding,
|
toggleKey = toggleKey,
|
||||||
onChange = { onContentResize() }
|
onChange = { onContentResize() }
|
||||||
) {
|
) {
|
||||||
it.add(showHideTable).fillX().right()
|
it.add(showHideTable).fillX().right()
|
||||||
@ -392,7 +396,7 @@ class CityStatsTable(private val cityScreen: CityScreen): Table() {
|
|||||||
greatPeopleTable.add(ImageGetter.getConstructionPortrait(greatPersonName, 50f)).row()
|
greatPeopleTable.add(ImageGetter.getConstructionPortrait(greatPersonName, 50f)).row()
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerTable.addCategory("Great People", greatPeopleTable)
|
lowerTable.addCategory("Great People", greatPeopleTable, KeyboardBinding.GreatPeopleDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import com.unciv.ui.components.extensions.packIfNeeded
|
|||||||
import com.unciv.ui.components.extensions.pad
|
import com.unciv.ui.components.extensions.pad
|
||||||
import com.unciv.ui.components.extensions.surroundWithCircle
|
import com.unciv.ui.components.extensions.surroundWithCircle
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
import com.unciv.ui.images.IconCircleGroup
|
import com.unciv.ui.images.IconCircleGroup
|
||||||
import com.unciv.ui.popups.Popup
|
import com.unciv.ui.popups.Popup
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
@ -176,14 +177,11 @@ class DetailedStatsPopup(
|
|||||||
val button = label
|
val button = label
|
||||||
.surroundWithCircle(25f, color = BaseScreen.skinStrings.skinConfig.baseColor)
|
.surroundWithCircle(25f, color = BaseScreen.skinStrings.skinConfig.baseColor)
|
||||||
.surroundWithCircle(27f, false)
|
.surroundWithCircle(27f, false)
|
||||||
button.keyShortcuts.run {
|
button.onActivation(binding = KeyboardBinding.ShowStatDetails) {
|
||||||
add(Input.Keys.PLUS)
|
|
||||||
add(Input.Keys.NUMPAD_ADD)
|
|
||||||
}
|
|
||||||
button.onActivation {
|
|
||||||
isDetailed = !isDetailed
|
isDetailed = !isDetailed
|
||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
|
button.keyShortcuts.add(Input.Keys.PLUS) //todo Choose alternative (alt binding, remove, auto-equivalence, multikey bindings)
|
||||||
return button
|
return button
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import com.unciv.ui.components.extensions.darken
|
|||||||
import com.unciv.ui.components.extensions.surroundWithCircle
|
import com.unciv.ui.components.extensions.surroundWithCircle
|
||||||
import com.unciv.ui.components.extensions.toGroup
|
import com.unciv.ui.components.extensions.toGroup
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.components.input.KeyboardBinding
|
||||||
import com.unciv.ui.components.input.onClick
|
import com.unciv.ui.components.input.onClick
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
@ -141,6 +142,7 @@ class SpecialistAllocationTable(private val cityScreen: CityScreen) : Table(Base
|
|||||||
fontSize = Constants.defaultFontSize,
|
fontSize = Constants.defaultFontSize,
|
||||||
persistenceID = "CityStatsTable.Specialists",
|
persistenceID = "CityStatsTable.Specialists",
|
||||||
startsOutOpened = true,
|
startsOutOpened = true,
|
||||||
|
toggleKey = KeyboardBinding.SpecialistDetail,
|
||||||
onChange = onChange
|
onChange = onChange
|
||||||
) {
|
) {
|
||||||
it.add(this)
|
it.add(this)
|
||||||
|
@ -52,13 +52,12 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
|
|||||||
if (unitAction.type == UnitActionType.Promote && unitAction.action != null)
|
if (unitAction.type == UnitActionType.Promote && unitAction.action != null)
|
||||||
actionButton.color = Color.GREEN.cpy().lerp(Color.WHITE, 0.5f)
|
actionButton.color = Color.GREEN.cpy().lerp(Color.WHITE, 0.5f)
|
||||||
|
|
||||||
actionButton.addTooltip(binding)
|
|
||||||
actionButton.pack()
|
actionButton.pack()
|
||||||
|
|
||||||
if (unitAction.action == null) {
|
if (unitAction.action == null) {
|
||||||
actionButton.disable()
|
actionButton.disable()
|
||||||
} else {
|
} else {
|
||||||
actionButton.onActivation(unitAction.uncivSound) {
|
actionButton.onActivation(unitAction.uncivSound, binding) {
|
||||||
unitAction.action.invoke()
|
unitAction.action.invoke()
|
||||||
GUI.setUpdateWorldOnNextRender()
|
GUI.setUpdateWorldOnNextRender()
|
||||||
// We keep the unit action/selection overlay from the previous unit open even when already selecting another unit
|
// We keep the unit action/selection overlay from the previous unit open even when already selecting another unit
|
||||||
@ -70,7 +69,6 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
|
|||||||
worldScreen.switchToNextUnit()
|
worldScreen.switchToNextUnit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actionButton.keyShortcuts.add(binding)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return actionButton
|
return actionButton
|
||||||
|
Loading…
x
Reference in New Issue
Block a user