Add button 'Remove | add grid' on lastGrid TUO & reposition triolet,hairpin arc
This commit is contained in:
parent
36a002256d
commit
792efc5aeb
3 changed files with 309 additions and 29 deletions
|
|
@ -0,0 +1,53 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun GridActionMenu(
|
||||||
|
onAddMeasureRight: () -> Unit,
|
||||||
|
onDeleteMeasure: () -> Unit,
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Row {
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = {
|
||||||
|
onAddMeasureRight()
|
||||||
|
},
|
||||||
|
colors = ButtonDefaults.filledTonalButtonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = {
|
||||||
|
onDeleteMeasure()
|
||||||
|
},
|
||||||
|
colors = ButtonDefaults.outlinedButtonColors(
|
||||||
|
contentColor = MaterialTheme.colorScheme.secondary
|
||||||
|
),
|
||||||
|
border = BorderStroke(1.dp, MaterialTheme.colorScheme.secondary.copy(alpha = 0.5f))
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Delete,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,6 @@ import kotlin.math.min
|
||||||
//@todo: bug ffpm-629.txt sssszd au lieu de ssss-d(-rrrrtdr)
|
//@todo: bug ffpm-629.txt sssszd au lieu de ssss-d(-rrrrtdr)
|
||||||
//@todo: bug ews-103.txt Triolet adds one blank
|
//@todo: bug ews-103.txt Triolet adds one blank
|
||||||
//@todo: bug ffpm-167-1.txt shoud start with : and not |
|
//@todo: bug ffpm-167-1.txt shoud start with : and not |
|
||||||
//@todo: bug ffpm-491-2.txt ${x:} and ${DS: } not in sync
|
|
||||||
|
|
||||||
class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository: FileRepository) {
|
class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository: FileRepository) {
|
||||||
private val T: MutableList<PTemplate> = mutableListOf()
|
private val T: MutableList<PTemplate> = mutableListOf()
|
||||||
|
|
@ -625,6 +624,11 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
targetIdx: Int,
|
targetIdx: Int,
|
||||||
editState: TUOEditState
|
editState: TUOEditState
|
||||||
) {
|
) {
|
||||||
|
// Ajout/Enlèvement grille de fin
|
||||||
|
if (editState.tuoIndex == 9999) {
|
||||||
|
AddOrRemoveGridActionOnNotes(lines, editState)
|
||||||
|
return
|
||||||
|
}
|
||||||
val notesByVoice = editState.notesByVoice
|
val notesByVoice = editState.notesByVoice
|
||||||
val originalNotes = editState.originalNotes
|
val originalNotes = editState.originalNotes
|
||||||
val templateFragment = editState.templateFragment
|
val templateFragment = editState.templateFragment
|
||||||
|
|
@ -821,12 +825,76 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun AddOrRemoveGridActionOnNotes(
|
||||||
|
lines: MutableList<String>,
|
||||||
|
editState: TUOEditState
|
||||||
|
) {
|
||||||
|
for (voiceNum in 0..3) {
|
||||||
|
val linePrefix = "N${voiceNum + 1}:"
|
||||||
|
val lineIdx = lines.indexOfFirst { it.startsWith(linePrefix) }
|
||||||
|
|
||||||
|
if (lineIdx != -1) {
|
||||||
|
val currentLine = lines[lineIdx]
|
||||||
|
val prefix = currentLine.substringBefore(":") + ":"
|
||||||
|
val noteBody = currentLine.substringAfter(":")
|
||||||
|
|
||||||
|
val regex = Regex("#\\S[',]*|\\s#\\S[',]*|/|\\(|\\)|[drmfsltDRFSTzw][0-9'¹²³⁴₁₂₃₄,]*|[-.―•]")
|
||||||
|
val allTokens = regex.findAll(expandNotes(noteBody.replace(Regex("\\s+"), "")))
|
||||||
|
.map { it.value }
|
||||||
|
.filter { it != "." }
|
||||||
|
.toMutableList()
|
||||||
|
|
||||||
|
/*if (editState.templateFragment == "-") {
|
||||||
|
var lastNoteIdx = -1
|
||||||
|
for (idx in allTokens.indices.reversed()) {
|
||||||
|
val token = allTokens[idx]
|
||||||
|
val isStructural = token.contains("#") || token == "/" || token == "(" || token == ")"
|
||||||
|
if (!isStructural) {
|
||||||
|
lastNoteIdx = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastNoteIdx != -1) {
|
||||||
|
allTokens.removeAt(lastNoteIdx)
|
||||||
|
}
|
||||||
|
} else {*/
|
||||||
|
// +
|
||||||
|
if (editState.templateFragment == "+") {
|
||||||
|
var lastNoteIdx = -1
|
||||||
|
for (idx in allTokens.indices.reversed()) {
|
||||||
|
val token = allTokens[idx]
|
||||||
|
val isStructural = token.contains("#") || token == "/" || token == "(" || token == ")"
|
||||||
|
if (!isStructural) {
|
||||||
|
lastNoteIdx = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastNoteIdx != -1) {
|
||||||
|
allTokens.add(lastNoteIdx + 1, "d")
|
||||||
|
} else {
|
||||||
|
allTokens.add("d")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines[lineIdx] = prefix + allTokens.joinToString("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateMarkerSource(
|
private fun updateMarkerSource(
|
||||||
templateLine: String,
|
templateLine: String,
|
||||||
targetPointer: Int,
|
targetPointer: Int,
|
||||||
initMarkerValue: String,
|
initMarkerValue: String,
|
||||||
editState: TUOEditState
|
editState: TUOEditState
|
||||||
): String {
|
): String {
|
||||||
|
/* Add or Remove lastGrid */
|
||||||
|
if (editState.tuoIndex == 9999) {
|
||||||
|
val finalResult = AddOrRemoveGridActionOnTemplate(templateLine, editState)
|
||||||
|
return finalResult
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Set Pending Hairpin */
|
/* Set Pending Hairpin */
|
||||||
when (initMarkerValue) {
|
when (initMarkerValue) {
|
||||||
|
|
@ -1060,6 +1128,126 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
return finalResult
|
return finalResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun AddOrRemoveGridActionOnTemplate(templateLine: String, editState: TUOEditState): String {
|
||||||
|
val prefix: String
|
||||||
|
val body: String
|
||||||
|
val suffix: String
|
||||||
|
|
||||||
|
val isT0 = templateLine.startsWith("T0")
|
||||||
|
if (isT0) {
|
||||||
|
prefix = "T0:{"
|
||||||
|
body = templateLine.substringAfter("{").substringBeforeLast("}")
|
||||||
|
suffix = "}"
|
||||||
|
} else {
|
||||||
|
val match = Regex("^(.*?z\\d|.*?z.):").find(templateLine)
|
||||||
|
if (match != null) {
|
||||||
|
prefix = match.value
|
||||||
|
body = templateLine.substring(match.value.length)
|
||||||
|
suffix = ""
|
||||||
|
} else {
|
||||||
|
prefix = templateLine.substringBefore(":") + ":"
|
||||||
|
body = templateLine.substringAfter(":")
|
||||||
|
suffix = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val separators = setOf(':', '|', '!', '/', ' ')
|
||||||
|
val tokens = mutableListOf<String>()
|
||||||
|
val isSeparator = mutableListOf<Boolean>()
|
||||||
|
|
||||||
|
var temp = StringBuilder()
|
||||||
|
for (char in body) {
|
||||||
|
if (char in separators) {
|
||||||
|
if (temp.isNotEmpty()) {
|
||||||
|
tokens.add(temp.toString())
|
||||||
|
isSeparator.add(false)
|
||||||
|
temp = StringBuilder()
|
||||||
|
}
|
||||||
|
tokens.add(char.toString())
|
||||||
|
isSeparator.add(true)
|
||||||
|
} else {
|
||||||
|
temp.append(char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (temp.isNotEmpty()) {
|
||||||
|
tokens.add(temp.toString())
|
||||||
|
isSeparator.add(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
val updatedBody = if (editState.templateFragment == "-") {
|
||||||
|
//-
|
||||||
|
var lastNoteBlockIdx = -1
|
||||||
|
for (idx in tokens.indices.reversed()) {
|
||||||
|
if (!isSeparator[idx]) {
|
||||||
|
lastNoteBlockIdx = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastNoteBlockIdx != -1) {
|
||||||
|
tokens.removeAt(lastNoteBlockIdx)
|
||||||
|
isSeparator.removeAt(lastNoteBlockIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cleanTokens = mutableListOf<String>()
|
||||||
|
var lastWasSeparator = false
|
||||||
|
for (i in tokens.indices) {
|
||||||
|
val token = tokens[i]
|
||||||
|
val isSep = isSeparator[i]
|
||||||
|
if (isSep) {
|
||||||
|
if (!lastWasSeparator) {
|
||||||
|
cleanTokens.add(token)
|
||||||
|
lastWasSeparator = true
|
||||||
|
} else {
|
||||||
|
val prevSep = cleanTokens.last()
|
||||||
|
if (prevSep == ":" && token != ":") {
|
||||||
|
cleanTokens[cleanTokens.lastIndex] = token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cleanTokens.add(token)
|
||||||
|
lastWasSeparator = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cleanTokens.joinToString("")
|
||||||
|
} else {
|
||||||
|
// +
|
||||||
|
var lastNoteBlockIdx = -1
|
||||||
|
for (idx in tokens.indices.reversed()) {
|
||||||
|
if (!isSeparator[idx]) {
|
||||||
|
lastNoteBlockIdx = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastNoteBlockIdx != -1) {
|
||||||
|
if (lastNoteBlockIdx < tokens.lastIndex && isSeparator[lastNoteBlockIdx + 1]) {
|
||||||
|
val nextSepIdx = lastNoteBlockIdx + 1
|
||||||
|
val sep = tokens[nextSepIdx]
|
||||||
|
tokens.add(nextSepIdx + 1, "D")
|
||||||
|
isSeparator.add(nextSepIdx + 1, false)
|
||||||
|
} else {
|
||||||
|
tokens.add("!")
|
||||||
|
isSeparator.add(true)
|
||||||
|
tokens.add("D")
|
||||||
|
isSeparator.add(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tokens.add("D")
|
||||||
|
isSeparator.add(false)
|
||||||
|
}
|
||||||
|
tokens.joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
val finalResult = "$prefix$updatedBody$suffix"
|
||||||
|
|
||||||
|
// println("=".repeat(100))
|
||||||
|
// println("EDITION FIN GRILLE (9999) : Action = ${editState.templateFragment}")
|
||||||
|
// println("RESULTAT COMPACTÉ (9999) : $finalResult")
|
||||||
|
// println("=".repeat(100))
|
||||||
|
return finalResult
|
||||||
|
}
|
||||||
|
|
||||||
private fun parseNoteAndOctave(s: String): Pair<String, Int> {
|
private fun parseNoteAndOctave(s: String): Pair<String, Int> {
|
||||||
val base = s.replace(Regex("[^drmfsltDRFSTzw]"), "")
|
val base = s.replace(Regex("[^drmfsltDRFSTzw]"), "")
|
||||||
var level = 0
|
var level = 0
|
||||||
|
|
@ -1211,6 +1399,27 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
return templateArray
|
return templateArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun encodeDuration(duration: Int): String {
|
||||||
|
return when (duration) {
|
||||||
|
1 -> "1";
|
||||||
|
2 -> "2";
|
||||||
|
3 -> "3";
|
||||||
|
4 -> "4"
|
||||||
|
6 -> "6";
|
||||||
|
8 -> "8";
|
||||||
|
10 -> "A";
|
||||||
|
11 -> "B";
|
||||||
|
12 -> "C";
|
||||||
|
14 -> "E";
|
||||||
|
16 -> "G";
|
||||||
|
20 -> "K";
|
||||||
|
24 -> "O";
|
||||||
|
28 -> "S";
|
||||||
|
32 -> "W"
|
||||||
|
else -> duration.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
private fun reconstructIt(finalResult: String, measure: Int): String {
|
private fun reconstructIt(finalResult: String, measure: Int): String {
|
||||||
if(finalResult.startsWith("T0:")) {
|
if(finalResult.startsWith("T0:")) {
|
||||||
/* T0 */
|
/* T0 */
|
||||||
|
|
@ -1312,27 +1521,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeDuration(duration: Int): String {
|
|
||||||
return when (duration) {
|
|
||||||
1 -> "1";
|
|
||||||
2 -> "2";
|
|
||||||
3 -> "3";
|
|
||||||
4 -> "4"
|
|
||||||
6 -> "6";
|
|
||||||
8 -> "8";
|
|
||||||
10 -> "A";
|
|
||||||
11 -> "B";
|
|
||||||
12 -> "C";
|
|
||||||
14 -> "E";
|
|
||||||
16 -> "G";
|
|
||||||
20 -> "K";
|
|
||||||
24 -> "O";
|
|
||||||
28 -> "S";
|
|
||||||
32 -> "W"
|
|
||||||
else -> duration.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var k = 0
|
var k = 0
|
||||||
while (k < buff.size) {
|
while (k < buff.size) {
|
||||||
if (consumedBlocks.contains(k)) {
|
if (consumedBlocks.contains(k)) {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,10 @@ import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.Build
|
import androidx.compose.material.icons.filled.Build
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.Description
|
import androidx.compose.material.icons.filled.Description
|
||||||
import androidx.compose.material.icons.filled.Save
|
import androidx.compose.material.icons.filled.Save
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
|
@ -780,13 +782,14 @@ fun LazyVerticalGridTUO(
|
||||||
|
|
||||||
if (tuo.isTriolet()) {
|
if (tuo.isTriolet()) {
|
||||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
val arcWidth = with(density) { size.width * 0.75f }
|
val arcWidth = with(density) { size.width * 0.65f }
|
||||||
drawArc(
|
drawArc(
|
||||||
color = FEUFAROO_TRIOLET_COLOR,
|
color = FEUFAROO_TRIOLET_COLOR,
|
||||||
startAngle = 200f,
|
startAngle = 200f,
|
||||||
sweepAngle = 140f,
|
sweepAngle = 140f,
|
||||||
useCenter = false,
|
useCenter = false,
|
||||||
topLeft = Offset(0f, yHeight / 2),
|
//topLeft = Offset(0f, yHeight/* / 2*/),
|
||||||
|
topLeft = Offset(size.width *0.05f, -yHeight * 0.1f),
|
||||||
size = Size(arcWidth, yHeight),
|
size = Size(arcWidth, yHeight),
|
||||||
style = Stroke(width = 2f)
|
style = Stroke(width = 2f)
|
||||||
|
|
||||||
|
|
@ -805,15 +808,24 @@ fun LazyVerticalGridTUO(
|
||||||
) {
|
) {
|
||||||
val xStart = if (lastHairPinSymbol == '>') -size.width * (tuo.numBlock - hairPinStart) else size.width / 2
|
val xStart = if (lastHairPinSymbol == '>') -size.width * (tuo.numBlock - hairPinStart) else size.width / 2
|
||||||
val xEnd = if (lastHairPinSymbol == '>') size.width / 2 else -size.width * (tuo.numBlock - hairPinStart)
|
val xEnd = if (lastHairPinSymbol == '>') size.width / 2 else -size.width * (tuo.numBlock - hairPinStart)
|
||||||
|
val offsetUp = -yHeight * 0.30f
|
||||||
|
val hairpinHeight = yHeight * 0.70f
|
||||||
|
|
||||||
|
val yTop = 0f + offsetUp
|
||||||
|
val yCenter = (hairpinHeight / 2) + offsetUp
|
||||||
|
val yBottom = hairpinHeight + offsetUp
|
||||||
|
|
||||||
drawLine(
|
drawLine(
|
||||||
Color.DarkGray,
|
Color.DarkGray,
|
||||||
start = Offset(x = xStart, y = 0f),
|
start = Offset(x = xStart, y = yTop),
|
||||||
end = Offset(xEnd, yHeight / 2)
|
end = Offset(xEnd, yCenter),
|
||||||
|
strokeWidth = 0.5f
|
||||||
)
|
)
|
||||||
drawLine(
|
drawLine(
|
||||||
Color.DarkGray,
|
Color.DarkGray,
|
||||||
start = Offset(xStart, yHeight),
|
start = Offset(xStart, yBottom),
|
||||||
end = Offset(xEnd, yHeight / 2)
|
end = Offset(xEnd, yCenter),
|
||||||
|
strokeWidth = 0.5f
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TimeUnitObject.endHairPin()
|
TimeUnitObject.endHairPin()
|
||||||
|
|
@ -942,6 +954,32 @@ fun LazyVerticalGridTUO(
|
||||||
val isActive = (globalIndex == activeRowIndex)
|
val isActive = (globalIndex == activeRowIndex)
|
||||||
val isSelectedByKeyboard = (globalIndex == selectedGridIndex)
|
val isSelectedByKeyboard = (globalIndex == selectedGridIndex)
|
||||||
|
|
||||||
|
val isAbsoluteLastGrid = (globalIndex == tuoList.size - 2)
|
||||||
|
if (editMode && isAbsoluteLastGrid) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(gridWidthDp / gridColumnCount)
|
||||||
|
.fillMaxHeight(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
GridActionMenu(
|
||||||
|
onAddMeasureRight = {
|
||||||
|
val state = TUOEditState(
|
||||||
|
tuoIndex = 9999,
|
||||||
|
templateFragment = "+"
|
||||||
|
)
|
||||||
|
sharedScreenModel.openTUOEditor(state)
|
||||||
|
},
|
||||||
|
onDeleteMeasure = {
|
||||||
|
val state = TUOEditState(
|
||||||
|
tuoIndex = 9999,
|
||||||
|
templateFragment = "-"
|
||||||
|
)
|
||||||
|
sharedScreenModel.openTUOEditor(state)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
val myTimestamp = sharedScreenModel.tuoTimestamps.value.getOrElse(globalIndex) { 0L }
|
val myTimestamp = sharedScreenModel.tuoTimestamps.value.getOrElse(globalIndex) { 0L }
|
||||||
var menuOffset by remember { mutableStateOf(Offset.Zero) }
|
var menuOffset by remember { mutableStateOf(Offset.Zero) }
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
@ -1135,6 +1173,7 @@ fun LazyVerticalGridTUO(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val allTemps = measureTUOs.map { it.getSingleSyllable(currentStanza) }
|
val allTemps = measureTUOs.map { it.getSingleSyllable(currentStanza) }
|
||||||
val maxLines = allTemps.maxOfOrNull { it.size } ?: 0
|
val maxLines = allTemps.maxOfOrNull { it.size } ?: 0
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue