Replace notebook long-press with overflow menu (rename/delete)
- Three-dot overflow button on each notebook card - Dropdown menu with Rename and Delete options - Rename dialog with title field (keyboard defaults to Title Case) - Removed combinedClickable — tap opens notebook, menu for actions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package net.metacircular.engpad.ui.notebooks
|
package net.metacircular.engpad.ui.notebooks
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -69,6 +68,7 @@ fun NotebookListScreen(
|
|||||||
val notebooks by viewModel.notebooks.collectAsState()
|
val notebooks by viewModel.notebooks.collectAsState()
|
||||||
var showCreateDialog by remember { mutableStateOf(false) }
|
var showCreateDialog by remember { mutableStateOf(false) }
|
||||||
var notebookToDelete by remember { mutableStateOf<Notebook?>(null) }
|
var notebookToDelete by remember { mutableStateOf<Notebook?>(null) }
|
||||||
|
var notebookToRename by remember { mutableStateOf<Notebook?>(null) }
|
||||||
|
|
||||||
var filterText by remember { mutableStateOf("") }
|
var filterText by remember { mutableStateOf("") }
|
||||||
var sortField by remember { mutableStateOf(SortField.LAST_EDITED) }
|
var sortField by remember { mutableStateOf(SortField.LAST_EDITED) }
|
||||||
@@ -169,7 +169,8 @@ fun NotebookListScreen(
|
|||||||
NotebookItem(
|
NotebookItem(
|
||||||
notebook = notebook,
|
notebook = notebook,
|
||||||
onClick = { onNotebookClick(notebook.id) },
|
onClick = { onNotebookClick(notebook.id) },
|
||||||
onLongClick = { notebookToDelete = notebook },
|
onRename = { notebookToRename = notebook },
|
||||||
|
onDelete = { notebookToDelete = notebook },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,27 +198,40 @@ fun NotebookListScreen(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notebookToRename?.let { notebook ->
|
||||||
|
RenameNotebookDialog(
|
||||||
|
notebook = notebook,
|
||||||
|
onDismiss = { notebookToRename = null },
|
||||||
|
onRename = { newTitle ->
|
||||||
|
viewModel.renameNotebook(notebook.id, newTitle)
|
||||||
|
notebookToRename = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NotebookItem(
|
private fun NotebookItem(
|
||||||
notebook: Notebook,
|
notebook: Notebook,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onRename: () -> Unit,
|
||||||
|
onDelete: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val dateFormat = remember { SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US) }
|
val dateFormat = remember { SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US) }
|
||||||
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 4.dp)
|
.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||||
.combinedClickable(
|
.clickable(onClick = onClick),
|
||||||
onClick = onClick,
|
|
||||||
onLongClick = onLongClick,
|
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
Row(
|
||||||
|
modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 16.dp, end = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text(
|
Text(
|
||||||
text = notebook.title,
|
text = notebook.title,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
@@ -236,6 +250,25 @@ private fun NotebookItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Box {
|
||||||
|
androidx.compose.material3.IconButton(onClick = { showMenu = true }) {
|
||||||
|
Text("\u22EE", style = MaterialTheme.typography.bodyLarge)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = showMenu,
|
||||||
|
onDismissRequest = { showMenu = false },
|
||||||
|
) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Rename") },
|
||||||
|
onClick = { showMenu = false; onRename() },
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text("Delete") },
|
||||||
|
onClick = { showMenu = false; onDelete() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,14 +345,44 @@ private fun DeleteNotebookDialog(
|
|||||||
title = { Text("Delete Notebook") },
|
title = { Text("Delete Notebook") },
|
||||||
text = { Text("Delete \"${notebook.title}\"? This cannot be undone.") },
|
text = { Text("Delete \"${notebook.title}\"? This cannot be undone.") },
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = onConfirm) {
|
TextButton(onClick = onConfirm) { Text("Delete") }
|
||||||
Text("Delete")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
TextButton(onClick = onDismiss) {
|
TextButton(onClick = onDismiss) { Text("Cancel") }
|
||||||
Text("Cancel")
|
},
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RenameNotebookDialog(
|
||||||
|
notebook: Notebook,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onRename: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
var title by remember { mutableStateOf(notebook.title) }
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = { Text("Rename Notebook") },
|
||||||
|
text = {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = title,
|
||||||
|
onValueChange = { title = it },
|
||||||
|
label = { Text("Title") },
|
||||||
|
singleLine = true,
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
capitalization = KeyboardCapitalization.Words,
|
||||||
|
),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { onRename(title.ifBlank { notebook.title }) },
|
||||||
|
) { Text("Rename") }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) { Text("Cancel") }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ class NotebookListViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun renameNotebook(id: Long, title: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
repository.updateTitle(id, title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Factory(private val repository: NotebookRepository) : ViewModelProvider.Factory {
|
class Factory(private val repository: NotebookRepository) : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
|||||||
Reference in New Issue
Block a user