More keyboard binding work - World, World Menu Popup, WASD (#9598)

* Groundwork for tooltips for user-bindable keys

* WorldScreen keyboard revisited

* WorldScreen menu popup keyboard support

* WorldScreen WASD bindable
This commit is contained in:
SomeTroglodyte 2023-06-18 17:11:10 +02:00 committed by GitHub
parent 5e9059cd1e
commit d79c68b273
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 210 additions and 107 deletions

View File

@ -18,6 +18,8 @@ import com.unciv.GUI
import com.unciv.models.translations.tr
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.input.KeyboardBindings
import com.unciv.ui.screens.basescreen.BaseScreen
/**
@ -203,13 +205,15 @@ class UncivTooltip <T: Actor>(
/**
* Add a [Label]-based Tooltip with a rounded-corner background to a [Table] or other [Group].
*
* Removes any previous tooltips (so this can be used to clear tips by passing an empty [text]).
* Tip is positioned over top right corner, slightly overshooting the receiver widget, longer tip [text]s will extend to the left.
* Note - since this is mainly used for keyboard tips, this is by default automatically suppressed on devices without keyboard. Use the [always] parameter to override.
*
* @param text Automatically translated tooltip text
* @param size _Vertical_ size of the entire Tooltip including background
* @param always override requirement: presence of physical keyboard
* @param targetAlign Point on the [target] widget to align the Tooltip to
* @param tipAlign Point on the Tooltip to align with the given point on the [target]
* @param hideIcons Do not automatically add ruleset object icons during translation
*/
fun Actor.addTooltip(
text: String,
@ -219,6 +223,11 @@ class UncivTooltip <T: Actor>(
tipAlign: Int = Align.top,
hideIcons: Boolean = false
) {
for (tip in listeners.filterIsInstance<UncivTooltip<*>>()) {
tip.hide(true)
removeListener(tip)
}
if (!(always || GUI.keyboardAvailable) || text.isEmpty()) return
val label = text.toLabel(BaseScreen.skinStrings.skinConfig.baseColor, 38, hideIcons = hideIcons)
@ -252,28 +261,42 @@ class UncivTooltip <T: Actor>(
}
/**
* Add a single Char [Label]-based Tooltip with a rounded-corner background to a [Table] or other [Group].
* Add a single-Char [Label]-based Tooltip with a rounded-corner background to a [Table] or other [Group].
*
* Note this is automatically suppressed on devices without keyboard.
* Tip is positioned over top right corner, slightly overshooting the receiver widget.
*
* @param size _Vertical_ size of the entire Tooltip including background
* @param always override requirement: presence of physical keyboard
*/
fun Actor.addTooltip(char: Char, size: Float = 26f, always: Boolean = false) {
addTooltip((if (char in "Ii") 'i' else char.uppercaseChar()).toString(), size, always)
fun Actor.addTooltip(char: Char, size: Float = 26f) {
addTooltip((if (char in "Ii") 'i' else char.uppercaseChar()).toString(), size)
}
/**
* Add a [Label]-based Tooltip for a keyboard binding with a rounded-corner background to a [Table] or other [Group].
*
* Note this is automatically suppressed on devices without keyboard.
* Tip is positioned over top right corner, slightly overshooting the receiver widget.
*
* @param size _Vertical_ size of the entire Tooltip including background
* @param always override requirement: presence of physical keyboard
*/
fun Actor.addTooltip(key: KeyCharAndCode, size: Float = 26f, always: Boolean = false) {
fun Actor.addTooltip(key: KeyCharAndCode, size: Float = 26f) {
if (key != KeyCharAndCode.UNKNOWN)
addTooltip(key.toString().tr(), size, always)
addTooltip(key.toString().tr(), size)
}
/**
* Add a [Label]-based Tooltip for a dynamic keyboard binding with a rounded-corner background to a [Table] or other [Group].
*
* Note this is automatically suppressed on devices without keyboard.
* Tip is positioned over top right corner, slightly overshooting the receiver widget.
*
* @param size _Vertical_ size of the entire Tooltip including background
*/
fun Actor.addTooltip(binding: KeyboardBinding, size: Float = 26f) {
val key = KeyboardBindings[binding]
if (key != KeyCharAndCode.UNKNOWN)
addTooltip(key.toString().tr(), size)
}
}
}

View File

@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener
import com.badlogic.gdx.scenes.scene2d.utils.Disableable
import com.unciv.models.UncivSound
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
/** Used to stop activation events if this returns `true`. */
internal fun Actor.isActive(): Boolean = isVisible && ((this as? Disableable)?.isDisabled != true)
@ -38,6 +39,18 @@ fun Actor.onActivation(
return this
}
/** Assigns an activation [handler][action] to your Widget, which reacts to clicks and a [key stroke][binding].
* A tooltip is attached automatically, if there is a keyboard and the [binding] has a mapping.
* A [sound] will be played (concurrently) on activation unless you specify [UncivSound.Silent].
* @return `this` to allow chaining
*/
fun Actor.onActivation(sound: UncivSound = UncivSound.Click, binding: KeyboardBinding, action: ActivationAction): Actor {
onActivation(ActivationTypes.Tap, sound, action = action)
keyShortcuts.add(binding)
addTooltip(binding)
return this
}
/** Routes clicks and [keyboard shortcuts][keyShortcuts] to your handler [action].
* A [sound] will be played (concurrently) on activation unless you specify [UncivSound.Silent].
* @return `this` to allow chaining

View File

@ -18,34 +18,60 @@ enum class KeyboardBinding(
None(Category.None, KeyCharAndCode.UNKNOWN),
// Worldscreen
Menu(Category.WorldScreen, KeyCharAndCode.TAB),
NextTurn(Category.WorldScreen),
NextTurnAlternate(Category.WorldScreen, KeyCharAndCode.SPACE),
Civilopedia(Category.WorldScreen, Input.Keys.F1),
EmpireOverview(Category.WorldScreen),
EmpireOverviewTrades(Category.WorldScreen, Input.Keys.F2),
EmpireOverviewUnits(Category.WorldScreen, Input.Keys.F3),
EmpireOverviewPolitics(Category.WorldScreen, Input.Keys.F4),
SocialPolicies(Category.WorldScreen, Input.Keys.F5),
TechnologyTree(Category.WorldScreen, Input.Keys.F6),
EmpireOverviewNotifications(Category.WorldScreen, Input.Keys.F7),
VictoryScreen(Category.WorldScreen, "Victory status", Input.Keys.F8),
EmpireOverviewStats(Category.WorldScreen, Input.Keys.F9),
EmpireOverviewResources(Category.WorldScreen, Input.Keys.F10),
QuickSave(Category.WorldScreen, Input.Keys.F11),
QuickLoad(Category.WorldScreen, Input.Keys.F12),
ViewCapitalCity(Category.WorldScreen, Input.Keys.HOME),
Options(Category.WorldScreen, KeyCharAndCode.ctrl('o')),
SaveGame(Category.WorldScreen, KeyCharAndCode.ctrl('s')),
LoadGame(Category.WorldScreen, KeyCharAndCode.ctrl('l')),
MusicPlayer(Category.WorldScreen, KeyCharAndCode.ctrl('m')),
/*
* These try to be faithful to default Civ5 key bindings as found in several places online
* Some are a little arbitrary, e.g. Economic info, Military info
* Some are very much so as Unciv *is* Strategic View.
* The comments show a description like found in the mentioned sources for comparison.
* @see http://gaming.stackexchange.com/questions/8122/ddg#8125
*/
Civilopedia(Category.WorldScreen, Input.Keys.F1), // Civilopedia
EmpireOverviewTrades(Category.WorldScreen, Input.Keys.F2), // Economic info
EmpireOverviewUnits(Category.WorldScreen, Input.Keys.F3), // Military info
EmpireOverviewPolitics(Category.WorldScreen, Input.Keys.F4), // Diplomacy info
SocialPolicies(Category.WorldScreen, Input.Keys.F5), // Social Policies Screen
TechnologyTree(Category.WorldScreen, Input.Keys.F6), // Tech Screen
EmpireOverviewNotifications(Category.WorldScreen, Input.Keys.F7), // Notification Log
VictoryScreen(Category.WorldScreen, "Victory status", Input.Keys.F8), // Victory Progress
EmpireOverviewStats(Category.WorldScreen, Input.Keys.F9), // Demographics
EmpireOverviewResources(Category.WorldScreen, Input.Keys.F10), // originally Strategic View
QuickSave(Category.WorldScreen, Input.Keys.F11), // Quick Save
QuickLoad(Category.WorldScreen, Input.Keys.F12), // Quick Load
ViewCapitalCity(Category.WorldScreen, Input.Keys.HOME), // Capital City View
Options(Category.WorldScreen, KeyCharAndCode.ctrl('o')), // Game Options
SaveGame(Category.WorldScreen, KeyCharAndCode.ctrl('s')), // Save
LoadGame(Category.WorldScreen, KeyCharAndCode.ctrl('l')), // Load
ToggleResourceDisplay(Category.WorldScreen, KeyCharAndCode.ctrl('r')), // Show Resources Icons
ToggleYieldDisplay(Category.WorldScreen, KeyCharAndCode.ctrl('y')), // Yield Icons, originally just "Y"
// End of Civ5-inspired bindings
QuitGame(Category.WorldScreen, KeyCharAndCode.ctrl('q')),
NewGame(Category.WorldScreen, KeyCharAndCode.ctrl('n')),
Diplomacy(Category.WorldScreen, KeyCharAndCode.UNKNOWN),
Espionage(Category.WorldScreen, KeyCharAndCode.UNKNOWN),
Undo(Category.WorldScreen, KeyCharAndCode.ctrl('z')),
ToggleUI(Category.WorldScreen, "Toggle UI", KeyCharAndCode.ctrl('u')),
ToggleResourceDisplay(Category.WorldScreen, KeyCharAndCode.ctrl('r')),
ToggleYieldDisplay(Category.WorldScreen, KeyCharAndCode.ctrl('y')),
ToggleWorkedTilesDisplay(Category.WorldScreen, KeyCharAndCode.UNKNOWN),
ToggleMovementDisplay(Category.WorldScreen, KeyCharAndCode.UNKNOWN),
ZoomIn(Category.WorldScreen, Input.Keys.NUMPAD_ADD),
ZoomOut(Category.WorldScreen, Input.Keys.NUMPAD_SUBTRACT),
// Map Panning - separate to get own expander. Map editor use will need to check this - it's arrows only
PanUp(Category.MapPanning, Input.Keys.UP),
PanLeft(Category.MapPanning, Input.Keys.LEFT),
PanDown(Category.MapPanning, Input.Keys.DOWN),
PanRight(Category.MapPanning, Input.Keys.RIGHT),
PanUpAlternate(Category.MapPanning, 'W'),
PanLeftAlternate(Category.MapPanning, 'A'),
PanDownAlternate(Category.MapPanning, 'S'),
PanRightAlternate(Category.MapPanning, 'D'),
// Unit actions - name MUST correspond to UnitActionType.name because the shorthand constructor
// there looks up bindings here by name - which also means we must not use UnitActionType
// here as it will not be guaranteed to already be fully initialized.
@ -97,7 +123,10 @@ enum class KeyboardBinding(
None,
WorldScreen {
// Conflict checking within group plus keys assigned to UnitActions are a problem
override fun checkConflictsIn() = sequenceOf(this, UnitActions)
override fun checkConflictsIn() = sequenceOf(this, MapPanning, UnitActions)
},
MapPanning {
override fun checkConflictsIn() = sequenceOf(this, WorldScreen)
},
UnitActions {
// Conflict checking within group disabled, but any key assigned on WorldScreen is a problem

View File

@ -20,9 +20,19 @@ class KeyboardPanningListener(
private val pressedKeys = mutableSetOf<Int>()
private var infiniteAction: RepeatAction? = null
private val keycodeUp = KeyboardBindings[KeyboardBinding.PanUp].code
private val keycodeLeft = KeyboardBindings[KeyboardBinding.PanLeft].code
private val keycodeDown = KeyboardBindings[KeyboardBinding.PanDown].code
private val keycodeRight = KeyboardBindings[KeyboardBinding.PanRight].code
private val keycodeUpAlt = KeyboardBindings[KeyboardBinding.PanUpAlternate].code
private val keycodeLeftAlt = KeyboardBindings[KeyboardBinding.PanLeftAlternate].code
private val keycodeDownAlt = KeyboardBindings[KeyboardBinding.PanDownAlternate].code
private val keycodeRightAlt = KeyboardBindings[KeyboardBinding.PanRightAlternate].code
private val allowedKeys =
setOf(Input.Keys.UP, Input.Keys.DOWN, Input.Keys.LEFT, Input.Keys.RIGHT) + (
if (allowWASD) setOf(Input.Keys.W, Input.Keys.S, Input.Keys.A, Input.Keys.D)
setOf(keycodeUp, keycodeLeft, keycodeDown, keycodeRight) + (
if (allowWASD) setOf(keycodeUpAlt, keycodeLeftAlt, keycodeDownAlt, keycodeRightAlt)
else setOf()
)
@ -70,10 +80,10 @@ class KeyboardPanningListener(
var deltaY = 0f
for (keycode in pressedKeys) {
when (keycode) {
Input.Keys.W, Input.Keys.UP -> deltaY -= 1f
Input.Keys.S, Input.Keys.DOWN -> deltaY += 1f
Input.Keys.A, Input.Keys.LEFT -> deltaX += 1f
Input.Keys.D, Input.Keys.RIGHT -> deltaX -= 1f
keycodeUp, keycodeUpAlt -> deltaY -= 1f
keycodeDown, keycodeDownAlt -> deltaY += 1f
keycodeLeft, keycodeLeftAlt -> deltaX += 1f
keycodeRight, keycodeRightAlt -> deltaX -= 1f
}
}
mapHolder.doKeyOrMousePanning(deltaX, deltaY)

View File

@ -20,16 +20,20 @@ import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.logic.event.EventBus
import com.unciv.ui.components.AutoScrollPane
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.input.KeyboardBindings
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.center
import com.unciv.ui.components.extensions.darken
import com.unciv.ui.components.input.keyShortcuts
import com.unciv.ui.components.input.onActivation
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.input.KeyboardBindings
import com.unciv.ui.components.input.keyShortcuts
import com.unciv.ui.components.input.onActivation
import com.unciv.ui.popups.Popup.Scrollability
import com.unciv.ui.popups.Popup.Scrollability.All
import com.unciv.ui.popups.Popup.Scrollability.None
import com.unciv.ui.popups.Popup.Scrollability.WithoutButtons
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.basescreen.UncivStage
@ -292,6 +296,11 @@ open class Popup(
@Suppress("unused") // Keep the offer to pass an Input.keys value
fun addButton(text: String, key: Int, style: TextButtonStyle? = null, action: () -> Unit)
= addButton(text, KeyCharAndCode(key), style, action).apply { row() }
fun addButton(text: String, binding: KeyboardBinding, style: TextButtonStyle? = null, action: () -> Unit): Cell<TextButton> {
val button = text.toTextButton(style)
button.onActivation(binding = binding) { action() }
return bottomTable.add(button)
}
/**
* Adds a [TextButton] that closes the popup, with [BACK][KeyCharAndCode.BACK] already mapped.

View File

@ -7,10 +7,10 @@ import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.translations.tr
import com.unciv.ui.components.ExpanderTab
import com.unciv.ui.components.KeyCapturingButton
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.TabbedPager
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
import com.unciv.ui.screens.civilopediascreen.FormattedLine
@ -109,7 +109,7 @@ class KeyBindingsTab(
}
}
fun save () {
fun save() {
for ((binding, widget) in groupedWidgets.asSequence().flatMap { it.value.entries }) {
keyBindings[binding] = widget.current
}

View File

@ -20,9 +20,9 @@ import com.unciv.models.TutorialTrigger
import com.unciv.models.skins.SkinStrings
import com.unciv.ui.components.Fonts
import com.unciv.ui.components.extensions.isNarrowerThan4to3
import com.unciv.ui.components.input.DispatcherVetoer
import com.unciv.ui.components.input.KeyShortcutDispatcher
import com.unciv.ui.components.input.KeyShortcutDispatcherVeto
import com.unciv.ui.components.input.DispatcherVetoer
import com.unciv.ui.components.input.installShortcutDispatcher
import com.unciv.ui.components.input.keyShortcuts
import com.unciv.ui.crashhandling.CrashScreen
@ -174,7 +174,7 @@ abstract class BaseScreen : Screen {
/** @return `true` if the screen is narrower than 4:3 landscape */
fun isNarrowerThan4to3() = stage.isNarrowerThan4to3()
fun openOptionsPopup(startingPage: Int = OptionsPopup.defaultPage, onClose: () -> Unit = {}) {
open fun openOptionsPopup(startingPage: Int = OptionsPopup.defaultPage, onClose: () -> Unit = {}) {
OptionsPopup(this, startingPage, onClose).open(force = true)
}
}

View File

@ -9,8 +9,10 @@ import com.unciv.models.UncivSound
import com.unciv.models.translations.tr
import com.unciv.ui.components.Fonts
import com.unciv.ui.components.extensions.colorFromRGB
import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.extensions.disable
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.images.ImageGetter
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.diplomacyscreen.DiplomacyScreen
@ -51,31 +53,28 @@ class TechPolicyDiplomacyButtons(val worldScreen: WorldScreen) : Table(BaseScree
pickTechButton.background = BaseScreen.skinStrings.getUiBackground("WorldScreen/PickTechButton", BaseScreen.skinStrings.roundedEdgeRectangleShape, colorFromRGB(7, 46, 43))
pickTechButton.defaults().pad(20f)
pickTechButton.add(pickTechLabel)
techButtonHolder.onClick(UncivSound.Paper) {
techButtonHolder.onActivation(UncivSound.Paper, KeyboardBinding.TechnologyTree) {
game.pushScreen(TechPickerScreen(viewingCiv))
}
undoButton.add(ImageGetter.getImage("OtherIcons/Resume")).size(30f).pad(15f)
undoButton.onClick {
Concurrency.run {
// Most of the time we won't load this, so we only set transients once we see it's relevant
worldScreen.preActionGameInfo.setTransients()
game.loadGame(worldScreen.preActionGameInfo)
}
undoButton.onActivation(binding = KeyboardBinding.Undo) {
handleUndo()
}
policyScreenButton.add(ImageGetter.getImage("PolicyIcons/Constitution")).size(30f).pad(15f)
policyButtonHolder.onClick {
policyButtonHolder.onActivation(binding = KeyboardBinding.SocialPolicies) {
game.pushScreen(PolicyPickerScreen(worldScreen.selectedCiv, worldScreen.canChangeState))
}
diplomacyButton.add(ImageGetter.getImage("OtherIcons/DiplomacyW")).size(30f).pad(15f)
diplomacyButtonHolder.onClick {
diplomacyButtonHolder.onActivation(binding = KeyboardBinding.Diplomacy) {
game.pushScreen(DiplomacyScreen(viewingCiv))
}
if (game.gameInfo!!.isEspionageEnabled()) {
espionageButton.add(ImageGetter.getImage("OtherIcons/Spy_White")).size(30f).pad(15f)
espionageButtonHolder.onClick {
espionageButtonHolder.onActivation(binding = KeyboardBinding.Espionage) {
game.pushScreen(EspionageOverviewScreen(viewingCiv))
}
}
@ -162,4 +161,13 @@ class TechPolicyDiplomacyButtons(val worldScreen: WorldScreen) : Table(BaseScree
espionageButtonHolder.actor = espionageButton
}
}
private fun handleUndo() {
undoButton.disable()
Concurrency.run {
// Most of the time we won't load this, so we only set transients once we see it's relevant
worldScreen.preActionGameInfo.setTransients()
game.loadGame(worldScreen.preActionGameInfo)
}
}
}

View File

@ -21,19 +21,20 @@ import com.unciv.logic.multiplayer.storage.FileStorageRateLimitReached
import com.unciv.logic.multiplayer.storage.MultiplayerAuthException
import com.unciv.logic.trade.TradeEvaluation
import com.unciv.models.TutorialTrigger
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.input.KeyboardPanningListener
import com.unciv.ui.components.extensions.centerX
import com.unciv.ui.components.extensions.darken
import com.unciv.ui.components.extensions.isEnabled
import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.extensions.setFontSize
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyShortcutDispatcherVeto
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.input.KeyboardPanningListener
import com.unciv.ui.components.input.onClick
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.AuthPopup
import com.unciv.ui.popups.Popup
@ -43,18 +44,18 @@ import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.cityscreen.CityScreen
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
import com.unciv.ui.screens.mainmenuscreen.MainMenuScreen
import com.unciv.ui.screens.newgamescreen.NewGameScreen
import com.unciv.ui.screens.overviewscreen.EmpireOverviewCategories
import com.unciv.ui.screens.overviewscreen.EmpireOverviewScreen
import com.unciv.ui.screens.pickerscreens.DiplomaticVoteResultScreen
import com.unciv.ui.screens.pickerscreens.GreatPersonPickerScreen
import com.unciv.ui.screens.pickerscreens.PolicyPickerScreen
import com.unciv.ui.screens.pickerscreens.TechPickerScreen
import com.unciv.ui.screens.savescreens.LoadGameScreen
import com.unciv.ui.screens.savescreens.QuickSave
import com.unciv.ui.screens.savescreens.SaveGameScreen
import com.unciv.ui.screens.victoryscreen.VictoryScreen
import com.unciv.ui.screens.worldscreen.bottombar.BattleTable
import com.unciv.ui.screens.worldscreen.bottombar.TileInfoTable
import com.unciv.ui.screens.worldscreen.mainmenu.WorldScreenMusicPopup
import com.unciv.ui.screens.worldscreen.minimap.MinimapHolder
import com.unciv.ui.screens.worldscreen.status.MultiplayerStatusButton
import com.unciv.ui.screens.worldscreen.status.NextTurnButton
@ -63,10 +64,10 @@ import com.unciv.ui.screens.worldscreen.status.StatusButtons
import com.unciv.ui.screens.worldscreen.unit.UnitTable
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsTable
import com.unciv.utils.Concurrency
import com.unciv.utils.debug
import com.unciv.utils.launchOnGLThread
import com.unciv.utils.launchOnThreadPool
import com.unciv.utils.withGLContext
import com.unciv.utils.debug
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
@ -224,27 +225,27 @@ class WorldScreen(
game.pushScreen(EmpireOverviewScreen(selectedCiv, category))
}
fun openNewGameScreen() {
val newGameSetupInfo = GameSetupInfo(gameInfo)
newGameSetupInfo.mapParameters.reseed()
val newGameScreen = NewGameScreen(newGameSetupInfo)
game.pushScreen(newGameScreen)
}
private fun addKeyboardPresses() {
// Space and N are assigned in NextTurnButton constructor
// Functions that have a big button are assigned there (WorldScreenTopBar, TechPolicyDiplomacyButtons..)
globalShortcuts.add(KeyboardBinding.Civilopedia) { game.pushScreen(CivilopediaScreen(gameInfo.ruleset)) }
globalShortcuts.add(KeyboardBinding.EmpireOverview) { openEmpireOverview() } // Empire overview last used page
/*
* These try to be faithful to default Civ5 key bindings as found in several places online
* Some are a little arbitrary, e.g. Economic info, Military info
* Some are very much so as Unciv *is* Strategic View
*/
globalShortcuts.add(KeyboardBinding.EmpireOverviewTrades) { openEmpireOverview(EmpireOverviewCategories.Trades) } // Economic info
globalShortcuts.add(KeyboardBinding.EmpireOverviewUnits) { openEmpireOverview(EmpireOverviewCategories.Units) } // Military info
globalShortcuts.add(KeyboardBinding.EmpireOverviewPolitics) { openEmpireOverview(EmpireOverviewCategories.Politics) } // Diplomacy info
globalShortcuts.add(KeyboardBinding.SocialPolicies) { game.pushScreen(PolicyPickerScreen(selectedCiv, canChangeState)) } // Social Policies Screen
globalShortcuts.add(KeyboardBinding.TechnologyTree) { game.pushScreen(TechPickerScreen(viewingCiv)) } // Tech Screen
globalShortcuts.add(KeyboardBinding.EmpireOverviewNotifications) { openEmpireOverview(EmpireOverviewCategories.Notifications) } // Notification Log
globalShortcuts.add(KeyboardBinding.VictoryScreen) { game.pushScreen(VictoryScreen(this)) } // Victory Progress
globalShortcuts.add(KeyboardBinding.EmpireOverviewStats) { openEmpireOverview(EmpireOverviewCategories.Stats) } // Demographics
globalShortcuts.add(KeyboardBinding.EmpireOverviewResources) { openEmpireOverview(EmpireOverviewCategories.Resources) } // originally Strategic View
globalShortcuts.add(KeyboardBinding.QuickSave) { QuickSave.save(gameInfo, this) } // Quick Save
globalShortcuts.add(KeyboardBinding.QuickLoad) { QuickSave.load(this) } // Quick Load
globalShortcuts.add(KeyboardBinding.ViewCapitalCity) { // Capital City View
globalShortcuts.add(KeyboardBinding.EmpireOverviewTrades) { openEmpireOverview(EmpireOverviewCategories.Trades) }
globalShortcuts.add(KeyboardBinding.EmpireOverviewUnits) { openEmpireOverview(EmpireOverviewCategories.Units) }
globalShortcuts.add(KeyboardBinding.EmpireOverviewPolitics) { openEmpireOverview(EmpireOverviewCategories.Politics) }
globalShortcuts.add(KeyboardBinding.EmpireOverviewNotifications) { openEmpireOverview(EmpireOverviewCategories.Notifications) }
globalShortcuts.add(KeyboardBinding.VictoryScreen) { game.pushScreen(VictoryScreen(this)) }
globalShortcuts.add(KeyboardBinding.EmpireOverviewStats) { openEmpireOverview(EmpireOverviewCategories.Stats) }
globalShortcuts.add(KeyboardBinding.EmpireOverviewResources) { openEmpireOverview(EmpireOverviewCategories.Resources) }
globalShortcuts.add(KeyboardBinding.QuickSave) { QuickSave.save(gameInfo, this) }
globalShortcuts.add(KeyboardBinding.QuickLoad) { QuickSave.load(this) }
globalShortcuts.add(KeyboardBinding.ViewCapitalCity) {
val capital = gameInfo.getCurrentPlayerCivilization().getCapital()
if (capital != null && !mapHolder.setCenterPosition(capital.location))
game.pushScreen(CityScreen(capital))
@ -257,6 +258,10 @@ class WorldScreen(
globalShortcuts.add(KeyboardBinding.SaveGame) { game.pushScreen(SaveGameScreen(gameInfo)) } // Save
globalShortcuts.add(KeyboardBinding.LoadGame) { game.pushScreen(LoadGameScreen()) } // Load
globalShortcuts.add(KeyboardBinding.QuitGame) { game.popScreen() } // WorldScreen is the last screen, so this quits
globalShortcuts.add(KeyboardBinding.NewGame) { openNewGameScreen() }
globalShortcuts.add(KeyboardBinding.MusicPlayer) {
WorldScreenMusicPopup(this).open(force = true)
}
globalShortcuts.add(Input.Keys.NUMPAD_ADD) { this.mapHolder.zoomIn() } // '+' Zoom
globalShortcuts.add(Input.Keys.NUMPAD_SUBTRACT) { this.mapHolder.zoomOut() } // '-' Zoom
globalShortcuts.add(KeyboardBinding.ToggleUI) { toggleUI() }
@ -266,6 +271,16 @@ class WorldScreen(
globalShortcuts.add(KeyboardBinding.ToggleMovementDisplay) { minimapWrapper.movementsImageButton.toggle() }
}
// Handle disabling and re-enabling WASD listener while Options are open
override fun openOptionsPopup(startingPage: Int, onClose: () -> Unit) {
val oldListener = stage.root.listeners.filterIsInstance<KeyboardPanningListener>().firstOrNull()
if (oldListener != null) stage.removeListener(oldListener)
super.openOptionsPopup(startingPage) {
addKeyboardListener()
onClose()
}
}
private fun toggleUI() {
uiEnabled = !uiEnabled
topBar.isVisible = uiEnabled

View File

@ -16,18 +16,18 @@ import com.unciv.models.stats.Stats
import com.unciv.models.translations.tr
import com.unciv.ui.components.Fonts
import com.unciv.ui.components.MayaCalendar
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
import com.unciv.ui.components.YearTextUtil
import com.unciv.ui.components.extensions.colorFromRGB
import com.unciv.ui.components.extensions.darken
import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.extensions.setFontColor
import com.unciv.ui.components.extensions.setFontSize
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toStringSigned
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.onClick
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.popups
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.civilopediascreen.CivilopediaCategories
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
@ -180,8 +180,9 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
}
val overviewButton = "Overview".toTextButton()
overviewButton.addTooltip('e')
overviewButton.onClick { worldScreen.openEmpireOverview() }
overviewButton.onActivation(binding = KeyboardBinding.EmpireOverview) {
worldScreen.openEmpireOverview()
}
unitSupplyCell = add()
add(overviewButton).pad(10f)
@ -209,10 +210,8 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
defaults().pad(10f)
menuButton.color = Color.WHITE
menuButton.onClick {
val worldScreenMenuPopup = worldScreen.popups.firstOrNull { it is WorldScreenMenuPopup }
if (worldScreenMenuPopup != null) worldScreenMenuPopup.close()
else WorldScreenMenuPopup(worldScreen).open(force = true)
menuButton.onActivation(binding = KeyboardBinding.Menu) {
WorldScreenMenuPopup(worldScreen).open(force = true)
}
selectedCivLabel.setFontSize(25)

View File

@ -1,9 +1,8 @@
package com.unciv.ui.screens.worldscreen.mainmenu
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.popups.Popup
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
import com.unciv.ui.screens.newgamescreen.NewGameScreen
import com.unciv.ui.screens.savescreens.LoadGameScreen
import com.unciv.ui.screens.savescreens.SaveGameScreen
import com.unciv.ui.screens.victoryscreen.VictoryScreen
@ -16,32 +15,27 @@ class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen, sc
addButton("Main menu") {
worldScreen.game.goToMainMenu()
}.row()
addButton("Civilopedia") {
addButton("Civilopedia", KeyboardBinding.Civilopedia) {
close()
worldScreen.game.pushScreen(CivilopediaScreen(worldScreen.gameInfo.ruleset))
}.row()
addButton("Save game") {
addButton("Save game", KeyboardBinding.SaveGame) {
close()
worldScreen.game.pushScreen(SaveGameScreen(worldScreen.gameInfo))
}.row()
addButton("Load game") {
addButton("Load game", KeyboardBinding.LoadGame) {
close()
worldScreen.game.pushScreen(LoadGameScreen())
}.row()
addButton("Start new game") {
addButton("Start new game", KeyboardBinding.NewGame) {
close()
val newGameSetupInfo = GameSetupInfo(worldScreen.gameInfo)
newGameSetupInfo.mapParameters.reseed()
val newGameScreen = NewGameScreen(newGameSetupInfo)
worldScreen.game.pushScreen(newGameScreen)
worldScreen.openNewGameScreen()
}.row()
addButton("Victory status") {
addButton("Victory status", KeyboardBinding.VictoryScreen) {
close()
worldScreen.game.pushScreen(VictoryScreen(worldScreen))
}.row()
addButton("Options") {
addButton("Options", KeyboardBinding.Options) {
close()
worldScreen.openOptionsPopup()
}.row()
@ -49,10 +43,11 @@ class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen, sc
close()
WorldScreenCommunityPopup(worldScreen).open(force = true)
}.row()
addButton("Music") {
addButton("Music", KeyboardBinding.MusicPlayer) {
close()
WorldScreenMusicPopup(worldScreen).open(force = true)
}.row()
addCloseButton()
pack()
}

View File

@ -5,13 +5,14 @@ import com.unciv.Constants
import com.unciv.logic.civilization.managers.ReligionState
import com.unciv.models.ruleset.BeliefType
import com.unciv.models.translations.tr
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.extensions.enable
import com.unciv.ui.components.extensions.isEnabled
import com.unciv.ui.components.extensions.setSize
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.components.extensions.setSize
import com.unciv.ui.images.IconTextButton
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.ConfirmPopup
@ -45,6 +46,8 @@ class NextTurnButton : IconTextButton("", null, 30) {
isEnabled = !worldScreen.hasOpenPopups() && worldScreen.isPlayersTurn
&& !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning()
if (isEnabled) addTooltip(KeyboardBinding.NextTurn) else addTooltip("")
}
internal fun updateButton(nextTurnAction: NextTurnAction) {
label.setText(nextTurnAction.text.tr())

View File

@ -12,7 +12,6 @@ import com.unciv.models.UnitActionType
import com.unciv.models.UpgradeUnitAction
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.input.KeyboardBindings
import com.unciv.ui.components.input.keyShortcuts
import com.unciv.ui.components.input.onActivation
import com.unciv.ui.components.input.onRightClick
@ -53,7 +52,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
if (unitAction.type == UnitActionType.Promote && unitAction.action != null)
actionButton.color = Color.GREEN.cpy().lerp(Color.WHITE, 0.5f)
actionButton.addTooltip(KeyboardBindings[binding])
actionButton.addTooltip(binding)
actionButton.pack()
if (unitAction.action == null) {