feat(editor): IMPL_05 - Auto-scroll on line wrap in checklist editor
Changes: - ChecklistItemRow.kt: Import mutableIntStateOf - ChecklistItemRow.kt: Add onHeightChanged callback parameter - ChecklistItemRow.kt: Track lastLineCount state for line wrap detection - ChecklistItemRow.kt: Detect line count increase in onTextLayout and trigger callback - NoteEditorScreen.kt: Add scrollToItemIndex state in ChecklistEditor - NoteEditorScreen.kt: Add LaunchedEffect for auto-scroll on height change - NoteEditorScreen.kt: Pass onHeightChanged callback to ChecklistItemRow When typing in a checklist item at the bottom of the list, the editor now automatically scrolls to keep the cursor visible when text wraps to a new line.
This commit is contained in:
@@ -338,6 +338,7 @@ private fun LazyItemScope.DraggableChecklistItem(
|
|||||||
onDelete: (String) -> Unit,
|
onDelete: (String) -> Unit,
|
||||||
onAddNewItemAfter: (String) -> Unit,
|
onAddNewItemAfter: (String) -> Unit,
|
||||||
onFocusHandled: () -> Unit,
|
onFocusHandled: () -> Unit,
|
||||||
|
onHeightChanged: () -> Unit, // 🆕 v1.8.1 (IMPL_05)
|
||||||
) {
|
) {
|
||||||
val isDragging = dragDropState.draggingItemIndex == visualIndex
|
val isDragging = dragDropState.draggingItemIndex == visualIndex
|
||||||
val elevation by animateDpAsState(
|
val elevation by animateDpAsState(
|
||||||
@@ -363,6 +364,7 @@ private fun LazyItemScope.DraggableChecklistItem(
|
|||||||
isDragging = isDragging,
|
isDragging = isDragging,
|
||||||
isAnyItemDragging = dragDropState.draggingItemIndex != null,
|
isAnyItemDragging = dragDropState.draggingItemIndex != null,
|
||||||
dragModifier = Modifier.dragContainer(dragDropState, visualIndex),
|
dragModifier = Modifier.dragContainer(dragDropState, visualIndex),
|
||||||
|
onHeightChanged = onHeightChanged, // 🆕 v1.8.1 (IMPL_05)
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(if (!isDragging) Modifier.animateItem() else Modifier)
|
.then(if (!isDragging) Modifier.animateItem() else Modifier)
|
||||||
.offset {
|
.offset {
|
||||||
@@ -404,6 +406,9 @@ private fun ChecklistEditor(
|
|||||||
onMove = onMove
|
onMove = onMove
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 🆕 v1.8.1 (IMPL_05): Auto-Scroll bei Zeilenumbruch
|
||||||
|
var scrollToItemIndex by remember { mutableStateOf<Int?>(null) }
|
||||||
|
|
||||||
// 🆕 v1.8.0 (IMPL_017 + IMPL_020): Separator nur bei MANUAL und UNCHECKED_FIRST anzeigen
|
// 🆕 v1.8.0 (IMPL_017 + IMPL_020): Separator nur bei MANUAL und UNCHECKED_FIRST anzeigen
|
||||||
val uncheckedCount = items.count { !it.isChecked }
|
val uncheckedCount = items.count { !it.isChecked }
|
||||||
val checkedCount = items.count { it.isChecked }
|
val checkedCount = items.count { it.isChecked }
|
||||||
@@ -418,6 +423,21 @@ private fun ChecklistEditor(
|
|||||||
dragDropState.separatorVisualIndex = separatorVisualIndex
|
dragDropState.separatorVisualIndex = separatorVisualIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🆕 v1.8.1 (IMPL_05): Auto-Scroll wenn ein Item durch Zeilenumbruch wächst
|
||||||
|
LaunchedEffect(scrollToItemIndex) {
|
||||||
|
scrollToItemIndex?.let { index ->
|
||||||
|
delay(50) // Warten bis Layout-Pass abgeschlossen
|
||||||
|
val lastVisibleIndex = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
|
||||||
|
if (index >= lastVisibleIndex - 1) {
|
||||||
|
listState.animateScrollToItem(
|
||||||
|
index = minOf(index + 1, items.size + if (showSeparator) 1 else 0),
|
||||||
|
scrollOffset = 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
scrollToItemIndex = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
@@ -438,7 +458,8 @@ private fun ChecklistEditor(
|
|||||||
onCheckedChange = onCheckedChange,
|
onCheckedChange = onCheckedChange,
|
||||||
onDelete = onDelete,
|
onDelete = onDelete,
|
||||||
onAddNewItemAfter = onAddNewItemAfter,
|
onAddNewItemAfter = onAddNewItemAfter,
|
||||||
onFocusHandled = onFocusHandled
|
onFocusHandled = onFocusHandled,
|
||||||
|
onHeightChanged = { scrollToItemIndex = index } // 🆕 v1.8.1 (IMPL_05)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,7 +487,8 @@ private fun ChecklistEditor(
|
|||||||
onCheckedChange = onCheckedChange,
|
onCheckedChange = onCheckedChange,
|
||||||
onDelete = onDelete,
|
onDelete = onDelete,
|
||||||
onAddNewItemAfter = onAddNewItemAfter,
|
onAddNewItemAfter = onAddNewItemAfter,
|
||||||
onFocusHandled = onFocusHandled
|
onFocusHandled = onFocusHandled,
|
||||||
|
onHeightChanged = { scrollToItemIndex = visualIndex } // 🆕 v1.8.1 (IMPL_05)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -70,6 +71,7 @@ fun ChecklistItemRow(
|
|||||||
isDragging: Boolean = false, // 🆕 v1.8.0: IMPL_023 - Drag state
|
isDragging: Boolean = false, // 🆕 v1.8.0: IMPL_023 - Drag state
|
||||||
isAnyItemDragging: Boolean = false, // 🆕 v1.8.0: IMPL_023 - Hide gradient during any drag
|
isAnyItemDragging: Boolean = false, // 🆕 v1.8.0: IMPL_023 - Hide gradient during any drag
|
||||||
dragModifier: Modifier = Modifier, // 🆕 v1.8.0: IMPL_023 - Drag modifier for handle
|
dragModifier: Modifier = Modifier, // 🆕 v1.8.0: IMPL_023 - Drag modifier for handle
|
||||||
|
onHeightChanged: (() -> Unit)? = null, // 🆕 v1.8.1: IMPL_05 - Auto-scroll callback
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
@@ -91,6 +93,9 @@ fun ChecklistItemRow(
|
|||||||
// 🆕 v1.8.0: ScrollState für dynamischen Gradient
|
// 🆕 v1.8.0: ScrollState für dynamischen Gradient
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
|
// 🆕 v1.8.1: IMPL_05 - Letzte Zeilenanzahl tracken für Auto-Scroll
|
||||||
|
var lastLineCount by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
// 🆕 v1.8.1: Gradient-Sichtbarkeit direkt berechnet (kein derivedStateOf)
|
// 🆕 v1.8.1: Gradient-Sichtbarkeit direkt berechnet (kein derivedStateOf)
|
||||||
// derivedStateOf mit remember{} fängt showGradient als stale val — nie aktualisiert.
|
// derivedStateOf mit remember{} fängt showGradient als stale val — nie aktualisiert.
|
||||||
val showGradient = hasOverflow && collapsedHeightDp != null && !isFocused && !isAnyItemDragging
|
val showGradient = hasOverflow && collapsedHeightDp != null && !isFocused && !isAnyItemDragging
|
||||||
@@ -216,8 +221,9 @@ fun ChecklistItemRow(
|
|||||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||||
onTextLayout = { textLayoutResult ->
|
onTextLayout = { textLayoutResult ->
|
||||||
// 🆕 v1.8.1: lineCount ist jetzt akkurat (maxLines=MAX_VALUE deckelt nicht)
|
// 🆕 v1.8.1: lineCount ist jetzt akkurat (maxLines=MAX_VALUE deckelt nicht)
|
||||||
|
val lineCount = textLayoutResult.lineCount
|
||||||
if (!isAnyItemDragging) {
|
if (!isAnyItemDragging) {
|
||||||
val overflow = textLayoutResult.lineCount > COLLAPSED_MAX_LINES
|
val overflow = lineCount > COLLAPSED_MAX_LINES
|
||||||
hasOverflow = overflow
|
hasOverflow = overflow
|
||||||
// Höhe der ersten 5 Zeilen berechnen (einmalig)
|
// Höhe der ersten 5 Zeilen berechnen (einmalig)
|
||||||
if (overflow && collapsedHeightDp == null) {
|
if (overflow && collapsedHeightDp == null) {
|
||||||
@@ -230,6 +236,11 @@ fun ChecklistItemRow(
|
|||||||
collapsedHeightDp = null
|
collapsedHeightDp = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 🆕 v1.8.1 (IMPL_05): Höhenänderung bei Zeilenumbruch melden
|
||||||
|
if (isFocused && lineCount > lastLineCount && lastLineCount > 0) {
|
||||||
|
onHeightChanged?.invoke()
|
||||||
|
}
|
||||||
|
lastLineCount = lineCount
|
||||||
},
|
},
|
||||||
decorationBox = { innerTextField ->
|
decorationBox = { innerTextField ->
|
||||||
Box {
|
Box {
|
||||||
|
|||||||
Reference in New Issue
Block a user