diff --git a/android/app/src/main/java/com/fabledsword/thoughtsync/ui/EditorChrome.kt b/android/app/src/main/java/com/fabledsword/thoughtsync/ui/EditorChrome.kt index 4a57648..ec7b8b5 100644 --- a/android/app/src/main/java/com/fabledsword/thoughtsync/ui/EditorChrome.kt +++ b/android/app/src/main/java/com/fabledsword/thoughtsync/ui/EditorChrome.kt @@ -157,48 +157,70 @@ fun EditorTopBar( } /** - * When the note was last written, in the bottom corner. + * The footer: when the note was last written, and the way out. * - * There is no save button. That is right — a note is saved continuously, so a - * button offering to do what already happened is a lie with a tap attached — but it - * leaves nothing on screen saying the work is safe, and "closing this saves it" is - * not a thing anyone should have to be told twice. + * **Where the note stands.** There is no save button, and there should not be — a + * note is saved continuously, so a button offering to do what already happened is a + * lie with a tap attached. But that left nothing on screen saying the work is safe, + * and "closing this keeps it" is not a thing anyone should have to be told twice. So + * the state says it, as a fact rather than an instruction: Not saved yet → Saving… → + * Edited just now is the whole lifecycle, and someone who watches it once never has + * to wonder again. * - * So the state says it instead, and it says it as a fact rather than an - * instruction. Not saved yet → Saving… → Edited just now is the whole lifecycle, - * visible in the corner, and someone who watches it once never has to wonder again. + * **The way out.** Down here because of where hands are. Moving the toolbar to the + * top took the back arrow with it, which left the only exit from a full-screen + * editor in the top-left corner — the furthest point on the display from a + * right-handed thumb, and reached over the whole note to get to. The operator hit + * that on the first device pass and was right to. So the exit lives in the bottom + * corner, which with the keyboard up sits directly above it. + * + * The top-left arrow stays as well. Two affordances for one action is usually + * clutter, but this is the case that earns it: the arrow is what habit, the system + * back gesture and TalkBack all expect of a full-screen surface, and removing it + * would strand the reflex to strike a duplicate that costs one icon slot. + * + * A WORD rather than a checkmark, like the overflow menu below and for the same + * reason. A tick in a notes app is a checklist item to anyone who has used one, and + * "Done" cannot be misread — including aloud. * * [DateUtils] rather than a hand-rolled formatter: it is localised, it already * knows the difference between minutes, hours and yesterday, and getting plurals * right in every language is not this app's problem to solve twice. */ @Composable -fun EditorSavedLine( +fun EditorFooter( updatedAt: String?, saving: Boolean, + onClose: () -> Unit, modifier: Modifier = Modifier, ) { Row( modifier = modifier .fillMaxWidth() - // Rides above the keyboard, like the bar it replaced. The content - // Column deliberately does not also inset for the IME: Scaffold - // measures this at its lifted height and passes the inset down. + // Rides above the keyboard, like the bar that used to be here. The + // content Column deliberately does not also inset for the IME: + // Scaffold measures this row at its lifted height and passes the + // inset down. .imePadding() .navigationBarsPadding() - .padding(horizontal = 16.dp, vertical = 8.dp), + // Asymmetric: the button brings its own padding, the text does not. + .padding(start = 16.dp, end = 4.dp), horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, ) { Text( text = savedLabel(updatedAt, saving), style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) + TextButton(onClick = onClose) { + Text(stringResource(R.string.editor_done)) + } } } -/** The three things [EditorSavedLine] can be saying, in the order it says them. */ +/** The three things the footer can be saying, in the order it says them. */ @Composable private fun savedLabel( updatedAt: String?, diff --git a/android/app/src/main/java/com/fabledsword/thoughtsync/ui/NoteEditorScreen.kt b/android/app/src/main/java/com/fabledsword/thoughtsync/ui/NoteEditorScreen.kt index d2d6289..1bb2f21 100644 --- a/android/app/src/main/java/com/fabledsword/thoughtsync/ui/NoteEditorScreen.kt +++ b/android/app/src/main/java/com/fabledsword/thoughtsync/ui/NoteEditorScreen.kt @@ -53,8 +53,9 @@ import kotlinx.coroutines.delay * opening a note reads as the same object growing to fill the display. * * No save button, deliberately. Writes are continuous, so a button offering to do - * what already happened would be a lie with a tap attached; [EditorSavedLine] in the - * bottom corner says the same thing as a fact instead. + * what already happened would be a lie with a tap attached; [EditorFooter] in the + * bottom corner says the same thing as a fact instead, beside the Done that + * leaves. */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -182,16 +183,23 @@ fun NoteEditorScreen( onAction = onAction, ) }, - // Where the action bar used to be. A note is saved as it is written, - // so what belongs at the bottom of the screen is not a control but - // the answer to "did that land" — see [EditorSavedLine]. - bottomBar = { EditorSavedLine(updatedAt = note.updatedAt, saving = saving) }, + // Where the action bar used to be, carrying the two things that + // belong within reach of a thumb: whether the note is safe, and the + // way out. See [EditorFooter] for why the exit is down here and not + // only in the top-left corner. + bottomBar = { + EditorFooter( + updatedAt = note.updatedAt, + saving = saving, + onClose = leave, + ) + }, ) { padding -> Column( modifier = Modifier .fillMaxSize() - // No imePadding here: EditorSavedLine carries it, so + // No imePadding here: EditorFooter carries it, so // Scaffold measures that row at its keyboard-lifted // height and the inset already reaches this Column // through `padding`. Adding it again would inset for the diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 0316b69..b8dd342 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -45,6 +45,7 @@ Not saved yet Edited %1$s just now + Done Pin Unpin Labels…