mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-11 00:08:58 +07:00
UncivSlider fixes (#6222)
* Fix UncivSlider isDisabled and snapToValues with the +/- buttons * Apply UncivSlider to convertGoldToScience * Apply percent tooltip formatting to sound volume sliders * Linting and guard against a border case
This commit is contained in:
@ -1,9 +1,9 @@
|
|||||||
package com.unciv.ui.overviewscreen
|
package com.unciv.ui.overviewscreen
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Slider
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.models.ruleset.ModOptionsConstants
|
import com.unciv.models.ruleset.ModOptionsConstants
|
||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
@ -60,15 +60,18 @@ class StatsOverviewTable (
|
|||||||
goldTable.addSeparator()
|
goldTable.addSeparator()
|
||||||
val sliderTable = Table()
|
val sliderTable = Table()
|
||||||
sliderTable.add("Convert gold to science".toLabel()).row()
|
sliderTable.add("Convert gold to science".toLabel()).row()
|
||||||
val slider = Slider(0f, 1f, 0.1f, false, BaseScreen.skin)
|
|
||||||
slider.value = viewingPlayer.tech.goldPercentConvertedToScience
|
|
||||||
|
|
||||||
slider.onChange {
|
val slider = UncivSlider(0f, 1f, 0.1f,
|
||||||
viewingPlayer.tech.goldPercentConvertedToScience = slider.value
|
initial = viewingPlayer.tech.goldPercentConvertedToScience,
|
||||||
viewingPlayer.cities.forEach { it.cityStats.update() }
|
getTipText = UncivSlider::formatPercent
|
||||||
|
) {
|
||||||
|
viewingPlayer.tech.goldPercentConvertedToScience = it
|
||||||
|
for (city in viewingPlayer.cities) { city.cityStats.update() }
|
||||||
overviewScreen.setCategoryActions["Stats"]!!() // ? will probably steal focus and so prevent dragging the slider
|
overviewScreen.setCategoryActions["Stats"]!!() // ? will probably steal focus and so prevent dragging the slider
|
||||||
}
|
}
|
||||||
sliderTable.add(slider)
|
slider.isDisabled = !UncivGame.Current.worldScreen.canChangeState
|
||||||
|
|
||||||
|
sliderTable.add(slider).padTop(15f)
|
||||||
goldTable.add(sliderTable).colspan(2)
|
goldTable.add(sliderTable).colspan(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.unciv.ui.utils
|
package com.unciv.ui.utils
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx
|
||||||
|
import com.badlogic.gdx.Input
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.math.Interpolation
|
import com.badlogic.gdx.math.Interpolation
|
||||||
import com.badlogic.gdx.math.Vector2
|
import com.badlogic.gdx.math.Vector2
|
||||||
@ -11,6 +13,8 @@ import com.badlogic.gdx.utils.Align
|
|||||||
import com.badlogic.gdx.utils.Timer
|
import com.badlogic.gdx.utils.Timer
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.models.UncivSound
|
import com.unciv.models.UncivSound
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.sign
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modified Gdx [Slider]
|
* Modified Gdx [Slider]
|
||||||
@ -40,8 +44,12 @@ class UncivSlider (
|
|||||||
private val getTipText: ((Float) -> String)? = null,
|
private val getTipText: ((Float) -> String)? = null,
|
||||||
onChange: ((Float) -> Unit)? = null
|
onChange: ((Float) -> Unit)? = null
|
||||||
): Table(BaseScreen.skin) {
|
): Table(BaseScreen.skin) {
|
||||||
// constants for geometry tuning
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/** Can be passed directly to the [getTipText] constructor parameter */
|
||||||
|
fun formatPercent(value: Float): String {
|
||||||
|
return (value * 100f + 0.5f).toInt().toString() + "%"
|
||||||
|
}
|
||||||
|
// constants for geometry tuning
|
||||||
const val plusMinusFontSize = Constants.defaultFontSize
|
const val plusMinusFontSize = Constants.defaultFontSize
|
||||||
const val plusMinusCircleSize = 20f
|
const val plusMinusCircleSize = 20f
|
||||||
const val padding = 5f // padding around the Slider, doubled between it and +/- buttons
|
const val padding = 5f // padding around the Slider, doubled between it and +/- buttons
|
||||||
@ -57,9 +65,14 @@ class UncivSlider (
|
|||||||
private val tipContainer: Container<Label> = Container(tipLabel)
|
private val tipContainer: Container<Label> = Container(tipLabel)
|
||||||
private val tipHideTask: Timer.Task
|
private val tipHideTask: Timer.Task
|
||||||
|
|
||||||
|
// copies of maliciously protected Slider members
|
||||||
|
private var snapToValues: FloatArray? = null
|
||||||
|
private var snapThreshold: Float = 0f
|
||||||
|
|
||||||
// Compatibility with default Slider
|
// Compatibility with default Slider
|
||||||
val minValue: Float
|
val minValue: Float
|
||||||
get() = slider.minValue
|
get() = slider.minValue
|
||||||
|
@Suppress("unused") // Part of the Slider API
|
||||||
val maxValue: Float
|
val maxValue: Float
|
||||||
get() = slider.maxValue
|
get() = slider.maxValue
|
||||||
var value: Float
|
var value: Float
|
||||||
@ -75,24 +88,32 @@ class UncivSlider (
|
|||||||
stepChanged()
|
stepChanged()
|
||||||
}
|
}
|
||||||
/** Returns true if the slider is being dragged. */
|
/** Returns true if the slider is being dragged. */
|
||||||
|
@Suppress("unused") // Part of the Slider API
|
||||||
val isDragging: Boolean
|
val isDragging: Boolean
|
||||||
get() = slider.isDragging
|
get() = slider.isDragging
|
||||||
|
/** Disables the slider - visually (if the skin supports it) and blocks interaction */
|
||||||
var isDisabled: Boolean
|
var isDisabled: Boolean
|
||||||
get() = slider.isDisabled
|
get() = slider.isDisabled
|
||||||
set(value) { slider.isDisabled = value }
|
set(value) {
|
||||||
/** Sets the range of this progress bar. The progress bar's current value is clamped to the range. */
|
slider.isDisabled = value
|
||||||
|
setPlusMinusEnabled()
|
||||||
|
}
|
||||||
|
/** Sets the range of this slider. The slider's current value is clamped to the range. */
|
||||||
fun setRange(min: Float, max: Float) {
|
fun setRange(min: Float, max: Float) {
|
||||||
slider.setRange(min, max)
|
slider.setRange(min, max)
|
||||||
setPlusMinusEnabled()
|
setPlusMinusEnabled()
|
||||||
}
|
}
|
||||||
/** Will make this progress bar snap to the specified values, if the knob is within the threshold. */
|
/** Will make this slider snap to the specified values, if the knob is within the threshold. */
|
||||||
fun setSnapToValues(values: FloatArray?, threshold: Float) {
|
fun setSnapToValues(values: FloatArray?, threshold: Float) {
|
||||||
|
snapToValues = values // make a copy so our plus/minus code can snap
|
||||||
|
snapThreshold = threshold
|
||||||
slider.setSnapToValues(values, threshold)
|
slider.setSnapToValues(values, threshold)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value tip format
|
// java format string for the value tip, set by changing stepSize
|
||||||
var tipFormat = "%.1f"
|
private var tipFormat = "%.1f"
|
||||||
|
|
||||||
|
/** Prevents hiding the value tooltip over the slider knob */
|
||||||
var permanentTip = false
|
var permanentTip = false
|
||||||
|
|
||||||
// Detect changes in isDragging
|
// Detect changes in isDragging
|
||||||
@ -114,7 +135,7 @@ class UncivSlider (
|
|||||||
.apply { setAlignment(Align.center) }
|
.apply { setAlignment(Align.center) }
|
||||||
.surroundWithCircle(plusMinusCircleSize)
|
.surroundWithCircle(plusMinusCircleSize)
|
||||||
minusButton.onClick {
|
minusButton.onClick {
|
||||||
value -= stepSize
|
addToValue(-stepSize)
|
||||||
}
|
}
|
||||||
add(minusButton).apply {
|
add(minusButton).apply {
|
||||||
if (vertical) padBottom(padding) else padLeft(padding)
|
if (vertical) padBottom(padding) else padLeft(padding)
|
||||||
@ -130,7 +151,7 @@ class UncivSlider (
|
|||||||
.apply { setAlignment(Align.center) }
|
.apply { setAlignment(Align.center) }
|
||||||
.surroundWithCircle(plusMinusCircleSize)
|
.surroundWithCircle(plusMinusCircleSize)
|
||||||
plusButton.onClick {
|
plusButton.onClick {
|
||||||
value += stepSize
|
addToValue(stepSize)
|
||||||
}
|
}
|
||||||
add(plusButton).apply {
|
add(plusButton).apply {
|
||||||
if (vertical) padTop(padding) else padRight(padding)
|
if (vertical) padTop(padding) else padRight(padding)
|
||||||
@ -157,6 +178,34 @@ class UncivSlider (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for plus/minus button onClick, non-trivial only if setSnapToValues is used
|
||||||
|
private fun addToValue(delta: Float) {
|
||||||
|
// un-snapping with Shift is taken from Slider source, and the loop mostly as well
|
||||||
|
// with snap active, plus/minus buttons will go to the next snap position regardless of stepSize
|
||||||
|
// this could be shorter if Slider.snap(), Slider.snapValues and Slider.threshold weren't protected
|
||||||
|
if (snapToValues?.isEmpty() != false ||
|
||||||
|
Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) ||
|
||||||
|
Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT)
|
||||||
|
) {
|
||||||
|
value += delta
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var bestDiff = -1f
|
||||||
|
var bestIndex = -1
|
||||||
|
for ((i, snapValue) in snapToValues!!.withIndex()) {
|
||||||
|
val diff = abs(value - snapValue)
|
||||||
|
if (diff <= snapThreshold) {
|
||||||
|
if (bestIndex == -1 || diff < bestDiff) {
|
||||||
|
bestDiff = diff
|
||||||
|
bestIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bestIndex += delta.sign.toInt()
|
||||||
|
if (bestIndex !in snapToValues!!.indices) return
|
||||||
|
value = snapToValues!![bestIndex]
|
||||||
|
}
|
||||||
|
|
||||||
// Visual feedback
|
// Visual feedback
|
||||||
private fun valueChanged() {
|
private fun valueChanged() {
|
||||||
if (getTipText == null)
|
if (getTipText == null)
|
||||||
@ -172,10 +221,10 @@ class UncivSlider (
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setPlusMinusEnabled() {
|
private fun setPlusMinusEnabled() {
|
||||||
val enableMinus = slider.value > slider.minValue
|
val enableMinus = slider.value > slider.minValue && !isDisabled
|
||||||
minusButton?.touchable = if(enableMinus) Touchable.enabled else Touchable.disabled
|
minusButton?.touchable = if(enableMinus) Touchable.enabled else Touchable.disabled
|
||||||
minusButton?.apply {circle.color.a = if(enableMinus) 1f else 0.5f}
|
minusButton?.apply {circle.color.a = if(enableMinus) 1f else 0.5f}
|
||||||
val enablePlus = slider.value < slider.maxValue
|
val enablePlus = slider.value < slider.maxValue && !isDisabled
|
||||||
plusButton?.touchable = if(enablePlus) Touchable.enabled else Touchable.disabled
|
plusButton?.touchable = if(enablePlus) Touchable.enabled else Touchable.disabled
|
||||||
plusButton?.apply {circle.color.a = if(enablePlus) 1f else 0.5f}
|
plusButton?.apply {circle.color.a = if(enablePlus) 1f else 0.5f}
|
||||||
}
|
}
|
||||||
@ -187,6 +236,7 @@ class UncivSlider (
|
|||||||
stepSize > 0.0099f -> "%.2f"
|
stepSize > 0.0099f -> "%.2f"
|
||||||
else -> "%.3f"
|
else -> "%.3f"
|
||||||
}
|
}
|
||||||
|
if (getTipText == null)
|
||||||
tipLabel.setText(tipFormat.format(slider.value))
|
tipLabel.setText(tipFormat.format(slider.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,7 +578,8 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
add("Sound effects volume".tr()).left().fillX()
|
add("Sound effects volume".tr()).left().fillX()
|
||||||
|
|
||||||
val soundEffectsVolumeSlider = UncivSlider(0f, 1.0f, 0.05f,
|
val soundEffectsVolumeSlider = UncivSlider(0f, 1.0f, 0.05f,
|
||||||
initial = settings.soundEffectsVolume
|
initial = settings.soundEffectsVolume,
|
||||||
|
getTipText = UncivSlider::formatPercent
|
||||||
) {
|
) {
|
||||||
settings.soundEffectsVolume = it
|
settings.soundEffectsVolume = it
|
||||||
settings.save()
|
settings.save()
|
||||||
@ -591,7 +592,8 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
|
|
||||||
val musicVolumeSlider = UncivSlider(0f, 1.0f, 0.05f,
|
val musicVolumeSlider = UncivSlider(0f, 1.0f, 0.05f,
|
||||||
initial = settings.musicVolume,
|
initial = settings.musicVolume,
|
||||||
sound = UncivSound.Silent
|
sound = UncivSound.Silent,
|
||||||
|
getTipText = UncivSlider::formatPercent
|
||||||
) {
|
) {
|
||||||
settings.musicVolume = it
|
settings.musicVolume = it
|
||||||
settings.save()
|
settings.save()
|
||||||
|
Reference in New Issue
Block a user