feat: 支持设置任务优先级
This commit is contained in:
parent
e962a9f361
commit
08d7706b15
11 changed files with 213 additions and 53 deletions
|
|
@ -9,5 +9,6 @@ data class TodoEntity(
|
|||
@ColumnInfo(name = "content") val content: String,
|
||||
@ColumnInfo(name = "subject") val subject: Int,
|
||||
@ColumnInfo(name = "completed") val isCompleted: Boolean = false,
|
||||
@ColumnInfo(name = "priority") val priority: Float,
|
||||
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Int = 0,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ fun FilterChipGroup(
|
|||
label = {
|
||||
Text(item)
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 5.dp)
|
||||
modifier = Modifier.padding(end = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||
import androidx.compose.material3.FloatingActionButtonElevation
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
|
|
@ -28,14 +30,18 @@ import androidx.compose.ui.unit.dp
|
|||
fun AnimatedExtendedFloatingActionButton(
|
||||
icon: @Composable () -> Unit,
|
||||
text: @Composable () -> Unit,
|
||||
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||
expanded: Boolean,
|
||||
containerColor: Color = FloatingActionButtonDefaults.containerColor,
|
||||
contentColor: Color = contentColorFor(containerColor),
|
||||
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
FloatingActionButton(
|
||||
onClick = onClick,
|
||||
elevation = elevation,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
modifier = modifier
|
||||
) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.exclude
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.safeContent
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||
|
|
@ -41,7 +43,7 @@ fun LargeTopAppBarScaffold(
|
|||
snackbarHost: @Composable () -> Unit = {},
|
||||
floatingActionButton: @Composable () -> Unit = {},
|
||||
floatingActionButtonPosition: FabPosition = FabPosition.End,
|
||||
contentWindowInsets: WindowInsets = WindowInsets.safeContent,
|
||||
contentWindowInsets: WindowInsets = WindowInsets.safeContent.exclude(WindowInsets.ime),
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable (PaddingValues) -> Unit
|
||||
) {
|
||||
|
|
@ -62,6 +64,8 @@ fun LargeTopAppBarScaffold(
|
|||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
floatingActionButton = floatingActionButton,
|
||||
floatingActionButtonPosition = floatingActionButtonPosition,
|
||||
contentWindowInsets = contentWindowInsets
|
||||
) { innerPadding ->
|
||||
Box { content(innerPadding) }
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ fun TodoNavigation(
|
|||
toDo = viewModel.selectedEditTodo,
|
||||
onSave = {
|
||||
viewModel.addTodo(it)
|
||||
viewModel.setEditTodoItem(null)
|
||||
navController.navigateUp()
|
||||
},
|
||||
onDelete = {
|
||||
|
|
@ -72,8 +73,8 @@ fun TodoNavigation(
|
|||
navController.navigateUp()
|
||||
},
|
||||
onNavigateUp = {
|
||||
viewModel.setEditTodoItem(null)
|
||||
navController.navigateUp()
|
||||
viewModel.setEditTodoItem(null)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package cn.super12138.todo.ui.pages.editor
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -9,17 +11,26 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Save
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Label
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.PlainTooltip
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.SliderDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -28,13 +39,17 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cn.super12138.todo.R
|
||||
import cn.super12138.todo.logic.database.TodoEntity
|
||||
import cn.super12138.todo.logic.model.Subjects
|
||||
import cn.super12138.todo.ui.TodoDefaults
|
||||
import cn.super12138.todo.ui.components.AnimatedExtendedFloatingActionButton
|
||||
import cn.super12138.todo.ui.components.FilterChipGroup
|
||||
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
|
||||
import cn.super12138.todo.ui.pages.main.components.getPriorityString
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -45,11 +60,65 @@ fun TodoEditorPage(
|
|||
onNavigateUp: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
BackHandler {
|
||||
onNavigateUp()
|
||||
}
|
||||
val context = LocalContext.current
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
var text by rememberSaveable { mutableStateOf(toDo?.content ?: "") }
|
||||
var isError by rememberSaveable { mutableStateOf(false) }
|
||||
var selectedItemIndex by rememberSaveable { mutableIntStateOf(toDo?.subject ?: 0) }
|
||||
var sliderPosition by rememberSaveable { mutableFloatStateOf(toDo?.priority ?: 0f) }
|
||||
|
||||
LargeTopAppBarScaffold(
|
||||
title = stringResource(if (toDo != null) R.string.title_edit_task else R.string.action_add_task),
|
||||
scrollBehavior = scrollBehavior,
|
||||
floatingActionButton = {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
if (toDo !== null) {
|
||||
AnimatedExtendedFloatingActionButton(
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = stringResource(R.string.action_delete)
|
||||
)
|
||||
},
|
||||
text = { Text(stringResource(R.string.action_delete)) },
|
||||
expanded = true,
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||
onClick = onDelete
|
||||
)
|
||||
}
|
||||
AnimatedExtendedFloatingActionButton(
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Save,
|
||||
contentDescription = stringResource(R.string.action_save)
|
||||
)
|
||||
},
|
||||
text = { Text(stringResource(R.string.action_save)) },
|
||||
expanded = true,
|
||||
onClick = {
|
||||
if (text.trim().isEmpty()) {
|
||||
isError = true
|
||||
return@AnimatedExtendedFloatingActionButton
|
||||
}
|
||||
|
||||
isError = false
|
||||
onSave(
|
||||
TodoEntity(
|
||||
content = text,
|
||||
subject = selectedItemIndex,
|
||||
isCompleted = toDo?.isCompleted ?: false,
|
||||
priority = sliderPosition,
|
||||
id = toDo?.id ?: 0
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
onBack = onNavigateUp,
|
||||
modifier = modifier
|
||||
) { innerPadding ->
|
||||
|
|
@ -60,8 +129,6 @@ fun TodoEditorPage(
|
|||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
var text by rememberSaveable { mutableStateOf(toDo?.content ?: "") }
|
||||
var isError by rememberSaveable { mutableStateOf(false) }
|
||||
TextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
|
|
@ -82,11 +149,13 @@ fun TodoEditorPage(
|
|||
it.getDisplayName(context)
|
||||
}
|
||||
}
|
||||
var selectedItemIndex by rememberSaveable { mutableIntStateOf(toDo?.subject ?: 0) }
|
||||
Text(
|
||||
text = stringResource(R.string.label_subject),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
Spacer(Modifier.size(5.dp))
|
||||
|
||||
FilterChipGroup(
|
||||
items = subjects,
|
||||
defaultSelectedItemIndex = toDo?.subject ?: 0,
|
||||
|
|
@ -96,47 +165,45 @@ fun TodoEditorPage(
|
|||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Spacer(Modifier.size(20.dp))
|
||||
Spacer(Modifier.size(10.dp))
|
||||
|
||||
// 操作按钮
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
if (toDo !== null) {
|
||||
FilledTonalButton(
|
||||
onClick = {
|
||||
onDelete()
|
||||
Text(
|
||||
text = stringResource(R.string.label_priority),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
Spacer(Modifier.size(5.dp))
|
||||
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
Slider(
|
||||
modifier = Modifier.semantics {
|
||||
contentDescription = context.getString(R.string.label_priority)
|
||||
},
|
||||
value = sliderPosition,
|
||||
onValueChange = { sliderPosition = it },
|
||||
valueRange = -10f..10f,
|
||||
steps = 3,
|
||||
interactionSource = interactionSource,
|
||||
thumb = {
|
||||
Label(
|
||||
label = {
|
||||
PlainTooltip(
|
||||
modifier = Modifier
|
||||
.sizeIn(45.dp, 25.dp)
|
||||
.wrapContentWidth()
|
||||
) {
|
||||
Text(stringResource(getPriorityString(sliderPosition)))
|
||||
}
|
||||
},
|
||||
colors = ButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onErrorContainer,
|
||||
disabledContainerColor = MaterialTheme.colorScheme.onSurface,
|
||||
disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
interactionSource = interactionSource
|
||||
) {
|
||||
Text(stringResource(R.string.action_delete))
|
||||
SliderDefaults.Thumb(interactionSource)
|
||||
}
|
||||
}
|
||||
FilledTonalButton(
|
||||
onClick = {
|
||||
// 文本为空
|
||||
if (text.trim().isEmpty()) {
|
||||
isError = true
|
||||
return@FilledTonalButton
|
||||
}
|
||||
)
|
||||
|
||||
isError = false
|
||||
onSave(
|
||||
TodoEntity(
|
||||
content = text,
|
||||
subject = selectedItemIndex,
|
||||
isCompleted = toDo?.isCompleted ?: false,
|
||||
id = toDo?.id ?: 0
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.action_save))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.size(40.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -125,6 +125,7 @@ fun MainPage(
|
|||
content = content,
|
||||
subject = subject,
|
||||
isCompleted = true,
|
||||
priority = priority,
|
||||
id = id
|
||||
)
|
||||
)
|
||||
|
|
@ -166,6 +167,7 @@ fun MainPage(
|
|||
content = content,
|
||||
subject = subject,
|
||||
isCompleted = true,
|
||||
priority = priority,
|
||||
id = id
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -69,10 +69,11 @@ fun ManagerFragment(
|
|||
content = item.content,
|
||||
subject = Subjects.fromId(item.subject).getDisplayName(context),
|
||||
completed = item.isCompleted,
|
||||
priority = item.priority,
|
||||
selected = selectedTodoIds.contains(item.id),
|
||||
onCardClick = { onItemClick(item) },
|
||||
onCardLongClick = { onItemLongClick(item) },
|
||||
onChecked = { onItemChecked(item) },
|
||||
selected = selectedTodoIds.contains(item.id),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
.animateItem() // TODO: 设置动画时间
|
||||
|
|
|
|||
|
|
@ -26,8 +26,10 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cn.super12138.todo.R
|
||||
|
||||
|
|
@ -37,10 +39,11 @@ fun TodoCard(
|
|||
content: String,
|
||||
subject: String,
|
||||
completed: Boolean,
|
||||
priority: Float,
|
||||
selected: Boolean,
|
||||
onCardClick: () -> Unit = {},
|
||||
onCardLongClick: () -> Unit = {},
|
||||
onChecked: () -> Unit = {},
|
||||
selected: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
ElevatedCard(
|
||||
|
|
@ -85,14 +88,27 @@ fun TodoCard(
|
|||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||
modifier = Modifier.basicMarquee() // TODO: 后续评估性能影响
|
||||
)
|
||||
Text(
|
||||
text = subject,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||
maxLines = 1
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.basicMarquee() // TODO: 后续评估性能影响
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = subject,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||
maxLines = 1
|
||||
)
|
||||
|
||||
|
||||
PriorityText(
|
||||
priority = priority,
|
||||
isCompleted = completed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(!selected && !completed) {
|
||||
|
|
@ -123,4 +139,54 @@ fun TodoCard(
|
|||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PriorityText(
|
||||
priority: Float,
|
||||
isCompleted: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(getPriorityString(priority)),
|
||||
style = MaterialTheme.typography.labelMedium.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = when (priority) {
|
||||
-10f -> MaterialTheme.colorScheme.outline
|
||||
-5f -> MaterialTheme.colorScheme.onSurfaceVariant
|
||||
0f -> MaterialTheme.colorScheme.secondary
|
||||
5f -> MaterialTheme.colorScheme.tertiary
|
||||
10f -> MaterialTheme.colorScheme.onErrorContainer
|
||||
else -> MaterialTheme.colorScheme.secondary
|
||||
}
|
||||
),
|
||||
textDecoration = if (isCompleted) TextDecoration.LineThrough else TextDecoration.None,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
|
||||
fun getPriorityString(priority: Float): Int {
|
||||
return when (priority) {
|
||||
-10f -> R.string.priority_not_urgent
|
||||
-5f -> R.string.priority_not_important
|
||||
0f -> R.string.priority_default
|
||||
5f -> R.string.priority_important
|
||||
10f -> R.string.priority_urgent
|
||||
else -> R.string.priority_default
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(locale = "zh-rCN", showBackground = true)
|
||||
@Composable
|
||||
private fun TodoCardPreview() {
|
||||
TodoCard(
|
||||
content = "背《岳阳楼记》《出师表》《琵琶行》",
|
||||
subject = "语文",
|
||||
completed = false,
|
||||
priority = 5f,
|
||||
selected = false,
|
||||
onCardClick = {},
|
||||
onCardLongClick = {},
|
||||
onChecked = {}
|
||||
)
|
||||
}
|
||||
|
|
@ -40,4 +40,10 @@
|
|||
<string name="pref_licence">开放源代码许可</string>
|
||||
<string name="pref_licence_desc">查看应用使用的开源库及其许可</string>
|
||||
<string name="label_subject">学科</string>
|
||||
<string name="priority_not_urgent">不紧急</string>
|
||||
<string name="priority_not_important">不重要</string>
|
||||
<string name="priority_default">默认</string>
|
||||
<string name="priority_important">重要</string>
|
||||
<string name="priority_urgent">紧急</string>
|
||||
<string name="label_priority">优先级</string>
|
||||
</resources>
|
||||
|
|
@ -41,4 +41,10 @@
|
|||
<string name="pref_licence">Open Source Licences</string>
|
||||
<string name="pref_licence_desc">Check the open source libraries used by the application and their licences.</string>
|
||||
<string name="label_subject">Subject</string>
|
||||
<string name="priority_not_urgent">Not Urgent</string>
|
||||
<string name="priority_not_important">Not Important</string>
|
||||
<string name="priority_default">Default</string>
|
||||
<string name="priority_important">Important</string>
|
||||
<string name="priority_urgent">Urgent</string>
|
||||
<string name="label_priority">Priority</string>
|
||||
</resources>
|
||||
Loading…
Reference in a new issue