diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index c4f11fb..81d5919 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -53,6 +53,8 @@ kotlin { implementation(libs.koin.compose) implementation(libs.koin.compose.viewmodel) implementation(libs.koin.compose.viewmodel.navigation) + implementation(libs.bundles.voyager) + implementation(libs.cafe.voyager.koin) } commonTest.dependencies { diff --git a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt index 57b0fb2..a04c26c 100644 --- a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt @@ -6,7 +6,6 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview -import org.koin.androidx.compose.koinViewModel import androidx.core.view.WindowCompat import android.view.WindowManager import androidx.core.view.WindowInsetsCompat @@ -21,8 +20,7 @@ class MainActivity : ComponentActivity() { setContent { - val sharedViewModel: SharedViewModel = koinViewModel() - App(sharedViewModel = sharedViewModel) + App() } } private fun hideSystemBar() { diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/App.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/App.kt index 59a779b..e44913c 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/App.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/App.kt @@ -1,65 +1,36 @@ 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 SharedScreenModel 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 +import cafe.adriel.voyager.navigator.CurrentScreen +import cafe.adriel.voyager.navigator.Navigator +import mg.dot.feufaro.viewmodel.SolfaScreenModel @Composable -@Preview -fun App(sharedViewModel: SharedViewModel = SharedViewModel() -) { +fun App() { + + Navigator(screen = ScreenSolfa) { + CurrentScreen() + } val fileRepository = koinInject() val displayConfigManager = koinInject() 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)} + val sharedScreenModel = koinInject() + val solfaScreenModel = koinInject() LaunchedEffect(Unit) { configScope.launch { @@ -70,12 +41,12 @@ fun App(sharedViewModel: SharedViewModel = SharedViewModel() } } } - val solfa = Solfa(sharedViewModel, fileRepository) + LaunchedEffect(currentDisplayConfig) { if (currentDisplayConfig.playlist.isNotEmpty()) { try { - sharedViewModel.setPlaylist(currentDisplayConfig.playlist) - solfa.loadNextInPlaylist() + sharedScreenModel.setPlaylist(currentDisplayConfig.playlist) + solfaScreenModel.loadNextInPlaylist() } catch (e: Exception) { println("Error loading playlist : ${e.message}") } @@ -83,116 +54,9 @@ fun App(sharedViewModel: SharedViewModel = SharedViewModel() } 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" diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ContextualMenu.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ContextualMenu.kt index a312d1a..f64354b 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ContextualMenu.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ContextualMenu.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit @@ -26,7 +27,7 @@ fun ContextualMenu (onMenuItemClick: (String) -> Unit){ ) { MenuItem(icon = Icons.Default.Add, text = "+") { onMenuItemClick("Ajouter")} MenuItem(icon = Icons.Default.Edit, text = "!") { onMenuItemClick("Modifier")} - MenuItem(icon = Icons.Default.List, text = "-") { onMenuItemClick("Liste")} + MenuItem(icon = Icons.AutoMirrored.Filled.List, text = "-") { onMenuItemClick("Liste")} } } diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ScreenSolfa.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ScreenSolfa.kt new file mode 100644 index 0000000..e9f369e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ScreenSolfa.kt @@ -0,0 +1,156 @@ +package mg.dot.feufaro + +import SharedScreenModel +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.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.safeDrawing +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +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.IntOffset +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Popup +import mg.dot.feufaro.solfa.LazyVerticalGridTUO +import kotlin.math.roundToInt +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.koin.koinScreenModel +import mg.dot.feufaro.data.GridTUOData +import mg.dot.feufaro.viewmodel.SolfaScreenModel +import java.io.ObjectStreamException + +object ScreenSolfa : Screen { + @Composable + override fun Content() { + val solfaScreenModel = koinScreenModel() + var menuPosition by remember { mutableStateOf(Offset.Zero)} + var showContextualMenu by remember { mutableStateOf(false)} + var gridWidthPx by remember { mutableStateOf(0) } + val sharedScreenModel = koinScreenModel() + val tuoList by sharedScreenModel.tuoList.collectAsState() + val measure by sharedScreenModel.measure.collectAsState() + val stanza by sharedScreenModel.stanza.collectAsState() + val gridTUOData = GridTUOData(measure, tuoList, stanza) + 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 sharedScreenModel.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 sharedScreenModel.measure.collectAsState() + val songTitle: String by sharedScreenModel.songTitle.collectAsState() + val songKey: String by sharedScreenModel.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) + } + LazyVerticalGridTUO( + gridTUOData, + gridWidthPx = gridWidthPx, + onGridWidthMeasured = { width -> gridWidthPx = width } + ) + MGButton(onClick = { + //showContent = !showContent + + solfaScreenModel.loadNextInPlaylist() + }, modifier = Modifier.height(40.dp)) { + val debugData: String by sharedScreenModel.data.collectAsState() + Text(debugData) + } + 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 nbStanzas: Int by sharedScreenModel.nbStanzas.collectAsState() + for (i in 1..nbStanzas) { + MGButton( + enabled = (i != stanza.toInt()), + onClick = { + sharedScreenModel.setStanza(i.toString()) + }, + modifier = Modifier.padding(horizontal = 4.dp) + ) { + Text("$i") + } + } + Text(text = "Stanza: $stanza") + val songAuthor: String by sharedScreenModel.songAuthor.collectAsState() + val songComposer: String by sharedScreenModel.songComposer.collectAsState() + val songRhythm: String by sharedScreenModel.songRhythm.collectAsState() + Text(text = songAuthor, fontStyle = FontStyle.Italic) + Text(text = songRhythm) + Text(text = songComposer) + + } + } + } + } + @Throws(ObjectStreamException::class) // C'est une méthode de sérialisation Java, donc l'exception est nécessaire + private fun readResolve(): Any { + return this // Toujours retourner l'instance unique de ce singleton + } +} diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/SongNumberInput.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/SongNumberInput.kt new file mode 100644 index 0000000..42cb0c4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/SongNumberInput.kt @@ -0,0 +1,83 @@ +// commonMain/kotlin/mg.dot.feufaro/SongNumberInput.kt +package mg.dot.feufaro + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp + +@Composable +fun SongNumberInput( + // Callback appelé lorsque le numéro de chant est validé + onSongNumberConfirmed: (Int) -> Unit +) { + // État local pour le texte saisi par l'utilisateur + var songNumberText by remember { mutableStateOf("") } + // État local pour afficher un message d'erreur + var errorMessage by remember { mutableStateOf(null) } + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + // Champ de texte pour la saisie du numéro de chant + OutlinedTextField( + value = songNumberText, + onValueChange = { newValue -> + // N'accepte que les chiffres + val filteredValue = newValue.filter { it.isDigit() } + // Limite la saisie à 3 chiffres + if (filteredValue.length <= 3) { + songNumberText = filteredValue + errorMessage = null // Réinitialise l'erreur si la saisie redevient valide + } + }, + label = { Text("Numéro du chant") }, + placeholder = { Text("Ex: 123") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), // Affiche un clavier numérique + isError = errorMessage != null, // Indique visuellement une erreur + modifier = Modifier.fillMaxWidth() + ) + + // Affichage du message d'erreur si présent + errorMessage?.let { + Text( + text = it, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(top = 4.dp) + ) + } + + // Bouton de confirmation + Button( + onClick = { + val number = songNumberText.toIntOrNull() + if (number == null) { + errorMessage = "Veuillez entrer un nombre valide." + } else if (songNumberText.length != 3) { + errorMessage = "Le numéro doit avoir 3 chiffres." + } else if (number < 1 || number > 999) { // Exemple de plage valide si besoin + errorMessage = "Le numéro doit être entre 001 et 999." + } else { + errorMessage = null + onSongNumberConfirmed(number) // Appelle le callback avec le numéro validé + } + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + Text("Confirmer") + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/data/GridTUOData.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/data/GridTUOData.kt new file mode 100644 index 0000000..4ec83c2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/data/GridTUOData.kt @@ -0,0 +1,9 @@ +package mg.dot.feufaro.data + +import mg.dot.feufaro.solfa.TimeUnitObject + +data class GridTUOData ( + val measure: String, + val tuoList: List, + val stanza: Int +) diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/di/AppModule.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/di/AppModule.kt index a8c2b5b..26466c3 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/di/AppModule.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/di/AppModule.kt @@ -1,11 +1,14 @@ package mg.dot.feufaro.di +import PlaylistRepository +import SharedScreenModel import mg.dot.feufaro.CommonFileRepository import mg.dot.feufaro.FileRepository -import mg.dot.feufaro.SharedViewModel import mg.dot.feufaro.DisplayConfigManager // Importez DisplayConfigManager import mg.dot.feufaro.config.AppConfig import mg.dot.feufaro.musicXML.MusicXML +import mg.dot.feufaro.solfa.Solfa +import mg.dot.feufaro.viewmodel.SolfaScreenModel import org.koin.dsl.module import org.koin.core.module.dsl.singleOf import org.koin.core.module.Module @@ -18,5 +21,9 @@ val commonModule = module { singleOf(::MusicXML) single { CommonFileRepository() } single { DisplayConfigManager(fileRepository = get())} - viewModel { SharedViewModel() } + single { SharedScreenModel(get()) } + //viewModel { SharedViewModel() } + single { PlaylistRepository() } + single { Solfa(get(), get()) } + single { SolfaScreenModel(get()) } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt index afe0f98..74765e4 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt @@ -1,15 +1,14 @@ package mg.dot.feufaro.solfa +import SharedScreenModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import mg.dot.feufaro.FileRepository import mg.dot.feufaro.CommonTools -import mg.dot.feufaro.SharedViewModel -//import mg.dot.feufaro.getOpt -class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: FileRepository) { +class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository: FileRepository) { private val T: MutableList = mutableListOf() private val N: MutableList = mutableListOf() private val L: MutableList = mutableListOf() @@ -40,13 +39,13 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi private val meta: MutableMap = mutableMapOf() private val lyricsComment: MutableList = mutableListOf() suspend fun nextTimeUnitObject() { - val lastTUO = sharedViewModel.lastTUO() + val lastTUO = sharedScreenModel.lastTUO() nextTIndex++ if (nextTIndex == 5) { - sharedViewModel.doneTUOList() + sharedScreenModel.doneTUOList() } if (T.getOrNull(nextTIndex) == null) { - sharedViewModel.doneTUOList() + sharedScreenModel.doneTUOList() return } val pTemplate = T[nextTIndex] @@ -89,19 +88,19 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi } } withContext(Dispatchers.Main) { - sharedViewModel.addTUO(unitObject) + sharedScreenModel.addTUO(unitObject) } nextTimeUnitObject() } fun loadSolfa() { - val sourceFileName: String = sharedViewModel.currentPlayed() - sharedViewModel.reset() + val sourceFileName: String = sharedScreenModel.currentPlayed() + sharedScreenModel.reset() parse(sourceFileName) } fun loadNextInPlaylist() { - val playlist = sharedViewModel.playlist.value + val playlist = sharedScreenModel.playlist.value if (playlist.isNotEmpty()) { - sharedViewModel.playNext() + sharedScreenModel.playNext() loadSolfa() } } @@ -123,8 +122,8 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi nextNIndex = -1 nextLIndex = -1 lyricsComment.clear() - sharedViewModel.setStanza("1") - sharedViewModel.setNbStanzas(0) + sharedScreenModel.setStanza("1") + sharedScreenModel.setNbStanzas(0) TimeUnitObject.hasMarker(false) lines.forEach { line -> run { @@ -264,11 +263,11 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi val metaChunks: List = value.split(REGEX_PARSE_META) metaChunks.forEach { parseMeta(it) } parseMeta(value) - sharedViewModel.setMeasure(meta["m"] ?: "") - sharedViewModel.setSongTitle(meta["t"] ?: "") - sharedViewModel.setSongAuthor(meta["a"] ?: "") - sharedViewModel.setSongComposer(meta["h"] ?: "") - sharedViewModel.setSongRhythm(meta["r"] ?: "") + sharedScreenModel.setMeasure(meta["m"] ?: "") + sharedScreenModel.setSongTitle(meta["t"] ?: "") + sharedScreenModel.setSongAuthor(meta["a"] ?: "") + sharedScreenModel.setSongComposer(meta["h"] ?: "") + sharedScreenModel.setSongRhythm(meta["r"] ?: "") } else if (key == "L") { loadL(index, value) } else if (key == "Y") { @@ -383,7 +382,7 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi if ("c" == subKey) { val tonality = line.uppercaseFirstMeta() meta["C"] = tonality - sharedViewModel.setSongKey(tonality) + sharedScreenModel.setSongKey(tonality) val keyOrigin = CommonTools.tonalityToNumber(tonality) val transposeTo = getOpt("transposeto") val transposeAsIf = getOpt("transposeasif") @@ -463,10 +462,10 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi } } } catch (e: Exception) { - sharedViewModel.appendData("\nErr: $e") + sharedScreenModel.appendData("\nErr: $e") } - if (stanzaNumber > sharedViewModel.nbStanzas.value) { - sharedViewModel.setNbStanzas(stanzaNumber) + if (stanzaNumber > sharedScreenModel.nbStanzas.value) { + sharedScreenModel.setNbStanzas(stanzaNumber) } } // loadY is a smart lyrics parser for Malagasy language diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt index 3748407..5bc7c78 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt @@ -12,7 +12,6 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -29,11 +28,9 @@ import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.sp -import mg.dot.feufaro.SharedViewModel +import mg.dot.feufaro.data.GridTUOData import kotlin.math.min -//import androidx.compose.ui.unit.times - class TimeUnitObject (val pTemplate: PTemplate, val prevTUO: TimeUnitObject?) { var mutableNoteVersion: Int by mutableStateOf(0) private var lyrics: MutableList = mutableListOf() @@ -334,13 +331,13 @@ fun AutoResizingText( } @Composable fun LazyVerticalGridTUO( - viewModel: SharedViewModel, + viewModel: GridTUOData, gridWidthPx: Int, onGridWidthMeasured: (Int) -> Unit, modifier: Modifier = Modifier ) { - val regexMeasure = Regex("(\\d)/\\d").find(viewModel.measure.value) - val tuoList by viewModel.tuoList.collectAsState() + val regexMeasure = Regex("(\\d)/\\d").find(viewModel.measure) + val tuoList = viewModel.tuoList Column( modifier = Modifier @@ -368,7 +365,7 @@ fun LazyVerticalGridTUO( GridCells.Fixed(calculatedCols.coerceAtLeast(columnGroup)) } - val currentStanza by viewModel.stanza.collectAsState() + val currentStanza = viewModel.stanza LazyVerticalGrid( columns = actualColumns, diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/SharedViewModel.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt similarity index 57% rename from composeApp/src/commonMain/kotlin/mg/dot/feufaro/SharedViewModel.kt rename to composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt index 4fa50fe..60e8b2b 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/SharedViewModel.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt @@ -1,38 +1,51 @@ -package mg.dot.feufaro - +// commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt +import cafe.adriel.voyager.core.model.ScreenModel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import com.rickclephas.kmp.observableviewmodel.ViewModel -import com.rickclephas.kmp.observableviewmodel.MutableStateFlow import mg.dot.feufaro.solfa.TimeUnitObject -open class SharedViewModel(): ViewModel() { - private val _data = MutableStateFlow(viewModelScope, "Next ...") +// Supposons que vous ayez une autre dépendance, par exemple pour gérer les données de la playlist +class PlaylistRepository { + private val _playlists = MutableStateFlow(listOf("Playlist par défaut 1", "Playlist par défaut 2")) + val playlists: StateFlow> = _playlists.asStateFlow() + + fun addPlaylist(name: String) { + _playlists.value = _playlists.value + name + } +} + +class SharedScreenModel( + private val playlistRepository: PlaylistRepository // Exemple de dépendance partagée +) : ScreenModel { + private val _data = MutableStateFlow("Next ...") val data: StateFlow = _data.asStateFlow() - private val _measure = MutableStateFlow(viewModelScope, "") + private val _measure = MutableStateFlow("") val measure: StateFlow = _measure.asStateFlow() - private val _songTitle = MutableStateFlow(viewModelScope, "") + private val _songTitle = MutableStateFlow("") val songTitle: StateFlow = _songTitle.asStateFlow() - private val _stanza = MutableStateFlow(viewModelScope, 0) + private val _stanza = MutableStateFlow( 0) val stanza: StateFlow = _stanza.asStateFlow() - private val _songKey = MutableStateFlow(viewModelScope, "") + private val _songKey = MutableStateFlow("") val songKey: StateFlow = _songKey.asStateFlow() - private val _songAuthor = MutableStateFlow(viewModelScope,"") + private val _songAuthor = MutableStateFlow("") val songAuthor: StateFlow = _songAuthor.asStateFlow() - private val _songComposer = MutableStateFlow(viewModelScope,"") + private val _songComposer = MutableStateFlow("") val songComposer: StateFlow = _songComposer.asStateFlow() - private val _songRhythm = MutableStateFlow(viewModelScope,"") + private val _songRhythm = MutableStateFlow("") val songRhythm: StateFlow = _songRhythm.asStateFlow() - private val _nbStanzas = MutableStateFlow(viewModelScope,0) + private val _nbStanzas = MutableStateFlow(0) val nbStanzas: StateFlow = _nbStanzas.asStateFlow() - private var _tuoList = MutableStateFlow>(viewModelScope, emptyList()) + private var _tuoList = + MutableStateFlow>(emptyList()) val tuoList: StateFlow> = _tuoList.asStateFlow() - private var _playlist = MutableStateFlow>(viewModelScope, emptyList()) + private var _playlist = + MutableStateFlow>(emptyList()) val playlist: StateFlow> = _playlist.asStateFlow() - private var _nextPlayed = MutableStateFlow(viewModelScope, -1) + private var _nextPlayed = MutableStateFlow(-1) val nextPlayed: StateFlow = _nextPlayed.asStateFlow() private val tempTimeUnitObjectList = mutableListOf() - var _hasMarker = MutableStateFlow(viewModelScope, false) + var _hasMarker = MutableStateFlow( false) val hasMarker: StateFlow = _hasMarker.asStateFlow() fun appendData(otherData: String) { @@ -93,4 +106,18 @@ open class SharedViewModel(): ViewModel() { val playlistIndex = _nextPlayed.value return _playlist.value[playlistIndex] } -} + private val _sharedData = MutableStateFlow("Donnée partagée initiale") + val sharedData: StateFlow = _sharedData.asStateFlow() + + val playlists: StateFlow> = playlistRepository.playlists + + fun updateSharedData(newData: String) { + _sharedData.value = newData + } + + fun addNewPlaylist(name: String) { + playlistRepository.addPlaylist(name) + } + + // Vous pouvez ajouter d'autres fonctions pour interagir avec les données partagées +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SolfaScreenModel.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SolfaScreenModel.kt new file mode 100644 index 0000000..b81124b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SolfaScreenModel.kt @@ -0,0 +1,13 @@ +package mg.dot.feufaro.viewmodel + +import cafe.adriel.voyager.core.model.ScreenModel +import mg.dot.feufaro.solfa.Solfa + +class SolfaScreenModel ( + private val solfa: Solfa +) : ScreenModel{ + init {} + fun loadNextInPlaylist() { + solfa.loadNextInPlaylist() + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/main.kt b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/main.kt index 8ad306c..3a630a4 100644 --- a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/main.kt +++ b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/main.kt @@ -25,8 +25,7 @@ fun main() = application { title = "Feufaro", ) { KoinContext { - val sharedViewModel: SharedViewModel = SharedViewModel() - App(sharedViewModel = sharedViewModel) + App() } } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 642a78b..9cbd080 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,8 +21,10 @@ core = "0.91.1" kmpObservableviewmodelCore = "1.0.0-BETA-3" kotlinxSerializationJson = "1.8.1" material3 = "1.3.2" +voyager = "1.1.0-beta03" [libraries] +cafe-voyager-koin = { module = "cafe.adriel.voyager:voyager-koin" } koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" } koin-compose-viewmodel-navigation = { module = "io.insert-koin:koin-compose-viewmodel-navigation", version.ref = "koin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } @@ -43,7 +45,6 @@ koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" } koin-core-viewmodel = { module = "io.insert-koin:koin-core-viewmodel", version.ref = "koin" } koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" } koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" } - core = { module = "io.github.pdvrieze.xmlutil:core", version.ref = "core" } #core-android = { module = "io.github.pdvrieze.xmlutil:core-android", version.ref = "core" } #core-jdk = { module = "io.github.pdvrieze.xmlutil:core-jdk", version.ref = "core" } @@ -51,6 +52,9 @@ serialization = { module = "io.github.pdvrieze.xmlutil:serialization", version.r kmp-observableviewmodel-core = { module = "com.rickclephas.kmp:kmp-observableviewmodel-core", version.ref = "kmpObservableviewmodelCore" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" } +voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } +voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" } +voyager-koin = { module = "cafe.adriel.voyager:voyager-koin", version.ref = "voyager" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } @@ -60,3 +64,6 @@ composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMul composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"} + +[bundles] +voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-koin"] \ No newline at end of file