diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/BlockPlacement.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/BlockPlacement.kt index 7258741..5605e13 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/BlockPlacement.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/BlockPlacement.kt @@ -4,7 +4,6 @@ import androidx.compose.animation.Crossfade import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.snap -import androidx.compose.foundation.layout.Column import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -15,14 +14,11 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Constraints -import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import de.jeanlucmakiola.floret.identity.rememberReduceMotion @@ -92,88 +88,34 @@ fun blockTimeLines(label: String, textWidth: Dp, spare: Dp): Int { } /** - * A timed block's title, over at most [maxLines]. + * A timed block's title, over at most [maxLines], breaking at word boundaries. * - * Wrapping and clipping pull against each other, which is why #164 left the - * ellipsis on multi-line chips: with `softWrap` on, the last visible line ends - * at a word boundary, so "Farmers Market" in a six-character column would clip - * to "Farmer" / "s" where the ellipsis at least reached "s Mar…". - * - * So the block wraps every line but the last through one `Text` and hands the - * remainder to a second that clips mid-glyph the way a single-line chip does. - * Every line is then full and none of them spends two of its few characters on - * a "…". RTL keeps the ellipsis for the reason [eventTitleOverflowFor] gives. + * #164 clipped the last line mid-glyph so none of a narrow chip's few + * characters went on an ellipsis. On a block that can wrap, whole words read + * better than full lines do: "Farmers Market" over two lines beats "Farmer" / + * "s Marke". A single line still clips, having nowhere to wrap to. */ @Composable fun BlockTitle( title: String, maxLines: Int, - textWidth: Dp, color: Color, modifier: Modifier = Modifier, textDecoration: TextDecoration? = null, fontWeight: FontWeight? = null, ) { - // Folded into the style rather than passed to the Text, so the wrap measured - // below is the wrap that gets drawn. - val style = MaterialTheme.typography.labelMedium - .let { if (fontWeight == null) it else it.copy(fontWeight = fontWeight) } - val rtl = LocalLayoutDirection.current == LayoutDirection.Rtl - val measurer = rememberTextMeasurer() - val widthPx = with(LocalDensity.current) { textWidth.roundToPx() } - // Where the wrapped lines stop and the clipped tail starts — null when the - // title fits, and nothing needs splitting. - val headEnd = remember(title, style, widthPx, maxLines, rtl, measurer) { - if (rtl || maxLines < 2 || widthPx <= 0) { - null - } else { - val layout = measurer.measure( - text = title, - style = style, - constraints = Constraints(maxWidth = widthPx), - ) - if (layout.lineCount <= maxLines) { - null - } else { - layout.getLineEnd(maxLines - 2, visibleEnd = true) - } - } - } - if (headEnd == null) { - val overflow = eventTitleOverflow(singleLine = maxLines == 1) - Text( - text = title, - modifier = modifier, - style = style, - maxLines = maxLines, - overflow = overflow.overflow, - softWrap = overflow.softWrap, - color = color, - textDecoration = textDecoration, - ) - } else { - val tail = eventTitleOverflow(singleLine = true) - Column(modifier = modifier) { - Text( - text = title.substring(0, headEnd), - style = style, - maxLines = maxLines - 1, - overflow = TextOverflow.Clip, - softWrap = true, - color = color, - textDecoration = textDecoration, - ) - Text( - text = title.substring(headEnd).trimStart(), - style = style, - maxLines = 1, - overflow = tail.overflow, - softWrap = tail.softWrap, - color = color, - textDecoration = textDecoration, - ) - } - } + val overflow = eventTitleOverflow(singleLine = maxLines == 1) + Text( + text = title, + modifier = modifier, + style = MaterialTheme.typography.labelMedium + .let { if (fontWeight == null) it else it.copy(fontWeight = fontWeight) }, + maxLines = maxLines, + overflow = overflow.overflow, + softWrap = overflow.softWrap, + color = color, + textDecoration = textDecoration, + ) } /** diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt index 72f9d06..fdcfbb6 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineDrag.kt @@ -814,7 +814,6 @@ private fun DragCopy( BlockTitle( title = title, maxLines = lines, - textWidth = textWidth, color = paint.titleInk, textDecoration = paint.decoration, fontWeight = paint.titleWeight, diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineScale.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineScale.kt index b236aef..d7c69f1 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineScale.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/TimelineScale.kt @@ -96,17 +96,6 @@ fun tappedMinuteOfDay(offsetY: Float, hourPx: Float): Int = */ const val MIN_EVENT_FRACTION = 26f / 60f -/** - * Narrowest an event block may be and still wrap its title over several lines. - * - * Wrapping is driven by the block's height, so a tall block on a lane-split - * column would otherwise stack two or three characters per line — "Da/ily", - * "Fa/rmer/s…" — which reads worse than one ellipsised line. A full week column - * clears this on any phone; a split one never does, while the day view's much - * wider columns keep wrapping even several lanes deep. - */ -val MIN_TITLE_WRAP_WIDTH = 36.dp - /** Smallest hour height [TimelineScale.FitDay] will resolve to. */ val FIT_DAY_MIN = 24.dp diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt index 0346190..d33f333 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/day/DayScreen.kt @@ -800,7 +800,6 @@ private fun EventBlock( BlockTitle( title = title, maxLines = titleMaxLines, - textWidth = textWidth, color = paint.titleInk, textDecoration = paint.decoration, fontWeight = paint.titleWeight, diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt index d9de65b..0e5d1cd 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/week/WeekScreen.kt @@ -130,7 +130,6 @@ import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat import de.jeanlucmakiola.calendula.ui.common.LocalShowHourLines import de.jeanlucmakiola.calendula.ui.common.LocalTimelineZoom import de.jeanlucmakiola.calendula.ui.common.MIN_EVENT_FRACTION -import de.jeanlucmakiola.calendula.ui.common.MIN_TITLE_WRAP_WIDTH import de.jeanlucmakiola.calendula.ui.common.hourHeight import de.jeanlucmakiola.calendula.ui.common.rememberTimelinePinchZoom import de.jeanlucmakiola.calendula.ui.common.formatMinuteOfDay @@ -905,18 +904,17 @@ private fun EventBlock( val contentHeight = available - if (showTime) timeLineHeight else 0.dp val titleBudget = (contentHeight / titleLineHeight).toInt().coerceAtLeast(1) val paint = eventPaint(block.event, dark) - val titleMaxLines = if (width < MIN_TITLE_WRAP_WIDTH) { - 1 - } else { - blockTextLines( - text = title, - // At the weight BlockTitle will set it in, or an invited block — - // drawn a weight lighter — is budgeted a line it never fills (#230). - style = MaterialTheme.typography.labelMedium.withTitleWeight(paint), - textWidth = textWidth, - max = titleBudget, - ) - } + // Every line the height affords, however narrow the lane: two events side by + // side leave columns well under a word wide, and cutting the title to one + // line there lost it outright where the block had the room to wrap it. + val titleMaxLines = blockTextLines( + text = title, + // At the weight BlockTitle will set it in, or an invited block — drawn a + // weight lighter — is budgeted a line it never fills (#230). + style = MaterialTheme.typography.labelMedium.withTitleWeight(paint), + textWidth = textWidth, + max = titleBudget, + ) val spare = available - titleLineHeight * titleMaxLines - if (showTime) timeLineHeight else 0.dp val timeMaxLines = if (showTime) blockTimeLines(timeLabel, textWidth, spare) else 1 @@ -970,7 +968,6 @@ private fun EventBlock( BlockTitle( title = title, maxLines = titleMaxLines, - textWidth = textWidth, color = paint.titleInk, textDecoration = paint.decoration, fontWeight = paint.titleWeight,