Compare commits

..

No commits in common. "6b8ce0cecf05ed10eb9b240aba7abec983b2c89e" and "a5977d5dd105174cb2dfea24103358f833f1bfe0" have entirely different histories.

6 changed files with 75 additions and 316 deletions

View file

@ -384,27 +384,13 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
// println("fullLine = '$fullLine'")
val afterU0 = fullLine.substringAfter("U0:")
val blankPrefixRegex = Regex("^(.*?z)([0-9A-Z_+\\-]+)(:)")
val blankPrefixRegex = Regex("^(.*?z[0-9A-Z]:)")
val match = blankPrefixRegex.find(afterU0)
var blankPrefix: String = ""
var body: String = ""
if (match != null) {
body = afterU0.substring(blankPrefix.length)
if (editState.silentDuration == "-1") {
blankPrefix = match.value
}
else {
println("voici ${match.groupValues.joinToString(",")}")
val beforeDuration = match.groups[1]?.value ?: ""
val restOfSilenceBlock = match.groups[3]?.value ?: ":"
blankPrefix = "$beforeDuration${editState.silentDuration}$restOfSilenceBlock"
}
body = afterU0.substring(match.value.length)
val blankPrefix = match?.value ?: ""
val body = if (match != null) {
afterU0.substring(blankPrefix.length)
} else {
blankPrefix = afterU0.substringBefore(":") + ":"
body = afterU0.substringAfter(":")
afterU0
}
val measure = measureString.split("/")[0].toIntOrNull() ?: 4
@ -520,26 +506,29 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
val fullPath = directory + fileName
val rawIncludedContent = fileRepository.readFileLines(fullPath)
if (parts.size > 2 && parts[2].isNotEmpty()) {
val regexIgnoreString = parts[2]
if (parts.size > 2 && parts[2].startsWith("^")) {
val patternInside = parts[2]
.substringAfter("(", "")
.substringBefore(")", "")
val ignoredLinesRegex = try {
Regex(regexIgnoreString)
} catch (e: Exception) {
Regex("^:")
val prefixesToIgnore = if (patternInside.isNotEmpty()) {
patternInside.split("|")
} else {
listOf(parts[2].substring(1))
}
val filteredLines = rawIncludedContent.filter { incLine ->
ignoredLinesRegex.find(incLine) == null
prefixesToIgnore.none { prefix ->
incLine.trim().startsWith(prefix)
}
}
lineBuilder.append(filteredLines.joinToString("\n"))
} else {
lineBuilder.append(rawIncludedContent.joinToString("\n"))
}
}
} catch (e: Exception) {
lineBuilder.append("// Erreur inclusion: ${e.message}")
lineBuilder.append("// Erreur: ${e.message}")
}
lastIndex = match.range.last + 1
}
@ -779,12 +768,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
newMarkerValue: String,
editState: TUOEditState
): String {
/* On ne modifie pas le template tant que les notes ne sont pas modifiées
* Et tant qu'une marker est ajouter */
if (newMarkerValue.isEmpty()) {
println("Aucune modification détectée (marqueur vide), on retourne le template original.")
return templateLine
}
val isT0 = templateLine.startsWith("T0")
val prefix: String
@ -1308,7 +1291,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
var lookK = k
var lookC = charIdx + 1
var searching = true
var crossedParenthesis = false
while (searching) {
if (lookC >= buff[lookK].length) {
@ -1335,14 +1317,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
lookC++
}
next == '(' || next == ')' -> {
crossedParenthesis = true
lookC++
}
next == 'z' && lookK == k && char == 'D' -> {
if (crossedParenthesis) {
searching = false
} else {
var hasFutureResonance = false
if (k + 1 < buff.size) {
if (buff[k + 1].replace(Regex("\\$\\{.*?\\}|\\$."), "")
@ -1358,7 +1333,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
searching = false
}
}
}
"., !:|(){}".contains(next) || next == '$' -> lookC++
else -> searching = false
@ -1437,15 +1411,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
}
val cleanedBlocks = buff2.map { it.replace(" ", "") }
/*val durationModifierRegex = Regex("^(.*?z)[0-9A-Z_+\\-](:|/)")
val match = durationModifierRegex.find(prefix)
val newPrefix = if (match != null) {
val beforeDuration = match.groups[1]?.value ?: ""
val separator = match.groups[2]?.value ?: ":"
"$beforeDuration${editState.silentDuration}$separator"
} else prefix*/
val finalLines = (prefix + cleanedBlocks.joinToString(""))
.replace("2z11", "y")
.replace("/", "/ ")

View file

@ -14,8 +14,6 @@ import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.KeyboardDoubleArrowLeft
import androidx.compose.material.icons.filled.KeyboardDoubleArrowRight
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@ -65,7 +63,6 @@ fun TUODetailDialog(
var templateFragment by remember { mutableStateOf(editState.templateFragment) }
var marker by remember { mutableStateOf(editState.marker) }
val initialMarker = remember { editState.marker }
LaunchedEffect(notes.toMap()) {
val tempState = TUOEditState(
@ -119,79 +116,6 @@ fun TUODetailDialog(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// Modif silence duration
if (currentIndex == 0) {
val silentDurationOrder = listOf(
"0", "4", "8", "A", "C", "E", "G", "K", "O", "S", "W"
)
Row(
modifier = Modifier.width(125.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
IconButton(
onClick = {
val currentIdx = silentDurationOrder.indexOf(editState.silentDuration)
val newIdx = if (currentIdx > 0) currentIdx - 1 else 0
val newDuration = silentDurationOrder[newIdx]
val state = TUOEditState(
tuoIndex = globalIndex,
notesByVoice = notes.toMap(),
originalNotes = originalNotes.toMap(),
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
templateFragment = templateFragment,
marker = if(marker != initialMarker) marker else "",
silentDuration = newDuration
)
onSave(state)
},
enabled = silentDurationOrder.indexOf(editState.silentDuration) >= 0,
modifier = Modifier.size(20.dp)
) {
Icon(
imageVector = Icons.Default.KeyboardDoubleArrowLeft,
contentDescription = "Diminuer",
tint = if (silentDurationOrder.indexOf(editState.silentDuration) > 0) Color.LightGray else Color.DarkGray
)
}
Text(
text = editState.silentDuration,
color = Color.White,
style = MaterialTheme.typography.bodyLarge
)
IconButton(
onClick = {
val currentIdx = silentDurationOrder.indexOf(editState.silentDuration)
val newIdx = if (currentIdx != -1 && currentIdx < silentDurationOrder.lastIndex) currentIdx + 1 else currentIdx
val newDuration = silentDurationOrder[newIdx]
val state = TUOEditState(
tuoIndex = globalIndex,
notesByVoice = notes.toMap(),
originalNotes = originalNotes.toMap(),
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
templateFragment = templateFragment,
marker = if(marker != initialMarker) marker else "",
silentDuration = newDuration
)
onSave(state)
},
enabled = silentDurationOrder.indexOf(editState.silentDuration) < silentDurationOrder.lastIndex,
modifier = Modifier.size(20.dp)
) {
Icon(
imageVector = Icons.Default.KeyboardDoubleArrowRight,
contentDescription = "Augmenter",
tint = if (silentDurationOrder.indexOf(editState.silentDuration) < silentDurationOrder.lastIndex) Color.LightGray else Color.DarkGray
)
}
}
}
if (currentIndex > 0) {
IconButton(
onClick = {
@ -206,6 +130,7 @@ fun TUODetailDialog(
tint = Color.White
)
}
}
Text(
text = "$currentIndex",
@ -213,7 +138,6 @@ fun TUODetailDialog(
fontWeight = FontWeight.Bold,
color = Color.White
)
}
IconButton(
onClick = {
@ -364,9 +288,7 @@ fun TUODetailDialog(
if (isEditable || canAdd) {
IconButton(
onClick = {
if (index >= 0) {
if (index == 0) lyricsLines.add("_") else lyricsLines.removeAt(index)
}
}) {
Icon(
imageVector = if (index == 0) Icons.Default.Add else Icons.Default.Clear,
@ -401,8 +323,7 @@ fun TUODetailDialog(
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
templateFragment = templateFragment,
/* On ne ré-insere pas la même marker*/
marker = if(marker != initialMarker) marker else "",
marker = marker
)
onSave(state)
},
@ -646,7 +567,7 @@ private fun notesToTemplate(notes: String): String {
.replace("si", "S")
.replace("ta", "T")
val noteRegex = Regex("""([drmfsltDRFSTz][ia]?|[.,()\s-])""")
val noteRegex = Regex("""([drmfsltDRFSTz]+|[.,()\s-])""")
val template = StringBuilder()
val matches = noteRegex.findAll(cleaned)

View file

@ -7,6 +7,5 @@ data class TUOEditState(
val lyricsByStanza: MutableMap<Int, String> = mutableMapOf(),
val originalLyricsByStanza: MutableMap<Int, String> = mutableMapOf(),
val templateFragment: String = "",
val marker: String = "",
val silentDuration: String = "-1"
val marker: String = ""
)

View file

@ -16,11 +16,9 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Description
import androidx.compose.material.icons.filled.KeyboardDoubleArrowLeft
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.*
import androidx.compose.runtime.*
@ -276,7 +274,7 @@ fun TimeUnitComposable(
)
Column(
modifier = Modifier
.background(animatedColor)
.background(/*if(gridActive) Color.Cyan.copy(alpha = 0.5f) else col*/animatedColor)
) {
if (TimeUnitObject._hasMarker) {
val lineHeight = 20.sp
@ -843,25 +841,6 @@ fun LazyVerticalGridTUO(
val template = oneTUO.pTemplate.template
val expectedTemplate = template.count { it.isLetter() } == 2 && template.contains(".")
val fileC = sharedScreenModel.fileContent.value ?: ""
val lines = fileC.split("\n").toMutableList()
val u0Idx = lines.indexOfFirst { it.startsWith("U0:") }
val t0Idx = lines.indexOfFirst { it.startsWith("T0:") }
val fullLine = when {
t0Idx != -1 -> {
lines[t0Idx]
}
u0Idx != -1 -> {
lines[u0Idx]
}
else -> ""
}
val afterU0T0 = fullLine.substringAfter("0:")
val blankPrefixRegex =Regex("""z([0-9A-Z])[:]""")
val match = blankPrefixRegex.find(afterU0T0)
val blankPrefix = match?.groups?.get(1)?.value ?: ""
val editState = TUOEditState(
tuoIndex = oneTUO.firstTuoIndex,
notesByVoice = (0..3).associate { i ->
@ -876,8 +855,7 @@ fun LazyVerticalGridTUO(
marker = listOfNotNull(
oneTUO.pTemplate.markerToString().takeIf { it.isNotBlank() },
oneTUO.hasHairPin()?.toString()?.takeIf { it.isNotBlank() }
).joinToString(" "),
silentDuration = blankPrefix
).joinToString(" ")
)
TUODetailDialog(
editState = editState,
@ -1296,16 +1274,12 @@ fun EditorActionButtons(isUndoVisible: Boolean, onUndo: () -> Unit, onBuild: ()
}
fun autoFixNote(rawNote: String, template: String): String {
// println("\n==================================================")
// println(" ENTRÉE autoFixNote")
// println(" • Template : \"$template\"")
// println(" • RawNote : \"$rawNote\"")
// println("==================================================")
println("ça on a t $template => $rawNote")
if (rawNote == "_") return rawNote
val noteRegex = Regex("([drmfsltDRFSTw][ia]?|―)[0-9'¹²³⁴₁₂₃₄,]*")
val actualNotes = if (rawNote.isEmpty()) emptyList() else noteRegex.findAll(rawNote).map { it.value }.toList()
// println(" • Notes détectées dans RawNote : $actualNotes (Taille: ${actualNotes.size})")
/*if (rawNote.contains("") || rawNote.contains("") || rawNote.contains(",")) {
val templateNotesCount = template.count { c ->
c in 'A'..'Y' || c in 'a'..'y'
@ -1315,32 +1289,24 @@ fun autoFixNote(rawNote: String, template: String): String {
val requiredNotesCount = template.count { it in 'A'..'Y' || it in 'a'..'y' }
val shouldIncludeParentheses = actualNotes.size >= requiredNotesCount
// println(" • Notes requises par le template : $requiredNotesCount")
// println(" • Inclusion des parenthèses autorisée ? : $shouldIncludeParentheses")
// println("--------------------------------------------------")
val result = StringBuilder()
var noteIdx = 0
for (i in template.indices) {
val char = template[i]
// print("[Étape $i] Caractère template = '$char' | Index note actuel = $noteIdx -> ")
when {
(char in 'A'..'Y' || char in 'a'..'y') -> {
if (noteIdx < actualNotes.size) {
val noteToAdd = actualNotes[noteIdx]
if (noteIdx > 0 && result.isNotEmpty()) {
val lastChar = result.last()
// println()
// println(" [INFO ESPACE] lastC '$lastChar' avant d'ajouter la note \"$noteToAdd\"")
/* if (lastChar != '•' && lastChar != ',' && lastChar != '(' && lastChar != ')') {
if (lastChar != '•' && lastChar != ',' && lastChar != '(' && lastChar != ' ') {
result.append(" ")
} */
// println(" [INFO ESPACE] Maintenant res est \"${result.toString()}\"")
// print(" -> Continuité Étape $i : ")
}
}
result.append(noteToAdd)
// println("Action : Ajout de la note utilisateur \"$noteToAdd\"")
noteIdx++
} else {
/*if (rawNote.isEmpty()) {*/
@ -1348,78 +1314,35 @@ fun autoFixNote(rawNote: String, template: String): String {
/*} else {
result.append("")
}*/
// println("Action : Plus de notes utilisateur ! Ajout du silence par défaut \"z\"")
}
}
char == '-' -> {
if (noteIdx < actualNotes.size && actualNotes[noteIdx] == "") {
result.append("")
// println("Action : Consommation et ajout du tiret utilisateur \"―\"")
noteIdx++
} else {
val prev = if (i > 0) template[i - 1] else null
val next = if (i < template.lastIndex) template[i + 1] else null
if (prev == '.' && next == ',') {
result.append(" ")
// println("Action : Structure .-, détectée -> Ajout d'un espace \" \"")
} else {
result.append("")
// println("Action : Ajout du tiret automatique \"―\"")
if (prev == '.' && next == ',') result.append(" ") else result.append("")
}
}
}
char == 'z' -> {
result.append(" ")
// println("Action : Caractère 'z' du template -> Ajout d'un espace \" \"")
}
char == 'z' -> result.append(" ")
char == '.' -> {
val currentText = result.toString()
if (!currentText.endsWith("")) {
result.append("")
// println("Action : Ajout du point musical \"•\"")
} else {
// println("Action : Ignoré (se termine déjà par un point)")
}
if (!currentText.endsWith("")) result.append("")
}
char == ',' -> {
val currentText = result.toString()
if (!currentText.endsWith(",")) {
result.append(",")
// println("Action : Ajout de la virgule \",\"")
} else {
// println("Action : Ignoré (se termine déjà par une virgule)")
}
if (!currentText.endsWith(",")) result.append(",")
}
char == '(' -> {
if (shouldIncludeParentheses) {
result.append("(")
// println("Action : Ajout parenthèse ouvrante \"(\"")
} else {
// println("Action : Ignoré (pas assez de notes)")
}
if (shouldIncludeParentheses) result.append("(")
}
char == ')' -> {
if (shouldIncludeParentheses) {
result.append(")")
// println("Action : Ajout parenthèse fermante \")\"")
} else {
// println("Action : Ignoré (pas assez de notes)")
if (shouldIncludeParentheses) result.append(")")
}
}
else -> {
// println("Action : Aucun comportement défini pour '$char'")
}
}
// println(" ↳ État actuel du buffer : \"${result.toString()}\"")
}
var finalResult = result.toString()
// println(" • Avant formatage final : \"$finalResult\"")
finalResult = finalResult
.replace("( ", "(")
.replace(" )", ")")
.replace("•,", "• ,")
// println("on a RES ${finalResult}")
return finalResult
// println("on a RES ${result.toString()}")
return result.toString()
}

View file

@ -16,7 +16,6 @@ import androidx.compose.material.icons.Icons
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.material.icons.filled.KeyboardDoubleArrowRight
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@ -250,7 +249,7 @@ fun MidiControlPanel(
modifier = Modifier.background(Color(0XFF2C3130)),
onClick = { updateTempoByBpmStep(-10) }) {
Icon(
Icons.Default.KeyboardDoubleArrowLeft,
Icons.AutoMirrored.Filled.KeyboardArrowLeft,
"gauche",
tint = Color.White.copy(0.7f),
modifier = Modifier.size(22.dp)
@ -298,7 +297,7 @@ fun MidiControlPanel(
modifier = Modifier.background(Color(0XFF2C3130)),
onClick = { updateTempoByBpmStep(10) }) {
Icon(
Icons.Default.KeyboardDoubleArrowRight,
Icons.AutoMirrored.Filled.KeyboardArrowRight,
"droite",
tint = Color.White.copy(0.7f),
modifier = Modifier.size(22.dp)

View file

@ -5,9 +5,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@ -16,20 +14,14 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import feufaro.composeapp.generated.resources.Res
import feufaro.composeapp.generated.resources.compose_multiplatform
import mg.dot.feufaro.FileRepository
import mg.dot.feufaro.solfa.ColorPrefixA
import mg.dot.feufaro.solfa.ColorPrefixB
import mg.dot.feufaro.solfa.ColorValue
import org.jetbrains.compose.resources.painterResource
import java.io.ByteArrayOutputStream
import java.util.zip.GZIPOutputStream
import kotlin.io.encoding.Base64
@ -55,91 +47,53 @@ fun QRDisplay(sharedScreenModel: SharedScreenModel, fileRepository: FileReposito
val compressedData = Base64.UrlSafe.encode(compressedBytes).trim('=')
val uri = "feufaro://song?file=${compressedData}&name=$fileName"
value = generateQRCode(uri, size = 1200)
value = generateQRCode(uri, size = 800)
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.6f)) // Assombrissement moderne
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.5f)) // Fond semi-transparent
.clickable { sharedScreenModel.toggleQRCodeVisibility() } // Fermer au clic
.imePadding()
.systemBarsPadding(),
contentAlignment = Alignment.Center
) {
Card(
modifier = Modifier
.width(400.dp)
.wrapContentHeight()
.shadow(24.dp, shape = RoundedCornerShape(28.dp)),
shape = RoundedCornerShape(28.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
modifier = Modifier.wrapContentSize().shadow(16.dp, shape = MaterialTheme.shapes.large)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(24.dp)
) {
Text(
text = "Partager la partition",
style = MaterialTheme.typography.titleLarge.copy(
fontWeight = FontWeight.Bold,
letterSpacing = 0.5.sp
)
)
Text(
text = "Scannez pour ouvrir instantanément",
style = MaterialTheme.typography.bodyMedium.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant
),
modifier = Modifier.padding(top = 4.dp, bottom = 24.dp)
)
Text("Scanner pour ouvrir ce partition")
if (qrCodeImage != null) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(350.dp)
.clip(RoundedCornerShape(16.dp))
.background(Color.White)
.padding(5.dp)
) {
Image(
bitmap = qrCodeImage!!,
contentDescription = "Code QR du fichier actif",
modifier = Modifier.fillMaxSize()
modifier = Modifier.size(400.dp)
)
Box(
modifier = Modifier
.size(54.dp)
.background(Color.White, shape = RoundedCornerShape(12.dp))
.padding(4.dp),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(Res.drawable.compose_multiplatform),
contentDescription = "App Logo",
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(8.dp))
)
}
}
} else {
Box(
modifier = Modifier.size(280.dp),
modifier = Modifier
.size(400.dp)
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(48.dp),
color = ColorPrefixA,
strokeWidth = 4.dp,
trackColor = ColorPrefixB.copy(alpha = 0.2f)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Génération du QR Code...",
style = MaterialTheme.typography.bodyMedium.copy(
@ -150,8 +104,6 @@ fun QRDisplay(sharedScreenModel: SharedScreenModel, fileRepository: FileReposito
}
}
}
Spacer(modifier = Modifier.height(12.dp))
}
}
}