Skip to content

Commit f169ef6

Browse files
author
Alexander Matečný
committed
Properly handle action's height being larger than available content height
1 parent c6bdf86 commit f169ef6

File tree

5 files changed

+74
-16
lines changed

5 files changed

+74
-16
lines changed

ui/src/androidMain/kotlin/kiwi/orbit/compose/ui/controls/Scaffold.kt

+57-16
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import androidx.compose.foundation.layout.ime
1414
import androidx.compose.foundation.layout.isImeVisible
1515
import androidx.compose.foundation.layout.systemBars
1616
import androidx.compose.foundation.layout.union
17+
import androidx.compose.foundation.layout.wrapContentSize
1718
import androidx.compose.runtime.Composable
1819
import androidx.compose.runtime.remember
20+
import androidx.compose.ui.Alignment
1921
import androidx.compose.ui.Modifier
2022
import androidx.compose.ui.graphics.Brush
2123
import androidx.compose.ui.graphics.Color
@@ -91,31 +93,28 @@ private fun ScaffoldLayout(
9193
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
9294

9395
val topBarPlaceables = subcompose(SlotTopAppBar, topBar).map { it.measure(looseConstraints) }
94-
val actionPlaceables = subcompose(SlotAction, action).map { it.measure(looseConstraints) }
95-
val toastPlaceables = subcompose(SlotToast, toast).map { it.measure(looseConstraints) }
96+
val topBarHeight = topBarPlaceables.maxOfOrNull { it.height } ?: 0
9697

98+
val maxActionRatio = 0.8f
99+
val maxActionHeight = (layoutHeight - topBarHeight) * maxActionRatio
100+
val actionConstraints = looseConstraints.copy(maxHeight = maxActionHeight.toInt())
101+
val actionPlaceables = subcompose(SlotAction, action).map { it.measure(actionConstraints) }
97102
val actionHeight = actionPlaceables.maxOfOrNull { it.height } ?: 0
98103
val actionFadeHeight = actionPlaceables.firstOrNull()?.get(ActionFadeLine)?.let { value ->
99104
if (value == AlignmentLine.Unspecified) 0 else value
100105
} ?: 0
101106

102107
val contentInsets = contentWindowInsets.asPaddingValues(this)
103-
val contentTop = topBarPlaceables.maxOfOrNull { it.height } ?: 0
104108
val contentBottom = if (actionHeight > 0) {
105-
(actionHeight - actionFadeHeight).coerceAtLeast(0)
109+
(actionHeight - actionFadeHeight)
106110
} else {
107-
if (imeOpened) {
108-
contentInsets.calculateBottomPadding().roundToPx()
109-
} else {
110-
0
111-
}
111+
if (imeOpened) contentInsets.calculateBottomPadding().roundToPx() else 0
112112
}
113113
val contentConstraints = looseConstraints.copy(
114-
maxHeight = layoutHeight - contentTop - contentBottom,
114+
maxHeight = layoutHeight - topBarHeight - contentBottom,
115115
)
116-
117116
val innerPadding = PaddingValues(
118-
top = if (contentTop > 0) {
117+
top = if (topBarHeight > 0) {
119118
0.dp
120119
} else {
121120
contentInsets.calculateTopPadding()
@@ -136,11 +135,13 @@ private fun ScaffoldLayout(
136135
content(innerPadding)
137136
}.map { it.measure(contentConstraints) }
138137

138+
val toastPlaceables = subcompose(SlotToast, toast).map { it.measure(looseConstraints) }
139+
139140
layout(layoutWidth, layoutHeight) {
140-
contentPlaceables.forEach { it.placeRelative(0, contentTop) }
141+
contentPlaceables.forEach { it.placeRelative(0, topBarHeight) }
141142
topBarPlaceables.forEach { it.placeRelative(0, 0) }
142143
actionPlaceables.forEach { it.placeRelative(0, layoutHeight - actionHeight) }
143-
toastPlaceables.forEach { it.placeRelative(0, contentTop) }
144+
toastPlaceables.forEach { it.placeRelative(0, topBarHeight) }
144145
}
145146
}
146147
}
@@ -174,15 +175,19 @@ public fun ScaffoldAction(
174175
},
175176
) { measurables, constraints ->
176177
val padding = 16.dp.roundToPx()
178+
val top = fadeHeight.roundToPx()
177179
val action = measurables.first().measure(
178-
constraints.offset(horizontal = padding * -2),
180+
constraints
181+
.offset(
182+
horizontal = padding * -2,
183+
vertical = -(padding + top),
184+
),
179185
)
180186

181187
if (action.height == 0) {
182188
return@Layout layout(0, 0) {}
183189
}
184190

185-
val top = fadeHeight.roundToPx()
186191
val width = constraints.maxWidth
187192
val height = top + action.height + padding + inset.calculateBottomPadding().roundToPx()
188193

@@ -220,3 +225,39 @@ internal fun ScaffoldPreview() {
220225
}
221226
}
222227
}
228+
229+
@OrbitPreviews
230+
@Composable
231+
internal fun ScaffoldWithFullScreenActionPreview() {
232+
Preview {
233+
Scaffold(
234+
topBar = {
235+
TopAppBar(title = { Text(text = "Title") })
236+
},
237+
action = {
238+
Box(
239+
modifier = Modifier
240+
.fillMaxSize()
241+
.background(OrbitTheme.colors.surface.normal),
242+
) {
243+
Text(
244+
text = "Custom full screen action",
245+
modifier = Modifier
246+
.fillMaxSize()
247+
.wrapContentSize(),
248+
)
249+
ButtonPrimary(
250+
onClick = {},
251+
modifier = Modifier
252+
.fillMaxWidth()
253+
.align(Alignment.BottomCenter),
254+
) {
255+
Text("Custom full screen action")
256+
}
257+
}
258+
},
259+
) {
260+
CustomPlaceholder(Modifier.fillMaxSize())
261+
}
262+
}
263+
}

ui/src/androidUnitTest/kotlin/kiwi/orbit/compose/ui/ScreenshotTest.kt

+8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import kiwi.orbit.compose.ui.controls.RadioFieldPreview
5353
import kiwi.orbit.compose.ui.controls.RadioPreview
5454
import kiwi.orbit.compose.ui.controls.RangeSliderPreview
5555
import kiwi.orbit.compose.ui.controls.ScaffoldPreview
56+
import kiwi.orbit.compose.ui.controls.ScaffoldWithFullScreenActionPreview
5657
import kiwi.orbit.compose.ui.controls.SeatLegendPreview
5758
import kiwi.orbit.compose.ui.controls.SeatPreview
5859
import kiwi.orbit.compose.ui.controls.SegmentedSwitchPreview
@@ -304,6 +305,13 @@ internal class ScreenshotTest {
304305
}
305306
}
306307

308+
@Test
309+
fun scaffoldFullScreenAction() {
310+
snapshot {
311+
ScaffoldWithFullScreenActionPreview()
312+
}
313+
}
314+
307315
@Test
308316
fun seat() {
309317
snapshot { SeatPreview() }

0 commit comments

Comments
 (0)