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 a7b70f8..1de2e66 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/MidiControlPanel.kt @@ -46,6 +46,8 @@ 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 @@ -80,7 +82,9 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.delay +import mg.dot.feufaro.getPlatform import mg.dot.feufaro.midi.MediaPlayer +import org.koin.core.component.getScopeId import javax.swing.Icon @OptIn(ExperimentalMaterial3Api::class) @@ -120,7 +124,7 @@ fun MidiControlPanel( var showInstruTools by remember { mutableStateOf(false) } var showSATBTools by remember { mutableStateOf(false) } var showVolumeTools by remember { mutableStateOf(false) } - + var expandedCtl by remember { mutableStateOf(false) } LaunchedEffect(tempo) { currentBpm = mediaPlayer.getCurrentBPM() } @@ -131,64 +135,48 @@ fun MidiControlPanel( .background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)), horizontalAlignment = Alignment.CenterHorizontally ) { - Row ( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) + AnimatedVisibility( + visible = !expandedCtl ) { - 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) - } - - - Row( - verticalAlignment = Alignment.CenterVertically - ){ - - - - - IconButton( - onClick = onPlayPauseClick, - modifier = Modifier.size(48.dp) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - Icon( - imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause, - contentDescription = "Pla", - tint = 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 }, @@ -302,10 +290,22 @@ fun MidiControlPanel( } } } + Row( - verticalAlignment = Alignment.CenterVertically + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceAround ){ - Column(horizontalAlignment = Alignment.CenterHorizontally) { + Box(contentAlignment = Alignment.CenterStart) { + IconButton( + onClick = { + showBPMTools = !showBPMTools + } + ) { + Icon(imageVector = Icons.Default.AvTimer, contentDescription = "Tempo") + } + } + Box( contentAlignment = Alignment.CenterStart) { val isAtStart = loopState.first == -1L val isWaitingForB = loopState.first != -1L && !loopState.third val isLooping = loopState.third @@ -326,8 +326,15 @@ fun MidiControlPanel( else -> Color.LightGray } ), - shape = RoundedCornerShape(5.dp) , - modifier = Modifier.padding(2.dp) + 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", @@ -349,80 +356,56 @@ fun MidiControlPanel( fontWeight = FontWeight.Bold, fontSize = 18.sp ) + } } } - - Column(horizontalAlignment = Alignment.CenterHorizontally) { - IconButton( - onClick = { - showBPMTools = !showBPMTools - }) { - Icon(imageVector = Icons.Default.AvTimer, contentDescription = "Tempo") - } - AnimatedVisibility(visible = showBPMTools){ - Box( - modifier = Modifier - .width(20.dp) - .height(3.dp) - .background(MaterialTheme.colorScheme.primary, RoundedCornerShape(2.dp)) - )} - } - - Column(horizontalAlignment = Alignment.CenterHorizontally) { - IconButton( - onClick = { - showInstruTools = !showInstruTools - }) { - Icon(imageVector = Icons.Default.Tune, contentDescription = "Instru") - } - AnimatedVisibility(visible = showInstruTools){ - Box( - modifier = Modifier - .width(20.dp) - .height(3.dp) - .background(MaterialTheme.colorScheme.primary, RoundedCornerShape(2.dp)) - )} - } - - Column(horizontalAlignment = Alignment.CenterHorizontally) { + Box(contentAlignment = Alignment.Center) { IconButton( onClick = { showSATBTools = !showSATBTools }) { Icon(imageVector = Icons.Default.SettingsVoice, contentDescription = "SATB") } - AnimatedVisibility(visible = showSATBTools){ - Box( - modifier = Modifier - .width(20.dp) - .height(3.dp) - .background(MaterialTheme.colorScheme.primary, RoundedCornerShape(2.dp)) - )} } - - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Icon( - imageVector = if (volume > 0) Icons.AutoMirrored.Filled.VolumeUp else Icons.AutoMirrored.Filled.VolumeOff, - contentDescription = null, - modifier = Modifier.size(20.dp) + Box(contentAlignment = Alignment.Center) { + IconButton( + onClick = onPlayPauseClick, + modifier = Modifier.size(48.dp) + ) { + Icon( + imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause, + contentDescription = "Pla", + tint = MaterialTheme.colorScheme.primary + ) + } + } + Box(contentAlignment = Alignment.CenterEnd) { + IconButton( + onClick = { + showInstruTools = !showInstruTools + } + ) { + Icon(imageVector = Icons.Default.Tune, contentDescription = "Instru") + } + } + val platform = getPlatform() + if (platform.name.startsWith("Java")) { + ModernVolumeSlider( + volume, onVolumeChange = onVolumeChange ) } - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Slider( - value = volume, - onValueChange = onVolumeChange, - valueRange = 0f..1f, - modifier = Modifier.width(100.dp), - thumb = { - Box( - modifier = Modifier.size(15.dp).background(Color.Blue, CircleShape) - ) - }, - track = { sliderState -> - SliderDefaults.Track( - sliderState = sliderState, modifier = Modifier.height(5.dp) - ) - }) + 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 + ) + } } } } diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/ModernVolumeSlider.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/ModernVolumeSlider.kt new file mode 100644 index 0000000..011250f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/ModernVolumeSlider.kt @@ -0,0 +1,73 @@ +package mg.dot.feufaro.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.VolumeDown +import androidx.compose.material.icons.automirrored.filled.VolumeOff +import androidx.compose.material.icons.automirrored.filled.VolumeUp +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Slider +import androidx.compose.material3.SliderDefaults +import androidx.compose.runtime.Composable +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.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalViewConfiguration +import androidx.compose.ui.unit.dp + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +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) + ) + + 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)) + } + ) + } +} \ No newline at end of file