diff --git a/android/Images/OtherIcons/Improvements.png b/android/Images/OtherIcons/Improvements.png new file mode 100644 index 0000000000..1778976ab8 Binary files /dev/null and b/android/Images/OtherIcons/Improvements.png differ diff --git a/android/Images/OtherIcons/Nations.png b/android/Images/OtherIcons/Nations.png new file mode 100644 index 0000000000..39487d3941 Binary files /dev/null and b/android/Images/OtherIcons/Nations.png differ diff --git a/android/Images/OtherIcons/Resources.png b/android/Images/OtherIcons/Resources.png new file mode 100644 index 0000000000..47da740e7d Binary files /dev/null and b/android/Images/OtherIcons/Resources.png differ diff --git a/android/Images/OtherIcons/Terrains.png b/android/Images/OtherIcons/Terrains.png new file mode 100644 index 0000000000..379aeb03e8 Binary files /dev/null and b/android/Images/OtherIcons/Terrains.png differ diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaCategories.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaCategories.kt index 2c3675531c..71e8b3537e 100644 --- a/core/src/com/unciv/ui/civilopedia/CivilopediaCategories.kt +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaCategories.kt @@ -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 } diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt index 73d09af070..6425a53d96 100644 --- a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt @@ -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>() - private val categoryToButtons = LinkedHashMap() + private class CategoryButtonInfo(val button: Button, val x: Float, val width: Float) + private val categoryToButtons = LinkedHashMap() private val entryIndex = LinkedHashMap() + 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() /** 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) { diff --git a/docs/Credits.md b/docs/Credits.md index 97dd2fba9f..13193a25ec 100644 --- a/docs/Credits.md +++ b/docs/Credits.md @@ -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