Compare commits

...
6 changed files with 70 additions and 6 deletions

No files matched your search

+6
View File
@@ -7,6 +7,12 @@ All notable changes to this project are documented here. The format follows
## [Unreleased]
### 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. 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
### Changed
@@ -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),
)
}
@@ -29,7 +29,16 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
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
import androidx.compose.foundation.lazy.LazyListScope
@@ -99,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
@@ -180,7 +190,10 @@ fun ListsScreen(
available = syncAvailable && !searchActive,
syncing = syncing,
onSync = viewModel::syncAll,
modifier = Modifier.fillMaxSize().padding(top = inner.calculateTopPadding()),
modifier = Modifier
.fillMaxSize()
.padding(top = inner.calculateTopPadding())
.sideInsetsPadding(inner),
) {
when (val s = state) {
ListsUiState.Loading -> Unit // brief; avoids a flash before first emission
@@ -513,7 +526,10 @@ private fun HomeTopBar(
Row(
modifier = Modifier
.fillMaxWidth()
.statusBarsPadding()
.windowInsetsPadding(
WindowInsets.systemBars.union(WindowInsets.displayCutout)
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
.height(72.dp)
.padding(horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically,
@@ -672,10 +688,14 @@ private fun SearchResults(
)
}
} else {
// The last result scrolls clear of the navigation bar and the keyboard.
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)))
}
}
}
}
@@ -115,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
@@ -250,7 +251,7 @@ fun TaskListScreen(
}
},
) { inner ->
Box(modifier = Modifier.fillMaxSize()) {
Box(modifier = Modifier.fillMaxSize().sideInsetsPadding(inner)) {
SyncPullBox(
available = syncAvailable,
syncing = syncing,
@@ -260,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,