From 08d7706b1545eb19a78496435105ef1d4a6d15cc Mon Sep 17 00:00:00 2001
From: Super12138 <70494801+Super12138@users.noreply.github.com>
Date: Fri, 17 Jan 2025 14:39:10 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=AE=BE=E7=BD=AE?=
=?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=BC=98=E5=85=88=E7=BA=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../todo/logic/database/TodoEntity.kt | 1 +
.../todo/ui/components/ChipGroup.kt | 2 +-
.../ui/components/FloatingActionButton.kt | 8 +-
.../todo/ui/components/Scaffolds.kt | 6 +-
.../todo/ui/navigation/TodoNavigation.kt | 3 +-
.../todo/ui/pages/editor/TodoEditorPage.kt | 147 +++++++++++++-----
.../super12138/todo/ui/pages/main/MainPage.kt | 2 +
.../todo/ui/pages/main/ManagerFragment.kt | 3 +-
.../todo/ui/pages/main/components/TodoCard.kt | 82 +++++++++-
app/src/main/res/values-zh-rCN/strings.xml | 6 +
app/src/main/res/values/strings.xml | 6 +
11 files changed, 213 insertions(+), 53 deletions(-)
diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/database/TodoEntity.kt b/app/src/main/kotlin/cn/super12138/todo/logic/database/TodoEntity.kt
index 6dc8b2d..90957b1 100644
--- a/app/src/main/kotlin/cn/super12138/todo/logic/database/TodoEntity.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/logic/database/TodoEntity.kt
@@ -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,
)
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt
index e4ff340..b6f25ad 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/ChipGroup.kt
@@ -56,7 +56,7 @@ fun FilterChipGroup(
label = {
Text(item)
},
- modifier = Modifier.padding(horizontal = 5.dp)
+ modifier = Modifier.padding(end = 10.dp)
)
}
}
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt
index b245f96..1102f2a 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/FloatingActionButton.kt
@@ -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(
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt
index 06741f6..7f9ef93 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/Scaffolds.kt
@@ -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) }
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt
index afff978..083e0cb 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt
@@ -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)
}
)
}
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt
index 6adb795..ff7cc4b 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/editor/TodoEditorPage.kt
@@ -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))
}
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt
index ef234a3..f33517a 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/MainPage.kt
@@ -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
)
)
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt
index 1f4ff8e..1869831 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/ManagerFragment.kt
@@ -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: 设置动画时间
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt
index 74cb07e..960a922 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/main/components/TodoCard.kt
@@ -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 = {}
+ )
}
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index a47fdc9..2772a14 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -40,4 +40,10 @@
开放源代码许可
查看应用使用的开源库及其许可
学科
+ 不紧急
+ 不重要
+ 默认
+ 重要
+ 紧急
+ 优先级
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b909faf..8e059ca 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -41,4 +41,10 @@
Open Source Licences
Check the open source libraries used by the application and their licences.
Subject
+ Not Urgent
+ Not Important
+ Default
+ Important
+ Urgent
+ Priority
\ No newline at end of file