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 7e34651..7258741 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 @@ -33,6 +33,9 @@ val BLOCK_OUTER_INSET = 1.dp /** Padding between a timed block's edge and its text. */ val BLOCK_TEXT_PADDING = 4.dp +/** The same, above and below — what a block's height has to pay before any text. */ +val BLOCK_TEXT_INSET = 2.dp + /** Most lines a time label may wrap over before it is worth more than a title line. */ const val MAX_TIME_LINES = 2 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 d9ee0b7..22b2baa 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 @@ -82,9 +82,10 @@ data class TimelineDrag( /** The event as the grid would draw it, one piece per day column it covers. */ val pieces: List, /** - * Lines the block the finger picked up gave its title. The floating copy is - * that block's size, so it has to spend its height the same way or the text - * re-wraps under the finger and snaps back on drop (#267). + * Lines the block the finger picked up drew its title over, zero for one too + * short to have drawn it at all. The floating copy is that block's size, so + * it has to spend its height the same way or the text re-wraps under the + * finger and snaps back on drop (#267). */ val titleLines: Int, /** @@ -317,8 +318,8 @@ class TimelineDragController { */ private var clipOffsetMin = 0 - /** What the picked-up block measured its title over — see [TimelineDrag.titleLines]. */ - private var titleLines = 1 + /** What the picked-up block drew its title over — see [TimelineDrag.titleLines]. */ + private var titleLines = 0 /** * The slot the block already occupied when it was picked up — not the same @@ -354,7 +355,7 @@ class TimelineDragController { fun cancel() { source = null clipOffsetMin = 0 - titleLines = 1 + titleLines = 0 isDragging = false liftedInstanceId = null originSlot = null @@ -717,18 +718,23 @@ private fun DragCopy( val width = with(density) { sizePx.width.toDp() } val height = with(density) { sizePx.height.toDp() } val textWidth = width - (BLOCK_OUTER_INSET + BLOCK_TEXT_PADDING) * 2 - // The copy is the block's size and spends its height the same way, so text - // wraps here exactly where it wrapped there. Held to one line the range - // clipped for the drag's duration and snapped back on drop (#267). The one - // thing the copy does that a short block won't is always show the range — - // that is the feedback the drag is for. val titleLineHeight = with(density) { MaterialTheme.typography.labelMedium.lineHeight.toDp() } val timeLineHeight = with(density) { MaterialTheme.typography.labelSmall.lineHeight.toDp() } - val spare = height - 4.dp - titleLineHeight * titleLines - timeLineHeight + // The copy is the block's size and spends its height the same way, so text + // wraps here exactly where it wrapped there rather than reflowing under the + // finger and snapping back on drop (#267). The range is paid for first — + // where the drop lands is what the drag is for, and the block may have been + // too short to show it — and the title takes what of its lines still fit, + // since nothing clips a copy that outgrows its block. + val available = height - BLOCK_TEXT_INSET * 2 + val reserved = if (label == null) 0.dp else timeLineHeight + val titleBudget = ((available - reserved) / titleLineHeight).toInt().coerceAtLeast(0) + val lines = titleLines.coerceAtMost(titleBudget) + val spare = available - titleLineHeight * lines - reserved val timeMaxLines = if (label == null) 1 else blockTimeLines(label, textWidth, spare) Box( modifier = Modifier @@ -751,17 +757,19 @@ private fun DragCopy( clip = false } .eventSurface(paint, shape, cuts) - .padding(horizontal = BLOCK_TEXT_PADDING, vertical = 2.dp), + .padding(horizontal = BLOCK_TEXT_PADDING, vertical = BLOCK_TEXT_INSET), ) { Column { - BlockTitle( - title = title, - maxLines = titleLines, - textWidth = textWidth, - color = paint.titleInk, - textDecoration = paint.decoration, - fontWeight = paint.titleWeight, - ) + if (lines > 0) { + BlockTitle( + title = title, + maxLines = lines, + textWidth = textWidth, + color = paint.titleInk, + textDecoration = paint.decoration, + fontWeight = paint.titleWeight, + ) + } if (label != null) { val overflow = eventTitleOverflow(singleLine = timeMaxLines == 1) Text( 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 ed1b713..0346190 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 @@ -83,6 +83,7 @@ import de.jeanlucmakiola.calendula.ui.common.CalendarView import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS import de.jeanlucmakiola.calendula.ui.common.BLOCK_OUTER_INSET import de.jeanlucmakiola.calendula.ui.common.BLOCK_TEXT_PADDING +import de.jeanlucmakiola.calendula.ui.common.BLOCK_TEXT_INSET import de.jeanlucmakiola.calendula.ui.common.BlockTimeLabel import de.jeanlucmakiola.calendula.ui.common.BlockTitle import de.jeanlucmakiola.calendula.ui.common.blockTimeLines @@ -736,12 +737,11 @@ private fun EventBlock( val timeLineHeight = with(density) { MaterialTheme.typography.labelSmall.lineHeight.toDp() } - // What's left for text once the 2.dp top/bottom padding is paid for. A block - // that cannot afford both lines spends its space on the title, and one too - // short even for that drops the title rather than serving a sliced one. + // A block that cannot afford both lines spends its space on the title, and + // one too short even for that drops the title rather than serving a sliced one. // Height alone decides: a duration threshold would keep hiding the time on a // half-hour block the user has pinched open to three times the room it needs. - val available = height - 4.dp + val available = height - BLOCK_TEXT_INSET * 2 val showTime = available >= titleLineHeight + timeLineHeight val showTitle = available >= titleLineHeight val textWidth = width - (BLOCK_OUTER_INSET + BLOCK_TEXT_PADDING) * 2 @@ -766,7 +766,13 @@ private fun EventBlock( enabled = draggable, key = block.event.instanceId, onPickUp = { pointer, blockRoot, _ -> - dragController.begin(block, clipOffset, titleMaxLines, pointer, blockRoot) + dragController.begin( + block = block, + clipOffsetMin = clipOffset, + titleLines = if (showTitle) titleMaxLines else 0, + pointerInRoot = pointer, + blockInRoot = blockRoot, + ) }, onMove = dragController::move, onDrop = { dragController.finish()?.let(onDrop) }, @@ -783,7 +789,7 @@ private fun EventBlock( // After clickable, so it is the inner node and wins the main pass; // the tap still works, since a drag consumes the up. .then(dragModifier) - .padding(horizontal = BLOCK_TEXT_PADDING, vertical = 2.dp) + .padding(horizontal = BLOCK_TEXT_PADDING, vertical = BLOCK_TEXT_INSET) .semantics { contentDescription = "$title, $timeLabel" if (moveAction != null) customActions = listOf(moveAction) 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 3d97b23..d9de65b 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 @@ -91,6 +91,7 @@ import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha import de.jeanlucmakiola.calendula.ui.common.BLOCK_OUTER_INSET import de.jeanlucmakiola.calendula.ui.common.BLOCK_TEXT_PADDING +import de.jeanlucmakiola.calendula.ui.common.BLOCK_TEXT_INSET import de.jeanlucmakiola.calendula.ui.common.BlockTimeLabel import de.jeanlucmakiola.calendula.ui.common.BlockTitle import de.jeanlucmakiola.calendula.ui.common.blockTextLines @@ -882,8 +883,7 @@ private fun EventBlock( val timeLineHeight = with(density) { MaterialTheme.typography.labelSmall.lineHeight.toDp() } - // What's left for text once the 2.dp top/bottom padding is paid for. - val available = height - 4.dp + val available = height - BLOCK_TEXT_INSET * 2 // Only full-width (non-overlapping) blocks that are tall enough show the // time. On narrow overlapping columns we drop it so the title can wrap to // fill the whole block, mirroring Google Calendar — and a block that cannot @@ -936,7 +936,13 @@ private fun EventBlock( enabled = draggable, key = block.event.instanceId, onPickUp = { pointer, blockRoot, _ -> - dragController.begin(block, clipOffset, titleMaxLines, pointer, blockRoot) + dragController.begin( + block = block, + clipOffsetMin = clipOffset, + titleLines = if (showTitle) titleMaxLines else 0, + pointerInRoot = pointer, + blockInRoot = blockRoot, + ) }, onMove = dragController::move, onDrop = { dragController.finish()?.let(onDrop) }, @@ -953,7 +959,7 @@ private fun EventBlock( // After clickable, so it is the inner node and wins the main pass; // the tap still works, since a drag consumes the up. .then(dragModifier) - .padding(horizontal = BLOCK_TEXT_PADDING, vertical = 2.dp) + .padding(horizontal = BLOCK_TEXT_PADDING, vertical = BLOCK_TEXT_INSET) .semantics { contentDescription = "$title, $timeLabel" if (moveAction != null) customActions = listOf(moveAction)