Use floret's checked, switch and custom rows, hints and option rows
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package de.jeanlucmakiola.calendula.ui.calendars
|
||||
|
||||
import android.net.Uri
|
||||
import android.text.format.DateUtils
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -58,10 +57,12 @@ import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.DialogAmountField
|
||||
import de.jeanlucmakiola.floret.components.DialogUnitDropdown
|
||||
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.GroupedSectionHeader
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.floret.locale.relativeTimeAgo
|
||||
import java.time.LocalDate
|
||||
|
||||
// SAF mime filter for the restore picker. Source apps hand `.ics` files out
|
||||
@@ -224,7 +225,7 @@ private fun BackupContent(
|
||||
|
||||
if (exportable.isEmpty()) {
|
||||
GroupedSectionHeader(stringResource(R.string.calendars_restore_header))
|
||||
HintText(restoreSummary)
|
||||
GroupedHint(restoreSummary, style = MaterialTheme.typography.bodySmall)
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.calendars_restore_action),
|
||||
position = Position.Alone,
|
||||
@@ -234,7 +235,7 @@ private fun BackupContent(
|
||||
return
|
||||
}
|
||||
|
||||
HintText(stringResource(R.string.calendars_backup_hint))
|
||||
GroupedHint(stringResource(R.string.calendars_backup_hint), style = MaterialTheme.typography.bodySmall)
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.calendars_backup_action),
|
||||
position = Position.Top,
|
||||
@@ -273,7 +274,7 @@ private fun BackupContent(
|
||||
position = Position.Bottom,
|
||||
onClick = onEditInterval,
|
||||
)
|
||||
HintText(backupStatusText(autoBackup.status))
|
||||
GroupedHint(backupStatusText(autoBackup.status), style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +313,7 @@ private fun ExportCalendarPicker(
|
||||
onDismiss = onDismiss,
|
||||
predictiveBack = true,
|
||||
) {
|
||||
HintText(stringResource(R.string.calendars_export_hint))
|
||||
GroupedHint(stringResource(R.string.calendars_export_hint), style = MaterialTheme.typography.bodySmall)
|
||||
calendars.forEachIndexed { index, calendar ->
|
||||
val isSelected = calendar.id in selected
|
||||
GroupedRow(
|
||||
@@ -378,9 +379,7 @@ private fun backupIntervalLabel(minutes: Long): String {
|
||||
@Composable
|
||||
private fun backupStatusText(status: BackupStatus): String {
|
||||
if (status.lastRun == 0L) return stringResource(R.string.calendars_auto_backup_status_never)
|
||||
val relative = DateUtils.getRelativeTimeSpanString(
|
||||
status.lastRun, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
|
||||
).toString()
|
||||
val relative = relativeTimeAgo(status.lastRun)
|
||||
return if (status.lastSuccess) {
|
||||
stringResource(R.string.calendars_auto_backup_status_ok, relative)
|
||||
} else {
|
||||
|
||||
@@ -83,12 +83,12 @@ import de.jeanlucmakiola.calendula.ui.common.LeadingAvatar
|
||||
import de.jeanlucmakiola.calendula.ui.common.SourceLogo
|
||||
import de.jeanlucmakiola.calendula.ui.common.curatedSourcePackage
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedSectionHeader
|
||||
import de.jeanlucmakiola.floret.identity.LocalFloretDarkTheme
|
||||
import de.jeanlucmakiola.floret.identity.collapseExit
|
||||
import de.jeanlucmakiola.floret.identity.expandEnter
|
||||
import de.jeanlucmakiola.floret.identity.predictiveBack
|
||||
import de.jeanlucmakiola.floret.components.GroupedListInset
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.InlineTextField
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
@@ -214,7 +214,7 @@ private fun CalendarsList(
|
||||
visible = visible,
|
||||
) {
|
||||
// What the per-calendar / per-account switches below actually do.
|
||||
HintText(stringResource(R.string.calendars_visibility_hint))
|
||||
GroupedHint(stringResource(R.string.calendars_visibility_hint), style = MaterialTheme.typography.bodySmall)
|
||||
|
||||
// Local (device-only) calendars — one collapsible group. The header's
|
||||
// "+" adds a calendar; the switch enables/disables them all at once;
|
||||
@@ -235,7 +235,7 @@ private fun CalendarsList(
|
||||
onToggleAll = { enabled -> onSetAccountVisible(local.map { it.id }, enabled) },
|
||||
) {
|
||||
if (local.isEmpty()) {
|
||||
HintText(stringResource(R.string.calendars_local_empty))
|
||||
GroupedHint(stringResource(R.string.calendars_local_empty), style = MaterialTheme.typography.bodySmall)
|
||||
} else {
|
||||
local.forEachIndexed { index, calendar ->
|
||||
val disabled = !calendar.isVisibleInSystem
|
||||
@@ -282,7 +282,7 @@ private fun CalendarsList(
|
||||
// collapsible group whose header opens the source app (icon) and toggles
|
||||
// all of its calendars at once (switch).
|
||||
GroupedSectionHeader(stringResource(R.string.calendars_synced_header))
|
||||
HintText(stringResource(R.string.calendars_synced_hint))
|
||||
GroupedHint(stringResource(R.string.calendars_synced_hint), style = MaterialTheme.typography.bodySmall)
|
||||
synced
|
||||
.groupByAccount()
|
||||
.forEach { group ->
|
||||
@@ -830,16 +830,6 @@ private fun CalendarGroupMenu(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun HintText(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = GroupedListInset, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pick the app to open for managing a synced calendar's account. The account's
|
||||
|
||||
@@ -34,7 +34,6 @@ import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.domain.CalendarSource
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
|
||||
/**
|
||||
* The app's single "which calendar" selection list, shared by the event editor
|
||||
@@ -114,17 +113,11 @@ private fun CalendarPickerGroup(
|
||||
leading = leading,
|
||||
)
|
||||
calendars.forEachIndexed { index, calendar ->
|
||||
val isSelected = calendar.id == selectedId
|
||||
GroupedRow(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
position = if (index == calendars.lastIndex) Position.Bottom else Position.Middle,
|
||||
selected = isSelected,
|
||||
checked = calendar.id == selectedId,
|
||||
leading = { CalendarColorChip(calendar.color) },
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = { onSelect(calendar.id) },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package de.jeanlucmakiola.calendula.ui.common
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -20,17 +16,16 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.floret.components.CustomAmountEditor
|
||||
import de.jeanlucmakiola.floret.components.CustomExpandRow
|
||||
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
||||
import de.jeanlucmakiola.floret.components.GroupedListInset
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.floret.identity.collapseExit
|
||||
import de.jeanlucmakiola.floret.identity.expandEnter
|
||||
import de.jeanlucmakiola.floret.locale.currentLocale
|
||||
import de.jeanlucmakiola.floret.reminders.ReminderOverride
|
||||
import de.jeanlucmakiola.floret.reminders.ReminderUnit
|
||||
import de.jeanlucmakiola.floret.reminders.minutesOrEmpty
|
||||
import de.jeanlucmakiola.floret.reminders.reminderOverrideForMinutes
|
||||
import de.jeanlucmakiola.calendula.ui.agenda.AgendaRange
|
||||
import de.jeanlucmakiola.calendula.ui.agenda.agendaRangeWindowSummary
|
||||
@@ -88,7 +83,7 @@ fun ReminderDefaultPicker(
|
||||
}
|
||||
val inherits = current is ReminderOverride.Inherit
|
||||
val isNone = current is ReminderOverride.None
|
||||
val selectedMinutes = (current as? ReminderOverride.Minutes)?.minutes.orEmpty()
|
||||
val selectedMinutes = current.minutesOrEmpty()
|
||||
// Custom (non-preset) lead times seen this session, so unchecking one keeps
|
||||
// its row (unchecked) until the picker closes instead of vanishing mid-tap,
|
||||
// which would strand a hand-entered value with no way to re-check it.
|
||||
@@ -119,24 +114,14 @@ fun ReminderDefaultPicker(
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.reminder_use_default),
|
||||
position = Position.Top,
|
||||
selected = inherits,
|
||||
trailing = if (inherits) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = inherits,
|
||||
onClick = { apply(ReminderOverride.Inherit) },
|
||||
)
|
||||
}
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.reminder_none),
|
||||
position = if (allowInherit) Position.Bottom else Position.Alone,
|
||||
selected = isNone,
|
||||
trailing = if (isNone) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = isNone,
|
||||
onClick = { apply(ReminderOverride.None) },
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
@@ -152,17 +137,11 @@ fun ReminderDefaultPicker(
|
||||
onClick = { toggle(minute) },
|
||||
)
|
||||
}
|
||||
// When expanded, the Custom row connects downward into the editor card
|
||||
// so the two read as one grouped container (the per-calendar pattern).
|
||||
GroupedRow(
|
||||
CustomExpandRow(
|
||||
title = stringResource(R.string.event_edit_reminder_custom),
|
||||
position = if (customExpanded) Position.Top else positionOf(rows.size, rowCount),
|
||||
onClick = { customExpanded = !customExpanded },
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = customExpanded,
|
||||
enter = expandEnter(),
|
||||
exit = collapseExit(),
|
||||
position = positionOf(rows.size, rowCount),
|
||||
expanded = customExpanded,
|
||||
onExpandedChange = { customExpanded = it },
|
||||
) {
|
||||
CustomReminderEditor(
|
||||
amountText = amountText,
|
||||
@@ -210,19 +189,6 @@ private fun CustomReminderEditor(
|
||||
)
|
||||
}
|
||||
|
||||
/** A short explanatory paragraph shown under a picker's title, above the rows,
|
||||
* on the same left margin as the rows themselves. */
|
||||
@Composable
|
||||
internal fun PickerDescription(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = GroupedListInset, vertical = 8.dp),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Agenda-range picker, full-screen. Two grouped lists — the calendar-aligned
|
||||
* options (today / this week / this month) and the rolling windows (next 7 / 30
|
||||
@@ -263,17 +229,11 @@ fun AgendaRangePicker(
|
||||
}
|
||||
|
||||
val rangeRow: @Composable (AgendaRange, Position) -> Unit = { option, position ->
|
||||
val isSelected = option == selected
|
||||
GroupedRow(
|
||||
title = agendaRangeLabel(option),
|
||||
summary = windowSummary(option),
|
||||
position = position,
|
||||
selected = isSelected,
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = option == selected,
|
||||
onClick = {
|
||||
onSelect(option)
|
||||
onDismiss()
|
||||
@@ -282,7 +242,7 @@ fun AgendaRangePicker(
|
||||
}
|
||||
|
||||
FullScreenPicker(title = title, onDismiss = onDismiss) {
|
||||
PickerDescription(description)
|
||||
GroupedHint(description, verticalPadding = 8.dp)
|
||||
|
||||
// Calendar-aligned windows.
|
||||
calendarAligned.forEachIndexed { index, option ->
|
||||
@@ -295,7 +255,7 @@ fun AgendaRangePicker(
|
||||
rolling.forEachIndexed { index, option ->
|
||||
rangeRow(option, positionOf(index, rollingRowCount))
|
||||
}
|
||||
GroupedRow(
|
||||
CustomExpandRow(
|
||||
title = if (customSelected) {
|
||||
agendaRangeLabel(selected)
|
||||
} else {
|
||||
@@ -304,19 +264,10 @@ fun AgendaRangePicker(
|
||||
// An unset custom window has no day count yet, so the row stays
|
||||
// single-line until it does.
|
||||
summary = if (customSelected) windowSummary(selected) else null,
|
||||
position = if (customExpanded) Position.Top else positionOf(rolling.size, rollingRowCount),
|
||||
selected = customSelected,
|
||||
trailing = if (customSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = { customExpanded = !customExpanded },
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = customExpanded,
|
||||
enter = expandEnter(),
|
||||
exit = collapseExit(),
|
||||
position = positionOf(rolling.size, rollingRowCount),
|
||||
expanded = customExpanded,
|
||||
onExpandedChange = { customExpanded = it },
|
||||
checked = customSelected,
|
||||
) {
|
||||
CustomDaysEditor(
|
||||
amountText = amountText,
|
||||
@@ -394,17 +345,12 @@ fun DurationPicker(
|
||||
}
|
||||
|
||||
FullScreenPicker(title = title, onDismiss = onDismiss, predictiveBack = true) {
|
||||
if (description != null) PickerDescription(description)
|
||||
if (description != null) GroupedHint(description, verticalPadding = 8.dp)
|
||||
if (inheritLabel != null) {
|
||||
GroupedRow(
|
||||
title = inheritLabel,
|
||||
position = Position.Alone,
|
||||
selected = inherits,
|
||||
trailing = if (inherits) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = inherits,
|
||||
onClick = {
|
||||
onSelect(null)
|
||||
onDismiss()
|
||||
@@ -413,39 +359,22 @@ fun DurationPicker(
|
||||
Spacer(Modifier.height(24.dp))
|
||||
}
|
||||
presets.forEachIndexed { index, minute ->
|
||||
val isSelected = minute == selected
|
||||
GroupedRow(
|
||||
title = label(minute),
|
||||
position = positionOf(index, rowCount),
|
||||
selected = isSelected,
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = minute == selected,
|
||||
onClick = {
|
||||
onSelect(minute)
|
||||
onDismiss()
|
||||
},
|
||||
)
|
||||
}
|
||||
// The Custom row connects downward into the editor card when expanded, so
|
||||
// the two read as one grouped container (the shared custom-expand pattern).
|
||||
GroupedRow(
|
||||
CustomExpandRow(
|
||||
title = custom?.let { label(it) } ?: stringResource(R.string.event_edit_reminder_custom),
|
||||
position = if (customExpanded) Position.Top else positionOf(presets.size, rowCount),
|
||||
selected = custom != null,
|
||||
trailing = if (custom != null) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = { customExpanded = !customExpanded },
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = customExpanded,
|
||||
enter = expandEnter(),
|
||||
exit = collapseExit(),
|
||||
position = positionOf(presets.size, rowCount),
|
||||
expanded = customExpanded,
|
||||
onExpandedChange = { customExpanded = it },
|
||||
checked = custom != null,
|
||||
) {
|
||||
CustomDurationEditor(
|
||||
amountText = amountText,
|
||||
|
||||
@@ -42,7 +42,6 @@ import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.GroupedSectionHeader
|
||||
import de.jeanlucmakiola.floret.components.InlineTextField
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.floret.locale.currentLocale
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -117,13 +116,8 @@ fun TimeZonePickerDialog(
|
||||
title = stringResource(R.string.event_edit_timezone_device),
|
||||
summary = deviceSummary,
|
||||
position = Position.Alone,
|
||||
selected = selected == null,
|
||||
checked = selected == null,
|
||||
leading = { Icon(Icons.Default.Public, contentDescription = null) },
|
||||
trailing = if (selected == null) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = { choose(null) },
|
||||
)
|
||||
}
|
||||
@@ -244,12 +238,7 @@ private fun ZoneRow(
|
||||
title = zone.label,
|
||||
summary = zoneDescriptor(zone),
|
||||
position = position,
|
||||
selected = selected,
|
||||
trailing = if (selected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = selected,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -160,7 +160,6 @@ import de.jeanlucmakiola.floret.components.InlineTextField
|
||||
import de.jeanlucmakiola.floret.components.OptionCard
|
||||
import de.jeanlucmakiola.floret.components.OptionPicker
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.groupedShape as floretGroupedShape
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.calendula.ui.common.REMINDER_PRESETS
|
||||
@@ -1672,12 +1671,7 @@ private fun RecurrencePickerDialog(
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.event_edit_recurrence_none),
|
||||
position = positionOf(0, rowCount),
|
||||
selected = current == null,
|
||||
trailing = if (current == null) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = current == null,
|
||||
onClick = { onSelect(null) },
|
||||
)
|
||||
RecurrenceFreq.entries.forEachIndexed { index, entry ->
|
||||
@@ -1685,24 +1679,14 @@ private fun RecurrencePickerDialog(
|
||||
title = stringResource(recurrencePresetLabel(entry)),
|
||||
summary = nextOccurrencesText(SimpleRecurrence(entry), startDate, locale),
|
||||
position = positionOf(index + 1, rowCount),
|
||||
selected = isPlainPreset && parsed?.freq == entry,
|
||||
trailing = if (isPlainPreset && parsed?.freq == entry) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = isPlainPreset && parsed?.freq == entry,
|
||||
onClick = { onSelect(SimpleRecurrence(entry).toRRule()) },
|
||||
)
|
||||
}
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.event_edit_recurrence_custom),
|
||||
position = positionOf(rowCount - 1, rowCount),
|
||||
selected = current != null && !isPlainPreset,
|
||||
trailing = if (current != null && !isPlainPreset) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = current != null && !isPlainPreset,
|
||||
onClick = { customMode = true },
|
||||
)
|
||||
} else {
|
||||
@@ -1831,24 +1815,14 @@ private fun RecurrencePickerDialog(
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.event_edit_recurrence_end_never),
|
||||
position = Position.Top,
|
||||
selected = endMode == RecurrenceEndMode.Never,
|
||||
trailing = if (endMode == RecurrenceEndMode.Never) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = endMode == RecurrenceEndMode.Never,
|
||||
onClick = { endMode = RecurrenceEndMode.Never },
|
||||
)
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.event_edit_recurrence_end_until),
|
||||
summary = untilSummary,
|
||||
position = Position.Middle,
|
||||
selected = endMode == RecurrenceEndMode.Until,
|
||||
trailing = if (endMode == RecurrenceEndMode.Until) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = endMode == RecurrenceEndMode.Until,
|
||||
onClick = {
|
||||
endMode = RecurrenceEndMode.Until
|
||||
showUntilPicker = true
|
||||
@@ -1861,12 +1835,7 @@ private fun RecurrencePickerDialog(
|
||||
} else {
|
||||
Position.Bottom
|
||||
},
|
||||
selected = endMode == RecurrenceEndMode.Count,
|
||||
trailing = if (endMode == RecurrenceEndMode.Count) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = endMode == RecurrenceEndMode.Count,
|
||||
onClick = { endMode = RecurrenceEndMode.Count },
|
||||
)
|
||||
// Animated for the same reason the weekday card is: this is the
|
||||
|
||||
@@ -19,7 +19,6 @@ import de.jeanlucmakiola.calendula.ui.agenda.AgendaViewPreview
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
||||
import de.jeanlucmakiola.calendula.ui.common.LocalMultiDayCount
|
||||
import de.jeanlucmakiola.calendula.ui.common.PickerDescription
|
||||
import de.jeanlucmakiola.calendula.ui.common.icon
|
||||
import de.jeanlucmakiola.calendula.ui.common.label
|
||||
import de.jeanlucmakiola.calendula.ui.month.MonthStylePreview
|
||||
@@ -27,11 +26,11 @@ import de.jeanlucmakiola.calendula.ui.month.MonthViewStyle
|
||||
import de.jeanlucmakiola.calendula.ui.month.descriptionRes
|
||||
import de.jeanlucmakiola.calendula.ui.month.labelRes
|
||||
import de.jeanlucmakiola.calendula.ui.week.WeekViewPreview
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedListInset
|
||||
import de.jeanlucmakiola.floret.components.OnboardingScaffold
|
||||
import de.jeanlucmakiola.floret.components.OnboardingSpace
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.ViewPreviewFrame
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.floret.locale.currentLocale
|
||||
@@ -77,11 +76,10 @@ internal fun ViewStep(
|
||||
}
|
||||
}
|
||||
IMPLEMENTED_VIEWS.forEachIndexed { index, view ->
|
||||
val isSelected = view == choice.defaultView
|
||||
GroupedRow(
|
||||
title = view.label(),
|
||||
position = positionOf(index, IMPLEMENTED_VIEWS.size),
|
||||
selected = isSelected,
|
||||
checked = view == choice.defaultView,
|
||||
leading = {
|
||||
Icon(
|
||||
imageVector = view.icon,
|
||||
@@ -89,11 +87,6 @@ internal fun ViewStep(
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
},
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = { onSelect(view) },
|
||||
)
|
||||
}
|
||||
@@ -127,18 +120,12 @@ internal fun MonthStyleStep(
|
||||
StepPreview(selected = choice.monthViewStyle) { style ->
|
||||
MonthStylePreview(style = style, weekStart = weekStart, height = PREVIEW_HEIGHT)
|
||||
}
|
||||
PickerDescription(stringResource(choice.monthViewStyle.descriptionRes))
|
||||
GroupedHint(stringResource(choice.monthViewStyle.descriptionRes), verticalPadding = 8.dp)
|
||||
options.forEachIndexed { index, style ->
|
||||
val isSelected = style == choice.monthViewStyle
|
||||
GroupedRow(
|
||||
title = stringResource(style.labelRes),
|
||||
position = positionOf(index, options.size),
|
||||
selected = isSelected,
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
checked = style == choice.monthViewStyle,
|
||||
onClick = { onSelect(style) },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,18 +51,18 @@ import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.data.appname.LauncherName
|
||||
import de.jeanlucmakiola.calendula.data.prefs.ThemeMode
|
||||
import de.jeanlucmakiola.calendula.domain.FontRole
|
||||
import de.jeanlucmakiola.calendula.ui.common.PickerDescription
|
||||
import de.jeanlucmakiola.calendula.ui.theme.BundledFont
|
||||
import de.jeanlucmakiola.calendula.ui.theme.FONT_CUSTOM_TOKEN
|
||||
import de.jeanlucmakiola.calendula.ui.theme.FONT_SYSTEM_TOKEN
|
||||
import de.jeanlucmakiola.calendula.ui.theme.resolveFontFamily
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedListInset
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.GroupedSwitchRow
|
||||
import de.jeanlucmakiola.floret.components.OptionPicker
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
|
||||
/**
|
||||
@@ -127,17 +127,12 @@ internal fun AppearanceScreen(
|
||||
null
|
||||
},
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_soften_colors),
|
||||
checked = state.softenColors,
|
||||
onCheckedChange = viewModel::setSoftenColors,
|
||||
summary = stringResource(R.string.settings_soften_colors_summary),
|
||||
position = Position.Bottom,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.softenColors,
|
||||
onCheckedChange = viewModel::setSoftenColors,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setSoftenColors(!state.softenColors) },
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
@@ -209,7 +204,7 @@ internal fun AppearanceScreen(
|
||||
val systemDark = isSystemInDarkTheme()
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_theme),
|
||||
header = { PickerDescription(stringResource(R.string.settings_theme_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_theme_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = ThemeMode.entries,
|
||||
selected = state.themeMode,
|
||||
@@ -407,7 +402,7 @@ private fun FontOptionRow(
|
||||
GroupedRow(
|
||||
title = label,
|
||||
position = position,
|
||||
selected = selected,
|
||||
checked = selected,
|
||||
leading = {
|
||||
if (preview != null) {
|
||||
Text(
|
||||
@@ -419,11 +414,6 @@ private fun FontOptionRow(
|
||||
Icon(imageVector = leadingIcon, contentDescription = null)
|
||||
}
|
||||
},
|
||||
trailing = if (selected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,13 +5,10 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material.icons.filled.Keyboard
|
||||
import androidx.compose.material.icons.filled.Schedule
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -29,7 +26,10 @@ import de.jeanlucmakiola.calendula.ui.common.durationLabel
|
||||
import de.jeanlucmakiola.calendula.ui.common.eventFormFieldIcon
|
||||
import de.jeanlucmakiola.calendula.ui.common.eventFormFieldLabel
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.ExpandIcon
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.GroupedSwitchRow
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.floret.identity.collapseExit
|
||||
@@ -56,13 +56,15 @@ internal fun EventFormScreen(
|
||||
predictiveBack = true,
|
||||
visible = visible,
|
||||
) {
|
||||
SettingsHint(stringResource(R.string.settings_form_fields_hint))
|
||||
GroupedHint(stringResource(R.string.settings_form_fields_hint))
|
||||
Spacer(Modifier.height(8.dp))
|
||||
val fields = EventFormField.entries
|
||||
fields.forEachIndexed { index, field ->
|
||||
val checked = field in state.defaultFormFields
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(eventFormFieldLabel(field)),
|
||||
checked = checked,
|
||||
onCheckedChange = { viewModel.setFormFieldDefault(field, it) },
|
||||
position = positionOf(index, fields.size),
|
||||
// The same icon the field carries in the new-event form.
|
||||
leading = {
|
||||
@@ -72,20 +74,15 @@ internal fun EventFormScreen(
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = checked,
|
||||
onCheckedChange = { viewModel.setFormFieldDefault(field, it) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setFormFieldDefault(field, !checked) },
|
||||
)
|
||||
}
|
||||
|
||||
// Auto-focus the title on a new event (#10), on by default.
|
||||
Spacer(Modifier.height(24.dp))
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_autofocus_title),
|
||||
checked = state.autofocusEventTitle,
|
||||
onCheckedChange = { viewModel.setAutofocusEventTitle(it) },
|
||||
summary = stringResource(R.string.settings_autofocus_title_hint),
|
||||
position = Position.Alone,
|
||||
leading = {
|
||||
@@ -95,13 +92,6 @@ internal fun EventFormScreen(
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.autofocusEventTitle,
|
||||
onCheckedChange = { viewModel.setAutofocusEventTitle(it) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setAutofocusEventTitle(!state.autofocusEventTitle) },
|
||||
)
|
||||
|
||||
// How long a new timed event opens (#54), globally and per calendar —
|
||||
@@ -127,13 +117,7 @@ internal fun EventFormScreen(
|
||||
// Expanded, the header opens the calendar run below it and the
|
||||
// two read as one container (the shared expand pattern).
|
||||
position = if (durationSectionExpanded) Position.Top else Position.Bottom,
|
||||
trailing = {
|
||||
Icon(
|
||||
imageVector = if (durationSectionExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
},
|
||||
trailing = { ExpandIcon(durationSectionExpanded) },
|
||||
onClick = { durationSectionExpanded = !durationSectionExpanded },
|
||||
)
|
||||
AnimatedVisibility(
|
||||
@@ -165,21 +149,12 @@ internal fun EventFormScreen(
|
||||
// Per-event colour on calendars that publish no colour set (some
|
||||
// CalDAV); off by default, since it may not survive their next sync.
|
||||
Spacer(Modifier.height(24.dp))
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_color_unsupported),
|
||||
checked = state.allowColorOnUnsupportedCalendars,
|
||||
onCheckedChange = { viewModel.setAllowColorOnUnsupportedCalendars(it) },
|
||||
summary = stringResource(R.string.settings_color_unsupported_hint),
|
||||
position = Position.Alone,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.allowColorOnUnsupportedCalendars,
|
||||
onCheckedChange = { viewModel.setAllowColorOnUnsupportedCalendars(it) },
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
viewModel.setAllowColorOnUnsupportedCalendars(
|
||||
!state.allowColorOnUnsupportedCalendars,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,14 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.ui.common.PickerDescription
|
||||
import de.jeanlucmakiola.calendula.ui.month.MonthStylePreview
|
||||
import de.jeanlucmakiola.calendula.ui.month.MonthViewStyle
|
||||
import de.jeanlucmakiola.calendula.ui.month.descriptionRes
|
||||
import de.jeanlucmakiola.calendula.ui.month.labelRes
|
||||
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.OptionRows
|
||||
import de.jeanlucmakiola.floret.components.ViewPreviewFrame
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import kotlinx.datetime.DayOfWeek
|
||||
|
||||
/**
|
||||
@@ -29,7 +27,7 @@ import kotlinx.datetime.DayOfWeek
|
||||
*
|
||||
* Under the preview sits the selected style's own one-line blurb, then the
|
||||
* family's standard picker shape: connected single-line grouped rows with a
|
||||
* tonal highlight and [SelectedCheck] on the current one.
|
||||
* tonal highlight and a check on the current one.
|
||||
*/
|
||||
@Composable
|
||||
internal fun MonthViewStylePicker(
|
||||
@@ -58,23 +56,15 @@ internal fun MonthViewStylePicker(
|
||||
// The selected style's own blurb lives here, under its preview, rather
|
||||
// than on every row — the rows stay single-line and dense, and the words
|
||||
// describe exactly what is being shown above.
|
||||
PickerDescription(stringResource(selected.descriptionRes))
|
||||
options.forEachIndexed { index, style ->
|
||||
val isSelected = style == selected
|
||||
GroupedRow(
|
||||
title = stringResource(style.labelRes),
|
||||
position = positionOf(index, options.size),
|
||||
selected = isSelected,
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
// Applies straight away; the preview above is the confirmation,
|
||||
// so there is nothing to dismiss for.
|
||||
onClick = { onSelect(style) },
|
||||
)
|
||||
}
|
||||
GroupedHint(stringResource(selected.descriptionRes), verticalPadding = 8.dp)
|
||||
// Applies straight away; the preview above is the confirmation, so
|
||||
// there is nothing to dismiss for.
|
||||
OptionRows(
|
||||
options = options,
|
||||
selected = selected,
|
||||
label = { stringResource(it.labelRes) },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,8 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -43,12 +40,15 @@ import de.jeanlucmakiola.calendula.ui.common.ReminderDefaultPicker
|
||||
import de.jeanlucmakiola.calendula.ui.common.TimePickerAlert
|
||||
import de.jeanlucmakiola.calendula.ui.common.durationLabel
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.ExpandIcon
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.GroupedSwitchRow
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.identity.collapseExit
|
||||
import de.jeanlucmakiola.floret.identity.expandEnter
|
||||
import de.jeanlucmakiola.floret.reminders.ReminderOverride
|
||||
import de.jeanlucmakiola.floret.reminders.minutesOrEmpty
|
||||
import de.jeanlucmakiola.floret.reminders.reminderOverrideFor
|
||||
import kotlinx.datetime.LocalTime
|
||||
|
||||
@@ -95,14 +95,12 @@ internal fun NotificationsScreen(
|
||||
predictiveBack = true,
|
||||
visible = visible,
|
||||
) {
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_reminders),
|
||||
checked = state.remindersEnabled,
|
||||
onCheckedChange = toggleReminders,
|
||||
summary = stringResource(R.string.settings_reminders_hint),
|
||||
position = Position.Top,
|
||||
trailing = {
|
||||
Switch(checked = state.remindersEnabled, onCheckedChange = toggleReminders)
|
||||
},
|
||||
onClick = { toggleReminders(!state.remindersEnabled) },
|
||||
)
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.settings_default_reminder),
|
||||
@@ -162,13 +160,7 @@ internal fun NotificationsScreen(
|
||||
title = stringResource(R.string.settings_calendar_reminders_title),
|
||||
summary = stringResource(R.string.settings_calendar_reminders_hint),
|
||||
position = Position.Alone,
|
||||
trailing = {
|
||||
Icon(
|
||||
imageVector = if (calendarSectionExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
},
|
||||
trailing = { ExpandIcon(calendarSectionExpanded) },
|
||||
onClick = { calendarSectionExpanded = !calendarSectionExpanded },
|
||||
)
|
||||
AnimatedVisibility(
|
||||
@@ -203,13 +195,7 @@ internal fun NotificationsScreen(
|
||||
title = calendar.displayName.ifBlank { stringResource(R.string.calendar_unnamed) },
|
||||
position = if (expanded) Position.Top else Position.Alone,
|
||||
leading = { CalendarColorChip(calendar.color) },
|
||||
trailing = {
|
||||
Icon(
|
||||
imageVector = if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
},
|
||||
trailing = { ExpandIcon(expanded) },
|
||||
onClick = {
|
||||
expandedCalendars = if (expanded) {
|
||||
expandedCalendars - calendar.id
|
||||
@@ -263,7 +249,7 @@ internal fun NotificationsScreen(
|
||||
presets = REMINDER_PRESETS,
|
||||
selected = state.defaultReminderMinutes.toReminderChoice(),
|
||||
allowInherit = false,
|
||||
onSelect = { viewModel.setDefaultReminderMinutes(it.toMinutesList()) },
|
||||
onSelect = { viewModel.setDefaultReminderMinutes(it.minutesOrEmpty()) },
|
||||
onDismiss = { showDefaultReminder = false },
|
||||
)
|
||||
}
|
||||
@@ -273,7 +259,7 @@ internal fun NotificationsScreen(
|
||||
presets = ALLDAY_REMINDER_PRESETS,
|
||||
selected = state.defaultAllDayReminderMinutes.toReminderChoice(),
|
||||
allowInherit = false,
|
||||
onSelect = { viewModel.setDefaultAllDayReminderMinutes(it.toMinutesList()) },
|
||||
onSelect = { viewModel.setDefaultAllDayReminderMinutes(it.minutesOrEmpty()) },
|
||||
onDismiss = { showAllDayReminder = false },
|
||||
leadTimeSummary = allDayFiringTimeSummary(state.allDayReminderTimeMinutes),
|
||||
)
|
||||
@@ -332,10 +318,6 @@ private data class OverrideTarget(val calendarId: Long, val isAllDay: Boolean)
|
||||
private fun List<Int>.toReminderChoice(): ReminderOverride =
|
||||
if (isEmpty()) ReminderOverride.None else ReminderOverride.Minutes(this)
|
||||
|
||||
/** A picked choice as global-default minutes (Inherit isn't offered for globals). */
|
||||
private fun ReminderOverride.toMinutesList(): List<Int> =
|
||||
(this as? ReminderOverride.Minutes)?.minutes ?: emptyList()
|
||||
|
||||
/** Row summary for a calendar: its override, or the inherited global default. */
|
||||
@Composable
|
||||
private fun calendarOverrideSummary(
|
||||
|
||||
@@ -27,10 +27,9 @@ import de.jeanlucmakiola.calendula.data.prefs.PastEventDisplay
|
||||
import de.jeanlucmakiola.calendula.domain.EventInstance
|
||||
import de.jeanlucmakiola.calendula.ui.agenda.AgendaDayHeader
|
||||
import de.jeanlucmakiola.calendula.ui.agenda.AgendaEventRow
|
||||
import de.jeanlucmakiola.calendula.ui.common.PickerDescription
|
||||
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.OptionRows
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.floret.identity.rememberReduceMotion
|
||||
import kotlinx.datetime.LocalDate
|
||||
@@ -80,21 +79,13 @@ internal fun PastEventsPicker(
|
||||
}
|
||||
}
|
||||
}
|
||||
PickerDescription(stringResource(R.string.settings_past_events_hint))
|
||||
options.forEachIndexed { index, mode ->
|
||||
val isSelected = mode == selected
|
||||
GroupedRow(
|
||||
title = stringResource(pastEventDisplayLabelRes(mode)),
|
||||
position = positionOf(index, options.size),
|
||||
selected = isSelected,
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = { onSelect(mode) },
|
||||
)
|
||||
}
|
||||
GroupedHint(stringResource(R.string.settings_past_events_hint), verticalPadding = 8.dp)
|
||||
OptionRows(
|
||||
options = options,
|
||||
selected = selected,
|
||||
label = { stringResource(pastEventDisplayLabelRes(it)) },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -22,8 +20,6 @@ import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat
|
||||
import de.jeanlucmakiola.calendula.ui.common.formatMinuteOfDay
|
||||
import de.jeanlucmakiola.calendula.ui.common.reminderLeadTimeLabel
|
||||
import de.jeanlucmakiola.floret.components.GroupedListInset
|
||||
import de.jeanlucmakiola.floret.components.GroupedSectionHeader
|
||||
import de.jeanlucmakiola.floret.locale.currentLocale
|
||||
|
||||
/**
|
||||
@@ -64,17 +60,6 @@ internal fun CategoryIcon(icon: ImageVector, accent: ChipAccent) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Muted supporting text under a [GroupedSectionHeader], matching the form-fields hint. */
|
||||
@Composable
|
||||
internal fun SettingsHint(text: String) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = GroupedListInset, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun openUrl(context: Context, url: String) {
|
||||
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
|
||||
runCatching { context.startActivity(intent) }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.jeanlucmakiola.calendula.ui.settings
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -14,7 +13,6 @@ import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -34,9 +32,12 @@ import de.jeanlucmakiola.calendula.domain.contacts.SpecialDateType
|
||||
import de.jeanlucmakiola.calendula.ui.common.ReminderDefaultPicker
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.GroupedSwitchRow
|
||||
import de.jeanlucmakiola.floret.components.InlineTextField
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.locale.relativeTimeAgo
|
||||
import de.jeanlucmakiola.floret.reminders.ReminderOverride
|
||||
import de.jeanlucmakiola.floret.reminders.minutesOrEmpty
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Contact special dates (issue #15)
|
||||
@@ -106,38 +107,26 @@ internal fun SpecialDatesScreen(
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_special_dates_enable),
|
||||
checked = state.enabled,
|
||||
onCheckedChange = { want -> if (want) requestOrEnable() else confirmDisableAll = true },
|
||||
summary = stringResource(R.string.settings_special_dates_enable_hint),
|
||||
position = Position.Alone,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.enabled,
|
||||
onCheckedChange = { want -> if (want) requestOrEnable() else confirmDisableAll = true },
|
||||
)
|
||||
},
|
||||
onClick = { if (state.enabled) confirmDisableAll = true else requestOrEnable() },
|
||||
)
|
||||
|
||||
if (state.enabled) {
|
||||
SpecialDateType.entries.forEachIndexed { index, type ->
|
||||
Spacer(Modifier.height(if (index == 0) 24.dp else 16.dp))
|
||||
val on = type in state.types
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(specialDateTypeLabel(type)),
|
||||
checked = on,
|
||||
onCheckedChange = { want ->
|
||||
if (want) viewModel.setSpecialDateTypeEnabled(type, true)
|
||||
else confirmDisableType = type
|
||||
},
|
||||
position = if (on) Position.Top else Position.Alone,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = on,
|
||||
onCheckedChange = { want ->
|
||||
if (want) viewModel.setSpecialDateTypeEnabled(type, true)
|
||||
else confirmDisableType = type
|
||||
},
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
if (on) confirmDisableType = type else viewModel.setSpecialDateTypeEnabled(type, true)
|
||||
},
|
||||
)
|
||||
if (on) {
|
||||
GroupedRow(
|
||||
@@ -148,7 +137,7 @@ internal fun SpecialDatesScreen(
|
||||
)
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.settings_special_dates_reminders),
|
||||
summary = reminderChoiceLabel(specialDatesReminderMinutes(state.reminderChoices[type])),
|
||||
summary = reminderChoiceLabel(state.reminderChoices[type]?.minutesOrEmpty().orEmpty()),
|
||||
position = Position.Bottom,
|
||||
onClick = { reminderPickerType = type },
|
||||
)
|
||||
@@ -156,21 +145,16 @@ internal fun SpecialDatesScreen(
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_special_dates_show_year),
|
||||
checked = state.showYear,
|
||||
onCheckedChange = viewModel::setSpecialDatesShowYear,
|
||||
summary = stringResource(R.string.settings_special_dates_show_year_hint),
|
||||
position = Position.Top,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.showYear,
|
||||
onCheckedChange = viewModel::setSpecialDatesShowYear,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setSpecialDatesShowYear(!state.showYear) },
|
||||
)
|
||||
GroupedRow(
|
||||
title = stringResource(R.string.settings_special_dates_sync_now),
|
||||
summary = specialDatesLastRunLabel(context, state.lastRun),
|
||||
summary = specialDatesLastRunLabel(state.lastRun),
|
||||
position = Position.Bottom,
|
||||
onClick = viewModel::syncSpecialDatesNow,
|
||||
)
|
||||
@@ -223,10 +207,6 @@ internal fun SpecialDatesScreen(
|
||||
}
|
||||
}
|
||||
|
||||
/** The lead-time list backing a managed calendar's reminder choice (for the summary label). */
|
||||
private fun specialDatesReminderMinutes(choice: ReminderOverride?): List<Int> =
|
||||
(choice as? ReminderOverride.Minutes)?.minutes.orEmpty()
|
||||
|
||||
@Composable
|
||||
private fun SpecialDatesDisableDialog(
|
||||
message: String,
|
||||
@@ -305,16 +285,12 @@ private fun specialDateTypeLabel(type: SpecialDateType): Int = when (type) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun specialDatesLastRunLabel(context: Context, lastRun: Long): String =
|
||||
private fun specialDatesLastRunLabel(lastRun: Long): String =
|
||||
if (lastRun <= 0L) {
|
||||
stringResource(R.string.settings_special_dates_never_synced)
|
||||
} else {
|
||||
stringResource(
|
||||
R.string.settings_special_dates_last_synced,
|
||||
android.text.format.DateUtils.getRelativeTimeSpanString(
|
||||
lastRun,
|
||||
System.currentTimeMillis(),
|
||||
android.text.format.DateUtils.MINUTE_IN_MILLIS,
|
||||
).toString(),
|
||||
relativeTimeAgo(lastRun),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import de.jeanlucmakiola.calendula.data.prefs.resolveFirstDay
|
||||
import de.jeanlucmakiola.calendula.ui.common.AgendaRangePicker
|
||||
import de.jeanlucmakiola.calendula.ui.common.CalendarView
|
||||
import de.jeanlucmakiola.calendula.ui.common.IMPLEMENTED_VIEWS
|
||||
import de.jeanlucmakiola.calendula.ui.common.PickerDescription
|
||||
import de.jeanlucmakiola.calendula.ui.common.LocalUse24HourFormat
|
||||
import de.jeanlucmakiola.calendula.ui.common.TimelineHours
|
||||
import de.jeanlucmakiola.calendula.ui.common.TimelineScale
|
||||
@@ -46,8 +45,10 @@ import de.jeanlucmakiola.calendula.ui.common.label
|
||||
import de.jeanlucmakiola.calendula.ui.common.labelRes
|
||||
import de.jeanlucmakiola.calendula.ui.month.labelRes
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.GroupedSectionHeader
|
||||
import de.jeanlucmakiola.floret.components.GroupedSwitchRow
|
||||
import de.jeanlucmakiola.floret.components.OptionPicker
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
import de.jeanlucmakiola.floret.components.ReorderableColumn
|
||||
@@ -112,29 +113,19 @@ internal fun ViewsScreen(
|
||||
position = Position.Middle,
|
||||
onClick = { showTimeFormat = true },
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_today_toolbar),
|
||||
checked = state.todayButtonInToolbar,
|
||||
onCheckedChange = viewModel::setTodayButtonInToolbar,
|
||||
summary = stringResource(R.string.settings_today_toolbar_summary),
|
||||
position = Position.Middle,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.todayButtonInToolbar,
|
||||
onCheckedChange = viewModel::setTodayButtonInToolbar,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setTodayButtonInToolbar(!state.todayButtonInToolbar) },
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_dim_completed),
|
||||
checked = state.dimCompletedEvents,
|
||||
onCheckedChange = viewModel::setDimCompletedEvents,
|
||||
summary = stringResource(R.string.settings_dim_completed_summary),
|
||||
position = Position.Bottom,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.dimCompletedEvents,
|
||||
onCheckedChange = viewModel::setDimCompletedEvents,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setDimCompletedEvents(!state.dimCompletedEvents) },
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
@@ -147,17 +138,12 @@ internal fun ViewsScreen(
|
||||
position = Position.Top,
|
||||
onClick = { showTapOpens = true },
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_drag_to_reschedule),
|
||||
checked = state.dragToReschedule,
|
||||
onCheckedChange = viewModel::setDragToReschedule,
|
||||
summary = stringResource(R.string.settings_drag_to_reschedule_summary),
|
||||
position = Position.Bottom,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.dragToReschedule,
|
||||
onCheckedChange = viewModel::setDragToReschedule,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setDragToReschedule(!state.dragToReschedule) },
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
@@ -168,17 +154,12 @@ internal fun ViewsScreen(
|
||||
position = Position.Top,
|
||||
onClick = { showMonthStyle = true },
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_week_numbers),
|
||||
checked = state.showWeekNumbers,
|
||||
onCheckedChange = viewModel::setShowWeekNumbers,
|
||||
summary = stringResource(R.string.settings_week_numbers_summary),
|
||||
position = Position.Bottom,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.showWeekNumbers,
|
||||
onCheckedChange = viewModel::setShowWeekNumbers,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setShowWeekNumbers(!state.showWeekNumbers) },
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
@@ -200,8 +181,10 @@ internal fun ViewsScreen(
|
||||
dimmed = hours.enabled,
|
||||
onClick = if (hours.enabled) null else ({ showTimelineScale = true }),
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_visible_hours),
|
||||
checked = hours.enabled,
|
||||
onCheckedChange = { viewModel.setTimelineHours(hours.copy(enabled = it)) },
|
||||
summary = if (hours.enabled) {
|
||||
stringResource(
|
||||
R.string.settings_visible_hours_on_summary,
|
||||
@@ -212,13 +195,6 @@ internal fun ViewsScreen(
|
||||
stringResource(R.string.settings_visible_hours_summary)
|
||||
},
|
||||
position = Position.Middle,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = hours.enabled,
|
||||
onCheckedChange = { viewModel.setTimelineHours(hours.copy(enabled = it)) },
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setTimelineHours(hours.copy(enabled = !hours.enabled)) },
|
||||
)
|
||||
if (hours.enabled) {
|
||||
GroupedRow(
|
||||
@@ -234,17 +210,12 @@ internal fun ViewsScreen(
|
||||
onClick = { showHoursEnd = true },
|
||||
)
|
||||
}
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_hour_lines),
|
||||
checked = state.showHourGrid,
|
||||
onCheckedChange = viewModel::setShowHourGrid,
|
||||
summary = stringResource(R.string.settings_hour_lines_summary),
|
||||
position = Position.Bottom,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.showHourGrid,
|
||||
onCheckedChange = viewModel::setShowHourGrid,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setShowHourGrid(!state.showHourGrid) },
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
@@ -261,34 +232,24 @@ internal fun ViewsScreen(
|
||||
position = Position.Middle,
|
||||
onClick = { showPastEvents = true },
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_agenda_show_today),
|
||||
checked = state.agendaShowToday,
|
||||
onCheckedChange = viewModel::setAgendaShowToday,
|
||||
summary = stringResource(R.string.settings_agenda_show_today_hint),
|
||||
position = Position.Middle,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.agendaShowToday,
|
||||
onCheckedChange = viewModel::setAgendaShowToday,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setAgendaShowToday(!state.agendaShowToday) },
|
||||
)
|
||||
GroupedRow(
|
||||
GroupedSwitchRow(
|
||||
title = stringResource(R.string.settings_agenda_range_bar),
|
||||
checked = state.agendaShowRangeBar,
|
||||
onCheckedChange = viewModel::setAgendaShowRangeBar,
|
||||
summary = stringResource(R.string.settings_agenda_range_bar_hint),
|
||||
position = Position.Bottom,
|
||||
trailing = {
|
||||
Switch(
|
||||
checked = state.agendaShowRangeBar,
|
||||
onCheckedChange = viewModel::setAgendaShowRangeBar,
|
||||
)
|
||||
},
|
||||
onClick = { viewModel.setAgendaShowRangeBar(!state.agendaShowRangeBar) },
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
GroupedSectionHeader(stringResource(R.string.settings_quick_switch_header))
|
||||
SettingsHint(stringResource(R.string.settings_quick_switch_hint))
|
||||
GroupedHint(stringResource(R.string.settings_quick_switch_hint))
|
||||
Spacer(Modifier.height(8.dp))
|
||||
ReorderableColumn(
|
||||
items = config.order,
|
||||
@@ -313,7 +274,7 @@ internal fun ViewsScreen(
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
GroupedSectionHeader(stringResource(R.string.settings_drawer_order_header))
|
||||
SettingsHint(stringResource(R.string.settings_drawer_order_hint))
|
||||
GroupedHint(stringResource(R.string.settings_drawer_order_hint))
|
||||
Spacer(Modifier.height(8.dp))
|
||||
ReorderableColumn(
|
||||
items = state.drawerViewOrder,
|
||||
@@ -341,7 +302,7 @@ internal fun ViewsScreen(
|
||||
if (showDefaultView) {
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_default_view),
|
||||
header = { PickerDescription(stringResource(R.string.settings_default_view_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_default_view_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = IMPLEMENTED_VIEWS,
|
||||
selected = state.defaultView,
|
||||
@@ -372,7 +333,7 @@ internal fun ViewsScreen(
|
||||
val systemIs24Hour = DateFormat.is24HourFormat(LocalContext.current)
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_time_format),
|
||||
header = { PickerDescription(stringResource(R.string.settings_time_format_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_time_format_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = TimeFormatPref.entries,
|
||||
selected = state.timeFormat,
|
||||
@@ -403,7 +364,7 @@ internal fun ViewsScreen(
|
||||
if (showTapOpens) {
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_tap_opens),
|
||||
header = { PickerDescription(stringResource(R.string.settings_tap_opens_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_tap_opens_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = listOf(false, true),
|
||||
selected = state.tapOpensEditor,
|
||||
@@ -422,7 +383,7 @@ internal fun ViewsScreen(
|
||||
if (showMultiDayCount) {
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_multi_day_count),
|
||||
header = { PickerDescription(stringResource(R.string.settings_multi_day_count_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_multi_day_count_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = SettingsPrefs.MULTI_DAY_COUNTS.toList(),
|
||||
selected = state.multiDayCount,
|
||||
@@ -434,7 +395,7 @@ internal fun ViewsScreen(
|
||||
if (showTimelineScale) {
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_timeline_scale),
|
||||
header = { PickerDescription(stringResource(R.string.settings_timeline_scale_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_timeline_scale_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
// A height pinched on the timeline is listed alongside the presets
|
||||
// rather than left as a silently unticked list: it is the current
|
||||
@@ -453,7 +414,7 @@ internal fun ViewsScreen(
|
||||
if (showHoursStart) {
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_visible_hours_start),
|
||||
header = { PickerDescription(stringResource(R.string.settings_visible_hours_start_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_visible_hours_start_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = TimelineHours.startOptions(hours.endHour),
|
||||
selected = hours.startHour,
|
||||
@@ -465,7 +426,7 @@ internal fun ViewsScreen(
|
||||
if (showHoursEnd) {
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_visible_hours_end),
|
||||
header = { PickerDescription(stringResource(R.string.settings_visible_hours_end_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_visible_hours_end_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = TimelineHours.endOptions(hours.startHour),
|
||||
selected = hours.endHour,
|
||||
|
||||
@@ -8,14 +8,12 @@ import androidx.compose.ui.unit.dp
|
||||
import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.data.prefs.WeekStartPref
|
||||
import de.jeanlucmakiola.calendula.data.prefs.resolveFirstDay
|
||||
import de.jeanlucmakiola.calendula.ui.common.PickerDescription
|
||||
import de.jeanlucmakiola.calendula.ui.month.MonthStylePreview
|
||||
import de.jeanlucmakiola.calendula.ui.month.MonthViewStyle
|
||||
import de.jeanlucmakiola.floret.components.FullScreenPicker
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.SelectedCheck
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.OptionRows
|
||||
import de.jeanlucmakiola.floret.components.ViewPreviewFrame
|
||||
import de.jeanlucmakiola.floret.components.positionOf
|
||||
import de.jeanlucmakiola.floret.locale.currentLocale
|
||||
|
||||
/**
|
||||
@@ -51,29 +49,23 @@ internal fun WeekStartPicker(
|
||||
height = PREVIEW_HEIGHT,
|
||||
)
|
||||
}
|
||||
PickerDescription(stringResource(R.string.settings_week_start_hint))
|
||||
options.forEachIndexed { index, option ->
|
||||
val isSelected = option == selected
|
||||
GroupedRow(
|
||||
title = weekStartLabel(option),
|
||||
summary = if (option == WeekStartPref.Auto) {
|
||||
GroupedHint(stringResource(R.string.settings_week_start_hint), verticalPadding = 8.dp)
|
||||
OptionRows(
|
||||
options = options,
|
||||
selected = selected,
|
||||
label = { weekStartLabel(it) },
|
||||
onSelect = onSelect,
|
||||
summary = { option ->
|
||||
if (option == WeekStartPref.Auto) {
|
||||
stringResource(
|
||||
R.string.settings_week_start_auto_summary,
|
||||
weekStartLabel(WeekStartPref.Day(option.resolveFirstDay(locale))),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
position = positionOf(index, options.size),
|
||||
selected = isSelected,
|
||||
trailing = if (isSelected) {
|
||||
{ SelectedCheck() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = { onSelect(option) },
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ import de.jeanlucmakiola.calendula.R
|
||||
import de.jeanlucmakiola.calendula.data.prefs.resolveFirstDay
|
||||
import de.jeanlucmakiola.calendula.qs.NewEventTileService
|
||||
import de.jeanlucmakiola.calendula.ui.common.AgendaRangePicker
|
||||
import de.jeanlucmakiola.calendula.ui.common.PickerDescription
|
||||
import de.jeanlucmakiola.calendula.ui.common.agendaRangeLabel
|
||||
import de.jeanlucmakiola.calendula.widget.WidgetSize
|
||||
import de.jeanlucmakiola.calendula.widget.agenda.metricsFor
|
||||
import de.jeanlucmakiola.floret.components.CollapsingScaffold
|
||||
import de.jeanlucmakiola.floret.components.GroupedHint
|
||||
import de.jeanlucmakiola.floret.components.GroupedRow
|
||||
import de.jeanlucmakiola.floret.components.OptionPicker
|
||||
import de.jeanlucmakiola.floret.components.Position
|
||||
@@ -53,7 +53,7 @@ internal fun WidgetsScreen(
|
||||
predictiveBack = true,
|
||||
visible = visible,
|
||||
) {
|
||||
SettingsHint(stringResource(R.string.settings_widgets_hint))
|
||||
GroupedHint(stringResource(R.string.settings_widgets_hint))
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
GroupedRow(
|
||||
@@ -90,7 +90,7 @@ internal fun WidgetsScreen(
|
||||
if (showWidgetSize) {
|
||||
OptionPicker(
|
||||
title = stringResource(R.string.settings_widget_size),
|
||||
header = { PickerDescription(stringResource(R.string.settings_widget_size_hint)) },
|
||||
header = { GroupedHint(stringResource(R.string.settings_widget_size_hint), verticalPadding = 8.dp) },
|
||||
predictiveBack = true,
|
||||
options = WidgetSize.entries,
|
||||
selected = state.widgetSize,
|
||||
|
||||
Reference in New Issue
Block a user