239 lines
No EOL
9.8 KiB
Kotlin
239 lines
No EOL
9.8 KiB
Kotlin
package mg.dot.feufaro
|
|
|
|
import androidx.compose.animation.AnimatedVisibility
|
|
import androidx.compose.foundation.Image
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.gestures.detectTapGestures
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.FlowRow
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.RowScope
|
|
import androidx.compose.foundation.layout.WindowInsets
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.height
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.safeContentPadding
|
|
import androidx.compose.foundation.layout.safeDrawing
|
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
|
import androidx.compose.foundation.rememberScrollState
|
|
import androidx.compose.foundation.verticalScroll
|
|
import androidx.compose.material3.Button
|
|
import androidx.compose.material3.ButtonDefaults
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.input.pointer.pointerInput
|
|
import androidx.compose.ui.text.font.FontStyle
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.unit.dp
|
|
import org.jetbrains.compose.resources.painterResource
|
|
import org.jetbrains.compose.ui.tooling.preview.Preview
|
|
import mg.dot.feufaro.musicXML.MusicXML
|
|
|
|
import feufaro.composeapp.generated.resources.Res
|
|
import feufaro.composeapp.generated.resources.compose_multiplatform
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
import mg.dot.feufaro.solfa.LazyVerticalGridTUO
|
|
import mg.dot.feufaro.solfa.Solfa
|
|
import org.koin.compose.koinInject
|
|
import androidx.compose.ui.geometry.Offset
|
|
import androidx.compose.ui.unit.IntOffset
|
|
import androidx.compose.ui.window.Popup
|
|
import kotlin.math.roundToInt
|
|
|
|
@Composable
|
|
@Preview
|
|
fun App(sharedViewModel: SharedViewModel = SharedViewModel()
|
|
) {
|
|
val fileRepository = koinInject<FileRepository>()
|
|
|
|
val displayConfigManager = koinInject<DisplayConfigManager>()
|
|
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
|
// Load Configurations
|
|
val configScope = CoroutineScope(Dispatchers.Default)
|
|
var showContextualMenu by remember { mutableStateOf(false)}
|
|
var menuPosition by remember { mutableStateOf(Offset.Zero)}
|
|
|
|
LaunchedEffect(Unit) {
|
|
configScope.launch {
|
|
try {
|
|
displayConfigManager.loadConfigFromFile("assets://config.json")
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
}
|
|
}
|
|
}
|
|
val solfa = Solfa(sharedViewModel, fileRepository)
|
|
LaunchedEffect(currentDisplayConfig) {
|
|
if (currentDisplayConfig.playlist.isNotEmpty()) {
|
|
try {
|
|
sharedViewModel.setPlaylist(currentDisplayConfig.playlist)
|
|
solfa.loadNextInPlaylist()
|
|
} catch (e: Exception) {
|
|
println("Error loading playlist : ${e.message}")
|
|
}
|
|
}
|
|
}
|
|
|
|
val musicXML = MusicXML(fileRepository)
|
|
// Start App
|
|
//solfa.parse()
|
|
MaterialTheme {
|
|
MaterialTheme {
|
|
var showContent by remember { mutableStateOf(false) }
|
|
var gridWidthPx by remember { mutableStateOf(0) }
|
|
Column(
|
|
Modifier.fillMaxSize()
|
|
.pointerInput(Unit) {
|
|
detectTapGestures(
|
|
onDoubleTap = {
|
|
offset ->
|
|
menuPosition = offset
|
|
showContextualMenu = true
|
|
}
|
|
)
|
|
},
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
) {
|
|
if (showContextualMenu) {
|
|
Popup(
|
|
alignment = Alignment.TopStart,
|
|
offset = IntOffset(menuPosition.x.roundToInt(),
|
|
menuPosition.y.roundToInt()),
|
|
onDismissRequest = {
|
|
showContextualMenu = false
|
|
}
|
|
) {
|
|
ContextualMenu(onMenuItemClick = { item ->
|
|
println("Clicked: $item")
|
|
showContextualMenu = false
|
|
})
|
|
}
|
|
}
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.weight(1f)
|
|
.verticalScroll(rememberScrollState())
|
|
) {
|
|
val stanza: Int by sharedViewModel.stanza.collectAsState()
|
|
FlowRow(
|
|
modifier = Modifier.fillMaxWidth()
|
|
.windowInsetsPadding(WindowInsets.safeDrawing)
|
|
.padding(start = 8.dp, end = 8.dp, top = 8.dp),
|
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
|
) {
|
|
val measureString: String by sharedViewModel.measure.collectAsState()
|
|
val songTitle: String by sharedViewModel.songTitle.collectAsState()
|
|
val songKey: String by sharedViewModel.songKey.collectAsState()
|
|
Text(text = songTitle, fontWeight = FontWeight.Bold)
|
|
Text(
|
|
text = songKey,
|
|
modifier = Modifier.background(Color(0xff, 0xea, 0xe7))
|
|
.padding(horizontal = 4.dp)
|
|
)
|
|
Text(text = measureString)
|
|
Text(text = "Stanza: $stanza")
|
|
val songAuthor: String by sharedViewModel.songAuthor.collectAsState()
|
|
val songComposer: String by sharedViewModel.songComposer.collectAsState()
|
|
val songRhythm: String by sharedViewModel.songRhythm.collectAsState()
|
|
val nbStanzas: Int by sharedViewModel.nbStanzas.collectAsState()
|
|
Text(text = songAuthor, fontStyle = FontStyle.Italic)
|
|
Text(text = songRhythm)
|
|
Text(text = songComposer)
|
|
Row() {
|
|
for (i in 1..nbStanzas) {
|
|
MGButton(
|
|
enabled = (i != stanza.toInt()),
|
|
onClick = {
|
|
sharedViewModel.setStanza(i.toString())
|
|
},
|
|
modifier = Modifier.padding(horizontal = 4.dp)
|
|
) {
|
|
Text("$i")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LazyVerticalGridTUO(
|
|
sharedViewModel,
|
|
gridWidthPx = gridWidthPx,
|
|
onGridWidthMeasured = { width -> gridWidthPx = width }
|
|
)
|
|
MGButton(onClick = {
|
|
//showContent = !showContent
|
|
|
|
solfa.loadNextInPlaylist()
|
|
}, modifier = Modifier.height(40.dp)) {
|
|
val debugData: String by sharedViewModel.data.collectAsState()
|
|
Text(debugData)
|
|
}
|
|
AnimatedVisibility(showContent) {
|
|
val greeting = remember { Greeting().greet() }
|
|
Column(
|
|
Modifier.fillMaxWidth().height(180.dp),
|
|
horizontalAlignment = Alignment.CenterHorizontally
|
|
) {
|
|
Image(painterResource(Res.drawable.compose_multiplatform), null)
|
|
Text("Compose: $greeting")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
fun String?.toColor(foreGround: Boolean = false): Color {
|
|
val defaultColor = if (foreGround) {
|
|
"00FFFFFF"
|
|
} else {
|
|
"00008800"
|
|
}
|
|
var hex = this?.removePrefix("#") ?: defaultColor
|
|
if (hex.length == 6) {
|
|
hex = "00" + hex
|
|
}
|
|
val red = hex.substring(2, 4).toInt(16)
|
|
val green = hex.substring(4, 6).toInt(16)
|
|
val blue = hex.substring(6, 8).toInt(16)
|
|
|
|
return try {
|
|
Color(red, green, blue)
|
|
} catch (e: NumberFormatException) {
|
|
if (foreGround) Color.White else Color.Black
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun MGButton(
|
|
onClick: () -> Unit,
|
|
modifier: Modifier = Modifier,
|
|
enabled: Boolean = true,
|
|
content: @Composable RowScope.() -> Unit
|
|
) {
|
|
val displayConfigManager = koinInject<DisplayConfigManager>()
|
|
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
|
val buttonColors = ButtonDefaults.buttonColors(
|
|
containerColor = currentDisplayConfig.buttonContainerColorHex.toColor(),
|
|
disabledContainerColor = currentDisplayConfig.buttonDisabledContainerColorHex.toColor(),
|
|
contentColor = currentDisplayConfig.buttonDisabledContentColorHex.toColor(),
|
|
disabledContentColor = currentDisplayConfig.buttonDisabledContentColorHex.toColor()
|
|
)
|
|
Button(
|
|
onClick = onClick,
|
|
modifier = modifier,
|
|
enabled = enabled,
|
|
colors = buttonColors,
|
|
content = content
|
|
)
|
|
} |