From 2f0be71dd83097a859e7e669925f943d2b816a22 Mon Sep 17 00:00:00 2001 From: "hasinarak3@gmail.com" Date: Wed, 4 Feb 2026 16:02:32 +0300 Subject: [PATCH] update ui tempo settings --- .../androidMain/res/drawable/ic_metronome.xml | 2344 +++++++++++++++++ .../res/drawable/ic_mixer_satb.xml | 33 + .../src/androidMain/res/drawable/ic_organ.xml | 9 + .../drawable/ic_metronome.xml | 2344 +++++++++++++++++ .../drawable/ic_mixer_satb.xml | 33 + .../composeResources/drawable/ic_organ.xml | 9 + .../mg/dot/feufaro/ui/MidiControlPanel.kt | 623 +++-- .../mg/dot/feufaro/ui/ModernVolumeSlider.kt | 78 +- 8 files changed, 5157 insertions(+), 316 deletions(-) create mode 100644 composeApp/src/androidMain/res/drawable/ic_metronome.xml create mode 100644 composeApp/src/androidMain/res/drawable/ic_mixer_satb.xml create mode 100644 composeApp/src/androidMain/res/drawable/ic_organ.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/ic_metronome.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/ic_mixer_satb.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/ic_organ.xml diff --git a/composeApp/src/androidMain/res/drawable/ic_metronome.xml b/composeApp/src/androidMain/res/drawable/ic_metronome.xml new file mode 100644 index 0000000..faabc57 --- /dev/null +++ b/composeApp/src/androidMain/res/drawable/ic_metronome.xml @@ -0,0 +1,2344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composeApp/src/androidMain/res/drawable/ic_mixer_satb.xml b/composeApp/src/androidMain/res/drawable/ic_mixer_satb.xml new file mode 100644 index 0000000..08c0ed0 --- /dev/null +++ b/composeApp/src/androidMain/res/drawable/ic_mixer_satb.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/drawable/ic_organ.xml b/composeApp/src/androidMain/res/drawable/ic_organ.xml new file mode 100644 index 0000000..107f81d --- /dev/null +++ b/composeApp/src/androidMain/res/drawable/ic_organ.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_metronome.xml b/composeApp/src/commonMain/composeResources/drawable/ic_metronome.xml new file mode 100644 index 0000000..faabc57 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_metronome.xml @@ -0,0 +1,2344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_mixer_satb.xml b/composeApp/src/commonMain/composeResources/drawable/ic_mixer_satb.xml new file mode 100644 index 0000000..08c0ed0 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_mixer_satb.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_organ.xml b/composeApp/src/commonMain/composeResources/drawable/ic_organ.xml new file mode 100644 index 0000000..107f81d --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_organ.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt index cb1a6a2..d66a21a 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt @@ -1,84 +1,22 @@ - package mg.dot.feufaro.ui +package mg.dot.feufaro.ui -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.scaleIn -import androidx.compose.animation.scaleOut -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically -import androidx.compose.foundation.BorderStroke +import androidx.compose.animation.* import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AvTimer -import androidx.compose.material.icons.filled.Church -import androidx.compose.material.icons.filled.Clear -import androidx.compose.material.icons.filled.ClearAll -import androidx.compose.material.icons.filled.Keyboard -import androidx.compose.material.icons.filled.Loop -import androidx.compose.material.icons.filled.MusicNote -import androidx.compose.material.icons.filled.Pause -import androidx.compose.material.icons.filled.Piano -import androidx.compose.material.icons.filled.PianoOff -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Refresh -import androidx.compose.material.icons.filled.SettingsVoice -import androidx.compose.material.icons.filled.Star -import androidx.compose.material.icons.filled.Tonality -import androidx.compose.material.icons.filled.Tune -import androidx.compose.material.icons.automirrored.filled.VolumeUp -import androidx.compose.material.icons.automirrored.filled.VolumeOff -import androidx.compose.material.icons.filled.MoreHoriz -import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilledIconToggleButton -import androidx.compose.material3.FilterChip -import androidx.compose.material3.FilterChipDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Slider -import androidx.compose.material3.SliderDefaults -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.produceState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import feufaro.composeapp.generated.resources.Res @@ -88,8 +26,6 @@ import kotlinx.coroutines.delay import mg.dot.feufaro.getPlatform import mg.dot.feufaro.midi.MediaPlayer import org.jetbrains.compose.resources.painterResource -import org.koin.core.component.getScopeId -import javax.swing.Icon @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -107,240 +43,371 @@ fun MidiControlPanel( val momo = duration.toInt() - currentPos.toInt() val loopState by produceState(Triple(-1L, -1L, false), mediaPlayer) { - while(true) { + while (true) { value = mediaPlayer.getLoopState() delay(200) } } val voiceStates = mediaPlayer.getVoiceStates() - val labels = listOf("S","A","T","B") - val fullLabels = listOf("Soprano","Alto","Ténor","Basse") + val labels = listOf("S", "A", "T", "B") + listOf("Soprano", "Alto", "Ténor", "Basse") var tempo by remember { mutableStateOf(1.0f) } var currentBpm by remember { mutableStateOf(mediaPlayer.getCurrentBPM()) } val basseBpm = 120f - var bpmInput by remember { mutableStateOf((basseBpm * tempo).toInt().toString()) } var isPianoSelected by remember { mutableStateOf(true) } var showSATBTools by remember { mutableStateOf(false) } - var showVolumeTools by remember { mutableStateOf(false) } var expandedCtl by remember { mutableStateOf(false) } LaunchedEffect(tempo) { currentBpm = mediaPlayer.getCurrentBPM() } - Column ( - modifier = modifier - .fillMaxWidth() - .padding(16.dp) - .background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)), - horizontalAlignment = Alignment.CenterHorizontally - ) { - AnimatedVisibility( - visible = !expandedCtl - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - Text("${currentPos.toInt() / 1000}s", color = Color.White) - Slider( - value = currentPos, - onValueChange = onSeek, - valueRange = 0f..(if (duration > 0) duration else 1f), - modifier = Modifier.weight(1f), - colors = SliderDefaults.colors( - thumbColor = Color.Red, - activeTrackColor = Color.Green, - inactiveTrackColor = Color.Gray, - inactiveTickColor = Color(0xffb0BEC5), - disabledThumbColor = Color(0xff78909C), - disabledActiveTickColor = Color(0xff757575), - disabledActiveTrackColor = Color(0xffBDBDBD), - disabledInactiveTickColor = Color(0xff616161), - disabledInactiveTrackColor = Color(0xffBCAAA4), - ), - thumb = { - Box( - modifier = Modifier - .size(15.dp) - .background(Color.Gray, CircleShape) - ) - }, - track = { sliderState -> - SliderDefaults.Track( - sliderState = sliderState, - modifier = Modifier.height(5.dp) - ) - } - ) - Text("${momo / 1000}s", color = Color.White) - } - } + fun updateTempoByBpmStep(step: Int) { + val currentBpm = (basseBpm * tempo).toInt() + val newBpm = (currentBpm + step).coerceIn((basseBpm * 0.25f).toInt(), (basseBpm * 1.5f).toInt()) + val newFactor = newBpm.toFloat() / basseBpm - AnimatedVisibility( - visible = showSATBTools, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + tempo = newFactor + mediaPlayer?.setTempo(newFactor) + } + + fun updateTempoToBpm(targetBpm: Int) { + val clampedBpm = targetBpm.coerceIn((basseBpm * 0.25f).toInt(), (basseBpm * 1.5f).toInt()) + val newFactor = clampedBpm.toFloat() / basseBpm + + tempo = newFactor + mediaPlayer?.setTempo(newFactor) + println("tempo : $tempo") + } + Column( + modifier = Modifier.fillMaxWidth() + ) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.Center ) { - Row { - Column ( - horizontalAlignment = Alignment.CenterHorizontally + Column( + modifier = modifier + .fillMaxWidth(if (getPlatform().name.startsWith("Android")) 0.9f else 0.6f) + .padding(16.dp) + .background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AnimatedVisibility( + visible = !expandedCtl ) { Row( - modifier = Modifier.padding(vertical = 8.dp), - horizontalArrangement = Arrangement. spacedBy(4.dp) + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - labels.forEachIndexed { index, label -> - FilterChip( - selected = voiceStates[index], - onClick = { mediaPlayer.toggleVoice(index) }, - label = { Text(label) }, - colors = FilterChipDefaults.filterChipColors( - selectedContainerColor = MaterialTheme.colorScheme.primaryContainer, - selectedLabelColor = MaterialTheme.colorScheme.primary + Text("${currentPos.toInt() / 1000}s", color = Color.White) + Slider( + value = currentPos, + onValueChange = onSeek, + valueRange = 0f..(if (duration > 0) duration else 1f), + modifier = Modifier.weight(1f), + colors = SliderDefaults.colors( + thumbColor = Color.Red, + activeTrackColor = Color.Green, + inactiveTrackColor = Color.Gray, + inactiveTickColor = Color(0xffb0BEC5), + disabledThumbColor = Color(0xff78909C), + disabledActiveTickColor = Color(0xff757575), + disabledActiveTrackColor = Color(0xffBDBDBD), + disabledInactiveTickColor = Color(0xff616161), + disabledInactiveTrackColor = Color(0xffBCAAA4), + ), + thumb = { + Box( + modifier = Modifier + .size(15.dp) + .background(Color.Gray, CircleShape) ) + }, + track = { sliderState -> + SliderDefaults.Track( + sliderState = sliderState, + modifier = Modifier.height(5.dp) + ) + } + ) + Text("${momo / 1000}s", color = Color.White) + } + } + + AnimatedVisibility( + visible = showSATBTools, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + Row { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Row( + modifier = Modifier.padding(vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + labels.forEachIndexed { index, label -> + FilterChip( + selected = voiceStates[index], + onClick = { mediaPlayer.toggleVoice(index) }, + label = { Text(label) }, + colors = FilterChipDefaults.filterChipColors( + selectedContainerColor = MaterialTheme.colorScheme.primaryContainer, + selectedLabelColor = MaterialTheme.colorScheme.primary + ) + ) + } + } + } + } + } + AnimatedVisibility( + visible = !expandedCtl, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + Row { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Row( + modifier = Modifier + .height(45.dp) + .clip(RoundedCornerShape(12.dp)) + .background(Color(0xFF2C3135)) + .padding(vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (tempo >= 0.3) { // limite 40BPM + IconButton( + modifier = Modifier.background(Color(0XFF2C3130)), + onClick = { updateTempoByBpmStep(-10) }) { + Icon( + Icons.AutoMirrored.Filled.KeyboardArrowLeft, + "gauche", + tint = Color.White.copy(0.7f), + modifier = Modifier.size(22.dp) + ) + } + } + + val currentBpmInt = (basseBpm * tempo).toInt() + + Row( + modifier = Modifier.padding(horizontal = 2.dp), + verticalAlignment = Alignment.CenterVertically + ) { + val values = listOf(currentBpmInt - 1, currentBpmInt, currentBpmInt + 1) + + values.forEach { bpmValue -> + AnimatedContent( + targetState = bpmValue, + transitionSpec = { + if (targetState > initialState) { + slideInHorizontally { it } + fadeIn() togetherWith slideOutHorizontally { -it } + fadeOut() + } else { + slideInHorizontally { -it } + fadeIn() togetherWith slideOutHorizontally { it } + fadeOut() + } + }, + modifier = Modifier.padding(horizontal = 4.dp) + ) { displayedBpm -> + val isCenter = displayedBpm == currentBpmInt + Text( + text = "$displayedBpm", + color = if (isCenter) Color.White else Color.Gray.copy(alpha = 0.4f), + fontSize = if (isCenter) 22.sp else 14.sp, + fontWeight = if (isCenter) FontWeight.Bold else FontWeight.Normal, + modifier = Modifier + .clip(CircleShape) + .clickable(enabled = !isCenter) { updateTempoToBpm(displayedBpm) } + .padding(horizontal = 4.dp) + ) + } + } + } + + if (tempo <= 1.3) { // limite 156BPM + IconButton( + modifier = Modifier.background(Color(0XFF2C3130)), + onClick = { updateTempoByBpmStep(10) }) { + Icon( + Icons.AutoMirrored.Filled.KeyboardArrowRight, + "droite", + tint = Color.White.copy(0.7f), + modifier = Modifier.size(22.dp) + ) + } + } + Text( + text = "bpm", + color = Color.White.copy(0.6f), + fontSize = 12.sp, + modifier = Modifier.padding(end = 8.dp) + ) + } + } + } + } + + if (!expandedCtl) { + Spacer(modifier = Modifier.height(10.dp)) + } + Row( + modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Column { + val isAtStart = loopState.first == -1L + val isWaitingForB = loopState.first != -1L && !loopState.third + val isLooping = loopState.third + + OutlinedButton( + onClick = { + when { + isAtStart -> mediaPlayer.setPointA() + isWaitingForB -> mediaPlayer.setPointB() + isLooping -> { + mediaPlayer.clearLoop() + } + } + }, + colors = ButtonDefaults.buttonColors( + containerColor = when { + (isWaitingForB || isLooping) -> Color.Red + else -> Color.LightGray + } + ), + contentPadding = PaddingValues(horizontal = 2.dp, vertical = 0.dp), + shape = RoundedCornerShape(5.dp), + modifier = Modifier + .height(34.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = "A", + color = if (isWaitingForB || isLooping) Color.White else Color.Black, + fontSize = 16.sp + ) + + Icon( + imageVector = Icons.Default.Loop, + contentDescription = null, + tint = if (isLooping) Color.White else Color.Black, + modifier = Modifier.size(16.dp) + ) + + Text( + text = "B", + color = if (isLooping) Color.White else Color.Black, + fontSize = 16.sp + ) + } + } + } + Spacer(modifier = Modifier.weight(1f)) + + Column { + IconButton( + onClick = { + showSATBTools = !showSATBTools + }, modifier = Modifier + .size(40.dp) + .clip(CircleShape) + .background(if (showSATBTools) MaterialTheme.colorScheme.primary else Color.Transparent) + ) { + Icon( + painter = painterResource(Res.drawable.ic_mixer_satb), + contentDescription = "SATB", + tint = if (showSATBTools) Color.White else Color.Black, + modifier = Modifier.size(35.dp) + ) + } + } + Spacer(modifier = Modifier.weight(1f)) + + Column { + IconButton( + onClick = { + isPianoSelected = !isPianoSelected + if (isPianoSelected) { + mediaPlayer?.changeInstru(1) + } else { + mediaPlayer?.changeInstru(20) + } + + } + ) { + if (isPianoSelected) { + Icon( + Icons.Default.Piano, + contentDescription = "Piano", + tint = Color.Black + ) + } else { + Icon( + painter = painterResource(Res.drawable.ic_organ), + contentDescription = "Orgue", + tint = Color.Black, + modifier = Modifier.size(25.dp) + ) + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + + Column( + modifier = Modifier.wrapContentWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + IconButton( + onClick = onPlayPauseClick, + modifier = Modifier.size(48.dp).background(MaterialTheme.colorScheme.primary, CircleShape) + ) { + Icon( + imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause, + contentDescription = "Pla", + tint = Color.White + ) + } + } + + Spacer(modifier = Modifier.weight(1f)) + + val platform = getPlatform() + if (platform.name.startsWith("Java")) { + ModernVolumeSlider( + volume, onVolumeChange = onVolumeChange + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + Row( + modifier = Modifier.wrapContentWidth(), + horizontalArrangement = Arrangement.End, + ) { + IconButton( + onClick = { expandedCtl = !expandedCtl }, + modifier = Modifier.size(48.dp) + ) { + Icon( + imageVector = if (expandedCtl) Icons.Default.MoreHoriz else Icons.Default.MoreVert, + contentDescription = "More", + tint = Color.White ) } } } } } - - - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceAround - ){ - Box( contentAlignment = Alignment.CenterStart) { - val isAtStart = loopState.first == -1L - val isWaitingForB = loopState.first != -1L && !loopState.third - val isLooping = loopState.third - - OutlinedButton( - onClick = { - when { - isAtStart -> mediaPlayer.setPointA() - isWaitingForB -> mediaPlayer.setPointB() - isLooping -> { - mediaPlayer.clearLoop() - } - } - }, - colors = ButtonDefaults.buttonColors( - containerColor = when { - (isWaitingForB || isLooping) -> Color.Red - else -> Color.LightGray - } - ), - contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp), - shape = RoundedCornerShape(5.dp) , - modifier = Modifier - .height(34.dp) - .padding(horizontal = 4.dp) - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - Text( - text = "A", - color = if (isWaitingForB || isLooping) Color.White else Color.Black, - fontSize = 18.sp - ) - - Icon( - imageVector = Icons.Default.Loop, - contentDescription = null, - tint = if (isLooping) Color.White else Color.Black, - modifier = Modifier.size(18.dp) - ) - - Text( - text = "B", - color = if (isLooping) Color.White else Color.Black, - fontSize = 18.sp - ) - } - } - } - Box(contentAlignment = Alignment.Center) { - IconButton( - onClick = { - showSATBTools = !showSATBTools - } - ,modifier = Modifier - .size(40.dp) - .clip(CircleShape) - .background(if (showSATBTools) MaterialTheme.colorScheme.primary else Color.Transparent)) { - Icon( - painter = painterResource(Res.drawable.ic_mixer_satb), - contentDescription = "SATB", - tint = if (showSATBTools) Color.White else Color.Black, - modifier = Modifier.size(35.dp) - ) - } - } - Box(contentAlignment = Alignment.Center) { - IconButton( - onClick = onPlayPauseClick, - modifier = Modifier.size(48.dp).background(MaterialTheme.colorScheme.primary, CircleShape) - ) { - Icon( - imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause, - contentDescription = "Pla", - tint = Color.White - ) - } - } - Box(contentAlignment = Alignment.CenterEnd) { - IconButton( - onClick = { - isPianoSelected = !isPianoSelected - if (isPianoSelected) { - mediaPlayer?.changeInstru(1) - } else { - mediaPlayer?.changeInstru(20) - } - } - ) { - if (isPianoSelected) { - Icon( - Icons.Default.Piano, - contentDescription = "Piano", - tint = Color.Black - ) - } else { - Icon( - painter = painterResource(Res.drawable.ic_organ), - contentDescription = "Orgue", - tint = Color.Black, - modifier = Modifier.size(25.dp) - ) - } - } - } - val platform = getPlatform() - if (platform.name.startsWith("Java")) { - ModernVolumeSlider( - volume, onVolumeChange = onVolumeChange - ) - } - Row( - horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically - ) { - Spacer(modifier = Modifier.width(8.dp)) - IconButton(onClick = { expandedCtl = !expandedCtl }) { - Icon( - imageVector = if (expandedCtl) Icons.Default.MoreHoriz else Icons.Default.MoreVert, - contentDescription = "More", - tint = Color.White - ) - } - } - } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/ModernVolumeSlider.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/ModernVolumeSlider.kt index 011250f..7576eff 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/ModernVolumeSlider.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/ModernVolumeSlider.kt @@ -29,45 +29,47 @@ fun ModernVolumeSlider( volume: Float, onVolumeChange: (Float) -> Unit ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .height(42.dp) - .background(Color.White.copy(alpha = 0.1f), CircleShape) - .padding(horizontal = 12.dp, vertical = 4.dp) - ) { - Icon( - imageVector = when { - volume == 0f -> Icons.AutoMirrored.Filled.VolumeOff - volume < 0.5f -> Icons.AutoMirrored.Filled.VolumeDown - else -> Icons.AutoMirrored.Filled.VolumeUp - }, - contentDescription = null, - tint = if (volume > 0) Color.Black else Color.Gray, - modifier = Modifier.size(20.dp) - ) + Column { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .height(42.dp) + .background(Color.White.copy(alpha = 0.1f), CircleShape) + .padding(horizontal = 12.dp, vertical = 4.dp) + ) { + Icon( + imageVector = when { + volume == 0f -> Icons.AutoMirrored.Filled.VolumeOff + volume < 0.5f -> Icons.AutoMirrored.Filled.VolumeDown + else -> Icons.AutoMirrored.Filled.VolumeUp + }, + contentDescription = null, + tint = if (volume > 0) Color.Black else Color.Gray, + modifier = Modifier.size(20.dp) + ) - Spacer(modifier = Modifier.width(4.dp)) + Spacer(modifier = Modifier.width(4.dp)) - Slider( - value = volume, - onValueChange = onVolumeChange, - modifier = Modifier.fillMaxWidth(0.30f), - colors = SliderDefaults.colors( - activeTrackColor = Color(0xFFF59E0B), - inactiveTrackColor = Color.White.copy(alpha = 0.2f), - thumbColor = Color.Transparent - ), - track = { sliderState -> - SliderDefaults.Track( - sliderState = sliderState, - modifier = Modifier.height(15.dp), - thumbTrackGapSize = 0.dp - ) - }, - thumb = { - Box(Modifier.size(0.dp)) - } - ) + Slider( + value = volume, + onValueChange = onVolumeChange, + modifier = Modifier.fillMaxWidth(0.30f), + colors = SliderDefaults.colors( + activeTrackColor = Color(0xFFF59E0B), + inactiveTrackColor = Color.White.copy(alpha = 0.2f), + thumbColor = Color.Transparent + ), + track = { sliderState -> + SliderDefaults.Track( + sliderState = sliderState, + modifier = Modifier.height(15.dp), + thumbTrackGapSize = 0.dp + ) + }, + thumb = { + Box(Modifier.size(0.dp)) + } + ) + } } } \ No newline at end of file