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 0f56ab1..11bdbbf 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt @@ -445,31 +445,87 @@ data class TUOWidthMeasure(val width: Dp, val isReady: Boolean) data class HairpinRange(val startBlock: Int, val endBlock: Int, val symbol: Char) @Composable -fun bestTUOWidth(items: List): TUOWidthMeasure { +fun bestTUOWidth( + items: List, + stanzaNumber: Int = 1, + transpositionInterval: Int = 0, + noteStyle: TextStyle = MaterialTheme.typography.bodyLarge, + reducedLyricStyle: TextStyle = MaterialTheme.typography.labelLarge +): TUOWidthMeasure { val textMeasurer = rememberTextMeasurer() val density = LocalDensity.current - var maxWidth by remember(items) { mutableStateOf(0.dp) } - var isReady by remember(items) { mutableStateOf(false) } - LaunchedEffect(items) { + var maxWidth by remember(items, noteStyle, transpositionInterval) { + mutableStateOf(0.dp) + } + var isReady by remember(items, noteStyle, transpositionInterval) { + mutableStateOf(false) + } + + LaunchedEffect(items, noteStyle, transpositionInterval) { isReady = false - maxWidth = 0.dp - items.forEach { - val textLayoutResult: TextLayoutResult = textMeasurer.measure( - text = it.noteAsMultiString(), - maxLines = 1 + var max = 0.dp +// println("=== [DEBUG TUO] Début mesure de ${items.size} blocs TUO ===") + + items.forEach { tuo -> + val rawNotes = tuo.noteAsMultiString() + val transposedNotes = Transpose.transposeText(text = rawNotes, interval = transpositionInterval) + val noteLines = transposedNotes.lines() + var maxNoteWidthPx = 0 + + noteLines.forEachIndexed { lineIdx, line -> + val annotatedNote = parseNoteToAnnotatedString(line) + val noteResult = textMeasurer.measure( + text = annotatedNote, + style = noteStyle, + maxLines = 1, + softWrap = false ) - val textWidth = with(density) { - textLayoutResult.size.width.toDp() + val widthPx = noteResult.size.width + if (widthPx > maxNoteWidthPx) { + maxNoteWidthPx = widthPx + } +// println("TUO #${tuo.getNum()} | Ligne $lineIdx | Brut: \"$line\" | Annoté: \"${annotatedNote.text}\" -> Largeur: ${widthPx}px") } - if (textWidth > maxWidth) { - maxWidth = textWidth + + val tuoMaxWidthDp = with(density) { maxNoteWidthPx.toDp() } +// println("--> TUO #${tuo.getNum()} | Largeur max du bloc: $tuoMaxWidthDp ou ${maxNoteWidthPx}px") + + if (tuoMaxWidthDp > max) { + max = tuoMaxWidthDp } } + + maxWidth = max isReady = true +// println("=== [DEBUG TUO] Largeur retenue (maxWidth finale) : $maxWidth ===") } - return TUOWidthMeasure(maxWidth + 13.dp, isReady) + + return TUOWidthMeasure(maxWidth, isReady) } + + +fun parseNoteToAnnotatedString( + rawNote: String, + superscriptStyle: SpanStyle = SpanStyle( + fontSize = 10.sp, + baselineShift = BaselineShift.Superscript + ) +): AnnotatedString { + return buildAnnotatedString { + val parts = rawNote.split(">") + parts.forEachIndexed { index, text -> + if (index % 2 == 1) { + withStyle(superscriptStyle) { + append(text + " ") + } + } else { + append(text) + } + } + } +} + @Composable fun AutoResizingText( text: String, @@ -591,11 +647,20 @@ fun LazyVerticalGridTUO( focusRequester.requestFocus() } } + val numerator = viewModel.measure.substringBefore("/").toIntOrNull() ?: 4 + val multiple = when (numerator) { + 2, 4, 8, 10 -> 4 + 3, 6, 9, 12 -> 3 + else -> 4 + } val density = LocalDensity.current val horizontalArrangementSpacing: Dp = 0.dp val columnGroupString = regexMeasure?.groupValues?.get(1) ?: "1" val columnGroup = columnGroupString.toInt().coerceAtLeast(1) - val tuoWidthMeasure = bestTUOWidth(tuoList) + val tuoWidthMeasure = bestTUOWidth( + items = tuoList, + noteStyle = MaterialTheme.typography.bodyLarge + ) val itemMinBaseWidth = tuoWidthMeasure.width val gridWidthDp = with(density) { gridWidthPx.toDp() } @@ -604,18 +669,27 @@ fun LazyVerticalGridTUO( val gridCount by sharedScreenModel.gridCount.collectAsState() - var gridColumnCount: Int = remember(gridWidthDp, itemMinBaseWidth, columnGroup) { - if (gridWidthDp == 0.dp) return@remember columnGroup - var calculatedCols = - (gridWidthDp / (itemMinBaseWidth + horizontalArrangementSpacing)).toInt() - if (calculatedCols == 0) calculatedCols = columnGroup - if (calculatedCols % columnGroup != 0) { - calculatedCols = (calculatedCols / columnGroup) * columnGroup - if (calculatedCols == 0) calculatedCols = columnGroup + var gridColumnCount: Int = remember(gridWidthDp, tuoWidthMeasure.isReady, tuoWidthMeasure.width, multiple) { + if (!tuoWidthMeasure.isReady || gridWidthDp <= 0.dp || tuoWidthMeasure.width <= 0.dp) { + return@remember multiple } - val actualColumnCount: Int = calculatedCols.coerceAtLeast(columnGroup) - actualColumnCount + + val rawCapacity = (gridWidthDp + 9.dp) / tuoWidthMeasure.width + val rawColsInt = rawCapacity.toInt() + val roundedToMultiple = (rawColsInt / multiple) * multiple + val finalCols = roundedToMultiple.coerceAtLeast(multiple) + +// println("Largeur écran/grille : $gridWidthDp") +// println("Largeur bloc TUO max : ${tuoWidthMeasure.width}") +// println("Signature temps (multiple) : $multiple") +// println("Capacité brute d'accueil : $rawCapacity blocs ($rawColsInt entiers)") +// println("Arrondi au multiple inférieur : $roundedToMultiple colonnes") +// println("Nombre final retenu (min $multiple) : $finalCols colonnes") +// println("=========================") + + finalCols } + gridColumnCount += gridCount val flowRowSize: Float = 0.98f / gridColumnCount