mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-22 13:49:54 +07:00
UX: Dev Console easier to use without installing keyboard apps (#11706)
* UI replacements for Tab, Up, Down in DevConsolePopup * DevConsole history display via command or longpress on the Android UI
This commit is contained in:
@ -77,7 +77,7 @@ object GUI {
|
||||
}
|
||||
|
||||
private var keyboardAvailableCache: Boolean? = null
|
||||
/** Tests availability of a physical keyboard */
|
||||
/** Tests availability of a physical keyboard - cached (connecting a keyboard while the game is running won't be recognized until relaunch) */
|
||||
val keyboardAvailable: Boolean
|
||||
get() {
|
||||
// defer decision if Gdx.input not yet initialized
|
||||
|
@ -86,6 +86,10 @@ internal class ConsoleCommandRoot : ConsoleCommandNode {
|
||||
"unit" to ConsoleUnitCommands(),
|
||||
"city" to ConsoleCityCommands(),
|
||||
"tile" to ConsoleTileCommands(),
|
||||
"civ" to ConsoleCivCommands()
|
||||
"civ" to ConsoleCivCommands(),
|
||||
"history" to ConsoleAction("history") { console, _ ->
|
||||
console.showHistory()
|
||||
DevConsoleResponse.hint("") // Trick console into staying open
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -2,15 +2,24 @@ package com.unciv.ui.screens.devconsole
|
||||
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Scaling
|
||||
import com.unciv.Constants
|
||||
import com.unciv.GUI
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.ui.components.UncivTextField
|
||||
import com.unciv.ui.components.extensions.surroundWithCircle
|
||||
import com.unciv.ui.components.extensions.toCheckBox
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.input.KeyCharAndCode
|
||||
import com.unciv.ui.components.input.keyShortcuts
|
||||
import com.unciv.ui.components.input.onClick
|
||||
import com.unciv.ui.components.input.onRightClick
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.popups.Popup
|
||||
import com.unciv.ui.screens.devconsole.CliInput.Companion.splitToCliInput
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
@ -27,6 +36,7 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
|
||||
private val textField = UncivTextField.create("", "") // always has focus, so a hint won't show
|
||||
private val responseLabel = "".toLabel(Color.RED).apply { wrap = true }
|
||||
private val inputWrapper = Table()
|
||||
|
||||
private val commandRoot = ConsoleCommandRoot()
|
||||
internal val gameInfo = screen.gameInfo
|
||||
@ -34,12 +44,16 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
init {
|
||||
// Use untranslated text here! The entire console, including messages, should stay English.
|
||||
// But "Developer Console" *has* a translation from KeyboardBinding.DeveloperConsole.
|
||||
// The extensions still help, even with a "don't translate" kludge ("Keep open" has no template but might in the future).
|
||||
add("Developer Console {}".toLabel(fontSize = Constants.headingFontSize)).growX() // translation template is automatic via the keybinding
|
||||
// The extensions still help, even with a "don't translate" kludge
|
||||
add("Developer Console {}".toLabel(fontSize = Constants.headingFontSize)).growX()
|
||||
add("Keep open {}".toCheckBox(keepOpen) { keepOpen = it }).right().row()
|
||||
|
||||
add(textField).width(stageToShowOn.width / 2).colspan(2).row()
|
||||
textField.keyShortcuts.add(Input.Keys.ENTER, ::onEnter)
|
||||
inputWrapper.defaults().space(5f)
|
||||
if (!GUI.keyboardAvailable) inputWrapper.add(getAutocompleteButton())
|
||||
inputWrapper.add(textField).growX()
|
||||
if (!GUI.keyboardAvailable) inputWrapper.add(getHistoryButtons())
|
||||
|
||||
add(inputWrapper).minWidth(stageToShowOn.width / 2).growX().colspan(2).row()
|
||||
|
||||
// Without this, console popup will always contain the key used to open it - won't work perfectly if it's configured to a "dead key"
|
||||
textField.addAction(Actions.delay(0.05f, Actions.run { textField.text = "" }))
|
||||
@ -49,13 +63,8 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
keyShortcuts.add(KeyCharAndCode.BACK) { close() }
|
||||
clickBehindToClose = true
|
||||
|
||||
textField.keyShortcuts.add(KeyCharAndCode.TAB) {
|
||||
getAutocomplete()?.also {
|
||||
fun String.removeFromEnd(n: Int) = substring(0, (length - n).coerceAtLeast(0))
|
||||
textField.text = textField.text.removeFromEnd(it.first) + it.second
|
||||
textField.cursorPosition = Int.MAX_VALUE // because the setText implementation actively resets it after the paste it uses (auto capped at length)
|
||||
}
|
||||
}
|
||||
textField.keyShortcuts.add(Input.Keys.ENTER, ::onEnter)
|
||||
textField.keyShortcuts.add(KeyCharAndCode.TAB, ::onAutocomplete)
|
||||
|
||||
keyShortcuts.add(Input.Keys.UP) { navigateHistory(-1) }
|
||||
keyShortcuts.add(Input.Keys.DOWN) { navigateHistory(1) }
|
||||
@ -65,6 +74,35 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
screen.stage.keyboardFocus = textField
|
||||
}
|
||||
|
||||
private fun getAutocompleteButton() = ImageGetter.getArrowImage()
|
||||
.surroundWithCircle(50f, color = Color.DARK_GRAY).onClick(::onAutocomplete)
|
||||
|
||||
private fun getHistoryButtons(): Actor {
|
||||
val group = Table()
|
||||
fun getArrow(rotation: Float, delta: Int) = ImageGetter.getImage("OtherIcons/ForwardArrow").apply {
|
||||
name = if (delta > 0) "down" else "up"
|
||||
setScaling(Scaling.fillX)
|
||||
setSize(36f, 16f)
|
||||
scaleX = 0.75f // no idea why this works
|
||||
setOrigin(18f, 8f)
|
||||
this.rotation = rotation
|
||||
onClick {
|
||||
navigateHistory(delta)
|
||||
}
|
||||
}
|
||||
group.add(getArrow(90f, -1)).size(36f, 16f).padBottom(4f).row()
|
||||
group.add(getArrow(-90f, 1)).size(36f, 16f)
|
||||
group.setSize(40f, 40f)
|
||||
return group.surroundWithCircle(50f, false, Color.DARK_GRAY).onRightClick(action = ::showHistory)
|
||||
}
|
||||
|
||||
private fun onAutocomplete() {
|
||||
val (toRemove, toAdd) = getAutocomplete() ?: return
|
||||
fun String.removeFromEnd(n: Int) = substring(0, (length - n).coerceAtLeast(0))
|
||||
textField.text = textField.text.removeFromEnd(toRemove) + toAdd
|
||||
textField.cursorPosition = Int.MAX_VALUE // because the setText implementation actively resets it after the paste it uses (auto capped at length)
|
||||
}
|
||||
|
||||
private fun navigateHistory(delta: Int) {
|
||||
if (history.isEmpty()) return
|
||||
currentHistoryEntry = (currentHistoryEntry + delta).coerceIn(history.indices)
|
||||
@ -72,6 +110,29 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
textField.cursorPosition = textField.text.length
|
||||
}
|
||||
|
||||
internal fun showHistory() {
|
||||
if (history.isEmpty()) return
|
||||
val popup = object : Popup(stageToShowOn) {
|
||||
init {
|
||||
for ((index, entry) in history.withIndex()) {
|
||||
val label = Label(entry, skin)
|
||||
label.onClick {
|
||||
currentHistoryEntry = index
|
||||
navigateHistory(0)
|
||||
close()
|
||||
}
|
||||
add(label).row()
|
||||
}
|
||||
clickBehindToClose = true
|
||||
if (screen.game.settings.forbidPopupClickBehindToClose) addCloseButton()
|
||||
showListeners.add {
|
||||
getScrollPane()?.run { scrollY = maxY }
|
||||
}
|
||||
}
|
||||
}
|
||||
popup.open(true)
|
||||
}
|
||||
|
||||
private fun onEnter() {
|
||||
val handleCommandResponse = handleCommand()
|
||||
if (handleCommandResponse.isOK) {
|
||||
|
Reference in New Issue
Block a user