solve syllabe(lyrics) collision with adaptive padding
This commit is contained in:
parent
bf6b4a99fa
commit
dd0590f9fd
1 changed files with 171 additions and 61 deletions
|
|
@ -1,50 +1,34 @@
|
|||
package mg.dot.feufaro.solfa
|
||||
|
||||
import SharedScreenModel
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.material3.Text
|
||||
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.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.BaselineShift
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import mg.dot.feufaro.data.GridTUOData
|
||||
import kotlin.math.min
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.BaselineShift
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import SharedScreenModel
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
val FEUFAROO_TRIOLET_COLOR = Color.DarkGray
|
||||
val FEUFAROO_KEY_CHANGE_COLOR = Color.Blue
|
||||
|
|
@ -225,6 +209,23 @@ class TimeUnitObject (val pTemplate: PTemplate, val prevTUO: TimeUnitObject?, co
|
|||
}
|
||||
return result.joinToString(separator = "\n") { it }
|
||||
}
|
||||
fun getSingleSyllable(stanzaNumber: Int): List<String> {
|
||||
val finalLines = mutableListOf<String>()
|
||||
|
||||
lyrics.forEach { stanzaLyrics ->
|
||||
val lines = stanzaLyrics.toList(stanzaNumber)
|
||||
lines.forEachIndexed { index, text ->
|
||||
val trimmed = text.trim()
|
||||
if (index < finalLines.size) {
|
||||
finalLines[index] = "${finalLines[index]} $trimmed".trim()
|
||||
} else {
|
||||
finalLines.add(trimmed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finalLines
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -423,13 +424,13 @@ fun TimeUnitComposable(
|
|||
)
|
||||
}
|
||||
}
|
||||
AutoResizingText(
|
||||
/*AutoResizingText(
|
||||
text = tuo.lyricsAsMultiString(stanzaNumber),
|
||||
minFontSize = 16.sp,
|
||||
maxFontSize = 16.sp,
|
||||
maxLines = 2,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
)*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -562,41 +563,150 @@ fun LazyVerticalGridTUO(
|
|||
} else {
|
||||
-1
|
||||
}
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
||||
horizontalArrangement = Arrangement.spacedBy(horizontalArrangementSpacing),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
//contentPadding = PaddingValues(8.dp), // c'est juste le padding extérieur de toute la liste
|
||||
// state = lazyGridState
|
||||
) {
|
||||
tuoList.drop(n=1).forEachIndexed { relativeIndex, oneTUO ->
|
||||
val isActive = (relativeIndex == activeRowIndex)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(flowRowSize)
|
||||
.background(Color.Transparent)
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
sharedScreenModel.updatePositionFromPartition(relativeIndex, nbTotalDesRow)
|
||||
println("590: relative $relativeIndex active? $isActive")
|
||||
println("TimeUnitObj:566 Clicked: ${oneTUO.numBlock} / relative index: $relativeIndex FL $flowRowSize totaRow $nbTotalDesRow")
|
||||
} ,
|
||||
onDoubleClick = {
|
||||
println("Double-Clicked: ${oneTUO.numBlock} / relative index: $relativeIndex")
|
||||
val measures = tuoList.drop(1).chunked(gridColumnCount)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
measures.forEachIndexed { measureIndex, measureTUOs ->
|
||||
Column(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
measureTUOs.forEachIndexed { indexInMeasure, oneTUO ->
|
||||
val globalIndex = (measureIndex * gridColumnCount) + indexInMeasure
|
||||
val isActive = (globalIndex == activeRowIndex)
|
||||
Box(modifier = Modifier.weight(1f)
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
sharedScreenModel.updatePositionFromPartition(globalIndex, nbTotalDesRow)
|
||||
// println("tempNum ${oneTUO.numBlock} mesasindex ${measureIndex} & indexinM $indexInMeasure gridCount $gridColumnCount")
|
||||
} ,
|
||||
onDoubleClick = {
|
||||
println("Double-Clicked: ${oneTUO.numBlock} / relative index: ")
|
||||
}
|
||||
)) {
|
||||
TimeUnitComposable(
|
||||
tuo = oneTUO,
|
||||
stanzaNumber = currentStanza,
|
||||
gridColumnCount = gridColumnCount,
|
||||
gridActive = isActive
|
||||
)
|
||||
}
|
||||
)
|
||||
) {
|
||||
TimeUnitComposable(
|
||||
tuo = oneTUO,
|
||||
currentStanza,
|
||||
gridColumnCount,
|
||||
gridActive = isActive
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val allTemps = measureTUOs.map { it.getSingleSyllable(currentStanza) }
|
||||
val maxLines = allTemps.maxOfOrNull { it.size } ?: 0
|
||||
|
||||
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
|
||||
allTemps.forEachIndexed { syls_i, syllables ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.widthIn(min = gridWidthDp / gridColumnCount)
|
||||
) {
|
||||
val noteWidth = gridWidthDp / gridColumnCount
|
||||
val density = LocalDensity.current
|
||||
syllables.joinToString { " " }
|
||||
syllables.mapIndexed { index, syl ->
|
||||
var textWidth by remember { mutableStateOf(0.dp) }
|
||||
|
||||
|
||||
val textLayoutResult = textMeasurer.measure(
|
||||
text = syl,
|
||||
maxLines = 1,
|
||||
softWrap = false
|
||||
)
|
||||
val textWidthDp = with(density) { textLayoutResult.size.width.toDp() }
|
||||
val containerWidthDp = gridWidthDp / gridColumnCount
|
||||
val isTooLong = textWidthDp > containerWidthDp
|
||||
|
||||
val spacer = makeSpaceBetweenSyllables(textMeasurer, gridWidthDp / gridColumnCount)
|
||||
val dynamicSpaceSyl = spacer(syl, allTemps, syls_i, index)
|
||||
Text(
|
||||
text = dynamicSpaceSyl,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 4.dp)
|
||||
.wrapContentSize(unbounded = true, align = Alignment.Center,),
|
||||
softWrap = false,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Visible,
|
||||
style = TextStyle(
|
||||
color = when {
|
||||
(isTooLong) -> Color(0XFFBFAC00)
|
||||
(index == 0) -> Color.Black
|
||||
else -> Color(0XFF3B3A39)
|
||||
},
|
||||
fontSize = 16.sp,
|
||||
),
|
||||
onTextLayout = { txtLayoutRes ->
|
||||
textWidth = with(density) { txtLayoutRes.size.width.toDp() }
|
||||
val textWidthPx = txtLayoutRes.size.width
|
||||
val noteWidthPx = with(density) { noteWidth.toPx() }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun makeSpaceBetweenSyllables(
|
||||
textMeasurer: TextMeasurer,
|
||||
noteWidthDp: Dp,
|
||||
fontSize: TextUnit = 16.sp
|
||||
): (String, List<List<String>>, Int, Int) -> String {
|
||||
val density = LocalDensity.current
|
||||
val noteWidthPx = with(density) { noteWidthDp.toPx() }
|
||||
val style = TextStyle(fontSize = fontSize)
|
||||
val spaceWidthPx = textMeasurer.measure("\u00A0", style).size.width
|
||||
|
||||
return { currentSyl, allT, s_i, index ->
|
||||
if (currentSyl.isEmpty()) ""
|
||||
else {
|
||||
val currentWidth = textMeasurer.measure(currentSyl, style).size.width
|
||||
val next_syl = allT.getOrNull(s_i + 1)?.getOrNull(index) ?: ""
|
||||
val prev_syl = if (s_i > 0) allT.getOrNull(s_i - 1)?.getOrNull(index) ?: "" else ""
|
||||
|
||||
val excessPx = currentWidth - noteWidthPx
|
||||
val neededSpaces = if (excessPx > 0) (excessPx / spaceWidthPx).toInt() + 1 else 0
|
||||
|
||||
val nextNextSyl = allT.getOrNull(s_i + 2)?.getOrNull(index) ?: ""
|
||||
|
||||
when {
|
||||
excessPx > 0 && next_syl.length < prev_syl.length -> {
|
||||
// Ici, currentSyl se décale à gauche.
|
||||
"\u00A0".repeat(neededSpaces) + currentSyl
|
||||
}
|
||||
excessPx > 0 && next_syl.length >= prev_syl.length -> {
|
||||
currentSyl + "\u00A0".repeat(neededSpaces)
|
||||
}
|
||||
|
||||
next_syl.isNotEmpty() -> {
|
||||
val nextWidth = textMeasurer.measure(next_syl, style).size.width
|
||||
val nextExcess = nextWidth - noteWidthPx
|
||||
|
||||
if (nextExcess > 0 && (allT.getOrNull(s_i + 2)?.getOrNull(index)?.length ?: 0) > next_syl.length) {
|
||||
// Si le mot d'après va se décaler à gauche, moi je me décale aussi à gauche
|
||||
val compensationSpaces = ((nextExcess / spaceWidthPx) / 2).toInt() + 1
|
||||
"\u00A0".repeat(compensationSpaces) + currentSyl
|
||||
} else {
|
||||
currentSyl
|
||||
}
|
||||
}
|
||||
|
||||
else -> currentSyl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue