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