Civilopedia category icons and keyboard navigation (#5341)

* Civilopedia category icons and keyboard navigation

* Civilopedia category icons - white alternative
This commit is contained in:
SomeTroglodyte 2021-09-29 16:39:02 +02:00 committed by GitHub
parent 3220206bce
commit fb30a76e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 144 additions and 31 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,5 +1,6 @@
package com.unciv.ui.civilopedia
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.Container
@ -11,6 +12,7 @@ import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.ui.tilegroups.TileGroup
import com.unciv.ui.tilegroups.TileSetStrings
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.KeyCharAndCode
import com.unciv.ui.utils.surroundWithCircle
import java.io.File
@ -113,24 +115,89 @@ object CivilopediaImageGetters {
enum class CivilopediaCategories (
val label: String,
val hide: Boolean, // Omitted on CivilopediaScreen
val getImage: ((name: String, size: Float) -> Actor?)?
val getImage: ((name: String, size: Float) -> Actor?)?,
val key: KeyCharAndCode = KeyCharAndCode.UNKNOWN,
val headerIcon: String
) {
Building ("Buildings", false, CivilopediaImageGetters.construction ),
Wonder ("Wonders", false, CivilopediaImageGetters.construction ),
Resource ("Resources", false, CivilopediaImageGetters.resource ),
Terrain ("Terrains", false, CivilopediaImageGetters.terrain ),
Improvement ("Tile Improvements", false, CivilopediaImageGetters.improvement ),
Unit ("Units", false, CivilopediaImageGetters.construction ),
Nation ("Nations", false, CivilopediaImageGetters.nation ),
Technology ("Technologies", false, CivilopediaImageGetters.technology ),
Promotion ("Promotions", false, CivilopediaImageGetters.promotion ),
Policy ("Policies", false, CivilopediaImageGetters.policy ),
Belief("Religions and Beliefs", false, CivilopediaImageGetters.belief ),
Tutorial ("Tutorials", false, null ),
Difficulty ("Difficulty levels", false, null ),
;
Building ("Buildings", false,
CivilopediaImageGetters.construction,
KeyCharAndCode('B'),
"OtherIcons/Cities"
),
Wonder ("Wonders", false,
CivilopediaImageGetters.construction,
KeyCharAndCode('W'),
"OtherIcons/Wonders"
),
Resource ("Resources", false,
CivilopediaImageGetters.resource,
KeyCharAndCode('R'),
"OtherIcons/Resources"
),
Terrain ("Terrains", false,
CivilopediaImageGetters.terrain,
KeyCharAndCode('T'),
"OtherIcons/Terrains"
),
Improvement ("Tile Improvements", false,
CivilopediaImageGetters.improvement,
KeyCharAndCode('T'),
"OtherIcons/Improvements"
),
Unit ("Units", false,
CivilopediaImageGetters.construction,
KeyCharAndCode('U'),
"OtherIcons/Shield"
),
Nation ("Nations", false,
CivilopediaImageGetters.nation,
KeyCharAndCode('N'),
"OtherIcons/Nations"
),
Technology ("Technologies", false,
CivilopediaImageGetters.technology,
KeyCharAndCode('T'),
"TechIcons/Philosophy"
),
Promotion ("Promotions", false,
CivilopediaImageGetters.promotion,
KeyCharAndCode('P'),
"UnitPromotionIcons/Mobility"
),
Policy ("Policies", false,
CivilopediaImageGetters.policy,
KeyCharAndCode('P'),
"PolicyIcons/Constitution"
),
Belief("Religions and Beliefs", false,
CivilopediaImageGetters.belief,
KeyCharAndCode('R'),
"ReligionIcons/Religion"
),
Tutorial ("Tutorials", false,
getImage = null,
KeyCharAndCode(Input.Keys.F1),
"OtherIcons/ExclamationMark"
),
Difficulty ("Difficulty levels", false,
getImage = null,
KeyCharAndCode('D'),
"OtherIcons/Quickstart"
);
fun getByOffset(offset: Int) = values()[(ordinal + count + offset) % count]
fun nextForKey(key: KeyCharAndCode): CivilopediaCategories {
for (i in 1..count) {
val next = getByOffset(i)
if (next.key == key) return next
}
return this
}
companion object {
private val count = values().size
fun fromLink(name: String): CivilopediaCategories? =
values().firstOrNull { it.name == name }
?: values().firstOrNull { it.label == name }

View File

@ -1,5 +1,6 @@
package com.unciv.ui.civilopedia
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable
@ -11,6 +12,7 @@ import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.stats.INamed
import com.unciv.models.translations.tr
import com.unciv.ui.utils.*
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
/** Screen displaying the Civilopedia
@ -44,15 +46,19 @@ class CivilopediaScreen(
}
private val categoryToEntries = LinkedHashMap<CivilopediaCategories, Collection<CivilopediaEntry>>()
private val categoryToButtons = LinkedHashMap<CivilopediaCategories, Button>()
private class CategoryButtonInfo(val button: Button, val x: Float, val width: Float)
private val categoryToButtons = LinkedHashMap<CivilopediaCategories, CategoryButtonInfo>()
private val entryIndex = LinkedHashMap<String, CivilopediaEntry>()
private val buttonTableScroll: ScrollPane
private val entrySelectTable = Table().apply { defaults().pad(6f).left() }
private val entrySelectScroll: ScrollPane
private val flavourTable = Table()
private var currentCategory: CivilopediaCategories = CivilopediaCategories.Tutorial
private var currentEntry: String = ""
private val currentEntryPerCategory = HashMap<CivilopediaCategories, String>()
/** Jump to a "link" selecting both category and entry
*
@ -87,9 +93,11 @@ class CivilopediaScreen(
entryIndex.clear()
flavourTable.clear()
for (button in categoryToButtons.values) button.color = Color.WHITE
if (category !in categoryToButtons) return // defense against being passed a bad selector
categoryToButtons[category]!!.color = Color.BLUE
for (button in categoryToButtons.values) button.button.color = Color.WHITE
val buttonInfo = categoryToButtons[category]
?: return // defense against being passed a bad selector
buttonInfo.button.color = Color.BLUE
buttonTableScroll.scrollX = buttonInfo.x + (buttonInfo.width - buttonTableScroll.width) / 2
if (category !in categoryToEntries) return // defense, allowing buggy panes to remain empty while others work
var entries = categoryToEntries[category]!!
@ -123,6 +131,9 @@ class CivilopediaScreen(
}
entrySelectScroll.layout() // necessary for positioning in selectRow to work
val entry = currentEntryPerCategory[category]
if (entry != null) selectEntry(entry)
}
/** Select a specified entry within the current category. Unknown strings are ignored!
@ -132,15 +143,14 @@ class CivilopediaScreen(
fun selectEntry(name: String, noScrollAnimation: Boolean = false) {
val entry = entryIndex[name] ?: return
// fails: entrySelectScroll.scrollTo(0f, entry.y, 0f, entry.h, false, true)
entrySelectScroll.let {
it.scrollY = (entry.y + (entry.height - it.height) / 2).coerceIn(0f, it.maxY)
}
entrySelectScroll.scrollY = (entry.y + (entry.height - entrySelectScroll.height) / 2)
if (noScrollAnimation)
entrySelectScroll.updateVisualScroll() // snap without animation on fresh pedia open
selectEntry(entry)
}
private fun selectEntry(entry: CivilopediaEntry) {
currentEntry = entry.name
currentEntryPerCategory[currentCategory] = entry.name
flavourTable.clear()
if (entry.flavour != null) {
flavourTable.isVisible = true
@ -207,17 +217,22 @@ class CivilopediaScreen(
buttonTable.pad(15f)
buttonTable.defaults().pad(10f)
var currentX = 10f // = padLeft
for (categoryKey in categoryToEntries.keys) {
val button = categoryKey.label.toTextButton()
button.style = TextButton.TextButtonStyle(button.style)
categoryToButtons[categoryKey] = button
val button = Button(skin)
if (categoryKey.headerIcon.isNotEmpty())
button.add(ImageGetter.getImage(categoryKey.headerIcon)).size(20f).padRight(5f)
button.add(categoryKey.label.toLabel())
button.addTooltip(categoryKey.key)
// button.style = ImageButton.ImageButtonStyle(button.style)
button.onClick { selectCategory(categoryKey) }
buttonTable.add(button)
val cell = buttonTable.add(button)
categoryToButtons[categoryKey] = CategoryButtonInfo(button, currentX, cell.prefWidth)
currentX += cell.prefWidth + 20f
}
buttonTable.pack()
buttonTable.width = stage.width
val buttonTableScroll = ScrollPane(buttonTable)
buttonTableScroll = ScrollPane(buttonTable)
buttonTableScroll.setScrollingDisabled(false, true)
val goToGameButton = Constants.close.toTextButton()
@ -228,8 +243,9 @@ class CivilopediaScreen(
val topTable = Table()
topTable.add(goToGameButton).pad(10f)
topTable.add(buttonTableScroll)
topTable.pack()
topTable.add(buttonTableScroll).growX()
topTable.width = stage.width
topTable.layout()
val entryTable = Table()
val splitPane = SplitPane(topTable, entryTable, true, skin)
@ -257,6 +273,34 @@ class CivilopediaScreen(
selectLink(link)
else
selectEntry(link, noScrollAnimation = true)
for (categoryKey in CivilopediaCategories.values()) {
keyPressDispatcher[categoryKey.key] = { navigateCategories(categoryKey.key) }
}
keyPressDispatcher[Input.Keys.LEFT] = { selectCategory(currentCategory.getByOffset(-1)) }
keyPressDispatcher[Input.Keys.RIGHT] = { selectCategory(currentCategory.getByOffset(1)) }
keyPressDispatcher[Input.Keys.UP] = { navigateEntries(-1) }
keyPressDispatcher[Input.Keys.DOWN] = { navigateEntries(1) }
keyPressDispatcher[Input.Keys.PAGE_UP] = { navigateEntries(-10) }
keyPressDispatcher[Input.Keys.PAGE_DOWN] = { navigateEntries(10) }
keyPressDispatcher[Input.Keys.HOME] = { navigateEntries(Int.MIN_VALUE) }
keyPressDispatcher[Input.Keys.END] = { navigateEntries(Int.MAX_VALUE) }
}
private fun navigateCategories(key: KeyCharAndCode) {
selectCategory(currentCategory.nextForKey(key))
}
private fun navigateEntries(direction: Int) {
//todo this is abusing a Map as Array - there must be a collection allowing both easy positional and associative access
val index = entryIndex.keys.indexOf(currentEntry)
if (index < 0) return selectEntry(entryIndex.keys.first(), true)
val newIndex = when (direction) {
Int.MIN_VALUE -> 0
Int.MAX_VALUE -> entryIndex.size - 1
else -> (index + entryIndex.size + direction) % entryIndex.size
}
selectEntry(entryIndex.keys.drop(newIndex).first())
}
override fun resize(width: Int, height: Int) {

View File

@ -158,7 +158,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Anvil](https://thenounproject.com/term/anvil/166414/) By Jason Dilworth for Iron
* [Deer](https://thenounproject.com/term/deer/338013/) By Richard Nixon
* [Banana](https://thenounproject.com/term/banana/1262865/) By Adrian Coquet
* [Oil](https://thenounproject.com/term/oil/88649/) By Tiago Maricate
* [Oil](https://thenounproject.com/term/oil/88649/) By Tiago Maricate (also as Civilopedia category icon)
* [Statue](https://thenounproject.com/term/statue/5221/) By Joris Hoogendoorn for Marble
* [Ribbon](https://thenounproject.com/term/ribbon/418996) By Anton for Silk
* [Stone](https://thenounproject.com/term/stone/1373902/) By AFY Studio
@ -648,6 +648,8 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [ship helm](https://thenounproject.com/term/ship-helm/2170591/) by Vectors Market for Maritime City-States
* [Magnifying Glass](https://thenounproject.com/term/magnifying-glass/1311/) by John Caserta for Mod filter
* [tick](https://thenounproject.com/term/tick/3968142/) by Adrien Coquet on Nation picker
* [people](https://thenounproject.com/term/people/458671) by Wilson Joseph as base for Civilopedia category Nations
* [Mountains ](https://thenounproject.com/term/mountains/15616/) by Andrew J. Young as base for Civilopedia category Terrains
## Main menu