diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da84d4..f30c64a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 the title now simply runs to the edge of its chip — a few more letters per cell, which is often the difference between two events you can tell apart and two you can't ([#164]). +- The calendar titles now shorten instead of being cut off. When the full month + name doesn't fit the top bar, the month and week views fall back to its + three-letter form and the day view drops the weekday, rather than trailing off + mid-word at large font sizes. The view switcher, the title and the agenda's + range bar also line up with the grid underneath them ([#165]). ## [2.19.1] — 2026-08-11 @@ -1422,3 +1427,4 @@ automatically, with zero telemetry and no internet permission. [#163]: https://codeberg.org/jlmakiola/calendula/issues/163 [#173]: https://codeberg.org/jlmakiola/calendula/issues/173 [#164]: https://codeberg.org/jlmakiola/calendula/issues/164 +[#165]: https://codeberg.org/jlmakiola/calendula/issues/165 diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt index b7a18ba..7f07d0b 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt @@ -43,6 +43,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -51,6 +52,8 @@ import de.jeanlucmakiola.calendula.data.prefs.PastEventDisplay import de.jeanlucmakiola.calendula.domain.EventInstance import de.jeanlucmakiola.calendula.domain.hasEnded import de.jeanlucmakiola.calendula.ui.common.AgendaRangePicker +import de.jeanlucmakiola.calendula.ui.common.AppBarSpacing +import de.jeanlucmakiola.calendula.ui.common.QuickSwitchConfig import de.jeanlucmakiola.calendula.ui.common.agendaRangeLabel import de.jeanlucmakiola.floret.identity.animateItemMotion import de.jeanlucmakiola.calendula.ui.common.CalendarDrawer @@ -160,13 +163,24 @@ fun AgendaScreen( // One bar at the top: the "showing …" header on the left and the // session range switcher on the right (one settings toggle). successState?.takeIf { it.showRangeBar }?.let { s -> + // end lines the selector up with whatever ends the top bar: + // the view switcher's background, or — once #150 hides it — + // the search icon's glyph. + val selectorEnd = if (quickSwitchViews.size >= QuickSwitchConfig.MIN_CYCLE) { + AppBarSpacing.Inset + } else { + AppBarSpacing.IconTrailingInset + } Row( verticalAlignment = Alignment.CenterVertically, - // end aligns the selector's right edge with the top-bar view - // switcher (its 8.dp margin + the app bar's 4.dp inset). modifier = Modifier .fillMaxWidth() - .padding(start = 28.dp, end = 12.dp, top = 8.dp, bottom = 8.dp), + .padding( + start = RANGE_BAR_TEXT_INSET, + end = selectorEnd, + top = 8.dp, + bottom = 8.dp, + ), ) { AgendaRangeBanner( range = s.range, @@ -246,6 +260,9 @@ private fun AgendaRangePill( } } +/** Optical start inset for the range bar's text, set against the title above it. */ +private val RANGE_BAR_TEXT_INSET = 28.dp + /** * A header naming the concrete window currently shown under a "showing …" label, * e.g. "27 Jun 2026" / "27 Jun – 3 Jul 2026" / "June 2026". The range's name @@ -419,9 +436,14 @@ private fun AgendaTopBar( ) { TopAppBar( title = { + // A plain label rather than a CalendarTitleButton, so it takes that + // one's inset and one-line clamp itself. Text( text = stringResource(R.string.view_agenda), style = MaterialTheme.typography.titleLarge, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(start = AppBarSpacing.TitleInset), ) }, navigationIcon = { @@ -444,7 +466,6 @@ private fun AgendaTopBar( current = selectedView, cycle = quickSwitchViews, onCycle = onCycleView, - modifier = Modifier.padding(end = 8.dp), ) }, colors = TopAppBarDefaults.topAppBarColors( diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/AppBarSpacing.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/AppBarSpacing.kt new file mode 100644 index 0000000..3c1e85f --- /dev/null +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/AppBarSpacing.kt @@ -0,0 +1,40 @@ +package de.jeanlucmakiola.calendula.ui.common + +import androidx.compose.ui.unit.dp + +/** + * Trailing-edge and title spacing shared by the four calendar app bars (#165). + * + * Only the trailing edge and the title's start are shared; the leading edge and + * the gaps between the action icons stay on M3's defaults, which derive both + * from the icon buttons' width. + */ +object AppBarSpacing { + + /** Side inset shared by the bars' trailing edge and the month grid. */ + val Inset = 8.dp + + /** + * Start inset on the title, taking it clear of the navigation icon M3 places + * it flush against. Real padding rather than a negative offset, which would + * swallow taps meant for the menu button. + */ + val TitleInset = 8.dp + + /** M3's own padding around the actions row. */ + internal val BarPadding = 4.dp + + private val IconButtonSize = 48.dp + + private val IconSize = 24.dp + + /** + * End padding for a container-backed control that ends the bar, measured to + * its background. Coerced because [androidx.compose.foundation.layout.padding] + * throws on a negative value. + */ + val ContainerTrailingInset = (Inset - BarPadding).coerceAtLeast(0.dp) + + /** Screen edge to the glyph of an icon button that ends the bar. */ + val IconTrailingInset = BarPadding + (IconButtonSize - IconSize) / 2 +} diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarTitleButton.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarTitleButton.kt index 4857aa0..305f0ec 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarTitleButton.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/CalendarTitleButton.kt @@ -1,6 +1,7 @@ package de.jeanlucmakiola.calendula.ui.common import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding @@ -12,6 +13,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -19,6 +21,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import de.jeanlucmakiola.calendula.R import kotlinx.datetime.LocalDate @@ -31,10 +35,9 @@ import kotlinx.datetime.LocalDate * [currentDate] seeds the picker with whatever the bar is currently naming (the * visible day, week start or month anchor). * - * The row keeps its own 8.dp inset rather than shifting back onto the app bar's - * start alignment: M3 places the title flush against the navigation icon's - * trailing edge, and the title is hit-tested on top of it, so a negative offset - * would swallow taps meant for the menu button. + * [shortTitle] replaces [title] when the full one does not fit the width the app + * bar hands the title slot (#165). Either way the line clamps to one and + * ellipsises, so pass a [shortTitle] wherever there is something left to drop. */ @Composable fun CalendarTitleButton( @@ -42,6 +45,7 @@ fun CalendarTitleButton( currentDate: LocalDate, onJumpToDate: (LocalDate) -> Unit, modifier: Modifier = Modifier, + shortTitle: String = title, ) { var showDatePicker by rememberSaveable { mutableStateOf(false) } @@ -53,14 +57,27 @@ fun CalendarTitleButton( onClickLabel = stringResource(R.string.drawer_jump_to_date), role = Role.Button, ) { showDatePicker = true } - .padding(horizontal = 8.dp), + .padding(horizontal = AppBarSpacing.TitleInset), verticalAlignment = Alignment.CenterVertically, ) { - Text( - text = title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.weight(1f, fill = false), - ) + val style = MaterialTheme.typography.titleLarge + BoxWithConstraints(modifier = Modifier.weight(1f, fill = false)) { + val measurer = rememberTextMeasurer() + val shown = remember(title, shortTitle, style, constraints.maxWidth, measurer) { + titleFor( + title = title, + shortTitle = shortTitle, + titleWidth = measurer.measure(title, style).size.width, + availableWidth = constraints.maxWidth, + ) + } + Text( + text = shown, + style = style, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } Icon( imageVector = Icons.Default.ArrowDropDown, contentDescription = null, @@ -79,3 +96,11 @@ fun CalendarTitleButton( ) } } + +/** Falls back to [shortTitle] when [title] is wider than [availableWidth] (#165). */ +internal fun titleFor( + title: String, + shortTitle: String, + titleWidth: Int, + availableWidth: Int, +): String = if (titleWidth <= availableWidth) title else shortTitle diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/ViewSwitcherPill.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/ViewSwitcherPill.kt index 825e549..3c60627 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/ViewSwitcherPill.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/ViewSwitcherPill.kt @@ -1,11 +1,13 @@ package de.jeanlucmakiola.calendula.ui.common +import androidx.compose.foundation.layout.padding import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp /** * Top-bar pill that shows the current view and cycles to the next one on tap @@ -14,6 +16,10 @@ import androidx.compose.ui.res.stringResource * Renders nothing when [cycle] holds fewer than [QuickSwitchConfig.MIN_CYCLE] * views (#150), so it drops straight into an app bar's `actions` slot without a * wrapping condition — the same way [TodayAction] handles being turned off. + * + * [trailingInset] lands the pill's background on the bar's own rhythm; pass + * `0.dp` outside a top app bar, where M3's actions padding is not there to + * cancel (#165). */ @Composable fun ViewSwitcherPill( @@ -21,12 +27,13 @@ fun ViewSwitcherPill( cycle: List, onCycle: () -> Unit, modifier: Modifier = Modifier, + trailingInset: Dp = AppBarSpacing.ContainerTrailingInset, ) { if (cycle.size < QuickSwitchConfig.MIN_CYCLE) return FilledTonalButton( onClick = onCycle, shape = MaterialTheme.shapes.large, - modifier = modifier, + modifier = modifier.padding(end = trailingInset), ) { Text(stringResource(current.labelRes)) } 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 a965694..e912c83 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 @@ -421,12 +421,17 @@ private fun DayTopBar( scrollBehavior: androidx.compose.material3.TopAppBarScrollBehavior, ) { val locale = currentLocale() + val (title, shortTitle) = remember(date, locale, currentYear) { + formatDayTitle(date, locale, currentYear) to + formatDayTitle(date, locale, currentYear, abbreviated = true) + } TopAppBar( title = { CalendarTitleButton( - title = formatDayTitle(date, locale, currentYear), + title = title, currentDate = date, onJumpToDate = onJumpToDate, + shortTitle = shortTitle, ) }, navigationIcon = { @@ -449,7 +454,6 @@ private fun DayTopBar( current = selectedView, cycle = quickSwitchViews, onCycle = onCycleView, - modifier = Modifier.padding(end = 8.dp), ) }, colors = TopAppBarDefaults.topAppBarColors( @@ -815,10 +819,16 @@ private fun DayLoading() { private fun minToHm(min: Int, is24Hour: Boolean, locale: Locale): String = formatMinuteOfDay(min, is24Hour, locale) -private fun formatDayTitle(date: LocalDate, locale: Locale, currentYear: Int): String = +/** [abbreviated] drops the weekday, leaving the date itself (#165). */ +private fun formatDayTitle( + date: LocalDate, + locale: Locale, + currentYear: Int, + abbreviated: Boolean = false, +): String = formatCalendarTitle( date = java.time.LocalDate.of(date.year, date.month.ordinal + 1, date.day), locale = locale, currentYear = currentYear, - skeleton = "EEEdMMM", + skeleton = if (abbreviated) "dMMM" else "EEEdMMM", ) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt index 61d8fc9..e77e858 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/month/MonthScreen.kt @@ -131,6 +131,7 @@ import de.jeanlucmakiola.calendula.ui.agenda.AgendaEmptyDayRow import de.jeanlucmakiola.calendula.ui.agenda.AgendaEventRow import de.jeanlucmakiola.calendula.ui.common.formatCalendarTitle import de.jeanlucmakiola.calendula.ui.common.CalendarDrawer +import de.jeanlucmakiola.calendula.ui.common.AppBarSpacing import de.jeanlucmakiola.calendula.ui.common.CalendarTitleButton import de.jeanlucmakiola.calendula.ui.common.CalendarFabColumn import de.jeanlucmakiola.calendula.ui.common.TodayAction @@ -292,10 +293,14 @@ fun MonthScreen( // carries the year instead of repeating it two lines further down. Dense has // no header to defer to, so it keeps the full title. val locale = currentLocale() - val topBarTitle = if (viewStyle == MonthViewStyle.Continuous) { - titleMonth.year.toString() - } else { - formatMonthTitle(titleMonth, locale, currentYear = today.year) + val (topBarTitle, topBarShortTitle) = remember(viewStyle, titleMonth, locale, today.year) { + if (viewStyle == MonthViewStyle.Continuous) { + val year = titleMonth.year.toString() + year to year + } else { + formatMonthTitle(titleMonth, locale, currentYear = today.year) to + formatMonthTitle(titleMonth, locale, currentYear = today.year, abbreviated = true) + } } // Slide direction for the grid transition: +1 = next, -1 = prev, 0 = jump (no slide). @@ -384,6 +389,7 @@ fun MonthScreen( topBar = { MonthTopBar( title = topBarTitle, + shortTitle = topBarShortTitle, titleDate = LocalDate(titleMonth.year, titleMonth.month, 1), selectedView = selectedView, onCycleView = { onSelectView(selectedView.next(quickSwitchViews)) }, @@ -649,6 +655,7 @@ private fun ContinuousMonthContent( @Composable private fun MonthTopBar( title: String, + shortTitle: String, titleDate: LocalDate, selectedView: CalendarView, onCycleView: () -> Unit, @@ -666,6 +673,7 @@ private fun MonthTopBar( title = title, currentDate = titleDate, onJumpToDate = onJumpToDate, + shortTitle = shortTitle, ) }, navigationIcon = { @@ -688,7 +696,6 @@ private fun MonthTopBar( current = selectedView, cycle = quickSwitchViews, onCycle = onCycleView, - modifier = Modifier.padding(end = 8.dp), ) }, scrollBehavior = scrollBehavior, @@ -706,7 +713,7 @@ internal fun WeekdayHeader(weekStart: DayOfWeek, showWeekNumbers: Boolean) { Row( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 8.dp, vertical = 4.dp), + .padding(horizontal = AppBarSpacing.Inset, vertical = 4.dp), ) { // Reserve the gutter so the weekday labels stay over their day columns. if (showWeekNumbers) Spacer(Modifier.width(WEEK_NUMBER_GUTTER)) @@ -769,10 +776,10 @@ internal fun MonthGrid( Column( modifier = Modifier .fillMaxSize() - // Match the weekday header's 8dp inset so day cells sit under their + // Match the weekday header's inset so day cells sit under their // labels, and so the week-number gutter's centre lines up with the // top bar's hamburger (4dp bar inset + 24dp half icon button). - .padding(horizontal = 8.dp, vertical = 4.dp), + .padding(horizontal = AppBarSpacing.Inset, vertical = 4.dp), verticalArrangement = Arrangement.spacedBy(2.dp), ) { val month = state.month @@ -864,7 +871,7 @@ private fun ContinuousMonthBlock( Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 8.dp) + .padding(horizontal = AppBarSpacing.Inset) .padding(bottom = CONTINUOUS_MONTH_GAP), verticalArrangement = Arrangement.spacedBy(2.dp), ) { @@ -946,7 +953,7 @@ internal fun DenseMonthGrid( state = listState, modifier = modifier .fillMaxSize() - .padding(horizontal = 8.dp), + .padding(horizontal = AppBarSpacing.Inset), verticalArrangement = Arrangement.spacedBy(2.dp), // Bottom inset clears the FAB stack so the last row stays tappable. contentPadding = PaddingValues(top = 4.dp, bottom = 96.dp), @@ -1406,7 +1413,7 @@ internal fun SplitMonthGrid( Column( modifier = modifier .fillMaxWidth() - .padding(horizontal = 8.dp, vertical = 4.dp), + .padding(horizontal = AppBarSpacing.Inset, vertical = 4.dp), verticalArrangement = Arrangement.spacedBy(2.dp), ) { state.weeks.forEach { week -> @@ -1771,7 +1778,7 @@ private fun ContinuousMonthSkeleton(dense: Boolean) { Column( modifier = Modifier .fillMaxSize() - .padding(horizontal = 8.dp) + .padding(horizontal = AppBarSpacing.Inset) .clipToBounds(), ) { if (!dense) { @@ -2419,7 +2426,7 @@ private fun MonthGridLoading() { Column( modifier = Modifier .fillMaxSize() - .padding(horizontal = 8.dp, vertical = 4.dp), + .padding(horizontal = AppBarSpacing.Inset, vertical = 4.dp), verticalArrangement = Arrangement.spacedBy(4.dp), ) { repeat(6) { @@ -2445,10 +2452,16 @@ private fun MonthGridLoading() { } } -private fun formatMonthTitle(ym: YearMonth, locale: Locale, currentYear: Int): String = +/** [abbreviated] swaps the full month name for its three-letter form (#165). */ +private fun formatMonthTitle( + ym: YearMonth, + locale: Locale, + currentYear: Int, + abbreviated: Boolean = false, +): String = formatCalendarTitle( date = java.time.LocalDate.of(ym.year, ym.month.ordinal + 1, 1), locale = locale, currentYear = currentYear, - skeleton = "LLLL", + skeleton = if (abbreviated) "LLL" else "LLLL", ) 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 ce365af..9fdfc5f 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 @@ -456,12 +456,17 @@ private fun WeekTopBar( scrollBehavior: androidx.compose.material3.TopAppBarScrollBehavior, ) { val locale = currentLocale() + val (title, shortTitle) = remember(weekStart, locale, currentYear) { + formatWeekTitle(weekStart, locale, currentYear) to + formatWeekTitle(weekStart, locale, currentYear, abbreviated = true) + } TopAppBar( title = { CalendarTitleButton( - title = formatWeekTitle(weekStart, locale, currentYear), + title = title, currentDate = weekStart, onJumpToDate = onJumpToDate, + shortTitle = shortTitle, ) }, navigationIcon = { @@ -484,7 +489,6 @@ private fun WeekTopBar( current = selectedView, cycle = quickSwitchViews, onCycle = onCycleView, - modifier = Modifier.padding(end = 8.dp), ) }, // Match the static top section exactly: plain surface, lifting to @@ -1019,13 +1023,18 @@ private fun minToHm(min: Int, is24Hour: Boolean, locale: java.util.Locale): Stri * "December" with no year would be actively misleading while the reader is * looking straight at January dates. */ -private fun formatWeekTitle(weekStart: LocalDate, locale: Locale, currentYear: Int): String { +private fun formatWeekTitle( + weekStart: LocalDate, + locale: Locale, + currentYear: Int, + abbreviated: Boolean = false, +): String { val weekEnd = weekStart.plus(6, kotlinx.datetime.DateTimeUnit.DAY) return formatCalendarTitle( date = java.time.LocalDate.of(weekStart.year, weekStart.month.ordinal + 1, 1), locale = locale, currentYear = currentYear, - skeleton = "LLLL", + skeleton = if (abbreviated) "LLL" else "LLLL", forceYear = weekEnd.year != weekStart.year, ) } diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/ui/common/AppBarSpacingTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/ui/common/AppBarSpacingTest.kt new file mode 100644 index 0000000..55810e3 --- /dev/null +++ b/app/src/test/java/de/jeanlucmakiola/calendula/ui/common/AppBarSpacingTest.kt @@ -0,0 +1,28 @@ +package de.jeanlucmakiola.calendula.ui.common + +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.Test + +class AppBarSpacingTest { + + @Test + fun `the pill's background ends one inset from the screen edge`() { + // The padding is applied inside M3's actions row, so it carries that + // row's own inset on top of whatever the pill asks for (#165). + val rendered = AppBarSpacing.BarPadding + AppBarSpacing.ContainerTrailingInset + assertThat(rendered).isEqualTo(AppBarSpacing.Inset) + } + + @Test + fun `trailing insets stay non-negative`() { + // Modifier.padding throws on a negative value. + assertThat(AppBarSpacing.ContainerTrailingInset.value).isAtLeast(0f) + assertThat(AppBarSpacing.IconTrailingInset.value).isAtLeast(0f) + } + + @Test + fun `an icon button's glyph ends further in than a container's edge`() { + assertThat(AppBarSpacing.IconTrailingInset.value) + .isGreaterThan(AppBarSpacing.Inset.value) + } +} diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/ui/common/CalendarTitleButtonTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/ui/common/CalendarTitleButtonTest.kt new file mode 100644 index 0000000..31786e3 --- /dev/null +++ b/app/src/test/java/de/jeanlucmakiola/calendula/ui/common/CalendarTitleButtonTest.kt @@ -0,0 +1,31 @@ +package de.jeanlucmakiola.calendula.ui.common + +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.Test + +class CalendarTitleButtonTest { + + @Test + fun `keeps the full title when it fits`() { + assertThat(titleFor("September", "Sep", titleWidth = 200, availableWidth = 300)) + .isEqualTo("September") + } + + @Test + fun `keeps the full title when it fills the slot exactly`() { + assertThat(titleFor("September", "Sep", titleWidth = 300, availableWidth = 300)) + .isEqualTo("September") + } + + @Test + fun `falls back to the short title when the full one overflows`() { + assertThat(titleFor("September", "Sep", titleWidth = 400, availableWidth = 300)) + .isEqualTo("Sep") + } + + @Test + fun `a caller with nothing to shorten still gets its own title back`() { + assertThat(titleFor("Wed, 2 Sep", "Wed, 2 Sep", titleWidth = 400, availableWidth = 300)) + .isEqualTo("Wed, 2 Sep") + } +}