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
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
import SharedScreenModel
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.*
|
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.material3.LocalTextStyle
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
import androidx.compose.ui.draw.drawWithContent
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.combinedClickable
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.text.TextLayoutResult
|
import androidx.compose.ui.text.*
|
||||||
import androidx.compose.ui.text.TextStyle
|
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import mg.dot.feufaro.data.GridTUOData
|
import mg.dot.feufaro.data.GridTUOData
|
||||||
import kotlin.math.min
|
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_TRIOLET_COLOR = Color.DarkGray
|
||||||
val FEUFAROO_KEY_CHANGE_COLOR = Color.Blue
|
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 }
|
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
|
@Composable
|
||||||
|
|
@ -423,13 +424,13 @@ fun TimeUnitComposable(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AutoResizingText(
|
/*AutoResizingText(
|
||||||
text = tuo.lyricsAsMultiString(stanzaNumber),
|
text = tuo.lyricsAsMultiString(stanzaNumber),
|
||||||
minFontSize = 16.sp,
|
minFontSize = 16.sp,
|
||||||
maxFontSize = 16.sp,
|
maxFontSize = 16.sp,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -562,41 +563,150 @@ fun LazyVerticalGridTUO(
|
||||||
} else {
|
} else {
|
||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
FlowRow(
|
val measures = tuoList.drop(1).chunked(gridColumnCount)
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
horizontalArrangement = Arrangement.spacedBy(horizontalArrangementSpacing),
|
){
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
measures.forEachIndexed { measureIndex, measureTUOs ->
|
||||||
//contentPadding = PaddingValues(8.dp), // c'est juste le padding extérieur de toute la liste
|
Column(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)) {
|
||||||
// state = lazyGridState
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
) {
|
measureTUOs.forEachIndexed { indexInMeasure, oneTUO ->
|
||||||
tuoList.drop(n=1).forEachIndexed { relativeIndex, oneTUO ->
|
val globalIndex = (measureIndex * gridColumnCount) + indexInMeasure
|
||||||
val isActive = (relativeIndex == activeRowIndex)
|
val isActive = (globalIndex == activeRowIndex)
|
||||||
Box(
|
Box(modifier = Modifier.weight(1f)
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth(flowRowSize)
|
|
||||||
.background(Color.Transparent)
|
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
sharedScreenModel.updatePositionFromPartition(relativeIndex, nbTotalDesRow)
|
sharedScreenModel.updatePositionFromPartition(globalIndex, nbTotalDesRow)
|
||||||
println("590: relative $relativeIndex active? $isActive")
|
// println("tempNum ${oneTUO.numBlock} mesasindex ${measureIndex} & indexinM $indexInMeasure gridCount $gridColumnCount")
|
||||||
println("TimeUnitObj:566 Clicked: ${oneTUO.numBlock} / relative index: $relativeIndex FL $flowRowSize totaRow $nbTotalDesRow")
|
|
||||||
} ,
|
} ,
|
||||||
onDoubleClick = {
|
onDoubleClick = {
|
||||||
println("Double-Clicked: ${oneTUO.numBlock} / relative index: $relativeIndex")
|
println("Double-Clicked: ${oneTUO.numBlock} / relative index: ")
|
||||||
}
|
}
|
||||||
)
|
)) {
|
||||||
) {
|
|
||||||
TimeUnitComposable(
|
TimeUnitComposable(
|
||||||
tuo = oneTUO,
|
tuo = oneTUO,
|
||||||
currentStanza,
|
stanzaNumber = currentStanza,
|
||||||
gridColumnCount,
|
gridColumnCount = gridColumnCount,
|
||||||
gridActive = isActive
|
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