mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 01:39:40 +07:00
WASD listener out of WorldScreen and add to map editor (#8947)
This commit is contained in:
74
core/src/com/unciv/ui/components/KeyboardPanningListener.kt
Normal file
74
core/src/com/unciv/ui/components/KeyboardPanningListener.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.unciv.ui.components
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx
|
||||||
|
import com.badlogic.gdx.Input
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputEvent
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputListener
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.actions.Actions
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.TextField
|
||||||
|
|
||||||
|
class KeyboardPanningListener(
|
||||||
|
private val mapHolder: ZoomableScrollPane,
|
||||||
|
allowWASD: Boolean,
|
||||||
|
) : InputListener() {
|
||||||
|
private val pressedKeys = mutableSetOf<Int>()
|
||||||
|
private var infiniteAction: RepeatAction? = null
|
||||||
|
private val amountToMove = 6 / mapHolder.scaleX
|
||||||
|
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)
|
||||||
|
else setOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun keyDown(event: InputEvent, keycode: Int): Boolean {
|
||||||
|
if (event.target is TextField) return false
|
||||||
|
if (keycode !in allowedKeys) return false
|
||||||
|
// Without the following Ctrl-S would leave WASD map scrolling stuck
|
||||||
|
// Might be obsolete with keyboard shortcut refactoring
|
||||||
|
if (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT)) return false
|
||||||
|
if (Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) return false
|
||||||
|
pressedKeys.add(keycode)
|
||||||
|
startLoop()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun keyUp(event: InputEvent?, keycode: Int): Boolean {
|
||||||
|
if (keycode !in allowedKeys) return false
|
||||||
|
pressedKeys.remove(keycode)
|
||||||
|
if (pressedKeys.isEmpty()) stopLoop()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startLoop() {
|
||||||
|
if (infiniteAction != null) return
|
||||||
|
// create a copy of the action, because removeAction() will destroy this instance
|
||||||
|
infiniteAction = Actions.forever(
|
||||||
|
Actions.delay(
|
||||||
|
0.01f,
|
||||||
|
Actions.run { whileKeyPressedLoop() })
|
||||||
|
)
|
||||||
|
mapHolder.addAction(infiniteAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopLoop() {
|
||||||
|
if (infiniteAction == null) return
|
||||||
|
// stop the loop otherwise it keeps going even after removal
|
||||||
|
infiniteAction?.finish()
|
||||||
|
// remove and nil the action
|
||||||
|
mapHolder.removeAction(infiniteAction)
|
||||||
|
infiniteAction = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun whileKeyPressedLoop() {
|
||||||
|
for (keycode in pressedKeys) {
|
||||||
|
when (keycode) {
|
||||||
|
Input.Keys.W, Input.Keys.UP -> mapHolder.scrollY = mapHolder.restrictY(-amountToMove)
|
||||||
|
Input.Keys.S, Input.Keys.DOWN -> mapHolder.scrollY = mapHolder.restrictY(amountToMove)
|
||||||
|
Input.Keys.A, Input.Keys.LEFT -> mapHolder.scrollX = mapHolder.restrictX(amountToMove)
|
||||||
|
Input.Keys.D, Input.Keys.RIGHT -> mapHolder.scrollX = mapHolder.restrictX(-amountToMove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapHolder.updateVisualScroll()
|
||||||
|
}
|
||||||
|
}
|
@ -22,9 +22,7 @@ import com.unciv.models.metadata.GameSetupInfo
|
|||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
import com.unciv.ui.components.AutoScrollPane
|
import com.unciv.ui.components.AutoScrollPane
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
|
||||||
import com.unciv.ui.components.KeyCharAndCode
|
import com.unciv.ui.components.KeyCharAndCode
|
||||||
import com.unciv.ui.screens.basescreen.RecreateOnResize
|
|
||||||
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
|
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
|
||||||
import com.unciv.ui.components.extensions.center
|
import com.unciv.ui.components.extensions.center
|
||||||
import com.unciv.ui.components.extensions.keyShortcuts
|
import com.unciv.ui.components.extensions.keyShortcuts
|
||||||
@ -33,21 +31,24 @@ 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.tilegroups.TileGroupMap
|
import com.unciv.ui.components.tilegroups.TileGroupMap
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.screens.mapeditorscreen.MapEditorScreen
|
|
||||||
import com.unciv.ui.screens.multiplayerscreens.MultiplayerScreen
|
|
||||||
import com.unciv.ui.popups.Popup
|
import com.unciv.ui.popups.Popup
|
||||||
import com.unciv.ui.popups.ToastPopup
|
import com.unciv.ui.popups.ToastPopup
|
||||||
import com.unciv.ui.popups.closeAllPopups
|
import com.unciv.ui.popups.closeAllPopups
|
||||||
import com.unciv.ui.popups.hasOpenPopups
|
import com.unciv.ui.popups.hasOpenPopups
|
||||||
import com.unciv.ui.popups.popups
|
import com.unciv.ui.popups.popups
|
||||||
import com.unciv.ui.screens.savescreens.LoadGameScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
import com.unciv.ui.screens.savescreens.QuickSave
|
import com.unciv.ui.screens.basescreen.RecreateOnResize
|
||||||
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
|
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
|
||||||
|
import com.unciv.ui.screens.mainmenuscreen.EasterEggRulesets.modifyForEasterEgg
|
||||||
|
import com.unciv.ui.screens.mapeditorscreen.EditorMapHolder
|
||||||
|
import com.unciv.ui.screens.mapeditorscreen.MapEditorScreen
|
||||||
|
import com.unciv.ui.screens.multiplayerscreens.MultiplayerScreen
|
||||||
import com.unciv.ui.screens.newgamescreen.NewGameScreen
|
import com.unciv.ui.screens.newgamescreen.NewGameScreen
|
||||||
import com.unciv.ui.screens.pickerscreens.ModManagementScreen
|
import com.unciv.ui.screens.pickerscreens.ModManagementScreen
|
||||||
import com.unciv.ui.screens.worldscreen.mainmenu.WorldScreenMenuPopup
|
import com.unciv.ui.screens.savescreens.LoadGameScreen
|
||||||
import com.unciv.ui.screens.mainmenuscreen.EasterEggRulesets.modifyForEasterEgg
|
import com.unciv.ui.screens.savescreens.QuickSave
|
||||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||||
|
import com.unciv.ui.screens.worldscreen.mainmenu.WorldScreenMenuPopup
|
||||||
import com.unciv.utils.concurrency.Concurrency
|
import com.unciv.utils.concurrency.Concurrency
|
||||||
import com.unciv.utils.concurrency.launchOnGLThread
|
import com.unciv.utils.concurrency.launchOnGLThread
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -132,7 +133,7 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
|
|||||||
|
|
||||||
launchOnGLThread { // for GL context
|
launchOnGLThread { // for GL context
|
||||||
ImageGetter.setNewRuleset(mapRuleset)
|
ImageGetter.setNewRuleset(mapRuleset)
|
||||||
val mapHolder = com.unciv.ui.screens.mapeditorscreen.EditorMapHolder(
|
val mapHolder = EditorMapHolder(
|
||||||
this@MainMenuScreen,
|
this@MainMenuScreen,
|
||||||
newMap
|
newMap
|
||||||
) {}
|
) {}
|
||||||
|
@ -18,6 +18,7 @@ import com.unciv.ui.popups.ConfirmPopup
|
|||||||
import com.unciv.ui.components.tilegroups.TileGroup
|
import com.unciv.ui.components.tilegroups.TileGroup
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
import com.unciv.ui.components.KeyCharAndCode
|
import com.unciv.ui.components.KeyCharAndCode
|
||||||
|
import com.unciv.ui.components.KeyboardPanningListener
|
||||||
import com.unciv.ui.screens.basescreen.RecreateOnResize
|
import com.unciv.ui.screens.basescreen.RecreateOnResize
|
||||||
import com.unciv.ui.screens.worldscreen.ZoomButtonPair
|
import com.unciv.ui.screens.worldscreen.ZoomButtonPair
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ class MapEditorScreen(map: TileMap? = null): BaseScreen(), RecreateOnResize {
|
|||||||
var tileMatchFuzziness = MapEditorOptionsTab.TileMatchFuzziness.CompleteMatch
|
var tileMatchFuzziness = MapEditorOptionsTab.TileMatchFuzziness.CompleteMatch
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
var mapHolder: com.unciv.ui.screens.mapeditorscreen.EditorMapHolder
|
var mapHolder: EditorMapHolder
|
||||||
val tabs: MapEditorMainTabs
|
val tabs: MapEditorMainTabs
|
||||||
var tileClickHandler: ((tile: Tile)->Unit)? = null
|
var tileClickHandler: ((tile: Tile)->Unit)? = null
|
||||||
private var zoomController: ZoomButtonPair? = null
|
private var zoomController: ZoomButtonPair? = null
|
||||||
@ -117,7 +118,7 @@ class MapEditorScreen(map: TileMap? = null): BaseScreen(), RecreateOnResize {
|
|||||||
|
|
||||||
fun getToolsWidth() = stage.width * 0.4f
|
fun getToolsWidth() = stage.width * 0.4f
|
||||||
|
|
||||||
private fun newMapHolder(): com.unciv.ui.screens.mapeditorscreen.EditorMapHolder {
|
private fun newMapHolder(): EditorMapHolder {
|
||||||
ImageGetter.setNewRuleset(ruleset)
|
ImageGetter.setNewRuleset(ruleset)
|
||||||
// setNewRuleset is missing some graphics - those "EmojiIcons"&co already rendered as font characters
|
// setNewRuleset is missing some graphics - those "EmojiIcons"&co already rendered as font characters
|
||||||
// so to get the "Water" vs "Gold" icons when switching between Deciv and Vanilla to render properly,
|
// so to get the "Water" vs "Gold" icons when switching between Deciv and Vanilla to render properly,
|
||||||
@ -130,9 +131,12 @@ class MapEditorScreen(map: TileMap? = null): BaseScreen(), RecreateOnResize {
|
|||||||
tileMap.setStartingLocationsTransients()
|
tileMap.setStartingLocationsTransients()
|
||||||
UncivGame.Current.translations.translationActiveMods = ruleset.mods
|
UncivGame.Current.translations.translationActiveMods = ruleset.mods
|
||||||
|
|
||||||
val result = com.unciv.ui.screens.mapeditorscreen.EditorMapHolder(this, tileMap) {
|
val result = EditorMapHolder(this, tileMap) {
|
||||||
tileClickHandler?.invoke(it)
|
tileClickHandler?.invoke(it)
|
||||||
}
|
}
|
||||||
|
for (oldPanningListener in stage.root.listeners.filterIsInstance<KeyboardPanningListener>())
|
||||||
|
stage.removeListener(oldPanningListener) // otherwise they accumulate
|
||||||
|
stage.addListener(KeyboardPanningListener(result, allowWASD = false))
|
||||||
|
|
||||||
stage.root.addActorAt(0, result)
|
stage.root.addActorAt(0, result)
|
||||||
stage.scrollFocus = result
|
stage.scrollFocus = result
|
||||||
|
@ -4,13 +4,8 @@ import com.badlogic.gdx.Application
|
|||||||
import com.badlogic.gdx.Gdx
|
import com.badlogic.gdx.Gdx
|
||||||
import com.badlogic.gdx.Input
|
import com.badlogic.gdx.Input
|
||||||
import com.badlogic.gdx.math.Vector2
|
import com.badlogic.gdx.math.Vector2
|
||||||
import com.badlogic.gdx.scenes.scene2d.InputEvent
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.InputListener
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextField
|
|
||||||
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
|
||||||
@ -27,8 +22,9 @@ import com.unciv.logic.trade.TradeEvaluation
|
|||||||
import com.unciv.models.TutorialTrigger
|
import com.unciv.models.TutorialTrigger
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.ui.components.KeyboardBinding
|
|
||||||
import com.unciv.ui.components.KeyCharAndCode
|
import com.unciv.ui.components.KeyCharAndCode
|
||||||
|
import com.unciv.ui.components.KeyboardBinding
|
||||||
|
import com.unciv.ui.components.KeyboardPanningListener
|
||||||
import com.unciv.ui.components.extensions.centerX
|
import com.unciv.ui.components.extensions.centerX
|
||||||
import com.unciv.ui.components.extensions.darken
|
import com.unciv.ui.components.extensions.darken
|
||||||
import com.unciv.ui.components.extensions.isEnabled
|
import com.unciv.ui.components.extensions.isEnabled
|
||||||
@ -277,67 +273,7 @@ class WorldScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addKeyboardListener() {
|
private fun addKeyboardListener() {
|
||||||
stage.addListener(
|
stage.addListener(KeyboardPanningListener(mapHolder, allowWASD = true))
|
||||||
object : InputListener() {
|
|
||||||
private val pressedKeys = mutableSetOf<Int>()
|
|
||||||
private var infiniteAction: RepeatAction? = null
|
|
||||||
private val amountToMove = 6 / mapHolder.scaleX
|
|
||||||
private val ALLOWED_KEYS = setOf(Input.Keys.W, Input.Keys.S, Input.Keys.A, Input.Keys.D,
|
|
||||||
Input.Keys.UP, Input.Keys.DOWN, Input.Keys.LEFT, Input.Keys.RIGHT)
|
|
||||||
|
|
||||||
|
|
||||||
override fun keyDown(event: InputEvent, keycode: Int): Boolean {
|
|
||||||
if (event.target !is TextField) {
|
|
||||||
if (keycode !in ALLOWED_KEYS) return false
|
|
||||||
// Without the following Ctrl-S would leave WASD map scrolling stuck
|
|
||||||
// Might be obsolete with keyboard shortcut refactoring
|
|
||||||
if (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(
|
|
||||||
Input.Keys.CONTROL_RIGHT
|
|
||||||
)
|
|
||||||
) return false
|
|
||||||
|
|
||||||
pressedKeys.add(keycode)
|
|
||||||
if (infiniteAction == null) {
|
|
||||||
// create a copy of the action, because removeAction() will destroy this instance
|
|
||||||
infiniteAction = Actions.forever(
|
|
||||||
Actions.delay(
|
|
||||||
0.01f,
|
|
||||||
Actions.run { whileKeyPressedLoop() })
|
|
||||||
)
|
|
||||||
mapHolder.addAction(infiniteAction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun whileKeyPressedLoop() {
|
|
||||||
for (keycode in pressedKeys) {
|
|
||||||
when (keycode) {
|
|
||||||
Input.Keys.W, Input.Keys.UP -> mapHolder.scrollY = mapHolder.restrictY(-amountToMove)
|
|
||||||
Input.Keys.S, Input.Keys.DOWN -> mapHolder.scrollY = mapHolder.restrictY(amountToMove)
|
|
||||||
Input.Keys.A, Input.Keys.LEFT -> mapHolder.scrollX = mapHolder.restrictX(amountToMove)
|
|
||||||
Input.Keys.D, Input.Keys.RIGHT -> mapHolder.scrollX = mapHolder.restrictX(-amountToMove)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mapHolder.updateVisualScroll()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun keyUp(event: InputEvent?, keycode: Int): Boolean {
|
|
||||||
if (keycode !in ALLOWED_KEYS) return false
|
|
||||||
|
|
||||||
pressedKeys.remove(keycode)
|
|
||||||
if (infiniteAction != null && pressedKeys.isEmpty()) {
|
|
||||||
// stop the loop otherwise it keeps going even after removal
|
|
||||||
infiniteAction?.finish()
|
|
||||||
// remove and nil the action
|
|
||||||
mapHolder.removeAction(infiniteAction)
|
|
||||||
infiniteAction = null
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadLatestMultiplayerState(): Unit = coroutineScope {
|
private suspend fun loadLatestMultiplayerState(): Unit = coroutineScope {
|
||||||
|
Reference in New Issue
Block a user