Update ui & Dynamic factor slider - Settings

This commit is contained in:
Hasinjato 2026-10-07 15:32:32 +03:00
parent b5fdd7c30b
commit 83a26110e6
2 changed files with 623 additions and 571 deletions

View file

@ -5,14 +5,14 @@ import mg.dot.feufaro.provideSettings
import java.util.prefs.Preferences import java.util.prefs.Preferences
enum class Dynamic(val velocity: Int, val label: String, val defaultFactor: Float) { enum class Dynamic(val velocity: Int, val label: String, val defaultFactor: Float) {
PPP(16, "ppp", 0.55f), PPP(16, "ppp", 0.80f),
PP (33, "pp", 0.66f), PP (32, "pp", 0.85f),
P (49, "p", 0.77f), P (48, "p", 0.90f),
MP (64, "mp", 0.88f), MP (64, "mp", 0.95f),
MF (80, "mf", 1.00f), MF (80, "mf", 1.00f),
F (96, "f", 1.15f), F (96, "f", 1.05f),
FF (112, "ff", 1.35f), FF (112, "ff", 1.10f),
FFF(127, "fff", 1.60f); FFF(127, "fff", 1.15f);
private val prefs: Settings = provideSettings() private val prefs: Settings = provideSettings()
var savedFactor: Float = prefs.getFloat("dynamic_${name.lowercase()}", defaultFactor) var savedFactor: Float = prefs.getFloat("dynamic_${name.lowercase()}", defaultFactor)
set(value) { set(value) {

View file

@ -95,7 +95,7 @@ fun Settings(
?: MidiInstrument(program = globalInstrumentProgram, name = "Instrument $globalInstrumentProgram") ?: MidiInstrument(program = globalInstrumentProgram, name = "Instrument $globalInstrumentProgram")
val menuScrollState = rememberScrollState() val menuScrollState = rememberScrollState()
var expandedDropdown by remember { mutableStateOf(false) } var expandedDropdown by remember { mutableStateOf(true) }
LaunchedEffect(expandedDropdown, globalInstrumentProgram) { LaunchedEffect(expandedDropdown, globalInstrumentProgram) {
if (expandedDropdown) { if (expandedDropdown) {
@ -128,10 +128,25 @@ fun Settings(
) )
}, },
text = { text = {
val settingsScrollState = rememberScrollState()
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
MyVerticalScrollbar(
scrollState = settingsScrollState,
modifier = Modifier
.align(Alignment.CenterEnd)
.fillMaxHeight()
.width(8.dp)
) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.verticalScroll(rememberScrollState()), .padding(end = 11.dp)
.verticalScroll(settingsScrollState),
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
@ -233,7 +248,12 @@ fun Settings(
}, },
modifier = Modifier.size(32.dp).background(MaterialTheme.colorScheme.primary, CircleShape) modifier = Modifier.size(32.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
) { ) {
Text("-", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.titleLarge, color = Color.White) Text(
"-",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.titleLarge,
color = Color.White
)
} }
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
@ -255,9 +275,15 @@ fun Settings(
displayConfigManager.updateFontSize(currentDisplayConfig.fontSize + 0.5f) displayConfigManager.updateFontSize(currentDisplayConfig.fontSize + 0.5f)
} }
}, },
modifier = Modifier.size(32.dp).background(MaterialTheme.colorScheme.primary, CircleShape) modifier = Modifier.size(32.dp)
.background(MaterialTheme.colorScheme.primary, CircleShape)
) { ) {
Text("+", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.titleLarge, color = Color.White) Text(
"+",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.titleLarge,
color = Color.White
)
} }
} }
} }
@ -278,9 +304,21 @@ fun Settings(
) )
val colorPickers = listOf( val colorPickers = listOf(
Triple("Couleur Primaire", "primaryBlueHex", currentDisplayConfig.primaryBlueHex), Triple(
Triple("Couleur Secondaire", "accentGreenHex", currentDisplayConfig.accentGreenHex), "Couleur Primaire",
Triple("Couleur Tertiaire", "accentYellowHex", currentDisplayConfig.accentYellowHex), "primaryBlueHex",
currentDisplayConfig.primaryBlueHex
),
Triple(
"Couleur Secondaire",
"accentGreenHex",
currentDisplayConfig.accentGreenHex
),
Triple(
"Couleur Tertiaire",
"accentYellowHex",
currentDisplayConfig.accentYellowHex
),
Triple("Autre couleur", "accentPinkHex", currentDisplayConfig.accentPinkHex) Triple("Autre couleur", "accentPinkHex", currentDisplayConfig.accentPinkHex)
) )
@ -311,7 +349,10 @@ fun Settings(
FlowRow( FlowRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f), RoundedCornerShape(8.dp)) .background(
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f),
RoundedCornerShape(8.dp)
)
.padding(12.dp), .padding(12.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp), horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp) verticalArrangement = Arrangement.spacedBy(10.dp)
@ -328,7 +369,10 @@ fun Settings(
shape = CircleShape shape = CircleShape
) )
.clickable { .clickable {
displayConfigManager.updateCustomColor(configKey, colorHex) displayConfigManager.updateCustomColor(
configKey,
colorHex
)
} }
) )
} }
@ -359,7 +403,8 @@ fun Settings(
openExclusive("Audio") openExclusive("Audio")
} }
) { ) {
Column(modifier = Modifier.padding(top = 8.dp), Column(
modifier = Modifier.padding(top = 8.dp),
verticalArrangement = Arrangement.spacedBy(2.dp) verticalArrangement = Arrangement.spacedBy(2.dp)
) { ) {
Text( Text(
@ -422,9 +467,10 @@ fun Settings(
key(refreshTrigger) { key(refreshTrigger) {
val dynamicEntries = Dynamic.entries val dynamicEntries = Dynamic.entries
val minF = 0.22f val minF = 0.20f
val maxF = 2.33f val maxF = 1.59f
val step = 0.05f val gapStep = 0.01f
val mfIndex = dynamicEntries.indexOfFirst { it == Dynamic.MF }.let { if (it == -1) 4 else it }
Box( Box(
modifier = Modifier modifier = Modifier
@ -449,17 +495,16 @@ fun Settings(
FilledIconButton( FilledIconButton(
onClick = { onClick = {
if (isDynamicEnabled) { if (isDynamicEnabled) {
val delta = - step val deltaGap = gapStep
var insideBounds = true val projected = dynamicEntries.mapIndexed { i, item ->
dynamicEntries.forEach { item -> item.savedFactor + ((i - mfIndex) * deltaGap)
val projectedValue = item.savedFactor + delta
if (projectedValue < minF || projectedValue > maxF) {
insideBounds = false
} }
} val insideBounds = projected.all { it in minF..maxF }
if (insideBounds) { val maintainsOrder = projected.zipWithNext().all { (a, b) -> a < b }
dynamicEntries.forEach { item ->
item.savedFactor = (item.savedFactor + delta).coerceIn(minF, maxF) if (insideBounds && maintainsOrder) {
dynamicEntries.forEachIndexed { i, item ->
item.savedFactor = projected[i].coerceIn(minF, maxF)
} }
refreshTrigger++ refreshTrigger++
} }
@ -477,6 +522,7 @@ fun Settings(
} else { } else {
Spacer(modifier = Modifier.width(32.dp)) Spacer(modifier = Modifier.width(32.dp))
} }
Text( Text(
text = dynamicItem.label.lowercase(), text = dynamicItem.label.lowercase(),
style = MaterialTheme.typography.displaySmall.copy( style = MaterialTheme.typography.displaySmall.copy(
@ -494,22 +540,23 @@ fun Settings(
if (isDynamicEnabled) { if (isDynamicEnabled) {
val oldValue = dynamicItem.savedFactor val oldValue = dynamicItem.savedFactor
val delta = newValue - oldValue val delta = newValue - oldValue
var insideBounds = true
dynamicEntries.forEach { item -> if (delta != 0f) {
val projectedValue = item.savedFactor + delta val deltaGap = delta
if (projectedValue < minF || projectedValue > maxF) { val projected = dynamicEntries.mapIndexed { i, item ->
insideBounds = false item.savedFactor + ((i - mfIndex) * deltaGap)
}
} }
val insideBounds = projected.all { it in minF..maxF }
val maintainsOrder = projected.zipWithNext().all { (a, b) -> a < b }
if (insideBounds) { if (insideBounds && maintainsOrder) {
dynamicEntries.forEach { item -> dynamicEntries.forEachIndexed { i, item ->
item.savedFactor = (item.savedFactor + delta).coerceIn(minF, maxF) item.savedFactor = projected[i].coerceIn(minF, maxF)
} }
refreshTrigger++ refreshTrigger++
} }
} }
}
}, },
valueRange = minF..maxF, valueRange = minF..maxF,
enabled = isDynamicEnabled, enabled = isDynamicEnabled,
@ -527,7 +574,9 @@ fun Settings(
thumb = { thumb = {
SliderDefaults.Thumb( SliderDefaults.Thumb(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
colors = SliderDefaults.colors(thumbColor = if(isDynamicEnabled) Color(0xFFFF4081) else Color.LightGray), colors = SliderDefaults.colors(
thumbColor = if (isDynamicEnabled) Color(0xFFFF4081) else Color.LightGray
),
modifier = Modifier.size(12.dp), modifier = Modifier.size(12.dp),
) )
}, },
@ -542,7 +591,10 @@ fun Settings(
) )
Text( Text(
text = String.format("%.2f", if (isDynamicEnabled) dynamicItem.savedFactor else 1.00f), text = String.format(
"%.2f",
if (isDynamicEnabled) dynamicItem.savedFactor else 1.00f
),
fontSize = 11.sp, fontSize = 11.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = if (isDynamicEnabled) Color(0xFFDEFF9A) else Color.DarkGray, color = if (isDynamicEnabled) Color(0xFFDEFF9A) else Color.DarkGray,
@ -555,18 +607,16 @@ fun Settings(
FilledIconButton( FilledIconButton(
onClick = { onClick = {
if (isDynamicEnabled) { if (isDynamicEnabled) {
val delta = step val deltaGap = -gapStep
var insideBounds = true val projected = dynamicEntries.mapIndexed { i, item ->
dynamicEntries.forEach { item -> item.savedFactor + ((i - mfIndex) * deltaGap)
val projectedValue = item.savedFactor + delta
if (projectedValue < minF || projectedValue > maxF) {
insideBounds = false
} }
} val insideBounds = projected.all { it in minF..maxF }
if (insideBounds) { val maintainsOrder = projected.zipWithNext().all { (a, b) -> a < b }
dynamicEntries.forEach { item ->
item.savedFactor = if (insideBounds && maintainsOrder) {
(item.savedFactor + delta).coerceIn(minF, maxF) dynamicEntries.forEachIndexed { i, item ->
item.savedFactor = projected[i].coerceIn(minF, maxF)
} }
refreshTrigger++ refreshTrigger++
} }
@ -756,6 +806,8 @@ fun Settings(
} }
} }
} }
}
}
}, },
confirmButton = { confirmButton = {
TextButton(onClick = onDismissRequest) { TextButton(onClick = onDismissRequest) {