diff --git a/android/assets/jsons/translations/German.properties b/android/assets/jsons/translations/German.properties index bf64bb218d..4c781f984e 100644 --- a/android/assets/jsons/translations/German.properties +++ b/android/assets/jsons/translations/German.properties @@ -686,7 +686,8 @@ Landscape (fixed) = Quer (fixiert) Portrait (fixed) = Hochkant (fixiert) Auto (sensor adjusted) = Auto (durch Sensor justiert) -Map mouse auto-scroll = Automatisches Maus-Scrollen zuweisen +Map mouse auto-scroll = Karte durch Maus am Fensterrand verschieben +Map panning speed = Geschwindigkeit der Kartenverschiebung Order trade offers by amount = Handelsangebote nach Menge sortieren Experimental Demographics scoreboard = Experimentelle Demographien-Übersicht Unit icon opacity = Deckkraft des Einheitensymbols diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 17fdf8e153..092dc66da1 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -686,7 +686,9 @@ Landscape (fixed) = Portrait (fixed) = Auto (sensor adjusted) = +### Enable panning the map when you move the mouse to the edge of the window Map mouse auto-scroll = +Map panning speed = Order trade offers by amount = Experimental Demographics scoreboard = Unit icon opacity = diff --git a/core/src/com/unciv/models/metadata/GameSettings.kt b/core/src/com/unciv/models/metadata/GameSettings.kt index b1d43bf808..dacf165906 100644 --- a/core/src/com/unciv/models/metadata/GameSettings.kt +++ b/core/src/com/unciv/models/metadata/GameSettings.kt @@ -31,7 +31,11 @@ enum class ScreenSize(val virtualWidth:Float, val virtualHeight:Float){ class GameSettings { + /** Allows panning the map by moving the pointer to the screen edges */ var mapAutoScroll: Boolean = false + /** How fast the map pans using keyboard or with [mapAutoScroll] and mouse */ + var mapPanningSpeed: Float = 6f + var showWorkedTiles: Boolean = false var showResourcesAndImprovements: Boolean = true var showTileYields: Boolean = false diff --git a/core/src/com/unciv/ui/components/KeyboardPanningListener.kt b/core/src/com/unciv/ui/components/KeyboardPanningListener.kt index fdea302947..88cbe288e7 100644 --- a/core/src/com/unciv/ui/components/KeyboardPanningListener.kt +++ b/core/src/com/unciv/ui/components/KeyboardPanningListener.kt @@ -10,11 +10,15 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextField class KeyboardPanningListener( private val mapHolder: ZoomableScrollPane, - allowWASD: Boolean, + allowWASD: Boolean ) : InputListener() { + companion object { + /** The delay between panning steps */ + const val deltaTime = 0.01f + } + private val pressedKeys = mutableSetOf() 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) @@ -45,7 +49,7 @@ class KeyboardPanningListener( // create a copy of the action, because removeAction() will destroy this instance infiniteAction = Actions.forever( Actions.delay( - 0.01f, + deltaTime, Actions.run { whileKeyPressedLoop() }) ) mapHolder.addAction(infiniteAction) @@ -61,14 +65,16 @@ class KeyboardPanningListener( } private fun whileKeyPressedLoop() { + var deltaX = 0f + var deltaY = 0f 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) + 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 } } - mapHolder.updateVisualScroll() + mapHolder.doKeyOrMousePanning(deltaX, deltaY) } } diff --git a/core/src/com/unciv/ui/components/ZoomableScrollPane.kt b/core/src/com/unciv/ui/components/ZoomableScrollPane.kt index f2f93b0eaa..d19e7bca75 100644 --- a/core/src/com/unciv/ui/components/ZoomableScrollPane.kt +++ b/core/src/com/unciv/ui/components/ZoomableScrollPane.kt @@ -15,6 +15,7 @@ import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener import com.badlogic.gdx.scenes.scene2d.utils.Cullable +import com.unciv.models.metadata.GameSettings import com.unciv.UncivGame import java.lang.Float.max import java.lang.Float.min @@ -39,7 +40,10 @@ open class ZoomableScrollPane( private val horizontalPadding get() = width / 2 private val verticalPadding get() = height / 2 + /** Will be set from [GameSettings.mapAutoScroll] */ var isAutoScrollEnabled = false + /** Will be set from [GameSettings.mapPanningSpeed] */ + var mapPanningSpeed: Float = 6f init { this.addListener(zoomListener) @@ -274,21 +278,26 @@ open class ZoomableScrollPane( if (isAutoScrollEnabled && !Gdx.input.isTouched) { val posX = Gdx.input.x - val posY = Gdx.input.y + val posY = Gdx.input.y // Viewport coord: goes down, unlike world coordinates - if (posX <= 2f) { - scrollX -= 3f - } else if (posX >= stage.viewport.screenWidth - 2f) { - scrollX += 3f + val deltaX = when { + posX <= 2 -> 1 + posX >= stage.viewport.screenWidth - 2 -> -1 + else -> 0 + } + val deltaY = when { + posY <= 6 -> -1 + posY >= stage.viewport.screenHeight - 6 -> 1 + else -> 0 } - if (posY <= 6f) { - scrollY -= 3f - } else if (posY >= stage.viewport.screenHeight - 6f) { - scrollY += 3f + if (deltaX != 0 || deltaY != 0) { + // if Gdx deltaTime is > KeyboardPanningListener.deltaTime, then mouse auto scroll would be slower + // (Gdx deltaTime is measured, not a constant, depends on framerate) + // The extra factor is empirical to make mouse and WASD keyboard feel the same + val relativeSpeed = Gdx.graphics.deltaTime / KeyboardPanningListener.deltaTime * 0.3f + doKeyOrMousePanning(deltaX * relativeSpeed, deltaY * relativeSpeed) } - - updateVisualScroll() } super.draw(batch, parentAlpha) } @@ -319,7 +328,21 @@ open class ZoomableScrollPane( } open fun restrictX(deltaX: Float): Float = scrollX - deltaX - open fun restrictY(deltaY:Float): Float = scrollY + deltaY + open fun restrictY(deltaY: Float): Float = scrollY + deltaY + + /** + * Perform keyboard WASD or mouse-at-edge panning. + * Called from [KeyboardPanningListener] and [draw] if [isAutoScrollEnabled] is on. + * + * Positive [deltaX] = Left, Positive [deltaY] = DOWN + */ + fun doKeyOrMousePanning(deltaX: Float, deltaY: Float) { + if (deltaX == 0f && deltaY == 0f) return + val amountToMove = mapPanningSpeed / scaleX + scrollX = restrictX(deltaX * amountToMove) + scrollY = restrictY(deltaY * amountToMove) + updateVisualScroll() + } override fun getFlickScrollListener(): ActorGestureListener { return FlickScrollListener() diff --git a/core/src/com/unciv/ui/popups/options/DisplayTab.kt b/core/src/com/unciv/ui/popups/options/DisplayTab.kt index c1fbffb4cb..d93b23036e 100644 --- a/core/src/com/unciv/ui/popups/options/DisplayTab.kt +++ b/core/src/com/unciv/ui/popups/options/DisplayTab.kt @@ -45,6 +45,7 @@ fun displayTab( if (GUI.isWorldLoaded()) GUI.getMap().isAutoScrollEnabled = settings.mapAutoScroll } + addScrollSpeedSlider(this, settings, optionsPopup.selectBoxMinWidth) } optionsPopup.addCheckbox(this, "Show unit movement arrows", settings.showUnitMovements, true) { settings.showUnitMovements = it } @@ -121,6 +122,20 @@ private fun addMinimapSizeSlider(table: Table, settings: GameSettings, selectBox table.add(minimapSlider).minWidth(selectBoxMinWidth).pad(10f).row() } +private fun addScrollSpeedSlider(table: Table, settings: GameSettings, selectBoxMinWidth: Float) { + table.add("Map panning speed".toLabel()).left().fillX() + + val scrollSpeedSlider = UncivSlider( + 0.2f, 25f, 0.2f, initial = settings.mapPanningSpeed + ) { + settings.mapPanningSpeed = it + settings.save() + if (GUI.isWorldLoaded()) + GUI.getMap().mapPanningSpeed = settings.mapPanningSpeed + } + table.add(scrollSpeedSlider).minWidth(selectBoxMinWidth).pad(10f).row() +} + private fun addUnitIconAlphaSlider(table: Table, settings: GameSettings, selectBoxMinWidth: Float) { table.add("Unit icon opacity".toLabel()).left().fillX() diff --git a/core/src/com/unciv/ui/screens/mapeditorscreen/EditorMapHolder.kt b/core/src/com/unciv/ui/screens/mapeditorscreen/EditorMapHolder.kt index 7dc9f5fc17..2df2ca7007 100644 --- a/core/src/com/unciv/ui/screens/mapeditorscreen/EditorMapHolder.kt +++ b/core/src/com/unciv/ui/screens/mapeditorscreen/EditorMapHolder.kt @@ -28,7 +28,7 @@ class EditorMapHolder( internal val tileMap: TileMap, private val onTileClick: (Tile) -> Unit ): ZoomableScrollPane(20f, 20f) { - val editorScreen = parentScreen as? com.unciv.ui.screens.mapeditorscreen.MapEditorScreen + val editorScreen = parentScreen as? MapEditorScreen val tileGroups = HashMap() private lateinit var tileGroupMap: TileGroupMap @@ -47,7 +47,7 @@ class EditorMapHolder( reloadMaxZoom() } - internal fun addTiles(stage: Stage) { + private fun addTiles(stage: Stage) { val tileSetStrings = TileSetStrings() val daTileGroups = tileMap.values.map { TileGroup(it, tileSetStrings) } @@ -108,7 +108,7 @@ class EditorMapHolder( } /** - * Copy-pasted from [com.unciv.ui.worldscreen.WorldMapHolder.setCenterPosition] + * Copy-pasted from [com.unciv.ui.screens.worldscreen.WorldMapHolder.setCenterPosition] * TODO remove code duplication */ fun setCenterPosition(vector: Vector2, blink: Boolean = false) { diff --git a/core/src/com/unciv/ui/screens/mapeditorscreen/MapEditorScreen.kt b/core/src/com/unciv/ui/screens/mapeditorscreen/MapEditorScreen.kt index 940133914d..914c86d458 100644 --- a/core/src/com/unciv/ui/screens/mapeditorscreen/MapEditorScreen.kt +++ b/core/src/com/unciv/ui/screens/mapeditorscreen/MapEditorScreen.kt @@ -1,5 +1,6 @@ package com.unciv.ui.screens.mapeditorscreen +import com.badlogic.gdx.Application import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color import com.unciv.UncivGame @@ -136,7 +137,10 @@ class MapEditorScreen(map: TileMap? = null): BaseScreen(), RecreateOnResize { } for (oldPanningListener in stage.root.listeners.filterIsInstance()) stage.removeListener(oldPanningListener) // otherwise they accumulate + result.mapPanningSpeed = UncivGame.Current.settings.mapPanningSpeed stage.addListener(KeyboardPanningListener(result, allowWASD = false)) + if (Gdx.app.type == Application.ApplicationType.Desktop) + result.isAutoScrollEnabled = UncivGame.Current.settings.mapAutoScroll stage.root.addActorAt(0, result) stage.scrollFocus = result diff --git a/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt index 11dfe7ead2..c941042b1b 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt @@ -173,6 +173,7 @@ class WorldScreen( } mapHolder.isAutoScrollEnabled = Gdx.app.type == Application.ApplicationType.Desktop && game.settings.mapAutoScroll + mapHolder.mapPanningSpeed = game.settings.mapPanningSpeed // Don't select unit and change selectedCiv when centering as spectator if (viewingCiv.isSpectator())