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