From 219f476e58175d5ff38ffeaf2f2d6526d760acdd Mon Sep 17 00:00:00 2001 From: Robert Huselius Date: Fri, 27 Oct 2023 03:48:19 +0200 Subject: [PATCH] Skip the "go to previous line on backspace" stuff that was just buggy anyway --- .../us/huseli/retain/compose/HomeScreen.kt | 8 +-- .../java/us/huseli/retain/compose/NoteCard.kt | 3 +- .../compose/notescreen/BaseNoteScreen.kt | 6 ++- .../notescreen/ChecklistNoteChecklist.kt | 2 + .../notescreen/ChecklistNoteChecklistRow.kt | 25 ++------- .../compose/notescreen/ChecklistNoteScreen.kt | 8 ++- .../compose/notescreen/NoteScreenTopAppBar.kt | 3 +- .../us/huseli/retain/ui/theme/RetainColor.kt | 30 ++++++----- .../viewmodels/BaseEditNoteViewModel.kt | 14 ++--- .../viewmodels/EditChecklistNoteViewModel.kt | 52 ++----------------- 10 files changed, 49 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/us/huseli/retain/compose/HomeScreen.kt b/app/src/main/java/us/huseli/retain/compose/HomeScreen.kt index 1beff6a..b403b00 100644 --- a/app/src/main/java/us/huseli/retain/compose/HomeScreen.kt +++ b/app/src/main/java/us/huseli/retain/compose/HomeScreen.kt @@ -1,7 +1,6 @@ package us.huseli.retain.compose import androidx.activity.compose.BackHandler -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement @@ -44,7 +43,7 @@ import us.huseli.retain.data.entities.Note import us.huseli.retain.viewmodels.NoteViewModel import us.huseli.retain.viewmodels.SettingsViewModel -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterialApi::class) @Composable fun HomeScreen( modifier: Modifier = Modifier, @@ -179,7 +178,10 @@ fun HomeScreen( } } else { LazyColumn( - modifier = Modifier.reorderable(reorderableState).detectReorder(reorderableState), + modifier = Modifier + .reorderable(reorderableState) + .detectReorder(reorderableState) + .fillMaxHeight(), state = reorderableState.listState, verticalArrangement = Arrangement.spacedBy(8.dp), ) { diff --git a/app/src/main/java/us/huseli/retain/compose/NoteCard.kt b/app/src/main/java/us/huseli/retain/compose/NoteCard.kt index b1bbabe..1c7efa6 100644 --- a/app/src/main/java/us/huseli/retain/compose/NoteCard.kt +++ b/app/src/main/java/us/huseli/retain/compose/NoteCard.kt @@ -59,7 +59,8 @@ fun NoteCard( isDragging: Boolean, ) { val elevation by animateDpAsState(if (isDragging) 16.dp else 0.dp) - val noteColor = getNoteColor(note.color) + val defaultColor = MaterialTheme.colorScheme.background + val noteColor = getNoteColor(note.color, defaultColor) val border = if (isSelected) BorderStroke(3.dp, MaterialTheme.colorScheme.primary) else BorderStroke(1.dp, MaterialTheme.colorScheme.onSurface.copy(alpha = 0.25f)) diff --git a/app/src/main/java/us/huseli/retain/compose/notescreen/BaseNoteScreen.kt b/app/src/main/java/us/huseli/retain/compose/notescreen/BaseNoteScreen.kt index d8323c6..d97d731 100644 --- a/app/src/main/java/us/huseli/retain/compose/notescreen/BaseNoteScreen.kt +++ b/app/src/main/java/us/huseli/retain/compose/notescreen/BaseNoteScreen.kt @@ -72,8 +72,10 @@ fun BaseNoteScreen( val context = LocalContext.current val images by viewModel.images.collectAsStateWithLifecycle() val trashedImageCount by viewModel.trashedImageCount.collectAsStateWithLifecycle(0) - val noteColor by remember(color) { mutableStateOf(getNoteColor(context, color)) } - val appBarColor by remember(color) { mutableStateOf(getAppBarColor(context, color)) } + val background = MaterialTheme.colorScheme.background + val surface = MaterialTheme.colorScheme.surface + val noteColor by remember(color) { mutableStateOf(getNoteColor(context, color, background)) } + val appBarColor by remember(color) { mutableStateOf(getAppBarColor(context, color, surface)) } val selectedImages by viewModel.selectedImages.collectAsStateWithLifecycle() val imageAdded by viewModel.imageAdded.collectAsStateWithLifecycle(null) val isImageSelectEnabled = selectedImages.isNotEmpty() diff --git a/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklist.kt b/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklist.kt index f33e698..66c75b7 100644 --- a/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklist.kt +++ b/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklist.kt @@ -1,3 +1,5 @@ +@file:Suppress("AnimateAsStateLabel") + package us.huseli.retain.compose.notescreen import androidx.compose.animation.core.animateFloatAsState diff --git a/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklistRow.kt b/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklistRow.kt index bbce21f..5384cda 100644 --- a/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklistRow.kt +++ b/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteChecklistRow.kt @@ -34,19 +34,6 @@ import androidx.compose.ui.unit.sp import org.burnoutcrew.reorderable.ReorderableLazyListState import org.burnoutcrew.reorderable.detectReorder -/** - * A row with a checkbox and a textfield. Multiple rows of these emulate - * multiline text editing behaviour, in that pressing enter in the middle of - * a row will bring the characters after the cursor down to a new row, and - * pressing backspace at the beginning of a row will paste the entire row - * contents to the end of the previous row. - * - * This is done via a hack where the textfield internally has an invisible - * null character at the beginning. Normally, there is no way for us to know - * that the user has pressed backspace when there are no characters before the - * cursor, but in this way, there actually are, even though it looks like the - * cursor is at the beginning of the row. - */ @Composable fun ChecklistNoteChecklistRow( modifier: Modifier = Modifier, @@ -104,23 +91,17 @@ fun ChecklistNoteChecklistRow( onValueChange = onTextFieldValueChange, keyboardOptions = KeyboardOptions.Default.copy( imeAction = ImeAction.Next, - capitalization = - if (textFieldValue.selection.start == 1) KeyboardCapitalization.Characters - else KeyboardCapitalization.Sentences, + capitalization = KeyboardCapitalization.Sentences, ), keyboardActions = KeyboardActions( onNext = { onNext() } ), modifier = Modifier - .onFocusChanged { - if (it.isFocused) onFocus() - } + .onFocusChanged { if (it.isFocused) onFocus() } .weight(1f) .fillMaxWidth() .focusRequester(focusRequester) - .onGloballyPositioned { - if (isFocused) focusRequester.requestFocus() - }, + .onGloballyPositioned { if (isFocused) focusRequester.requestFocus() }, ) IconButton(onClick = onDeleteClick) { Icon( diff --git a/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteScreen.kt b/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteScreen.kt index ff65980..3c670f3 100644 --- a/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteScreen.kt +++ b/app/src/main/java/us/huseli/retain/compose/notescreen/ChecklistNoteScreen.kt @@ -51,7 +51,8 @@ fun ChecklistNoteScreen( val reorderableState = rememberReorderableLazyListState( onMove = { from, to -> viewModel.switchItemPositions(from, to) }, ) - val noteColor by remember(note.color) { mutableStateOf(getNoteColor(context, note.color)) } + val defaultColor = MaterialTheme.colorScheme.background + val noteColor by remember(note.color) { mutableStateOf(getNoteColor(context, note.color, defaultColor)) } LaunchedEffect(trashedChecklistItems) { if (trashedChecklistItems.isNotEmpty()) { @@ -119,10 +120,7 @@ fun ChecklistNoteScreen( Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .clickable { - val index = uncheckedItems.size - viewModel.insertItem(text = "", checked = false, index = index) - } + .clickable { viewModel.insertItem(text = "", checked = false, index = uncheckedItems.size) } .padding(vertical = 8.dp) .fillMaxWidth() ) { diff --git a/app/src/main/java/us/huseli/retain/compose/notescreen/NoteScreenTopAppBar.kt b/app/src/main/java/us/huseli/retain/compose/notescreen/NoteScreenTopAppBar.kt index f317b75..eca7ce1 100644 --- a/app/src/main/java/us/huseli/retain/compose/notescreen/NoteScreenTopAppBar.kt +++ b/app/src/main/java/us/huseli/retain/compose/notescreen/NoteScreenTopAppBar.kt @@ -15,6 +15,7 @@ import androidx.compose.material.icons.sharp.SelectAll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults @@ -76,7 +77,7 @@ fun NoteScreenTopAppBar( ) } ColorDropdownMenu( - colors = getNoteColors(), + colors = getNoteColors(default = MaterialTheme.colorScheme.background), isExpanded = isColorDropdownExpanded, onDismiss = { isColorDropdownExpanded = false }, onColorClick = { diff --git a/app/src/main/java/us/huseli/retain/ui/theme/RetainColor.kt b/app/src/main/java/us/huseli/retain/ui/theme/RetainColor.kt index 7a6e42d..0b300fd 100644 --- a/app/src/main/java/us/huseli/retain/ui/theme/RetainColor.kt +++ b/app/src/main/java/us/huseli/retain/ui/theme/RetainColor.kt @@ -10,9 +10,9 @@ import us.huseli.retaintheme.ui.theme.RetainBasicColorsDark import us.huseli.retaintheme.ui.theme.RetainBasicColorsLight import kotlin.math.max -val noteColors: (RetainBasicColors) -> Map = { colorScheme -> +val noteColors: (RetainBasicColors, Color) -> Map = { colorScheme, default -> mapOf( - "DEFAULT" to Color.Transparent, + "DEFAULT" to default, "RED" to colorScheme.Red, "ORANGE" to colorScheme.Orange, "YELLOW" to colorScheme.Yellow, @@ -28,25 +28,26 @@ val noteColors: (RetainBasicColors) -> Map = { colorScheme -> } @Composable -fun getNoteColors(): Map = - noteColors(if (isSystemInDarkTheme()) RetainBasicColorsDark else RetainBasicColorsLight) +fun getNoteColors(default: Color): Map = + noteColors(if (isSystemInDarkTheme()) RetainBasicColorsDark else RetainBasicColorsLight, default) -fun getNoteColor(key: String, dark: Boolean): Color { +fun getNoteColor(key: String, dark: Boolean, default: Color): Color { val colorScheme = if (dark) RetainBasicColorsDark else RetainBasicColorsLight - return noteColors(colorScheme).getOrDefault(key, Color.Transparent) + return noteColors(colorScheme, default).getOrDefault(key, default) } -fun getNoteColor(context: Context, key: String) = getNoteColor( +fun getNoteColor(context: Context, key: String, default: Color) = getNoteColor( key = key, - dark = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + dark = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES, + default = default, ) @Composable -fun getNoteColor(key: String): Color = getNoteColor(key, isSystemInDarkTheme()) +fun getNoteColor(key: String, default: Color): Color = getNoteColor(key, isSystemInDarkTheme(), default) -fun getAppBarColor(key: String, dark: Boolean): Color { - return if (key == "DEFAULT") Color.Transparent - else getNoteColor(key, dark).let { +fun getAppBarColor(key: String, dark: Boolean, default: Color): Color { + return if (key == "DEFAULT") default + else getNoteColor(key, dark, default).let { it.copy( red = max(it.red - 0.05f, 0f), green = max(it.green - 0.05f, 0f), @@ -55,7 +56,8 @@ fun getAppBarColor(key: String, dark: Boolean): Color { } } -fun getAppBarColor(context: Context, key: String) = getAppBarColor( +fun getAppBarColor(context: Context, key: String, default: Color) = getAppBarColor( key = key, - dark = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + dark = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES, + default = default, ) diff --git a/app/src/main/java/us/huseli/retain/viewmodels/BaseEditNoteViewModel.kt b/app/src/main/java/us/huseli/retain/viewmodels/BaseEditNoteViewModel.kt index a736a9c..5b0fc4c 100644 --- a/app/src/main/java/us/huseli/retain/viewmodels/BaseEditNoteViewModel.kt +++ b/app/src/main/java/us/huseli/retain/viewmodels/BaseEditNoteViewModel.kt @@ -29,22 +29,22 @@ data class ChecklistItemFlow( val checked: MutableStateFlow = MutableStateFlow(item.checked), val position: MutableStateFlow = MutableStateFlow(item.position), val textFieldValue: MutableStateFlow = MutableStateFlow( - TextFieldValue(addNullChar(item.text), TextRange(1)) + TextFieldValue(item.text, TextRange(0)) ) ) { override fun equals(other: Any?): Boolean { return when (other) { is ChecklistItemFlow -> other.position.value == position.value && - other.checked.value == checked.value && - other.id == id && - other.textFieldValue.value.text == textFieldValue.value.text + other.checked.value == checked.value && + other.id == id && + other.textFieldValue.value.text == textFieldValue.value.text is ChecklistItem -> other.id == id && - other.position == position.value && - other.checked == checked.value && - other.text == textFieldValue.value.text + other.position == position.value && + other.checked == checked.value && + other.text == textFieldValue.value.text else -> false } diff --git a/app/src/main/java/us/huseli/retain/viewmodels/EditChecklistNoteViewModel.kt b/app/src/main/java/us/huseli/retain/viewmodels/EditChecklistNoteViewModel.kt index 8b55b0b..e96c590 100644 --- a/app/src/main/java/us/huseli/retain/viewmodels/EditChecklistNoteViewModel.kt +++ b/app/src/main/java/us/huseli/retain/viewmodels/EditChecklistNoteViewModel.kt @@ -15,12 +15,6 @@ import us.huseli.retain.data.NoteRepository import us.huseli.retain.data.entities.ChecklistItem import java.util.UUID import javax.inject.Inject -import kotlin.math.max - -const val INVISIBLE_CHAR = '\u0000' -fun adjustSelection(range: TextRange): TextRange = if (range.start < 1) TextRange(1, max(range.end, 1)) else range -fun stripNullChar(str: String): String = str.filter { it != INVISIBLE_CHAR } -fun addNullChar(str: String): String = INVISIBLE_CHAR + stripNullChar(str) @HiltViewModel class EditChecklistNoteViewModel @Inject constructor( @@ -87,23 +81,6 @@ class EditChecklistNoteViewModel @Inject constructor( fun insertItem(text: String, checked: Boolean, index: Int) = insertItem(ChecklistItemFlow(ChecklistItem(text = text, checked = checked, noteId = _noteId, position = index))) - private fun mergeItemWithPrevious(item: ChecklistItemFlow) { - val items = _checklistItems.value.filter { it.checked.value == item.checked.value }.toMutableList() - val index = items.indexOfFirst { it.id == item.id } - val previousItem = items[index - 1] - - if (item.textFieldValue.value.text.isNotEmpty()) { - updateItemTextFieldValue( - item = previousItem, - text = previousItem.textFieldValue.value.text + stripNullChar(item.textFieldValue.value.text), - selection = TextRange(previousItem.textFieldValue.value.text.length), - composition = previousItem.textFieldValue.value.composition, - ) - } - _focusedItemId.value = previousItem.id - deleteItem(item, true) - } - fun onItemFocus(item: ChecklistItemFlow) { _focusedItemId.value = item.id } @@ -120,31 +97,13 @@ class EditChecklistNoteViewModel @Inject constructor( text = head, selection = item.textFieldValue.value.selection, composition = null, - // composition = item.textFieldValue.value.composition, ) insertItem(tail, item.checked.value, index + 1) } fun onTextFieldValueChange(item: ChecklistItemFlow, textFieldValue: TextFieldValue) { - /** - * If the new TextFieldValue does not start with the null character, - * that must mean the user has just erased it by inputting a backspace - * at the beginning of the field. In that case, join this row with the - * one above. If this is the first row: just re-insert the null - * character and move the selection start to after it. - */ if (item.id == _focusedItemId.value && textFieldValue != item.textFieldValue.value) { - log("onTextFieldValueChange: textFieldValue=$textFieldValue, item.textFieldValue.value=${item.textFieldValue.value}") - if ( - textFieldValue.text.getOrNull(0) != INVISIBLE_CHAR && - addNullChar(textFieldValue.text) == item.textFieldValue.value.text - ) { - val index = _checklistItems.value - .filter { it.checked.value == item.checked.value } - .indexOfFirst { it.id == item.id } - if (index > 0) mergeItemWithPrevious(item) - else if (index == 0 && textFieldValue.text.isEmpty()) deleteItem(item, true) - } else updateItemTextFieldValue( + updateItemTextFieldValue( item = item, text = textFieldValue.text, selection = textFieldValue.selection, @@ -210,16 +169,15 @@ class EditChecklistNoteViewModel @Inject constructor( selection: TextRange, composition: TextRange?, ) { - log("updateItemTextFieldValue before: ${item.textFieldValue.value}, text = $text, selection = $selection, composition=$composition") item.textFieldValue.value = item.textFieldValue.value.copy( - text = addNullChar(text), - selection = adjustSelection(selection), + text = text, + selection = selection, composition = composition, ) - log("updateItemTextFieldValue after: ${item.textFieldValue.value}, text = $text, selection = $selection, composition=$composition") _dirtyChecklistItems.removeIf { it.id == item.id } - if (_originalChecklistItems.none { it.id == item.id && it.text == stripNullChar(item.textFieldValue.value.text) }) + if (_originalChecklistItems.none { it.id == item.id && it.text == item.textFieldValue.value.text }) { _dirtyChecklistItems.add(item) + } } private fun updatePositions() {