Optimize midi control space & modernize volume slider

This commit is contained in:
hasinarak3@gmail.com 2026-02-03 16:06:30 +03:00
parent 337f1d9912
commit da1d130085
2 changed files with 177 additions and 121 deletions

View file

@ -46,6 +46,8 @@ import androidx.compose.material.icons.filled.Tonality
import androidx.compose.material.icons.filled.Tune import androidx.compose.material.icons.filled.Tune
import androidx.compose.material.icons.automirrored.filled.VolumeUp import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.automirrored.filled.VolumeOff 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.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import mg.dot.feufaro.getPlatform
import mg.dot.feufaro.midi.MediaPlayer import mg.dot.feufaro.midi.MediaPlayer
import org.koin.core.component.getScopeId
import javax.swing.Icon import javax.swing.Icon
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -120,7 +124,7 @@ fun MidiControlPanel(
var showInstruTools by remember { mutableStateOf(false) } var showInstruTools by remember { mutableStateOf(false) }
var showSATBTools by remember { mutableStateOf(false) } var showSATBTools by remember { mutableStateOf(false) }
var showVolumeTools by remember { mutableStateOf(false) } var showVolumeTools by remember { mutableStateOf(false) }
var expandedCtl by remember { mutableStateOf(false) }
LaunchedEffect(tempo) { LaunchedEffect(tempo) {
currentBpm = mediaPlayer.getCurrentBPM() currentBpm = mediaPlayer.getCurrentBPM()
} }
@ -131,7 +135,10 @@ fun MidiControlPanel(
.background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)), .background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Row ( AnimatedVisibility(
visible = !expandedCtl
) {
Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
@ -153,7 +160,7 @@ fun MidiControlPanel(
disabledInactiveTrackColor = Color(0xffBCAAA4), disabledInactiveTrackColor = Color(0xffBCAAA4),
), ),
thumb = { thumb = {
Box ( Box(
modifier = Modifier modifier = Modifier
.size(15.dp) .size(15.dp)
.background(Color.Gray, CircleShape) .background(Color.Gray, CircleShape)
@ -168,27 +175,8 @@ fun MidiControlPanel(
) )
Text("${momo / 1000}s", color = Color.White) Text("${momo / 1000}s", color = Color.White)
} }
Row(
verticalAlignment = Alignment.CenterVertically
){
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
)
} }
}
AnimatedVisibility( AnimatedVisibility(
visible = showSATBTools, visible = showSATBTools,
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
@ -302,10 +290,22 @@ fun MidiControlPanel(
} }
} }
} }
Row( 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 isAtStart = loopState.first == -1L
val isWaitingForB = loopState.first != -1L && !loopState.third val isWaitingForB = loopState.first != -1L && !loopState.third
val isLooping = loopState.third val isLooping = loopState.third
@ -326,8 +326,15 @@ fun MidiControlPanel(
else -> Color.LightGray else -> Color.LightGray
} }
), ),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
shape = RoundedCornerShape(5.dp) , shape = RoundedCornerShape(5.dp) ,
modifier = Modifier.padding(2.dp) modifier = Modifier
.height(34.dp)
.padding(horizontal = 4.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
Text( Text(
text = "A", text = "A",
@ -351,78 +358,54 @@ fun MidiControlPanel(
) )
} }
} }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
IconButton(
onClick = {
showBPMTools = !showBPMTools
}) {
Icon(imageVector = Icons.Default.AvTimer, contentDescription = "Tempo")
} }
AnimatedVisibility(visible = showBPMTools){ Box(contentAlignment = Alignment.Center) {
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) {
IconButton( IconButton(
onClick = { onClick = {
showSATBTools = !showSATBTools showSATBTools = !showSATBTools
}) { }) {
Icon(imageVector = Icons.Default.SettingsVoice, contentDescription = "SATB") 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))
)}
} }
Box(contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { IconButton(
onClick = onPlayPauseClick,
modifier = Modifier.size(48.dp)
) {
Icon( Icon(
imageVector = if (volume > 0) Icons.AutoMirrored.Filled.VolumeUp else Icons.AutoMirrored.Filled.VolumeOff, imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause,
contentDescription = null, contentDescription = "Pla",
modifier = Modifier.size(20.dp) tint = MaterialTheme.colorScheme.primary
) )
} }
Column(horizontalAlignment = Alignment.CenterHorizontally) { }
Slider( Box(contentAlignment = Alignment.CenterEnd) {
value = volume, IconButton(
onValueChange = onVolumeChange, onClick = {
valueRange = 0f..1f, showInstruTools = !showInstruTools
modifier = Modifier.width(100.dp), }
thumb = { ) {
Box( Icon(imageVector = Icons.Default.Tune, contentDescription = "Instru")
modifier = Modifier.size(15.dp).background(Color.Blue, CircleShape) }
}
val platform = getPlatform()
if (platform.name.startsWith("Java")) {
ModernVolumeSlider(
volume, onVolumeChange = onVolumeChange
) )
}, }
track = { sliderState -> Row(
SliderDefaults.Track( horizontalArrangement = Arrangement.End,
sliderState = sliderState, modifier = Modifier.height(5.dp) 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
) )
}) }
} }
} }
} }

View file

@ -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))
}
)
}
}