Moddable UI Skins [Basics] (#7714)

* Made UI skins selectable

This allows mods to provide alternative skin pngs without overriding the default skin purely by being downloaded

* Added UI Skin loading from NinePatches

This allows mod creators to define the stretch region and padding directly in the png

* Update baseScreen skin on skin reload

* Merged displayTab onChange functions

As all of them are equal anyway
This commit is contained in:
Leonard Günther 2022-09-04 16:09:14 +02:00 committed by GitHub
parent 30ed0f544d
commit f4dc138186
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 49 additions and 21 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

View File

@ -81,6 +81,7 @@ object Constants {
const val dropboxMultiplayerServer = "Dropbox"
const val defaultTileset = "HexaRealm"
const val defaultSkin = "Minimal"
/**
* Use this to determine whether a [MapUnit][com.unciv.logic.map.MapUnit]'s movement is exhausted

View File

@ -38,6 +38,7 @@ class GameSettings {
var turnsBetweenAutosaves = 1
var tileSet: String = Constants.defaultTileset
var skin: String = Constants.defaultSkin
var showTutorials: Boolean = true
var autoAssignCityProduction: Boolean = true
var autoBuildingRoads: Boolean = true

View File

@ -47,6 +47,7 @@ object ImageGetter {
// We then shove all the drawables into a hashmap, because the atlas specifically tells us
// that the search on it is inefficient
private val textureRegionDrawables = HashMap<String, TextureRegionDrawable>()
private val ninePatchDrawables = HashMap<String, NinePatchDrawable>()
fun resetAtlases() {
atlases.values.forEach { it.dispose() }
@ -95,8 +96,13 @@ object ImageGetter {
atlases[extraAtlas] = tempAtlas // cache the freshly loaded
}
for (region in tempAtlas.regions) {
val drawable = TextureRegionDrawable(region)
textureRegionDrawables[region.name] = drawable
if (region.name.startsWith("Skins")) {
val ninePatch = tempAtlas.createPatch(region.name)
ninePatchDrawables[region.name] = NinePatchDrawable(ninePatch)
} else {
val drawable = TextureRegionDrawable(region)
textureRegionDrawables[region.name] = drawable
}
}
}
}
@ -175,36 +181,35 @@ object ImageGetter {
return textureRegionDrawables[fileName] ?: textureRegionDrawables[whiteDotLocation]!!
}
fun getNinePatch(fileName: String?): NinePatchDrawable {
return ninePatchDrawables[fileName] ?: NinePatchDrawable(NinePatch(textureRegionDrawables[whiteDotLocation]!!.region))
}
fun getRoundedEdgeRectangle(tintColor: Color? = null): NinePatchDrawable {
val region = getDrawable("Skin/roundedEdgeRectangle").region
val drawable = NinePatchDrawable(NinePatch(region, 25, 25, 0, 0))
drawable.setPadding(5f, 15f, 5f, 15f)
val drawable = getNinePatch("Skins/${UncivGame.Current.settings.skin}/roundedEdgeRectangle")
if (tintColor == null) return drawable
return drawable.tint(tintColor)
}
fun getRectangleWithOutline(): NinePatchDrawable {
val region = getDrawable("Skin/rectangleWithOutline").region
return NinePatchDrawable(NinePatch(region, 1, 1, 1, 1))
return getNinePatch("Skins/${UncivGame.Current.settings.skin}/rectangleWithOutline")
}
fun getSelectBox(): NinePatchDrawable {
val region = getDrawable("Skin/select-box").region
return NinePatchDrawable(NinePatch(region, 10, 25, 5, 5))
return getNinePatch("Skins/${UncivGame.Current.settings.skin}/select-box")
}
fun getSelectBoxPressed(): NinePatchDrawable {
val region = getDrawable("Skin/select-box-pressed").region
return NinePatchDrawable(NinePatch(region, 10, 25, 5, 5))
return getNinePatch("Skins/${UncivGame.Current.settings.skin}/select-box-pressed")
}
fun getCheckBox(): Drawable {
return getDrawable("Skin/checkbox")
fun getCheckBox(): NinePatchDrawable {
return getNinePatch("Skins/${UncivGame.Current.settings.skin}/checkbox")
}
fun getCheckBoxPressed(): Drawable {
return getDrawable("Skin/checkbox-pressed")
fun getCheckBoxPressed(): NinePatchDrawable {
return getNinePatch("Skins/${UncivGame.Current.settings.skin}/checkbox-pressed")
}
fun imageExists(fileName: String) = textureRegionDrawables.containsKey(fileName)
@ -459,6 +464,8 @@ object ImageGetter {
return specialist
}
fun getAvailableSkins() = ninePatchDrawables.keys.asSequence().map { it.split("/")[1] }.distinct()
fun getAvailableTilesets() = textureRegionDrawables.keys.asSequence().filter { it.startsWith("TileSets") }
.map { it.split("/")[1] }.distinct()
}

View File

@ -16,14 +16,12 @@ import com.unciv.ui.utils.WrappableLabel
import com.unciv.ui.utils.extensions.brighten
import com.unciv.ui.utils.extensions.onChange
import com.unciv.ui.utils.extensions.toLabel
import com.unciv.ui.worldscreen.WorldScreen
private val resolutionArray = com.badlogic.gdx.utils.Array(arrayOf("750x500", "900x600", "1050x700", "1200x800", "1500x1000"))
fun displayTab(
optionsPopup: OptionsPopup,
onResolutionChange: () -> Unit,
onTilesetChange: () -> Unit
onChange: () -> Unit,
) = Table(BaseScreen.skin).apply {
pad(10f)
defaults().pad(2.5f)
@ -44,9 +42,11 @@ fun displayTab(
addUnitIconAlphaSlider(this, settings, optionsPopup.selectBoxMinWidth)
addResolutionSelectBox(this, settings, optionsPopup.selectBoxMinWidth, onResolutionChange)
addResolutionSelectBox(this, settings, optionsPopup.selectBoxMinWidth, onChange)
addTileSetSelectBox(this, settings, optionsPopup.selectBoxMinWidth, onTilesetChange)
addTileSetSelectBox(this, settings, optionsPopup.selectBoxMinWidth, onChange)
addSkinSelectBox(this, settings, optionsPopup.selectBoxMinWidth, onChange)
optionsPopup.addCheckbox(this, "Continuous rendering", settings.continuousRendering) {
settings.continuousRendering = it
@ -147,3 +147,21 @@ private fun addTileSetSelectBox(table: Table, settings: GameSettings, selectBoxM
onTilesetChange()
}
}
private fun addSkinSelectBox(table: Table, settings: GameSettings, selectBoxMinWidth: Float, onSkinChange: () -> Unit) {
table.add("UI Skin".toLabel()).left().fillX()
val skinSelectBox = SelectBox<String>(table.skin)
val skinArray = Array<String>()
val skins = ImageGetter.getAvailableSkins()
for (skin in skins) skinArray.add(skin)
skinSelectBox.items = skinArray
skinSelectBox.selected = settings.skin
table.add(skinSelectBox).minWidth(selectBoxMinWidth).pad(10f).row()
skinSelectBox.onChange {
settings.skin = skinSelectBox.selected
// ImageGetter ruleset should be correct no matter what screen we're on
onSkinChange()
}
}

View File

@ -80,7 +80,7 @@ class OptionsPopup(
)
tabs.addPage(
"Display",
displayTab(this, ::reloadWorldAndOptions, ::reloadWorldAndOptions),
displayTab(this, ::reloadWorldAndOptions),
ImageGetter.getImage("UnitPromotionIcons/Scouting"), 24f
)
tabs.addPage(
@ -147,6 +147,7 @@ class OptionsPopup(
}
}
withGLContext {
BaseScreen.setSkin()
UncivGame.Current.screen?.openOptionsPopup(tabs.activePage)
}
}