Key bindings - unit actions, world screen and grouped UI (#8986)

* Keyboard Bindings: Simpler Widget

* Keyboard Bindings: Reset binding

* Keyboard Bindings: Simpler Widget - revert opening feature

* Keyboard Bindings Step 6: Unit Actions

* Keyboard Bindings Step 7: Grouped UI

* Keyboard Bindings Step 7: Grouped UI-TFW

* Keyboard Bindings Step 6: Unit Actions - patch

* Keyboard Bindings Step 9: World Screen

* Keyboard Bindings - German

* Keyboard Bindings: Fix merge errors

* Keyboard Bindings: Tiny forgotten things

* Merge fixes
This commit is contained in:
SomeTroglodyte 2023-06-12 09:13:57 +02:00 committed by GitHub
parent e109848e28
commit 404a148cfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 246 additions and 110 deletions

View File

@ -816,6 +816,7 @@ Enable display cutout (requires restart) = Aktiviere Bildschirmausschnitt (Neust
## Keys tab
Keys = Tastenzuordnung
Please see the Tutorial. = Bitte schau dir das Tutorial an.
Hit the desired key now = Drücke die gewünschte Taste
## Locate mod errors tab
Locate mod errors = Mod-Probleme
@ -2411,9 +2412,34 @@ You returned captured units to us = Ihr habt uns gefangene Einheiten zurückgege
#################### Lines from key bindings #######################
Unit Actions = Einheiten-Aktionen
Popups = Dialoge
Next Turn = Nächste Runde
Next Turn Alternate = Nächste Runde Alternativ
Empire Overview = Reichsübersicht
Empire Overview Trades = Handels-Übersicht
Empire Overview Units = Einheiten-Übersicht
Empire Overview Politics = Politiken-Übersicht
Social Policies = Sozialpolitiken
Technology Tree = Technologie-Baum
Empire Overview Notifications = Benachrichtigungen Historie
Empire Overview Stats = Statistiken-Übersicht
Empire Overview Resources = Ressourcen-Übersicht
Quick Save = Schnellspeichern
Quick Load = Schnellladen
View Capital City = Hauptstadt zeigen
Save Game = Spiel speichern
Load Game = Spiel laden
Quit Game = Spiel beenden
Toggle UI = Oberfläche verstecken an/aus
Toggle Resource Display = Ressourcen-Anzeige an/aus
Toggle Yield Display = Ertrags-Anzeige an/aus
Toggle Worked Tiles Display = Bewirtschaftet-Anzeige an/aus
Toggle Movement Display = Bewegungspfeile an/aus
Zoom In = Reinzoomen
Zoom Out = Rauszoomen
Transform = Transformieren
Repair = Reparieren
Confirm Dialog = Dialog bestätigen
Cancel Dialog = Dialog ablehnen
@ -6399,11 +6425,11 @@ This is a work in progress. = Dies ist noch in Arbeit.
For technical reasons, only direct keys or Ctrl-Letter combinations can be used. = Aus technischen Gründen können nur Direkttasten oder Strg-Buchstaben-Kombinationen verwendet werden.
The Escape key is intentionally excluded from being reassigned. = Die Escape-Taste wird absichtlich von einer Neuzuweisung ausgeschlossen.
Currently, there are no checks to prevent conflicting assignments. = Derzeit gibt es keine Prüfungen, um widersprüchliche Zuweisungen zu verhindern.
Using the Keys page = Verwendung der Tasten-Seite
Using the Keys page = Verwendung der Seite für Tastatur-Zuweisungen
Each binding has a button with an image looking like this: = Jede Verknüpfung hat eine Schaltfläche mit einem Bild, das wie folgt aussieht:
While hovering the mouse over the key button, you can press a desired key directly to assign it. = Wenn du mit der Maus über die Taste fährst, kannst du die gewünschte Taste direkt drücken, um sie zu belegen.
Double-click the image to reset the binding to default. = Mit einem Doppelklick auf das Bild setzt du die Verknüpfung auf die Standardeinstellungen zurück.
Bindings mapped to their default keys are displayed in gray, those reassigned by you in white. = Verknüpfungen, die den Standardtasten zugeordnet sind, werden grau angezeigt, die von dir neu zugewiesenen in weiß.
While hovering the mouse over the key button, you can press a desired key directly to assign it. = Wenn du mit der Maus über die Schaltfläche fährst, kannst du die gewünschte Taste direkt drücken, um sie zuzuweisen.
Double-click the image to reset the binding to default. = Mit einem Doppelklick auf das Bild setzt du die Zuweisung auf die Standardeinstellungen zurück.
Bindings mapped to their default keys are displayed in gray, those reassigned by you in white. = Zuweisungen, die den Standardtasten zugeordnet sind, werden grau angezeigt, die von dir neu zugewiesenen in weiß.
For discussion about missing entries, see the linked github issue. = Für Diskussionen über fehlende Einträge, siehe das verlinkte GitHub-Issue.
Welcome to the Civilopedia! = Willkommen in der Zivilopädie
@ -6419,4 +6445,3 @@ However, it will reflect the mods you are playing! The combination of base rules
If you opened the Civilopedia from the main menu, the "Ruleset" will be that of the last game you started. = Wenn du die Zivilopädie vom Hauptmenü öffnest, wird das "Regelwerk" des letzten gestarteten Spiels ausschlaggebend sein.
Letters can select categories, and when there are multiple categories matching the same letter, you can press that repeatedly to cycle between these. = Buchstaben können Kategorien auswählen. Wenn mehrere Kategorien mit dem gleichen Buchstaben anfangen, kannst du den Buchstaben wiederholt drücken, um durch diese durchzuwechseln. (Aktuell werden die Buchstaben aus der englischen Version verwendet.)
The arrow keys allow navigation as well - left/right for categories, up/down for entries. = Die Pfeiltasten können auch zur Navigation verwendet werden. - Links/rechts für Kategorien, hoch/runter für Einträge.

View File

@ -1,12 +1,11 @@
package com.unciv.models
import com.badlogic.gdx.Input
import com.badlogic.gdx.scenes.scene2d.Actor
import com.unciv.Constants
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.ui.components.Fonts
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.images.ImageGetter
@ -81,7 +80,8 @@ class UpgradeUnitAction(
*
* @param value _default_ label to display, can be overridden in UnitAction instantiation
* @param imageGetter optional lambda to get an Icon - `null` if icon is dependent on outside factors and needs special handling
* @param key keyboard binding - can be a [KeyCharAndCode], a [Char], or omitted.
* @param binding keyboard binding - omitting it will look up the KeyboardBinding of the same name (recommended)
* @param isSkippingToNextUnit if "Auto Unit Cycle" setting and this bit are on, this action will skip to the next unit
* @param uncivSound _default_ sound, can be overridden in UnitAction instantiation
*/
@ -91,95 +91,94 @@ class UpgradeUnitAction(
enum class UnitActionType(
val value: String,
val imageGetter: (()-> Actor)?,
val key: KeyCharAndCode,
binding: KeyboardBinding? = null,
val isSkippingToNextUnit: Boolean = true,
val uncivSound: UncivSound = UncivSound.Click
) {
SwapUnits("Swap units",
{ ImageGetter.getUnitActionPortrait("Swap") }, 'y', false),
{ ImageGetter.getUnitActionPortrait("Swap") }, false),
Automate("Automate",
{ ImageGetter.getUnitActionPortrait("Automate") }, 'm'),
{ ImageGetter.getUnitActionPortrait("Automate") }),
StopAutomation("Stop automation",
{ ImageGetter.getUnitActionPortrait("Stop") }, 'm', false),
{ ImageGetter.getUnitActionPortrait("Stop") }, false),
StopMovement("Stop movement",
{ ImageGetter.getUnitActionPortrait("StopMove") }, '.', false),
{ ImageGetter.getUnitActionPortrait("StopMove") }, false),
Sleep("Sleep",
{ ImageGetter.getUnitActionPortrait("Sleep") }, 'f'),
{ ImageGetter.getUnitActionPortrait("Sleep") }),
SleepUntilHealed("Sleep until healed",
{ ImageGetter.getUnitActionPortrait("Sleep") }, 'h'),
{ ImageGetter.getUnitActionPortrait("Sleep") }),
Fortify("Fortify",
{ ImageGetter.getUnitActionPortrait("Fortify") }, 'f', UncivSound.Fortify),
{ ImageGetter.getUnitActionPortrait("Fortify") }, UncivSound.Fortify),
FortifyUntilHealed("Fortify until healed",
{ ImageGetter.getUnitActionPortrait("FortifyUntilHealed") }, 'h', UncivSound.Fortify),
{ ImageGetter.getUnitActionPortrait("FortifyUntilHealed") }, UncivSound.Fortify),
Explore("Explore",
{ ImageGetter.getUnitActionPortrait("Explore") }, 'x'),
{ ImageGetter.getUnitActionPortrait("Explore") }),
StopExploration("Stop exploration",
{ ImageGetter.getUnitActionPortrait("Stop") }, 'x', false),
{ ImageGetter.getUnitActionPortrait("Stop") }, false),
Promote("Promote",
{ ImageGetter.getUnitActionPortrait("Promote") }, 'o', false, UncivSound.Promote),
{ ImageGetter.getUnitActionPortrait("Promote") }, false, UncivSound.Promote),
Upgrade("Upgrade",
{ ImageGetter.getUnitActionPortrait("Upgrade") }, 'u', UncivSound.Upgrade),
{ ImageGetter.getUnitActionPortrait("Upgrade") }, UncivSound.Upgrade),
Transform("Transform",
{ ImageGetter.getUnitActionPortrait("Transform") }, 'k', UncivSound.Upgrade),
{ ImageGetter.getUnitActionPortrait("Transform") }, UncivSound.Upgrade),
Pillage("Pillage",
{ ImageGetter.getUnitActionPortrait("Pillage") }, 'p', false),
{ ImageGetter.getUnitActionPortrait("Pillage") }, false),
Paradrop("Paradrop",
{ ImageGetter.getUnitActionPortrait("Paradrop") }, 'p', false),
{ ImageGetter.getUnitActionPortrait("Paradrop") }, false),
AirSweep("Air Sweep",
{ ImageGetter.getUnitActionPortrait("AirSweep") }, 'a', false),
{ ImageGetter.getUnitActionPortrait("AirSweep") }, false),
SetUp("Set up",
{ ImageGetter.getUnitActionPortrait("SetUp") }, 't', false, UncivSound.Setup),
{ ImageGetter.getUnitActionPortrait("SetUp") }, false, UncivSound.Setup),
FoundCity("Found city",
{ ImageGetter.getUnitActionPortrait("FoundCity") }, 'c', UncivSound.Silent),
{ ImageGetter.getUnitActionPortrait("FoundCity") }, UncivSound.Silent),
ConstructImprovement("Construct improvement",
{ ImageGetter.getUnitActionPortrait("ConstructImprovement") }, 'i', false),
{ ImageGetter.getUnitActionPortrait("ConstructImprovement") }, false),
Repair(Constants.repair,
{ ImageGetter.getUnitActionPortrait("Repair") }, 'r', UncivSound.Construction),
{ ImageGetter.getUnitActionPortrait("Repair") }, UncivSound.Construction),
Create("Create",
null, 'i', false, UncivSound.Chimes),
null, false, UncivSound.Chimes),
HurryResearch("{Hurry Research} (${Fonts.death})",
{ ImageGetter.getUnitActionPortrait("HurryResearch") }, 'g', UncivSound.Chimes),
{ ImageGetter.getUnitActionPortrait("HurryResearch") }, UncivSound.Chimes),
StartGoldenAge("Start Golden Age",
{ ImageGetter.getUnitActionPortrait("StartGoldenAge") }, 'g', UncivSound.Chimes),
{ ImageGetter.getUnitActionPortrait("StartGoldenAge") }, UncivSound.Chimes),
HurryWonder("{Hurry Wonder} (${Fonts.death})",
{ ImageGetter.getUnitActionPortrait("HurryConstruction") }, 'g', UncivSound.Chimes),
{ ImageGetter.getUnitActionPortrait("HurryConstruction") }, UncivSound.Chimes),
HurryBuilding("{Hurry Construction} (${Fonts.death})",
{ ImageGetter.getUnitActionPortrait("HurryConstruction") }, 'g', UncivSound.Chimes),
{ ImageGetter.getUnitActionPortrait("HurryConstruction") }, UncivSound.Chimes),
ConductTradeMission("{Conduct Trade Mission} (${Fonts.death})",
{ ImageGetter.getUnitActionPortrait("ConductTradeMission") }, 'g', UncivSound.Chimes),
{ ImageGetter.getUnitActionPortrait("ConductTradeMission") }, UncivSound.Chimes),
FoundReligion("Found a Religion",
{ ImageGetter.getUnitActionPortrait("FoundReligion") }, 'g', UncivSound.Choir),
{ ImageGetter.getUnitActionPortrait("FoundReligion") }, UncivSound.Choir),
TriggerUnique("Trigger unique",
{ ImageGetter.getUnitActionPortrait("Star") }, 'g', false, UncivSound.Chimes),
{ ImageGetter.getUnitActionPortrait("Star") }, false, UncivSound.Chimes),
SpreadReligion("Spread Religion",
null, 'g', UncivSound.Choir),
null, UncivSound.Choir),
RemoveHeresy("Remove Heresy",
{ ImageGetter.getUnitActionPortrait("RemoveHeresy") }, 'h', UncivSound.Fire),
{ ImageGetter.getUnitActionPortrait("RemoveHeresy") }, UncivSound.Fire),
EnhanceReligion("Enhance a Religion",
{ ImageGetter.getUnitActionPortrait("EnhanceReligion") }, 'g', UncivSound.Choir),
{ ImageGetter.getUnitActionPortrait("EnhanceReligion") }, UncivSound.Choir),
DisbandUnit("Disband unit",
{ ImageGetter.getUnitActionPortrait("DisbandUnit") }, KeyCharAndCode.DEL, false),
{ ImageGetter.getUnitActionPortrait("DisbandUnit") }, false),
GiftUnit("Gift unit",
{ ImageGetter.getUnitActionPortrait("Present") }, UncivSound.Silent),
Wait("Wait",
{ ImageGetter.getUnitActionPortrait("Wait") }, 'z', UncivSound.Silent),
{ ImageGetter.getUnitActionPortrait("Wait") }, UncivSound.Silent),
ShowAdditionalActions("Show more",
{ ImageGetter.getUnitActionPortrait("ShowMore") }, KeyCharAndCode(Input.Keys.PAGE_DOWN), false),
{ ImageGetter.getUnitActionPortrait("ShowMore") }, false),
HideAdditionalActions("Back",
{ ImageGetter.getUnitActionPortrait("HideMore") }, KeyCharAndCode(Input.Keys.PAGE_UP), false),
{ ImageGetter.getUnitActionPortrait("HideMore") }, false),
AddInCapital( "Add in capital",
{ ImageGetter.getUnitActionPortrait("AddInCapital")}, 'g', UncivSound.Chimes),
{ ImageGetter.getUnitActionPortrait("AddInCapital")}, UncivSound.Chimes),
;
// Allow shorter initializations
constructor(value: String, imageGetter: (() -> Actor)?, key: Char, uncivSound: UncivSound = UncivSound.Click)
: this(value, imageGetter, KeyCharAndCode(key), true, uncivSound)
constructor(value: String, imageGetter: (() -> Actor)?, uncivSound: UncivSound = UncivSound.Click)
: this(value, imageGetter, KeyCharAndCode.UNKNOWN, true,uncivSound)
constructor(value: String, imageGetter: (() -> Actor)?, key: Char, isSkippingToNextUnit: Boolean = true, uncivSound: UncivSound = UncivSound.Click)
: this(value, imageGetter, KeyCharAndCode(key), isSkippingToNextUnit, uncivSound)
: this(value, imageGetter, null, true, uncivSound)
constructor(value: String, imageGetter: (() -> Actor)?, isSkippingToNextUnit: Boolean = true, uncivSound: UncivSound = UncivSound.Click)
: this(value, imageGetter, KeyCharAndCode.UNKNOWN, isSkippingToNextUnit, uncivSound)
: this(value, imageGetter, null, isSkippingToNextUnit, uncivSound)
val binding: KeyboardBinding =
binding ?:
KeyboardBinding.values().firstOrNull { it.name == name } ?:
KeyboardBinding.None
}

View File

@ -135,6 +135,9 @@ object TranslationFileWriter {
linesToTranslate += "${diplomaticModifier.text} = "
linesToTranslate += "\n\n#################### Lines from key bindings #######################\n"
for (category in KeyboardBinding.Category.values()) {
linesToTranslate += "${category.label} = "
}
for (binding in KeyboardBinding.values()) {
linesToTranslate += "${binding.label} = "
}

View File

@ -330,23 +330,33 @@ fun Group.addToCenter(actor: Actor) {
* | FORWARD_DEL | 112 | Del | Forward Delete | -1 | 112 |
*
* This acts as proxy, you replace [Input.Keys] by [GdxKeyCodeFixes] and get sensible [DEL], [toString] and [valueOf].
* Differences in behaviour: toString will return an empty string for un-mapped keycodes and UNKNOWN
* instead of `null` or "Unknown" respectively,
* valueOf will return UNKNOWN for un-mapped names or "" instead of -1.
*/
@Suppress("GDX_KEYS_BUG", "MemberVisibilityCanBePrivate")
object GdxKeyCodeFixes {
const val DEL = Input.Keys.FORWARD_DEL
const val BACKSPACE = Input.Keys.BACKSPACE
const val UNKNOWN = Input.Keys.UNKNOWN
fun toString(keyCode: Int): String = when(keyCode) {
UNKNOWN -> ""
DEL -> "Del"
BACKSPACE -> "Backspace"
else -> Input.Keys.toString(keyCode)
?: ""
}
fun valueOf(name: String): Int = when (name) {
"" -> UNKNOWN
"Del" -> DEL
"Backspace" -> BACKSPACE
else -> Input.Keys.valueOf(name)
else -> {
val code = Input.Keys.valueOf(name)
if (code == -1) UNKNOWN else code
}
}
}

View File

@ -1,6 +1,7 @@
package com.unciv.ui.components.input
import com.badlogic.gdx.Input
import com.unciv.Constants
private val unCamelCaseRegex = Regex("([A-Z])([A-Z])([a-z])|([a-z])([A-Z])")
@ -11,21 +12,87 @@ enum class KeyboardBinding(
label: String? = null,
key: KeyCharAndCode? = null
) {
// Used by [KeyShortcutDispatcher.KeyShortcut] to mark an old-style shortcut with a hardcoded key
/** Used by [KeyShortcutDispatcher.KeyShortcut] to mark an old-style shortcut with a hardcoded key */
None(Category.None, KeyCharAndCode.UNKNOWN),
// Worldscreen
NextTurn(Category.WorldScreen),
NextTurnAlternate(Category.WorldScreen, KeyCharAndCode.SPACE),
Civilopedia(Category.WorldScreen, Input.Keys.F1),
EmpireOverview(Category.WorldScreen),
Wait(Category.None, 'z'), // Used but excluded from UI because UnitActionType.Wait needs to be done too
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')),
QuitGame(Category.WorldScreen, KeyCharAndCode.ctrl('q')),
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),
// 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.
SwapUnits(Category.UnitActions,"Swap units", 'y'),
Automate(Category.UnitActions, 'm'),
StopAutomation(Category.UnitActions,"Stop automation", 'm'),
StopMovement(Category.UnitActions,"Stop movement", '.'),
Sleep(Category.UnitActions, 'f'),
SleepUntilHealed(Category.UnitActions,"Sleep until healed", 'h'),
Fortify(Category.UnitActions, 'f'),
FortifyUntilHealed(Category.UnitActions,"Fortify until healed", 'h'),
Explore(Category.UnitActions, 'x'),
StopExploration(Category.UnitActions,"Stop exploration", 'x'),
Promote(Category.UnitActions, 'o'),
Upgrade(Category.UnitActions, 'u'),
Transform(Category.UnitActions, 'k'),
Pillage(Category.UnitActions, 'p'),
Paradrop(Category.UnitActions, 'p'),
AirSweep(Category.UnitActions, 'a'),
SetUp(Category.UnitActions,"Set up", 't'),
FoundCity(Category.UnitActions,"Found city", 'c'),
ConstructImprovement(Category.UnitActions,"Construct improvement", 'i'),
Repair(Category.UnitActions, Constants.repair, 'r'),
Create(Category.UnitActions, 'i'),
HurryResearch(Category.UnitActions, 'g'),
StartGoldenAge(Category.UnitActions, 'g'),
HurryWonder(Category.UnitActions, 'g'),
HurryBuilding(Category.UnitActions,"Hurry Construction", 'g'),
ConductTradeMission(Category.UnitActions, 'g'),
FoundReligion(Category.UnitActions,"Found a Religion", 'g'),
TriggerUnique(Category.UnitActions,"Trigger unique", 'g'),
SpreadReligion(Category.UnitActions, 'g'),
RemoveHeresy(Category.UnitActions, 'h'),
EnhanceReligion(Category.UnitActions,"Enhance a Religion", 'g'),
DisbandUnit(Category.UnitActions,"Disband unit", KeyCharAndCode.DEL),
GiftUnit(Category.UnitActions,"Gift unit", KeyCharAndCode.UNKNOWN),
Wait(Category.UnitActions, 'z'),
ShowAdditionalActions(Category.UnitActions,"Show more", Input.Keys.PAGE_DOWN),
HideAdditionalActions(Category.UnitActions,"Back", Input.Keys.PAGE_UP),
AddInCapital(Category.UnitActions, "Add in capital", 'g'),
// Popups
Confirm(Category.Popups, "Confirm Dialog", 'y'),
Cancel(Category.Popups, "Cancel Dialog", 'n'),
;
enum class Category {
None, WorldScreen, Popups
None, WorldScreen, UnitActions, Popups;
val label = unCamelCase(name)
}
val label: String
@ -41,8 +108,8 @@ enum class KeyboardBinding(
constructor(category: Category, label: String, key: Char) : this(category, label, KeyCharAndCode(key))
constructor(category: Category, label: String, key: Int) : this(category, label, KeyCharAndCode(key))
constructor(category: Category, key: KeyCharAndCode) : this(category, null, key)
constructor(category: Category, key: Char) : this(category, null, KeyCharAndCode(key))
constructor(category: Category, key: Int) : this(category, null, KeyCharAndCode(key))
constructor(category: Category, key: Char) : this(category, KeyCharAndCode(key))
constructor(category: Category, key: Int) : this(category, KeyCharAndCode(key))
/** Debug helper */
override fun toString() = "$category.$name($defaultKey)"

View File

@ -2,7 +2,10 @@ package com.unciv.ui.popups.options
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.GUI
import com.unciv.UncivGame
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.KeyboardBinding
import com.unciv.ui.components.TabbedPager
@ -15,10 +18,10 @@ import com.unciv.ui.screens.civilopediascreen.MarkupRenderer
class KeyBindingsTab(
optionsPopup: OptionsPopup,
labelWidth: Float
private val labelWidth: Float
) : Table(BaseScreen.skin), TabbedPager.IPageExtensions {
private val keyBindings = optionsPopup.settings.keyBindings
private val keyFields = HashMap<KeyboardBinding, KeyCapturingButton>(KeyboardBinding.values().size)
private val groupedWidgets: LinkedHashMap<KeyboardBinding.Category, LinkedHashMap<KeyboardBinding, KeyCapturingButton>>
private val disclaimer = MarkupRenderer.render(listOf(
FormattedLine("This is a work in progress.", color = "#b22222", centered = true), // FIREBRICK
FormattedLine(),
@ -32,31 +35,55 @@ class KeyBindingsTab(
}
init {
top()
pad(10f)
defaults().pad(5f)
for (binding in KeyboardBinding.values()) {
if (binding.hidden) continue
keyFields[binding] = KeyCapturingButton(binding.defaultKey)
}
val collator = UncivGame.Current.settings.getCollatorFromLocale()
groupedWidgets = KeyboardBinding.values().asSequence()
.filterNot { it.hidden }
.groupBy { it.category } // Materializes a Map<Category,List<KeyboardBinding>>
.asSequence()
.map { (category, bindings) ->
category to bindings.asSequence()
.sortedWith(compareBy(collator) { it.label.tr() })
.map { it to KeyCapturingButton(it.defaultKey) } // associate would materialize a map
.toMap(LinkedHashMap())
}
.sortedBy { it.first.name.tr() }
.toMap(LinkedHashMap())
}
private fun update() {
clear()
add(disclaimer).colspan(2).center().row()
add(disclaimer).center().row()
for (binding in KeyboardBinding.values()) {
if (binding.hidden) continue
add(binding.label.toLabel())
add(keyFields[binding]).row()
keyFields[binding]!!.current = keyBindings[binding]
for ((category, bindings) in groupedWidgets)
add(getCategoryWidget(category, bindings)).row()
}
private fun getCategoryWidget(
category: KeyboardBinding.Category,
bindings: LinkedHashMap<KeyboardBinding, KeyCapturingButton>
) = ExpanderTab(
category.label,
startsOutOpened = false,
defaultPad = 0f,
headerPad = 5f,
// expanderWidth = labelWidth,
persistenceID = "KeyBindings." + category.name
) {
it.defaults().padTop(5f)
for ((binding, widget) in bindings) {
it.add(binding.label.toLabel()).padRight(10f).minWidth(labelWidth / 2)
it.add(widget).row()
widget.current = keyBindings[binding]
}
}
fun save () {
for (binding in KeyboardBinding.values()) {
if (binding.hidden) continue
keyBindings[binding] = keyFields[binding]!!.current
for ((binding, widget) in groupedWidgets.asSequence().flatMap { it.value.entries }) {
keyBindings[binding] = widget.current
}
}

View File

@ -230,49 +230,54 @@ class WorldScreen(
/*
* 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 and the Notification log is always visible
* Some are very much so as Unciv *is* Strategic View
*/
globalShortcuts.add(Input.Keys.F2) { openEmpireOverview(EmpireOverviewCategories.Trades) } // Economic info
globalShortcuts.add(Input.Keys.F3) { openEmpireOverview(EmpireOverviewCategories.Units) } // Military info
globalShortcuts.add(Input.Keys.F4) { openEmpireOverview(EmpireOverviewCategories.Politics) } // Diplomacy info
globalShortcuts.add(Input.Keys.F5) { game.pushScreen(PolicyPickerScreen(selectedCiv, canChangeState)) } // Social Policies Screen
globalShortcuts.add(Input.Keys.F6) { game.pushScreen(TechPickerScreen(viewingCiv)) } // Tech Screen
globalShortcuts.add(Input.Keys.F7) { openEmpireOverview(EmpireOverviewCategories.Notifications) } // Notification Log
globalShortcuts.add(Input.Keys.F8) { game.pushScreen(VictoryScreen(this)) } // Victory Progress
globalShortcuts.add(Input.Keys.F9) { openEmpireOverview(EmpireOverviewCategories.Stats) } // Demographics
globalShortcuts.add(Input.Keys.F10) { openEmpireOverview(EmpireOverviewCategories.Resources) } // originally Strategic View
globalShortcuts.add(Input.Keys.F11) { QuickSave.save(gameInfo, this) } // Quick Save
globalShortcuts.add(Input.Keys.F12) { QuickSave.load(this) } // Quick Load
globalShortcuts.add(Input.Keys.HOME) { // Capital City 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
val capital = gameInfo.getCurrentPlayerCivilization().getCapital()
if (capital != null && !mapHolder.setCenterPosition(capital.location))
game.pushScreen(CityScreen(capital))
}
globalShortcuts.add(KeyCharAndCode.ctrl('O')) { // Game Options
globalShortcuts.add(KeyboardBinding.Options) { // Game Options
this.openOptionsPopup(onClose = {
nextTurnButton.update(this)
})
}
globalShortcuts.add(KeyCharAndCode.ctrl('S')) { game.pushScreen(SaveGameScreen(gameInfo)) } // Save
globalShortcuts.add(KeyCharAndCode.ctrl('L')) { game.pushScreen(LoadGameScreen()) } // Load
globalShortcuts.add(KeyCharAndCode.ctrl('Q')) { game.popScreen() } // WorldScreen is the last screen, so this quits
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(Input.Keys.NUMPAD_ADD) { this.mapHolder.zoomIn() } // '+' Zoom
globalShortcuts.add(Input.Keys.NUMPAD_SUBTRACT) { this.mapHolder.zoomOut() } // '-' Zoom
globalShortcuts.add(KeyboardBinding.ToggleUI) { toggleUI() }
globalShortcuts.add(KeyboardBinding.ToggleResourceDisplay) { minimapWrapper.resourceImageButton.toggle() }
globalShortcuts.add(KeyboardBinding.ToggleYieldDisplay) { minimapWrapper.yieldImageButton.toggle() }
globalShortcuts.add(KeyboardBinding.ToggleWorkedTilesDisplay) { minimapWrapper.populationImageButton.toggle() }
globalShortcuts.add(KeyboardBinding.ToggleMovementDisplay) { minimapWrapper.movementsImageButton.toggle() }
}
globalShortcuts.add(KeyCharAndCode.ctrl('U')){
uiEnabled = !uiEnabled
topBar.isVisible = uiEnabled
statusButtons.isVisible = uiEnabled
techPolicyAndDiplomacy.isVisible = uiEnabled
tutorialTaskTable.isVisible = uiEnabled
bottomTileInfoTable.isVisible = uiEnabled
unitActionsTable.isVisible = uiEnabled
notificationsScroll.isVisible = uiEnabled
minimapWrapper.isVisible = uiEnabled
bottomUnitTable.isVisible = uiEnabled
if (uiEnabled) battleTable.update() else battleTable.isVisible = false
fogOfWarButton.isVisible = uiEnabled && viewingCiv.isSpectator()
}
private fun toggleUI() {
uiEnabled = !uiEnabled
topBar.isVisible = uiEnabled
statusButtons.isVisible = uiEnabled
techPolicyAndDiplomacy.isVisible = uiEnabled
tutorialTaskTable.isVisible = uiEnabled
bottomTileInfoTable.isVisible = uiEnabled
unitActionsTable.isVisible = uiEnabled
notificationsScroll.isVisible = uiEnabled
minimapWrapper.isVisible = uiEnabled
bottomUnitTable.isVisible = uiEnabled
if (uiEnabled) battleTable.update() else battleTable.isVisible = false
fogOfWarButton.isVisible = uiEnabled && viewingCiv.isSpectator()
}
private fun addKeyboardListener() {

View File

@ -24,7 +24,7 @@ class MinimapHolder(val mapHolder: WorldMapHolder) : Table() {
backgroundColor = Color.GREEN
)
/** Button, next to the minimap, to toggle the tile yield map overlay. */
private val yieldImageButton = MapOverlayToggleButton(
val yieldImageButton = MapOverlayToggleButton(
ImageGetter.getImage("StatIcons/Food"),
// This is a use in the UI that has little to do with the stat… These buttons have more in common with each other than they do with other uses of getStatIcon().
getter = { UncivGame.Current.settings.showTileYields },

View File

@ -11,10 +11,10 @@ import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.models.UnitAction
import com.unciv.models.UnitActionType
import com.unciv.models.UpgradeUnitAction
import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.UncivTooltip
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.extensions.packIfNeeded
@ -31,7 +31,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
for (unitAction in UnitActions.getUnitActions(unit)) {
val button = getUnitActionButton(unit, unitAction)
if (unitAction is UpgradeUnitAction && GUI.keyboardAvailable) {
val tipTitle = "«RED»${unitAction.type.key}«»: {Upgrade}"
val tipTitle = "«RED»${KeyboardBindings[unitAction.type.binding]}«»: {Upgrade}"
val tipActor = BaseUnitDescriptions.getUpgradeTooltipActor(tipTitle, unit.baseUnit, unitAction.unitToUpgradeTo)
button.addListener(UncivTooltip(button, tipActor
, offset = Vector2(0f, tipActor.packIfNeeded().height * 0.333f) // scaling fails to express size in parent coordinates
@ -46,7 +46,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
private fun getUnitActionButton(unit: MapUnit, unitAction: UnitAction): Button {
val icon = unitAction.getIcon()
// If peripheral keyboard not detected, hotkeys will not be displayed
val key = if (GUI.keyboardAvailable) unitAction.type.key else KeyCharAndCode.UNKNOWN
val binding = unitAction.type.binding
val fontColor = if (unitAction.isCurrentAction) Color.YELLOW else Color.WHITE
val actionButton = IconTextButton(unitAction.title, icon, fontColor = fontColor)
@ -55,7 +55,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
actionButton.color = Color.GREEN.cpy().lerp(Color.WHITE, 0.5f)
if (unitAction !is UpgradeUnitAction) // Does its own toolTip
actionButton.addTooltip(key)
actionButton.addTooltip(KeyboardBindings[binding])
actionButton.pack()
if (unitAction.action == null) {
actionButton.disable()
@ -72,7 +72,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
worldScreen.switchToNextUnit()
}
}
actionButton.keyShortcuts.add(key)
actionButton.keyShortcuts.add(binding)
}
return actionButton