Share side-inset padding, fix keyboard gap on detail and edit (#152)

This commit is contained in:
makiolaj committed 2026-11-12 16:07:32 +01:00
1 parent d562cf85f2
commit 984da3c61c
6 files changed
+58 -32

No files matched your search

+2 -1
View File
@@ -10,7 +10,8 @@ All notable changes to this project are documented here. The format follows
### Fixed
- With 3-button navigation, the navigation bar no longer covers the last home
search results, and in landscape it no longer covers the home screen, the
task list or the home top bar's buttons (#152).
task list or the home top bar's buttons. The task details and editor no
longer leave an empty gap between the open keyboard and the text (#152).
## [1.3.1] - 2026-11-09
@@ -0,0 +1,27 @@
package de.jeanlucmakiola.agendula.ui.common
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
/**
* Pads by the start/end of a scaffold's [inner] padding and consumes it, so a
* side navigation bar or cutout (3-button nav in landscape) is cleared once.
*/
@Composable
fun Modifier.sideInsetsPadding(inner: PaddingValues): Modifier {
val layoutDirection = LocalLayoutDirection.current
val sides = PaddingValues(
start = inner.calculateStartPadding(layoutDirection),
top = 0.dp,
end = inner.calculateEndPadding(layoutDirection),
bottom = 0.dp,
)
return this.padding(sides).consumeWindowInsets(sides)
}
@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -268,6 +269,9 @@ private fun DetailBody(
modifier = Modifier
.fillMaxSize()
.padding(inner)
// Consume the bar insets so imePadding adds only what the keyboard
// covers beyond them.
.consumeWindowInsets(inner)
// Shrink the scroll viewport by the keyboard so the focused add-subtask
// field scrolls into view above the IME instead of being hidden.
.imePadding()
@@ -57,6 +57,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -219,7 +220,9 @@ fun TaskEditScreen(
viewModel = viewModel,
scroll = scroll,
onTitleBottom = { titleBottom = it },
modifier = Modifier.padding(inner),
// Consumed so EditContent's imePadding adds only what the keyboard
// covers beyond the bars.
modifier = Modifier.padding(inner).consumeWindowInsets(inner),
)
}
@@ -31,14 +31,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
@@ -84,7 +83,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
@@ -110,6 +108,7 @@ import de.jeanlucmakiola.agendula.ui.common.LocalUse24HourFormat
import de.jeanlucmakiola.agendula.ui.common.ShapedActionButton
import de.jeanlucmakiola.agendula.ui.common.SyncPullBox
import de.jeanlucmakiola.agendula.ui.common.priorityAccent
import de.jeanlucmakiola.agendula.ui.common.sideInsetsPadding
import de.jeanlucmakiola.agendula.ui.tasklist.priorityLabel
import de.jeanlucmakiola.floret.components.positionOf
import de.jeanlucmakiola.floret.time.formatDateTimeCompact
@@ -163,7 +162,6 @@ fun ListsScreen(
// System back closes search before leaving the screen.
BackHandler(enabled = searchActive, onBack = closeSearch)
val layoutDirection = LocalLayoutDirection.current
Scaffold(
modifier = modifier,
topBar = {
@@ -192,12 +190,10 @@ fun ListsScreen(
available = syncAvailable && !searchActive,
syncing = syncing,
onSync = viewModel::syncAll,
// Side insets too: in landscape a 3-button navigation bar sits at the side.
modifier = Modifier.fillMaxSize().padding(
start = inner.calculateStartPadding(layoutDirection),
top = inner.calculateTopPadding(),
end = inner.calculateEndPadding(layoutDirection),
),
modifier = Modifier
.fillMaxSize()
.padding(top = inner.calculateTopPadding())
.sideInsetsPadding(inner),
) {
when (val s = state) {
ListsUiState.Loading -> Unit // brief; avoids a flash before first emission
@@ -530,7 +526,10 @@ private fun HomeTopBar(
Row(
modifier = Modifier
.fillMaxWidth()
.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal))
.windowInsetsPadding(
WindowInsets.systemBars.union(WindowInsets.displayCutout)
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
.height(72.dp)
.padding(horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically,
@@ -690,15 +689,13 @@ private fun SearchResults(
}
} else {
// The last result scrolls clear of the navigation bar and the keyboard.
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = WindowInsets.navigationBars.union(WindowInsets.ime)
.only(WindowInsetsSides.Bottom)
.asPaddingValues(),
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(results, key = { it.occurrenceKey }) { task ->
UpcomingRow(task = task, onClick = { onOpenTask(task) })
}
item(key = "bottom-inset") {
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars.union(WindowInsets.ime)))
}
}
}
}
@@ -22,8 +22,6 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.isImeVisible
import androidx.compose.foundation.layout.navigationBars
@@ -92,7 +90,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
@@ -118,6 +115,7 @@ import de.jeanlucmakiola.agendula.ui.common.LocalUse24HourFormat
import de.jeanlucmakiola.agendula.ui.common.SyncPullBox
import de.jeanlucmakiola.agendula.ui.common.ignoringEdgeSwipes
import de.jeanlucmakiola.agendula.ui.common.priorityAccent
import de.jeanlucmakiola.agendula.ui.common.sideInsetsPadding
import de.jeanlucmakiola.agendula.ui.lists.ListEditorSheet
import de.jeanlucmakiola.agendula.ui.lists.listWriteFailureMessage
import de.jeanlucmakiola.agendula.ui.lists.ListWriteFailure
@@ -253,14 +251,7 @@ fun TaskListScreen(
}
},
) { inner ->
// Side insets too: in landscape a 3-button navigation bar sits at the side.
val layoutDirection = LocalLayoutDirection.current
Box(
modifier = Modifier.fillMaxSize().padding(
start = inner.calculateStartPadding(layoutDirection),
end = inner.calculateEndPadding(layoutDirection),
),
) {
Box(modifier = Modifier.fillMaxSize().sideInsetsPadding(inner)) {
SyncPullBox(
available = syncAvailable,
syncing = syncing,
@@ -270,7 +261,10 @@ fun TaskListScreen(
) {
when (val s = state) {
TaskListUiState.Loading -> Unit // brief; avoids a flash before first emission
TaskListUiState.Failure -> CenteredMessage(stringResource(R.string.task_list_failure), inner)
TaskListUiState.Failure -> CenteredMessage(
stringResource(R.string.task_list_failure),
PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding()),
)
is TaskListUiState.Content -> TaskListBody(
s, filter, inner, onOpenTask, onRequestDelete, viewModel,
onClearCompleted = if (listId != null && list?.isReadOnly != true) ({ confirmClear = true }) else null,