Compare commits
3 commits
38b6e17008
...
65c56db655
| Author | SHA1 | Date | |
|---|---|---|---|
| 65c56db655 | |||
| 68dd6216b2 | |||
| cadb2a6b3f |
5 changed files with 221 additions and 172 deletions
|
|
@ -1,15 +1,9 @@
|
|||
package mg.dot.feufaro.solfa
|
||||
|
||||
import SharedScreenModel
|
||||
import androidx.compose.runtime.remember
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import mg.dot.feufaro.FileRepository
|
||||
import mg.dot.feufaro.SaveSettings
|
||||
import mg.dot.feufaro.data.GridTUOData
|
||||
import mg.dot.feufaro.launchFilePicker
|
||||
import mg.dot.feufaro.midi.MidiPitch
|
||||
import mg.dot.feufaro.midi.MidiWriterKotlin
|
||||
|
|
@ -450,11 +444,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
MODIF NOTES N1 N2 N3 N4
|
||||
*/
|
||||
// println("taille tA: ${templatArray.size} et le trgt $targetIdx")
|
||||
val originalBlock = templatArray[targetIdx]
|
||||
val markerRegex = Regex("""\$\{.*?\}|\$\w+""")
|
||||
val deleteCount = originalBlock.replace(markerRegex, "").count { it.isLetter() }
|
||||
|
||||
val updatedNotes = updateSourceLines(lines, templatArray, targetIdx, deleteCount, editState)
|
||||
val updatedNotes = updateSourceLines(lines, templatArray, targetIdx, editState)
|
||||
|
||||
// RESTAURATION DES TEMPLATES
|
||||
if (templateString != "") {
|
||||
|
|
@ -523,11 +513,10 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
lines: MutableList<String>,
|
||||
fragments: List<String>,
|
||||
targetIdx: Int,
|
||||
deleteCount: Int,
|
||||
editState: TUOEditState
|
||||
): MutableList<String> {
|
||||
/* Notes N1 N2 N3 N4 */
|
||||
updateNotesInLines(lines, fragments, targetIdx, deleteCount, editState)
|
||||
updateNotesInLines(lines, fragments, targetIdx, editState)
|
||||
/* MODIF LYRICS */
|
||||
//updateLyricsInLines(lines, fragments, targetIdx, deleteCount, editState)
|
||||
return lines
|
||||
|
|
@ -632,7 +621,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
lines: MutableList<String>,
|
||||
fragments: List<String>,
|
||||
targetIdx: Int,
|
||||
deleteCount: Int,
|
||||
editState: TUOEditState
|
||||
) {
|
||||
val notesByVoice = editState.notesByVoice
|
||||
|
|
@ -647,7 +635,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val prefix = currentLine.substringBefore(":") + ":"
|
||||
val noteBody = currentLine.substringAfter(":")
|
||||
val hasAnchor = noteBody.contains("#")
|
||||
val noteExpanded = expandNotes(noteBody)
|
||||
val noteExpanded = expandNotes(noteBody.replace(Regex("\\s+"), ""))
|
||||
|
||||
// println("Notes===>$noteExpanded")
|
||||
val newNot = notesByVoice[voiceNum] ?: ""
|
||||
|
|
@ -666,10 +654,11 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val cleanNewNote = revertMusicalInput(newNote)
|
||||
|
||||
|
||||
val regex = Regex("#\\S[',]*|\\s#\\S[',]*|/|\\(|\\)|[drmfsltDRFSTzw][0-9'¹²³⁴⁵₁₂₃₄₅,]*|[-.]")
|
||||
|
||||
|
||||
val allTokens = regex.findAll(noteExpanded).map { it.value }.toList()
|
||||
val regex = Regex("#\\S[',]*|\\s#\\S[',]*|/|\\(|\\)|[drmfsltDRFSTzw][0-9'¹²³⁴⁵₁₂₃₄₅,]*|[-.―•]")
|
||||
val allTokens = regex.findAll(noteExpanded)
|
||||
.map { it.value }
|
||||
.filter { it != "." }
|
||||
.toList()
|
||||
|
||||
val targetPointer = getNotePointer(fragments, targetIdx, currentLine)
|
||||
|
||||
|
|
@ -693,7 +682,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val isStructural = token.contains("#") || token == "/" || token == "(" || token == ")"
|
||||
|
||||
if (isStructural) {
|
||||
// println(String.format("%-5d | %-10s | %-8s | %-12s | %-10s", i, token, "STRUCT", "-", "Keep"))
|
||||
println(String.format("%-5d | %-10s | %-8s | %-12s | %-10s", i, token, "STRUCT", "-", "Keep"))
|
||||
resultTokens.add(token)
|
||||
i++
|
||||
continue
|
||||
|
|
@ -701,65 +690,62 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
|
||||
if (currentLogicalIdx == targetPointer) {
|
||||
val originalToken = allTokens[i]
|
||||
var replacement = if (!hasAnchor) {
|
||||
val (newBase, newLevel) = parseNoteAndOctave(revertMusicalInput(newNot))
|
||||
val (oldBase, oldLevel) = parseNoteAndOctave(revertMusicalInput(oldNot))
|
||||
val octaveDiff = newLevel - oldLevel
|
||||
|
||||
val (tokenBase, tokenLevel) = parseNoteAndOctave(originalToken)
|
||||
newBase + formatOctave(tokenLevel + octaveDiff)
|
||||
//println("Insertition Multiples : $newNot")
|
||||
val rawUserTokens = regex.findAll(revertMusicalInput(newNot)).map { it.value }.toList()
|
||||
/** Si y a '-' dans template inutile d' ajouter au notes */
|
||||
val hasDashInTemplate = editState.templateFragment.contains("-")
|
||||
val newUserTokens = if (hasDashInTemplate) {
|
||||
rawUserTokens.filter { it.matches(Regex("[drmfsltDRFSTzw].*")) }
|
||||
} else {
|
||||
// SI y a '#'
|
||||
val anchorMatch = Regex("#([drmfsltDRFST])[',]*").find(noteBody)
|
||||
val anchorLevel = if (anchorMatch != null) {
|
||||
val anchorStr = anchorMatch.value
|
||||
anchorStr.count { it == '\'' } - anchorStr.count { it == ',' }
|
||||
} else 0
|
||||
rawUserTokens
|
||||
}
|
||||
//val newUserTokens = regex.findAll(revertMusicalInput(newNot)).map { it.value }.toList()
|
||||
val oldUserTokens = regex.findAll(revertMusicalInput(oldNot)).map { it.value }.toList()
|
||||
|
||||
val newNotCleaned = revertMusicalInput(newNot)
|
||||
val oldNotCleaned = revertMusicalInput(oldNot)
|
||||
val firstNewNote = newUserTokens.firstOrNull { it.matches(Regex("[drmfsltDRFSTzw].*")) } ?: ""
|
||||
val firstOldNote = oldUserTokens.firstOrNull { it.matches(Regex("[drmfsltDRFSTzw].*")) } ?: ""
|
||||
|
||||
val (newBase, newLevel) = parseNoteAndOctave(newNotCleaned)
|
||||
val (oldBase, oldLevel) = parseNoteAndOctave(oldNotCleaned)
|
||||
val (_, newLvl) = parseNoteAndOctave(firstNewNote)
|
||||
val (_, oldLvl) = parseNoteAndOctave(firstOldNote)
|
||||
val octaveDiff = newLvl - oldLvl
|
||||
|
||||
val sourceLevel = newLevel - anchorLevel
|
||||
val formattedOctave = formatOctave(sourceLevel)
|
||||
val finalPureNew = newBase + formattedOctave
|
||||
|
||||
val pureOldBase = oldBase + formatOctave(oldLevel - anchorLevel)
|
||||
val oldTokensForThisFragment = regex.findAll(revertMusicalInput(oldNot))
|
||||
.map { it.value }
|
||||
.filter { it.matches(Regex("[drmfsltDRFSTzw].*|[-―]")) }
|
||||
.toList()
|
||||
val dynamicDeleteCount = oldTokensForThisFragment.size
|
||||
|
||||
// println("Ancre niveau: $anchorLevel | UI Level: $newLevel -> Source Level: $sourceLevel")
|
||||
//
|
||||
if (finalPureNew.startsWith(pureOldBase) && pureOldBase.isNotEmpty()) {
|
||||
val addedPart = finalPureNew.substring(pureOldBase.length)
|
||||
originalToken + addedPart
|
||||
|
||||
newUserTokens.forEach { ut ->
|
||||
val processedToken = if (ut.matches(Regex("[drmfsltDRFSTzw].*"))) {
|
||||
val (fileBase, fileLvl) = parseNoteAndOctave(allTokens[i])
|
||||
val (uBase, _) = parseNoteAndOctave(ut)
|
||||
uBase + formatOctave(fileLvl + octaveDiff)
|
||||
} else {
|
||||
finalPureNew
|
||||
ut
|
||||
}
|
||||
// println("j'aoute $processedToken")
|
||||
|
||||
resultTokens.add(processedToken)
|
||||
|
||||
if (processedToken.matches(Regex("[drmfsltDRFSTzw].*|[-―]"))) {
|
||||
println(String.format("NEW | %-10s | INSERT | %-12d | REPLACE", processedToken, currentLogicalIdx))
|
||||
currentLogicalIdx++
|
||||
}
|
||||
}
|
||||
|
||||
// println(
|
||||
// String.format(
|
||||
// "%-5s | %-10s | %-8s | %-12s | %-10s",
|
||||
// "NEW", replacement, "INSERT", currentLogicalIdx, "REPLACE ($originalToken)"
|
||||
// )
|
||||
// )
|
||||
resultTokens.add(replacement)
|
||||
|
||||
var notesSkipped = 0
|
||||
while (notesSkipped < deleteCount && i < allTokens.size) {
|
||||
val nextToken = allTokens[i]
|
||||
val isNextStructural =
|
||||
nextToken.contains("#") || nextToken == "/" || nextToken == "(" || nextToken == ")"
|
||||
|
||||
if (!isNextStructural) {
|
||||
notesSkipped++
|
||||
var skipped = 0
|
||||
while (skipped < dynamicDeleteCount && i < allTokens.size) {
|
||||
val nextT = allTokens[i]
|
||||
if (!nextT.contains("#") && nextT != "/" && nextT != "(" && nextT != ")") {
|
||||
skipped++
|
||||
} else if (hasAnchor) {
|
||||
resultTokens.add(nextToken)
|
||||
resultTokens.add(nextT) // Garder les ancres si on est en mode ancre
|
||||
}
|
||||
i++
|
||||
}
|
||||
currentLogicalIdx += deleteCount
|
||||
} else {
|
||||
// AFFICHAGE NOTE NORMALE
|
||||
// println(
|
||||
|
|
@ -804,7 +790,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
private fun revertMusicalInput(input: String): String {
|
||||
return input
|
||||
.replace("• ,", "")
|
||||
.replace("―•", "")
|
||||
.replace("•", "")
|
||||
.replace("―", "-")
|
||||
.replace("•", "")
|
||||
.replace("³", "'''")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mg.dot.feufaro
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
|
@ -11,14 +12,10 @@ import androidx.compose.material.icons.Icons
|
|||
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.Save
|
||||
import androidx.compose.material.icons.filled.Savings
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
|
|
@ -26,17 +23,9 @@ import androidx.compose.ui.text.TextStyle
|
|||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.toUpperCase
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import mg.dot.feufaro.data.GridTUOData
|
||||
import mg.dot.feufaro.solfa.TUOEditState
|
||||
import mg.dot.feufaro.solfa.TimeUnitObject
|
||||
|
||||
|
|
@ -168,23 +157,47 @@ fun TUODetailDialog(
|
|||
verticalArrangement = Arrangement.spacedBy(5.dp)
|
||||
) {
|
||||
(0..3).forEach { voice ->
|
||||
val currentNote = notes[voice] ?: ""
|
||||
val isValid = validateMusicalInput(currentNote, templateFragment)
|
||||
val tooltipState = rememberTooltipState(isPersistent = false)
|
||||
Row(
|
||||
|
||||
) {
|
||||
MyTextEditField(
|
||||
value = notes[voice] ?: "",
|
||||
customFontSize = 14.sp,
|
||||
color = Color.White,
|
||||
customPadding = 8.dp,
|
||||
customBrush = SolidColor(Color.White),
|
||||
isEditable = isEditable,
|
||||
isAddable = canAdd,
|
||||
funTransform = ::transformMusicalInput,
|
||||
onValueChng = { newValue ->
|
||||
notes[voice] = newValue
|
||||
}
|
||||
)
|
||||
|
||||
TooltipBox(
|
||||
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||
tooltip = {
|
||||
if (!isValid) {
|
||||
PlainTooltip(
|
||||
containerColor = Color(0xFFF59E0B),
|
||||
contentColor = Color.Black
|
||||
) {
|
||||
Text("Veuillez suivre ce format: $templateFragment")
|
||||
}
|
||||
}
|
||||
},
|
||||
state = tooltipState
|
||||
) {
|
||||
MyTextEditField(
|
||||
value = currentNote ?: "",
|
||||
customFontSize = 14.sp,
|
||||
color = Color.White,
|
||||
customPadding = 8.dp,
|
||||
customBrush = SolidColor(Color.White),
|
||||
isEditable = isEditable,
|
||||
isAddable = canAdd,
|
||||
isWarn = !isValid,
|
||||
funTransform = ::transformMusicalInput,
|
||||
onValueChng = { newValue ->
|
||||
notes[voice] = newValue
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(isValid, currentNote) {
|
||||
if (!isValid && currentNote.isNotEmpty()) {
|
||||
tooltipState.show()
|
||||
} else {
|
||||
tooltipState.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -302,6 +315,7 @@ fun MyTextEditField(
|
|||
customBrush: Brush,
|
||||
isEditable: Boolean,
|
||||
isAddable: Boolean,
|
||||
isWarn: Boolean? = false,
|
||||
funTransform: ((String) -> String)? = null,
|
||||
onValueChng: (String) -> Unit
|
||||
) {
|
||||
|
|
@ -328,7 +342,62 @@ fun MyTextEditField(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color.Black.copy(alpha = 0.3f), RoundedCornerShape(4.dp))
|
||||
.border(
|
||||
if (isWarn!!) 1.dp else 0.dp,
|
||||
if (isWarn!!) Color(0xFFF59E0B) else Color.Transparent,
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
.padding(customPadding),
|
||||
cursorBrush = customBrush
|
||||
)
|
||||
}
|
||||
|
||||
fun validateMusicalInput(input: String, template: String): Boolean {
|
||||
if (template.isEmpty()) return true
|
||||
|
||||
val cleanInput = input.replace("(", "").replace(")", "").replace(" ", "")
|
||||
val cleanTemplate = template.replace("(", "").replace(")", "")
|
||||
|
||||
val regexPattern = buildString {
|
||||
cleanTemplate.forEachIndexed { index, char ->
|
||||
when (char) {
|
||||
in 'A'..'Y' -> {
|
||||
append("(?:―|di|ri|fi|si|ta|[drmfslt])[¹²³₁₂₃]*")
|
||||
}
|
||||
'z' -> {
|
||||
append(" ?")
|
||||
}
|
||||
|
||||
'.' -> append("•")
|
||||
',' -> append(",")
|
||||
'-' -> {
|
||||
val prev = if (index > 0) cleanTemplate[index - 1] else null
|
||||
val next = if (index < cleanTemplate.lastIndex) cleanTemplate[index + 1] else null
|
||||
|
||||
if (prev == '.' && next == ',') {
|
||||
append("?[\\s]*")
|
||||
} else {
|
||||
append("―")
|
||||
}
|
||||
}
|
||||
else -> append(Regex.escape(char.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val regex = Regex("^$regexPattern$")
|
||||
val isMatched = regex.matches(cleanInput)
|
||||
|
||||
/*println("""
|
||||
┌──────────────────────────────────────────────────────────
|
||||
│ DÉBOGAGE VALIDATION
|
||||
├──────────────────────────────────────────────────────────
|
||||
│ Template Origine : $template
|
||||
│ ️ Regex Générée : ^$regexPattern$
|
||||
│ Input Reçu : "$input"
|
||||
│ Résultat : ${if (isMatched) "VALIDE (OUI)" else "INVALIDE (NON)"}
|
||||
└──────────────────────────────────────────────────────────
|
||||
""".trimIndent())*/
|
||||
|
||||
return isMatched
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
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.ArrowBackIos
|
||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||
import androidx.compose.material.icons.filled.Build
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
|
|
@ -576,7 +575,6 @@ fun LazyVerticalGridTUO(
|
|||
selectedTUO = null
|
||||
}
|
||||
|
||||
var showFullChord by remember { mutableStateOf(false) }
|
||||
val editMode by sharedScreenModel.modeEditor.collectAsState()
|
||||
|
||||
Column(
|
||||
|
|
@ -760,63 +758,6 @@ fun LazyVerticalGridTUO(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
measureTUOs.forEachIndexed { indexInMeasure, tuo ->
|
||||
val chordMap = (1..4).mapNotNull { voice ->
|
||||
val noteStr = tuo.tuNotes.getOrNull(voice)?.toString()
|
||||
if (noteStr != null) voice to noteStr else null
|
||||
}.toMap()
|
||||
|
||||
val degreeName = remember(chordMap) {
|
||||
if (chordMap.isNotEmpty()) HarmonicAnalyzer.analyzeDegree(chordMap) else null
|
||||
}
|
||||
val chordName = remember(chordMap) {
|
||||
if (chordMap.isNotEmpty()) HarmonicAnalyzer.analyzeChordName(chordMap, sharedScreenModel.songKey.value) else null
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(gridWidthDp / gridColumnCount)
|
||||
) {
|
||||
// 1. Définir une plage de tailles de police
|
||||
val maxFontSize = 13.sp
|
||||
val minFontSize = 8.sp
|
||||
var currentFontSize by remember { mutableStateOf(maxFontSize) }
|
||||
|
||||
TextButton(
|
||||
onClick = { showFullChord = !showFullChord },
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
modifier = Modifier.height(24.dp).fillMaxWidth() // fillMaxWidth pour occuper toute la colonne
|
||||
) {
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
|
||||
val textToDisplay = if (showFullChord) (chordName ?: "") else (degreeName ?: "")
|
||||
|
||||
Text(
|
||||
text = textToDisplay,
|
||||
onTextLayout = { textLayoutResult ->
|
||||
if (textLayoutResult.hasVisualOverflow && currentFontSize > minFontSize) {
|
||||
currentFontSize = (currentFontSize.value * 0.9f).sp
|
||||
}
|
||||
},
|
||||
softWrap = false, // Empêche le retour à la ligne
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Visible,
|
||||
style = TextStyle(
|
||||
color = if (showFullChord) Color(0xFF1B5E20) else Color(0xFF0D47A1),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = currentFontSize // Utilisation de la taille dynamique[cite: 1]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
measureTUOs.forEachIndexed { indexInMeasure, oneTUO ->
|
||||
val globalIndex = (measureIndex * gridColumnCount) + indexInMeasure
|
||||
|
|
@ -837,11 +778,7 @@ fun LazyVerticalGridTUO(
|
|||
.combinedClickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = LocalIndication.current,
|
||||
onClick = {
|
||||
if(!editMode) {
|
||||
sharedScreenModel.seekToGrid(globalIndex)
|
||||
}
|
||||
}
|
||||
onClick = { }
|
||||
)
|
||||
.pointerInput(globalIndex) {
|
||||
detectTapGestures(
|
||||
|
|
@ -851,6 +788,8 @@ fun LazyVerticalGridTUO(
|
|||
selectedTUO = oneTUO
|
||||
selectedIndex = globalIndex
|
||||
showContextualMenu = true
|
||||
} else {
|
||||
sharedScreenModel.seekToGrid(globalIndex)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -908,18 +847,7 @@ fun LazyVerticalGridTUO(
|
|||
},*/
|
||||
notesByVoice = (0..3).associate { i ->
|
||||
val rawNote = oneTUO.tuNotes.getOrNull(i + 1)?.toString() ?: ""
|
||||
val fixedNote =
|
||||
if (!expectedTemplate) {
|
||||
rawNote
|
||||
} else {
|
||||
val count = rawNote.count()
|
||||
|
||||
if (/*count < 2 && */!rawNote.contains("•")) {
|
||||
rawNote + "•―"
|
||||
} else {
|
||||
rawNote
|
||||
}
|
||||
}
|
||||
val fixedNote = autoFixNote(rawNote, template)
|
||||
i to fixedNote
|
||||
},
|
||||
lyricsByStanza = (1..currentStanza).associate { s ->
|
||||
|
|
@ -1299,4 +1227,50 @@ fun EditorActionButtons(onUndo: () -> Unit, onBuild: () -> Unit, onSave: () -> U
|
|||
IconButton(onClick = onSave) { Icon(Icons.Default.Save, null, tint = Color(0xFF4CAF50)) }
|
||||
IconButton(onClick = onClose) { Icon(Icons.Default.Close, null, tint = Color.Red) }
|
||||
}
|
||||
}
|
||||
|
||||
fun autoFixNote(rawNote: String, template: String): String {
|
||||
if (rawNote.isEmpty() || rawNote == "_") return rawNote
|
||||
|
||||
val noteRegex = Regex("([drmfsltDRFSTw][ia]?|―)[0-9'¹²³⁴₁₂₃₄,]*")
|
||||
val actualNotes = noteRegex.findAll(rawNote).map { it.value }.toList()
|
||||
|
||||
if (rawNote.contains("•") || rawNote.contains("―") || rawNote.contains(",")) {
|
||||
val templateNotesCount = template.count { it in 'A'..'Y' }
|
||||
if (actualNotes.size >= templateNotesCount) return rawNote
|
||||
}
|
||||
|
||||
val result = StringBuilder()
|
||||
var noteIdx = 0
|
||||
|
||||
for (i in template.indices) {
|
||||
val char = template[i]
|
||||
when {
|
||||
char in 'A'..'Y' -> {
|
||||
if (noteIdx < actualNotes.size) {
|
||||
result.append(actualNotes[noteIdx])
|
||||
noteIdx++
|
||||
} else {
|
||||
result.append("―")
|
||||
}
|
||||
}
|
||||
char == '-' -> {
|
||||
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(" ") else result.append("―")
|
||||
}
|
||||
char == 'z' -> result.append(" ")
|
||||
char == '.' -> {
|
||||
val currentText = result.toString()
|
||||
if (!currentText.endsWith("•")) result.append("•")
|
||||
}
|
||||
char == ',' -> {
|
||||
val currentText = result.toString()
|
||||
if (!currentText.endsWith(",")) result.append(",")
|
||||
}
|
||||
char == '(' -> result.append("(")
|
||||
char == ')' -> result.append(")")
|
||||
}
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
|
@ -106,6 +106,20 @@ fun MainScreenWithDrawer(
|
|||
val currentActiveFilePath = sharedScreenModel.activeFilePath.value
|
||||
val fileName = currentActiveFilePath.substringAfterLast('/')
|
||||
|
||||
var sourceTitle = sharedScreenModel.songTitle.value
|
||||
val originalPath = remember(sourceTitle) {
|
||||
if (!currentActiveFilePath.contains("_tmp") && !currentActiveFilePath.contains("/tmp/")) {
|
||||
currentActiveFilePath
|
||||
} else {
|
||||
currentActiveFilePath
|
||||
}
|
||||
}
|
||||
|
||||
var sourceContent = sharedScreenModel.fileContent.value ?: ""
|
||||
var codeContent by remember { mutableStateOf(sourceContent?: "") }
|
||||
LaunchedEffect(sourceContent) {
|
||||
codeContent = sourceContent
|
||||
}
|
||||
val saveLauncher = rememberFileSaveLauncher { chosenPath ->
|
||||
if (chosenPath != null) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
|
|
@ -246,7 +260,13 @@ fun MainScreenWithDrawer(
|
|||
) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
//sharedScreenModel.
|
||||
scope.launch {
|
||||
codeContent = sourceContent
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
solfaScreenModel.loadExternalFile(originalPath)
|
||||
}
|
||||
}
|
||||
}, modifier = Modifier.alpha(0.45f)
|
||||
) {
|
||||
Icon(
|
||||
|
|
|
|||
|
|
@ -649,7 +649,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
|||
_editModeState.value = false
|
||||
_midiMarkersList.value = emptyList()
|
||||
_tuoTimestamps.value = emptyList()
|
||||
updateSearchTxt("")
|
||||
_searchTitle.value = ""
|
||||
tempTimeUnitObjectList.clear()
|
||||
resetGridCount()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue