feat(widget): IMPL_04 - Add sorting & separator to widget checklists
Changes: - NoteWidgetContent.kt: Import sortChecklistItemsForPreview and ChecklistSortOption - NoteWidgetContent.kt: Add WidgetCheckedItemsSeparator composable - NoteWidgetContent.kt: Apply note.checklistSortOption in ChecklistCompactView - NoteWidgetContent.kt: Apply note.checklistSortOption in ChecklistFullView - NoteWidgetContent.kt: Integrate separator for MANUAL/UNCHECKED_FIRST modes - NoteWidgetContent.kt: Change ✅ to ☑️ emoji (IMPL_06) - NoteWidgetActions.kt: Add auto-sort after toggle in ToggleChecklistItemAction Widgets now match editor behavior: apply saved sort option, show separator between unchecked/checked items, and auto-sort when toggling checkboxes.
This commit is contained in:
@@ -5,6 +5,7 @@ import androidx.glance.GlanceId
|
||||
import androidx.glance.action.ActionParameters
|
||||
import androidx.glance.appwidget.action.ActionCallback
|
||||
import androidx.glance.appwidget.state.updateAppWidgetState
|
||||
import dev.dettmer.simplenotes.models.ChecklistSortOption
|
||||
import dev.dettmer.simplenotes.models.SyncStatus
|
||||
import dev.dettmer.simplenotes.storage.NotesStorage
|
||||
import dev.dettmer.simplenotes.utils.Logger
|
||||
@@ -51,14 +52,32 @@ class ToggleChecklistItemAction : ActionCallback {
|
||||
} else item
|
||||
} ?: return
|
||||
|
||||
// 🆕 v1.8.1 (IMPL_04): Auto-Sort nach Toggle
|
||||
// Konsistent mit NoteEditorViewModel.updateChecklistItemChecked
|
||||
val sortOption = try {
|
||||
note.checklistSortOption?.let { ChecklistSortOption.valueOf(it) }
|
||||
} catch (e: IllegalArgumentException) { null }
|
||||
?: ChecklistSortOption.MANUAL
|
||||
|
||||
val sortedItems = if (sortOption == ChecklistSortOption.MANUAL ||
|
||||
sortOption == ChecklistSortOption.UNCHECKED_FIRST) {
|
||||
val unchecked = updatedItems.filter { !it.isChecked }
|
||||
val checked = updatedItems.filter { it.isChecked }
|
||||
(unchecked + checked).mapIndexed { index, item ->
|
||||
item.copy(order = index)
|
||||
}
|
||||
} else {
|
||||
updatedItems.mapIndexed { index, item -> item.copy(order = index) }
|
||||
}
|
||||
|
||||
val updatedNote = note.copy(
|
||||
checklistItems = updatedItems,
|
||||
checklistItems = sortedItems,
|
||||
updatedAt = System.currentTimeMillis(),
|
||||
syncStatus = SyncStatus.PENDING
|
||||
)
|
||||
|
||||
storage.saveNote(updatedNote)
|
||||
Logger.d(TAG, "Toggled checklist item '$itemId' in widget")
|
||||
Logger.d(TAG, "Toggled + auto-sorted checklist item '$itemId' in widget")
|
||||
|
||||
// 🐛 FIX: Glance-State ändern um Re-Render zu erzwingen
|
||||
updateAppWidgetState(context, glanceId) { prefs ->
|
||||
|
||||
@@ -38,9 +38,11 @@ import androidx.glance.layout.width
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import dev.dettmer.simplenotes.R
|
||||
import dev.dettmer.simplenotes.models.ChecklistSortOption
|
||||
import dev.dettmer.simplenotes.models.Note
|
||||
import dev.dettmer.simplenotes.models.NoteType
|
||||
import dev.dettmer.simplenotes.ui.editor.ComposeNoteEditorActivity
|
||||
import dev.dettmer.simplenotes.ui.main.components.sortChecklistItemsForPreview
|
||||
|
||||
/**
|
||||
* 🆕 v1.8.0: Glance Composable Content für das Notiz-Widget
|
||||
@@ -72,6 +74,29 @@ private fun DpSize.toSizeClass(): WidgetSizeClass = when {
|
||||
else -> WidgetSizeClass.WIDE_TALL
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 v1.8.1 (IMPL_04): Separator zwischen erledigten und unerledigten Items im Widget.
|
||||
* Glance-kompatible Version von CheckedItemsSeparator.
|
||||
*/
|
||||
@Composable
|
||||
private fun WidgetCheckedItemsSeparator(checkedCount: Int) {
|
||||
Row(
|
||||
modifier = GlanceModifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp, horizontal = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "── $checkedCount ✔ ──",
|
||||
style = TextStyle(
|
||||
color = GlanceTheme.colors.outline,
|
||||
fontSize = 11.sp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NoteWidgetContent(
|
||||
note: Note?,
|
||||
@@ -404,13 +429,35 @@ private fun ChecklistCompactView(
|
||||
isLocked: Boolean,
|
||||
glanceId: GlanceId
|
||||
) {
|
||||
val items = note.checklistItems?.sortedBy { it.order } ?: return
|
||||
// 🆕 v1.8.1 (IMPL_04): Sortierung aus Editor übernehmen
|
||||
val items = note.checklistItems?.let { rawItems ->
|
||||
sortChecklistItemsForPreview(rawItems, note.checklistSortOption)
|
||||
} ?: return
|
||||
|
||||
// 🆕 v1.8.1 (IMPL_04): Separator-Logik
|
||||
val uncheckedCount = items.count { !it.isChecked }
|
||||
val checkedCount = items.count { it.isChecked }
|
||||
val sortOption = try {
|
||||
note.checklistSortOption?.let { ChecklistSortOption.valueOf(it) }
|
||||
} catch (e: IllegalArgumentException) { null }
|
||||
?: ChecklistSortOption.MANUAL
|
||||
|
||||
val showSeparator = (sortOption == ChecklistSortOption.MANUAL ||
|
||||
sortOption == ChecklistSortOption.UNCHECKED_FIRST) &&
|
||||
uncheckedCount > 0 && checkedCount > 0
|
||||
|
||||
val visibleItems = items.take(maxItems)
|
||||
val remainingCount = items.size - visibleItems.size
|
||||
val checkedCount = items.count { it.isChecked }
|
||||
|
||||
Column(modifier = GlanceModifier.padding(horizontal = 8.dp, vertical = 2.dp)) {
|
||||
var separatorShown = false
|
||||
visibleItems.forEach { item ->
|
||||
// 🆕 v1.8.1: Separator vor dem ersten checked Item anzeigen
|
||||
if (showSeparator && !separatorShown && item.isChecked) {
|
||||
WidgetCheckedItemsSeparator(checkedCount = checkedCount)
|
||||
separatorShown = true
|
||||
}
|
||||
|
||||
if (isLocked) {
|
||||
Row(
|
||||
modifier = GlanceModifier
|
||||
@@ -419,7 +466,7 @@ private fun ChecklistCompactView(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = if (item.isChecked) "✅" else "☐",
|
||||
text = if (item.isChecked) "☑️" else "☐", // 🆕 v1.8.1 (IMPL_06)
|
||||
style = TextStyle(fontSize = 14.sp)
|
||||
)
|
||||
Spacer(modifier = GlanceModifier.width(6.dp))
|
||||
@@ -477,15 +524,41 @@ private fun ChecklistFullView(
|
||||
isLocked: Boolean,
|
||||
glanceId: GlanceId
|
||||
) {
|
||||
val items = note.checklistItems?.sortedBy { it.order } ?: return
|
||||
// 🆕 v1.8.1 (IMPL_04): Sortierung aus Editor übernehmen
|
||||
val items = note.checklistItems?.let { rawItems ->
|
||||
sortChecklistItemsForPreview(rawItems, note.checklistSortOption)
|
||||
} ?: return
|
||||
|
||||
// 🆕 v1.8.1 (IMPL_04): Separator-Logik
|
||||
val uncheckedCount = items.count { !it.isChecked }
|
||||
val checkedCount = items.count { it.isChecked }
|
||||
val sortOption = try {
|
||||
note.checklistSortOption?.let { ChecklistSortOption.valueOf(it) }
|
||||
} catch (e: IllegalArgumentException) { null }
|
||||
?: ChecklistSortOption.MANUAL
|
||||
|
||||
val showSeparator = (sortOption == ChecklistSortOption.MANUAL ||
|
||||
sortOption == ChecklistSortOption.UNCHECKED_FIRST) &&
|
||||
uncheckedCount > 0 && checkedCount > 0
|
||||
|
||||
// 🆕 v1.8.1: Berechne die Gesamtanzahl der Elemente inklusive Separator
|
||||
val totalItems = items.size + if (showSeparator) 1 else 0
|
||||
|
||||
LazyColumn(
|
||||
modifier = GlanceModifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 8.dp)
|
||||
) {
|
||||
items(items.size) { index ->
|
||||
val item = items[index]
|
||||
items(totalItems) { index ->
|
||||
// 🆕 v1.8.1: Separator an Position uncheckedCount einfügen
|
||||
if (showSeparator && index == uncheckedCount) {
|
||||
WidgetCheckedItemsSeparator(checkedCount = checkedCount)
|
||||
return@items
|
||||
}
|
||||
|
||||
// Tatsächlichen Item-Index berechnen (nach Separator um 1 verschoben)
|
||||
val itemIndex = if (showSeparator && index > uncheckedCount) index - 1 else index
|
||||
val item = items.getOrNull(itemIndex) ?: return@items
|
||||
|
||||
if (isLocked) {
|
||||
Row(
|
||||
@@ -495,7 +568,7 @@ private fun ChecklistFullView(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = if (item.isChecked) "✅" else "☐",
|
||||
text = if (item.isChecked) "☑️" else "☐", // 🆕 v1.8.1 (IMPL_06)
|
||||
style = TextStyle(fontSize = 16.sp)
|
||||
)
|
||||
Spacer(modifier = GlanceModifier.width(8.dp))
|
||||
|
||||
Reference in New Issue
Block a user