Removed Char-only restriction in keyboard support for unit actions, remap a few keys, key tooltips for non-ascii keys (#4606)

This commit is contained in:
SomeTroglodyte 2021-07-22 23:02:20 +02:00 committed by GitHub
parent f2697ee8a1
commit e514df5b82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 24 deletions

View File

@ -35,9 +35,14 @@ data class KeyCharAndCode(val char: Char, val code: Int) {
// From Kotlin 1.5? on the Ctrl- line will need Char(char.code+64)
// see https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/char-int-conversions.md
override fun toString(): String {
// debug helper
// debug helper, but also used for tooltips
fun fixedKeysToString(code: Int) = when (code) {
Input.Keys.BACKSPACE -> "Backspace" // Gdx displaying this as "Delete" is Bullshit!
Input.Keys.FORWARD_DEL -> "Del" // Likewise
else -> Input.Keys.toString(code)
}
return when {
char == Char.MIN_VALUE -> Input.Keys.toString(code)
char == Char.MIN_VALUE -> fixedKeysToString(code)
this == ESC -> "ESC"
char < ' ' -> "Ctrl-" + (char.toInt()+64).toChar()
else -> "\"$char\""
@ -51,8 +56,8 @@ data class KeyCharAndCode(val char: Char, val code: Int) {
val RETURN = KeyCharAndCode(Input.Keys.ENTER)
val NUMPAD_ENTER = KeyCharAndCode(Input.Keys.NUMPAD_ENTER)
val SPACE = KeyCharAndCode(Input.Keys.SPACE)
val DEL = KeyCharAndCode(Input.Keys.DEL)
val FORWARD_DEL = KeyCharAndCode(Input.Keys.FORWARD_DEL) // this is what I see for both 'Del' keys
val BACKSPACE= KeyCharAndCode(Input.Keys.BACKSPACE)
val DEL = KeyCharAndCode(Input.Keys.FORWARD_DEL) // Gdx "DEL" is just plain wrong!
/** Guaranteed to be ignored by [KeyPressDispatcher.set] and never to be generated for an actual event, used as fallback to ensure no action is taken */
val UNKNOWN = KeyCharAndCode(Input.Keys.UNKNOWN)
@ -116,9 +121,6 @@ class KeyPressDispatcher(val name: String? = null) : HashMap<KeyCharAndCode, (()
// Likewise always match Back to ESC
if (key == KeyCharAndCode.BACK)
super.put(KeyCharAndCode.ESC, action)
// And make two codes for DEL equivalent
if (key == KeyCharAndCode.DEL)
super.put(KeyCharAndCode.FORWARD_DEL, action)
checkInstall()
}
override fun remove(key: KeyCharAndCode): (() -> Unit)? {
@ -128,8 +130,6 @@ class KeyPressDispatcher(val name: String? = null) : HashMap<KeyCharAndCode, (()
super.remove(KeyCharAndCode.NUMPAD_ENTER)
if (key == KeyCharAndCode.BACK)
super.remove(KeyCharAndCode.ESC)
if (key == KeyCharAndCode.DEL)
super.remove(KeyCharAndCode.FORWARD_DEL)
checkInstall()
return result
}

View File

@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.*
import com.badlogic.gdx.scenes.scene2d.actions.Actions
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.badlogic.gdx.utils.Align
import com.unciv.models.translations.tr
/**
* A **Replacement** for Gdx [Tooltip], placement does not follow the mouse.
@ -180,7 +181,8 @@ class UncivTooltip <T: Actor>(
addListener(UncivTooltip(this,
labelWithBackground,
forceContentSize = Vector2(size * widthHeightRatio, size),
offset = Vector2(size/4, 0f)
offset = Vector2(-size/4, size/4),
tipAlign = Align.top
))
}
@ -196,10 +198,17 @@ class UncivTooltip <T: Actor>(
addTooltip((if (char in "Ii") 'i' else char.toUpperCase()).toString(), size, always)
}
/* unused - template in case we need it - problem: how exactly to handle translation?
/**
* Add a [Label]-based Tooltip for a keyboard binding with a rounded-corner background to a [Table] or other [Group].
*
* 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 Group.addTooltip(key: KeyCharAndCode, size: Float = 26f, always: Boolean = false) {
addTooltip(key.toString(), size, always)
if (key != KeyCharAndCode.UNKNOWN)
addTooltip(key.toString().tr(), size, always)
}
*/
}
}

View File

@ -1,10 +1,10 @@
package com.unciv.ui.worldscreen.unit
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.map.MapUnit
@ -17,7 +17,9 @@ import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import com.unciv.ui.worldscreen.WorldScreen
import kotlin.concurrent.thread
private data class UnitIconAndKey(val Icon: Actor, var key: Char = Char.MIN_VALUE)
private data class UnitIconAndKey(val icon: Actor, var key: KeyCharAndCode = KeyCharAndCode.UNKNOWN) {
constructor(icon: Actor, key: Char) : this(icon, KeyCharAndCode(key))
}
class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
@ -47,12 +49,12 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
"Fortify until healed" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Shield").apply { color = Color.BLACK }, 'h')
// Move unit is not actually used anywhere
"Move unit" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement"))
"Stop movement" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement").apply { color = Color.RED }, '.')
"Stop movement" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement").apply { color = Color.RED }, KeyCharAndCode(Input.Keys.END))
"Swap units" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Swap"), 'y')
"Promote" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Star").apply { color = Color.GOLD }, 'o')
"Construct improvement" -> return UnitIconAndKey(ImageGetter.getUnitIcon(Constants.worker), 'i')
"Automate" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'm')
"Stop automation" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), 'm')
"Stop automation" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), KeyCharAndCode(Input.Keys.END))
"Found city" -> return UnitIconAndKey(ImageGetter.getUnitIcon(Constants.settler), 'c')
"Hurry Research" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Scientist"), 'g')
"Start Golden Age" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Artist"), 'g')
@ -64,10 +66,10 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
"Explore" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Scout"), 'x')
"Stop exploration" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), 'x')
"Pillage" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Pillage"), 'p')
"Disband unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/DisbandUnit"))
"Disband unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/DisbandUnit"), KeyCharAndCode.DEL)
"Gift unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Present"))
"Show more" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/ArrowRight"), 'm')
"Back" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/ArrowLeft"))
"Show more" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/ArrowRight"), KeyCharAndCode(Input.Keys.PAGE_DOWN))
"Back" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/ArrowLeft"), KeyCharAndCode(Input.Keys.PAGE_UP))
else -> {
// If the unit has been fortifying for some turns
if (unitAction.startsWith("Fortification")) return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Shield"))
@ -91,10 +93,10 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
val iconAndKey = getIconAndKeyForUnitAction(unitAction.title)
// If peripheral keyboard not detected, hotkeys will not be displayed
if (!keyboardAvailable) { iconAndKey.key = Char.MIN_VALUE }
if (!keyboardAvailable) { iconAndKey.key = KeyCharAndCode.UNKNOWN }
val actionButton = Button(CameraStageBaseScreen.skin)
actionButton.add(iconAndKey.Icon).size(20f).pad(5f)
actionButton.add(iconAndKey.icon).size(20f).pad(5f)
val fontColor = if (unitAction.isCurrentAction) Color.YELLOW else Color.WHITE
actionButton.add(unitAction.title.toLabel(fontColor)).pad(5f)
actionButton.addTooltip(iconAndKey.key)
@ -106,7 +108,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
if (unitAction.action == null) actionButton.disable()
else {
actionButton.onClick(unitAction.uncivSound, action)
if (iconAndKey.key != Char.MIN_VALUE)
if (iconAndKey.key != KeyCharAndCode.UNKNOWN)
worldScreen.keyPressDispatcher[iconAndKey.key] = {
thread(name = "Sound") { Sounds.play(unitAction.uncivSound) }
action()