mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 22:37:02 -04:00
Split off stuff from CameraStageBaseScreen that isn't the class itself (#3929)
This commit is contained in:
parent
d546b2b00e
commit
abaa678e2b
@ -11,87 +11,11 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
|||||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas
|
||||||
import com.badlogic.gdx.scenes.scene2d.*
|
import com.badlogic.gdx.scenes.scene2d.*
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.*
|
import com.badlogic.gdx.scenes.scene2d.ui.*
|
||||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.utils.Drawable
|
import com.badlogic.gdx.scenes.scene2d.utils.Drawable
|
||||||
import com.badlogic.gdx.utils.viewport.ExtendViewport
|
import com.badlogic.gdx.utils.viewport.ExtendViewport
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.models.Tutorial
|
import com.unciv.models.Tutorial
|
||||||
import com.unciv.models.UncivSound
|
|
||||||
import com.unciv.models.translations.tr
|
|
||||||
import com.unciv.ui.tutorials.TutorialController
|
import com.unciv.ui.tutorials.TutorialController
|
||||||
import java.util.HashMap
|
|
||||||
import kotlin.concurrent.thread
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For now, combination keys cannot easily be expressed.
|
|
||||||
* Pressing Ctrl-Letter will arrive one event for Input.Keys.CONTROL_LEFT and one for the ASCII control code point
|
|
||||||
* so Ctrl-R can be handled using KeyCharAndCode('\u0012')
|
|
||||||
* Pressing Alt-Something likewise will fire once for Alt and once for the unmodified keys with no indication Alt is held
|
|
||||||
* (Exception: international keyboard AltGr-combos)
|
|
||||||
* An update supporting easy declarations for any modifier combos would need to use Gdx.input.isKeyPressed()
|
|
||||||
* Gdx seems to omit support for a modifier mask (e.g. Ctrl-Alt-Shift) so we would need to reinvent this
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a key for use in an InputListener keyTyped() handler
|
|
||||||
*
|
|
||||||
* Example: KeyCharAndCode('R'), KeyCharAndCode(Input.Keys.F1)
|
|
||||||
*/
|
|
||||||
data class KeyCharAndCode(val char: Char, val code: Int) {
|
|
||||||
// express keys with a Char value
|
|
||||||
constructor(char: Char): this(char.toLowerCase(), 0)
|
|
||||||
// express keys that only have a keyCode like F1
|
|
||||||
constructor(code: Int): this(Char.MIN_VALUE, code)
|
|
||||||
// helper for use in InputListener keyTyped()
|
|
||||||
constructor(event: InputEvent?, character: Char)
|
|
||||||
: this (
|
|
||||||
character.toLowerCase(),
|
|
||||||
if (character == Char.MIN_VALUE && event!=null) event.keyCode else 0
|
|
||||||
)
|
|
||||||
|
|
||||||
@ExperimentalStdlibApi
|
|
||||||
override fun toString(): String {
|
|
||||||
// debug helper
|
|
||||||
return when {
|
|
||||||
char == Char.MIN_VALUE -> Input.Keys.toString(code)
|
|
||||||
char < ' ' -> "Ctrl-" + Char(char.toInt()+64)
|
|
||||||
else -> "\"$char\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class KeyPressDispatcher: HashMap<KeyCharAndCode, (() -> Unit)>() {
|
|
||||||
private var checkpoint: Set<KeyCharAndCode> = setOf()
|
|
||||||
|
|
||||||
// access by Char
|
|
||||||
operator fun get(char: Char) = this[KeyCharAndCode(char)]
|
|
||||||
operator fun set(char: Char, action: () -> Unit) {
|
|
||||||
this[KeyCharAndCode(char)] = action
|
|
||||||
}
|
|
||||||
operator fun contains(char: Char) = this.contains(KeyCharAndCode(char))
|
|
||||||
fun remove(char: Char) = this.remove(KeyCharAndCode(char))
|
|
||||||
|
|
||||||
// access by Int keyCodes
|
|
||||||
operator fun get(code: Int) = this[KeyCharAndCode(code)]
|
|
||||||
operator fun set(code: Int, action: () -> Unit) {
|
|
||||||
this[KeyCharAndCode(code)] = action
|
|
||||||
}
|
|
||||||
operator fun contains(code: Int) = this.contains(KeyCharAndCode(code))
|
|
||||||
fun remove(code: Int) = this.remove(KeyCharAndCode(code))
|
|
||||||
|
|
||||||
override fun clear() {
|
|
||||||
checkpoint = setOf()
|
|
||||||
super.clear()
|
|
||||||
}
|
|
||||||
fun setCheckpoint() {
|
|
||||||
checkpoint = keys.toSet()
|
|
||||||
}
|
|
||||||
fun revertToCheckPoint() {
|
|
||||||
keys.minus(checkpoint).forEach { remove(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class CameraStageBaseScreen : Screen {
|
open class CameraStageBaseScreen : Screen {
|
||||||
|
|
||||||
@ -195,176 +119,3 @@ open class CameraStageBaseScreen : Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun Button.disable(){
|
|
||||||
touchable= Touchable.disabled
|
|
||||||
color= Color.GRAY
|
|
||||||
}
|
|
||||||
fun Button.enable() {
|
|
||||||
color = Color.WHITE
|
|
||||||
touchable = Touchable.enabled
|
|
||||||
}
|
|
||||||
var Button.isEnabled: Boolean
|
|
||||||
//Todo: Use in PromotionPickerScreen, TradeTable, WorldScreen.updateNextTurnButton
|
|
||||||
get() = touchable == Touchable.enabled
|
|
||||||
set(value) = if (value) enable() else disable()
|
|
||||||
|
|
||||||
fun colorFromRGB(r: Int, g: Int, b: Int) = Color(r/255f, g/255f, b/255f, 1f)
|
|
||||||
fun colorFromRGB(rgb:List<Int>) = colorFromRGB(rgb[0],rgb[1],rgb[2])
|
|
||||||
|
|
||||||
fun Actor.centerX(parent:Actor){ x = parent.width/2 - width/2 }
|
|
||||||
fun Actor.centerY(parent:Actor){ y = parent.height/2- height/2}
|
|
||||||
fun Actor.center(parent:Actor){ centerX(parent); centerY(parent)}
|
|
||||||
|
|
||||||
fun Actor.centerX(parent:Stage){ x = parent.width/2 - width/2 }
|
|
||||||
fun Actor.centerY(parent:Stage){ y = parent.height/2- height/2}
|
|
||||||
fun Actor.center(parent:Stage){ centerX(parent); centerY(parent)}
|
|
||||||
|
|
||||||
|
|
||||||
/** same as [onClick], but sends the [InputEvent] and coordinates along */
|
|
||||||
fun Actor.onClickEvent(sound: UncivSound = UncivSound.Click, function: (event: InputEvent?, x: Float, y: Float) -> Unit) {
|
|
||||||
this.addListener(object : ClickListener() {
|
|
||||||
override fun clicked(event: InputEvent?, x: Float, y: Float) {
|
|
||||||
thread(name="Sound") { Sounds.play(sound) }
|
|
||||||
function(event, x, y)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are other buttons that require special clicks then we'll have an onclick that will accept a string parameter, no worries
|
|
||||||
fun Actor.onClick(sound: UncivSound = UncivSound.Click, function: () -> Unit) {
|
|
||||||
onClickEvent(sound) { _, _, _ -> function() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Actor.onClick(function: () -> Unit): Actor {
|
|
||||||
onClick(UncivSound.Click, function)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Actor.onChange(function: () -> Unit): Actor {
|
|
||||||
this.addListener(object : ChangeListener() {
|
|
||||||
override fun changed(event: ChangeEvent?, actor: Actor?) {
|
|
||||||
function()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Actor.surroundWithCircle(size: Float, resizeActor: Boolean = true, color: Color = Color.WHITE): IconCircleGroup {
|
|
||||||
return IconCircleGroup(size,this,resizeActor, color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Actor.addBorder(size:Float,color:Color,expandCell:Boolean=false):Table{
|
|
||||||
val table = Table()
|
|
||||||
table.pad(size)
|
|
||||||
table.background = ImageGetter.getBackground(color)
|
|
||||||
val cell = table.add(this)
|
|
||||||
if (expandCell) cell.expand()
|
|
||||||
cell.fill()
|
|
||||||
table.pack()
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Table.addSeparator(): Cell<Image> {
|
|
||||||
row()
|
|
||||||
val image = ImageGetter.getWhiteDot()
|
|
||||||
val cell = add(image).colspan(columns).height(2f).fill()
|
|
||||||
row()
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Table.addSeparatorVertical(): Cell<Image> {
|
|
||||||
val image = ImageGetter.getWhiteDot()
|
|
||||||
val cell = add(image).width(2f).fillY()
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : Actor> Table.addCell(actor: T): Table {
|
|
||||||
add(actor)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
|
||||||
*/
|
|
||||||
fun <T> ArrayList<T>.withItem(item:T): ArrayList<T> {
|
|
||||||
val newArrayList = ArrayList(this)
|
|
||||||
newArrayList.add(item)
|
|
||||||
return newArrayList
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
|
||||||
*/
|
|
||||||
fun <T> HashSet<T>.withItem(item:T): HashSet<T> {
|
|
||||||
val newHashSet = HashSet(this)
|
|
||||||
newHashSet.add(item)
|
|
||||||
return newHashSet
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
|
||||||
*/
|
|
||||||
fun <T> ArrayList<T>.withoutItem(item:T): ArrayList<T> {
|
|
||||||
val newArrayList = ArrayList(this)
|
|
||||||
newArrayList.remove(item)
|
|
||||||
return newArrayList
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
|
||||||
*/
|
|
||||||
fun <T> HashSet<T>.withoutItem(item:T): HashSet<T> {
|
|
||||||
val newHashSet = HashSet(this)
|
|
||||||
newHashSet.remove(item)
|
|
||||||
return newHashSet
|
|
||||||
}
|
|
||||||
|
|
||||||
fun String.toTextButton() = TextButton(this.tr(), CameraStageBaseScreen.skin)
|
|
||||||
|
|
||||||
/** also translates */
|
|
||||||
fun String.toLabel() = Label(this.tr(),CameraStageBaseScreen.skin)
|
|
||||||
fun Int.toLabel() = this.toString().toLabel()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// We don't want to use setFontSize and setFontColor because they set the font,
|
|
||||||
// which means we need to rebuild the font cache which means more memory allocation.
|
|
||||||
fun String.toLabel(fontColor:Color= Color.WHITE, fontSize:Int=18): Label {
|
|
||||||
var labelStyle = CameraStageBaseScreen.skin.get(Label.LabelStyle::class.java)
|
|
||||||
if(fontColor!= Color.WHITE || fontSize!=18) { // if we want the default we don't need to create another style
|
|
||||||
labelStyle = Label.LabelStyle(labelStyle) // clone this to another
|
|
||||||
labelStyle.fontColor = fontColor
|
|
||||||
if (fontSize != 18) labelStyle.font = Fonts.font
|
|
||||||
}
|
|
||||||
return Label(this.tr(), labelStyle).apply { setFontScale(fontSize/Fonts.ORIGINAL_FONT_SIZE) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun Label.setFontColor(color:Color): Label { style=Label.LabelStyle(style).apply { fontColor=color }; return this }
|
|
||||||
|
|
||||||
fun Label.setFontSize(size:Int): Label {
|
|
||||||
style = Label.LabelStyle(style)
|
|
||||||
style.font = Fonts.font
|
|
||||||
style = style // because we need it to call the SetStyle function. Yuk, I know.
|
|
||||||
return this.apply { setFontScale(size/Fonts.ORIGINAL_FONT_SIZE) } // for chaining
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun <T> List<T>.randomWeighted(weights: List<Float>, random: Random = Random): T {
|
|
||||||
if (this.isEmpty()) throw NoSuchElementException("Empty list.")
|
|
||||||
if (this.size != weights.size) throw UnsupportedOperationException("Weights size does not match this list size.")
|
|
||||||
|
|
||||||
val totalWeight = weights.sum()
|
|
||||||
val randDouble = random.nextDouble()
|
|
||||||
var sum = 0f
|
|
||||||
|
|
||||||
for (i in weights.indices) {
|
|
||||||
sum += weights[i] / totalWeight
|
|
||||||
if (randDouble <= sum)
|
|
||||||
return this[i]
|
|
||||||
}
|
|
||||||
return this.last()
|
|
||||||
}
|
|
||||||
|
187
core/src/com/unciv/ui/utils/ExtensionFunctions.kt
Normal file
187
core/src/com/unciv/ui/utils/ExtensionFunctions.kt
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package com.unciv.ui.utils
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputEvent
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.Stage
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.*
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener
|
||||||
|
import com.unciv.models.UncivSound
|
||||||
|
import com.unciv.models.translations.tr
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
|
fun Button.disable(){
|
||||||
|
touchable= Touchable.disabled
|
||||||
|
color= Color.GRAY
|
||||||
|
}
|
||||||
|
fun Button.enable() {
|
||||||
|
color = Color.WHITE
|
||||||
|
touchable = Touchable.enabled
|
||||||
|
}
|
||||||
|
var Button.isEnabled: Boolean
|
||||||
|
//Todo: Use in PromotionPickerScreen, TradeTable, WorldScreen.updateNextTurnButton
|
||||||
|
get() = touchable == Touchable.enabled
|
||||||
|
set(value) = if (value) enable() else disable()
|
||||||
|
|
||||||
|
fun colorFromRGB(r: Int, g: Int, b: Int) = Color(r/255f, g/255f, b/255f, 1f)
|
||||||
|
fun colorFromRGB(rgb:List<Int>) = colorFromRGB(rgb[0],rgb[1],rgb[2])
|
||||||
|
|
||||||
|
fun Actor.centerX(parent: Actor){ x = parent.width/2 - width/2 }
|
||||||
|
fun Actor.centerY(parent: Actor){ y = parent.height/2- height/2}
|
||||||
|
fun Actor.center(parent: Actor){ centerX(parent); centerY(parent)}
|
||||||
|
|
||||||
|
fun Actor.centerX(parent: Stage){ x = parent.width/2 - width/2 }
|
||||||
|
fun Actor.centerY(parent: Stage){ y = parent.height/2- height/2}
|
||||||
|
fun Actor.center(parent: Stage){ centerX(parent); centerY(parent)}
|
||||||
|
|
||||||
|
|
||||||
|
/** same as [onClick], but sends the [InputEvent] and coordinates along */
|
||||||
|
fun Actor.onClickEvent(sound: UncivSound = UncivSound.Click, function: (event: InputEvent?, x: Float, y: Float) -> Unit) {
|
||||||
|
this.addListener(object : ClickListener() {
|
||||||
|
override fun clicked(event: InputEvent?, x: Float, y: Float) {
|
||||||
|
thread(name="Sound") { Sounds.play(sound) }
|
||||||
|
function(event, x, y)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are other buttons that require special clicks then we'll have an onclick that will accept a string parameter, no worries
|
||||||
|
fun Actor.onClick(sound: UncivSound = UncivSound.Click, function: () -> Unit) {
|
||||||
|
onClickEvent(sound) { _, _, _ -> function() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Actor.onClick(function: () -> Unit): Actor {
|
||||||
|
onClick(UncivSound.Click, function)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Actor.onChange(function: () -> Unit): Actor {
|
||||||
|
this.addListener(object : ChangeListener() {
|
||||||
|
override fun changed(event: ChangeEvent?, actor: Actor?) {
|
||||||
|
function()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Actor.surroundWithCircle(size: Float, resizeActor: Boolean = true, color: Color = Color.WHITE): IconCircleGroup {
|
||||||
|
return IconCircleGroup(size,this,resizeActor, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Actor.addBorder(size:Float, color: Color, expandCell:Boolean=false): Table {
|
||||||
|
val table = Table()
|
||||||
|
table.pad(size)
|
||||||
|
table.background = ImageGetter.getBackground(color)
|
||||||
|
val cell = table.add(this)
|
||||||
|
if (expandCell) cell.expand()
|
||||||
|
cell.fill()
|
||||||
|
table.pack()
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Table.addSeparator(): Cell<Image> {
|
||||||
|
row()
|
||||||
|
val image = ImageGetter.getWhiteDot()
|
||||||
|
val cell = add(image).colspan(columns).height(2f).fill()
|
||||||
|
row()
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Table.addSeparatorVertical(): Cell<Image> {
|
||||||
|
val image = ImageGetter.getWhiteDot()
|
||||||
|
val cell = add(image).width(2f).fillY()
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Actor> Table.addCell(actor: T): Table {
|
||||||
|
add(actor)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
||||||
|
*/
|
||||||
|
fun <T> ArrayList<T>.withItem(item:T): ArrayList<T> {
|
||||||
|
val newArrayList = ArrayList(this)
|
||||||
|
newArrayList.add(item)
|
||||||
|
return newArrayList
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
||||||
|
*/
|
||||||
|
fun <T> HashSet<T>.withItem(item:T): HashSet<T> {
|
||||||
|
val newHashSet = HashSet(this)
|
||||||
|
newHashSet.add(item)
|
||||||
|
return newHashSet
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
||||||
|
*/
|
||||||
|
fun <T> ArrayList<T>.withoutItem(item:T): ArrayList<T> {
|
||||||
|
val newArrayList = ArrayList(this)
|
||||||
|
newArrayList.remove(item)
|
||||||
|
return newArrayList
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed
|
||||||
|
*/
|
||||||
|
fun <T> HashSet<T>.withoutItem(item:T): HashSet<T> {
|
||||||
|
val newHashSet = HashSet(this)
|
||||||
|
newHashSet.remove(item)
|
||||||
|
return newHashSet
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.toTextButton() = TextButton(this.tr(), CameraStageBaseScreen.skin)
|
||||||
|
|
||||||
|
/** also translates */
|
||||||
|
fun String.toLabel() = Label(this.tr(),CameraStageBaseScreen.skin)
|
||||||
|
fun Int.toLabel() = this.toString().toLabel()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// We don't want to use setFontSize and setFontColor because they set the font,
|
||||||
|
// which means we need to rebuild the font cache which means more memory allocation.
|
||||||
|
fun String.toLabel(fontColor: Color = Color.WHITE, fontSize:Int=18): Label {
|
||||||
|
var labelStyle = CameraStageBaseScreen.skin.get(Label.LabelStyle::class.java)
|
||||||
|
if(fontColor!= Color.WHITE || fontSize!=18) { // if we want the default we don't need to create another style
|
||||||
|
labelStyle = Label.LabelStyle(labelStyle) // clone this to another
|
||||||
|
labelStyle.fontColor = fontColor
|
||||||
|
if (fontSize != 18) labelStyle.font = Fonts.font
|
||||||
|
}
|
||||||
|
return Label(this.tr(), labelStyle).apply { setFontScale(fontSize/Fonts.ORIGINAL_FONT_SIZE) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Label.setFontColor(color: Color): Label { style= Label.LabelStyle(style).apply { fontColor=color }; return this }
|
||||||
|
|
||||||
|
fun Label.setFontSize(size:Int): Label {
|
||||||
|
style = Label.LabelStyle(style)
|
||||||
|
style.font = Fonts.font
|
||||||
|
style = style // because we need it to call the SetStyle function. Yuk, I know.
|
||||||
|
return this.apply { setFontScale(size/ Fonts.ORIGINAL_FONT_SIZE) } // for chaining
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun <T> List<T>.randomWeighted(weights: List<Float>, random: Random = Random): T {
|
||||||
|
if (this.isEmpty()) throw NoSuchElementException("Empty list.")
|
||||||
|
if (this.size != weights.size) throw UnsupportedOperationException("Weights size does not match this list size.")
|
||||||
|
|
||||||
|
val totalWeight = weights.sum()
|
||||||
|
val randDouble = random.nextDouble()
|
||||||
|
var sum = 0f
|
||||||
|
|
||||||
|
for (i in weights.indices) {
|
||||||
|
sum += weights[i] / totalWeight
|
||||||
|
if (randDouble <= sum)
|
||||||
|
return this[i]
|
||||||
|
}
|
||||||
|
return this.last()
|
||||||
|
}
|
74
core/src/com/unciv/ui/utils/KeyPressDispatcher.kt
Normal file
74
core/src/com/unciv/ui/utils/KeyPressDispatcher.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.unciv.ui.utils
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Input
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.InputEvent
|
||||||
|
import java.util.HashMap
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For now, combination keys cannot easily be expressed.
|
||||||
|
* Pressing Ctrl-Letter will arrive one event for Input.Keys.CONTROL_LEFT and one for the ASCII control code point
|
||||||
|
* so Ctrl-R can be handled using KeyCharAndCode('\u0012')
|
||||||
|
* Pressing Alt-Something likewise will fire once for Alt and once for the unmodified keys with no indication Alt is held
|
||||||
|
* (Exception: international keyboard AltGr-combos)
|
||||||
|
* An update supporting easy declarations for any modifier combos would need to use Gdx.input.isKeyPressed()
|
||||||
|
* Gdx seems to omit support for a modifier mask (e.g. Ctrl-Alt-Shift) so we would need to reinvent this
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a key for use in an InputListener keyTyped() handler
|
||||||
|
*
|
||||||
|
* Example: KeyCharAndCode('R'), KeyCharAndCode(Input.Keys.F1)
|
||||||
|
*/
|
||||||
|
data class KeyCharAndCode(val char: Char, val code: Int) {
|
||||||
|
// express keys with a Char value
|
||||||
|
constructor(char: Char): this(char.toLowerCase(), 0)
|
||||||
|
// express keys that only have a keyCode like F1
|
||||||
|
constructor(code: Int): this(Char.MIN_VALUE, code)
|
||||||
|
// helper for use in InputListener keyTyped()
|
||||||
|
constructor(event: InputEvent?, character: Char)
|
||||||
|
: this (
|
||||||
|
character.toLowerCase(),
|
||||||
|
if (character == Char.MIN_VALUE && event!=null) event.keyCode else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
@ExperimentalStdlibApi
|
||||||
|
override fun toString(): String {
|
||||||
|
// debug helper
|
||||||
|
return when {
|
||||||
|
char == Char.MIN_VALUE -> Input.Keys.toString(code)
|
||||||
|
char < ' ' -> "Ctrl-" + Char(char.toInt()+64)
|
||||||
|
else -> "\"$char\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyPressDispatcher: HashMap<KeyCharAndCode, (() -> Unit)>() {
|
||||||
|
private var checkpoint: Set<KeyCharAndCode> = setOf()
|
||||||
|
|
||||||
|
// access by Char
|
||||||
|
operator fun get(char: Char) = this[KeyCharAndCode(char)]
|
||||||
|
operator fun set(char: Char, action: () -> Unit) {
|
||||||
|
this[KeyCharAndCode(char)] = action
|
||||||
|
}
|
||||||
|
operator fun contains(char: Char) = this.contains(KeyCharAndCode(char))
|
||||||
|
fun remove(char: Char) = this.remove(KeyCharAndCode(char))
|
||||||
|
|
||||||
|
// access by Int keyCodes
|
||||||
|
operator fun get(code: Int) = this[KeyCharAndCode(code)]
|
||||||
|
operator fun set(code: Int, action: () -> Unit) {
|
||||||
|
this[KeyCharAndCode(code)] = action
|
||||||
|
}
|
||||||
|
operator fun contains(code: Int) = this.contains(KeyCharAndCode(code))
|
||||||
|
fun remove(code: Int) = this.remove(KeyCharAndCode(code))
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
checkpoint = setOf()
|
||||||
|
super.clear()
|
||||||
|
}
|
||||||
|
fun setCheckpoint() {
|
||||||
|
checkpoint = keys.toSet()
|
||||||
|
}
|
||||||
|
fun revertToCheckPoint() {
|
||||||
|
keys.minus(checkpoint).forEach { remove(it) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user