Skip to content

Commit eb0592c

Browse files
committed
fix: fix width anomaly when Dropdown item is too wide;
fix: fix crash when Dropdown list is too long;
1 parent 4987585 commit eb0592c

File tree

5 files changed

+81
-33
lines changed

5 files changed

+81
-33
lines changed

composeApp/src/commonMain/kotlin/UITest.kt

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.imePadding
1313
import androidx.compose.foundation.layout.padding
1414
import androidx.compose.foundation.layout.size
1515
import androidx.compose.foundation.layout.statusBarsPadding
16-
import androidx.compose.foundation.lazy.LazyColumn
1716
import androidx.compose.foundation.pager.PagerState
1817
import androidx.compose.foundation.pager.rememberPagerState
1918
import androidx.compose.runtime.Composable
@@ -38,6 +37,7 @@ import top.yukonga.miuix.kmp.basic.HorizontalPager
3837
import top.yukonga.miuix.kmp.basic.Icon
3938
import top.yukonga.miuix.kmp.basic.IconButton
4039
import top.yukonga.miuix.kmp.basic.ListPopup
40+
import top.yukonga.miuix.kmp.basic.ListPopupColumn
4141
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
4242
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
4343
import top.yukonga.miuix.kmp.basic.NavigationBar
@@ -131,10 +131,10 @@ fun UITest(
131131
isTopPopupExpanded.value = false
132132
}
133133
) {
134-
LazyColumn {
135-
items(items.take(3).size) { index ->
134+
ListPopupColumn {
135+
items.take(3).forEachIndexed { index, navigationItem ->
136136
DropdownImpl(
137-
text = items[index].label,
137+
text = navigationItem.label,
138138
optionSize = items.take(3).size,
139139
isSelected = items[index] == items[targetPage],
140140
onSelectedIndexChange = {
@@ -145,7 +145,6 @@ fun UITest(
145145
dismissPopup(showTopPopup)
146146
isTopPopupExpanded.value = false
147147
},
148-
textWidthDp = 100.dp,
149148
index = index
150149
)
151150
}
@@ -180,10 +179,10 @@ fun UITest(
180179
isTopPopupExpanded.value = false
181180
}
182181
) {
183-
LazyColumn {
184-
items(items.take(3).size) { index ->
182+
ListPopupColumn {
183+
items.take(3).forEachIndexed { index, navigationItem ->
185184
DropdownImpl(
186-
text = items[index].label,
185+
text = navigationItem.label,
187186
optionSize = items.take(3).size,
188187
isSelected = items[index] == items[targetPage],
189188
onSelectedIndexChange = {
@@ -194,7 +193,6 @@ fun UITest(
194193
dismissPopup(showTopPopup)
195194
isTopPopupExpanded.value = false
196195
},
197-
textWidthDp = 100.dp,
198196
index = index
199197
)
200198
}
@@ -234,10 +232,10 @@ fun UITest(
234232
isBottomPopupExpanded.value = false
235233
}
236234
) {
237-
LazyColumn {
238-
items(items.take(3).size) { index ->
235+
ListPopupColumn {
236+
items.take(3).forEachIndexed { index, navigationItem ->
239237
DropdownImpl(
240-
text = items[index].label,
238+
text = navigationItem.label,
241239
optionSize = items.take(3).size,
242240
isSelected = items[index] == items[targetPage],
243241
onSelectedIndexChange = {
@@ -248,7 +246,6 @@ fun UITest(
248246
dismissPopup(showBottomPopup)
249247
isBottomPopupExpanded.value = false
250248
},
251-
textWidthDp = 100.dp,
252249
index = index
253250
)
254251
}

composeApp/src/commonMain/kotlin/component/TextComponent.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,23 @@ fun TextComponent(
7474
miuixSuperSwitchAnimState: MutableState<Boolean>,
7575
) {
7676
val dropdownOptions = listOf("Option 1", "Option 2", "Option 3", "Option 4")
77+
val dropdownOptions2 = listOf(
78+
"Option 1", "Option 2 (this is a long long long option)", "Option 3", "Option 4",
79+
"Option 5", "Option 6", "Option 7", "Option 8",
80+
"Option 9", "Option 10", "Option 11", "Option 12"
81+
)
7782
val spinnerOptions = listOf(
7883
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFF5B29)) }, "Option 1", "Red"),
7984
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF36D167)) }, "Option 2", "Green"),
8085
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF3482FF)) }, "Option 3", "Blue"),
8186
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFFB21D)) }, "Option 4", "Yellow"),
8287
)
88+
val spinnerOptions2 = listOf(
89+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFF5B29)) }, "Option 1", "Red"),
90+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF36D167)) }, "Option 2 (this is a long option)", "Green"),
91+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFF3482FF)) }, "Option 3", "Blue"),
92+
SpinnerEntry(icon = { Icon(RoundedRectanglePainter(), "Icon", Modifier.padding(end = 12.dp), Color(0xFFFFB21D)) }, "Option 4", "Yellow"),
93+
)
8394
SmallTitle(text = "Basic")
8495
Card(
8596
modifier = Modifier
@@ -329,7 +340,7 @@ fun TextComponent(
329340
SuperDropdown(
330341
title = "Dropdown",
331342
summary = "Popup always on right",
332-
items = dropdownOptions,
343+
items = dropdownOptions2,
333344
selectedIndex = dropdownOptionSelectedRight.value,
334345
onSelectedIndexChange = { newOption -> dropdownOptionSelectedRight.value = newOption },
335346
mode = DropDownMode.AlwaysOnRight
@@ -361,7 +372,7 @@ fun TextComponent(
361372
SuperSpinner(
362373
title = "Spinner",
363374
summary = "Spinner always on right",
364-
items = spinnerOptions,
375+
items = spinnerOptions2,
365376
selectedIndex = spinnerOptionSelectedRight.value,
366377
onSelectedIndexChange = { newOption -> spinnerOptionSelectedRight.value = newOption },
367378
mode = SpinnerMode.AlwaysOnRight,

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ListPopup.kt

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.captionBar
99
import androidx.compose.foundation.layout.displayCutout
1010
import androidx.compose.foundation.layout.navigationBars
1111
import androidx.compose.foundation.layout.statusBars
12+
import androidx.compose.foundation.rememberScrollState
13+
import androidx.compose.foundation.verticalScroll
1214
import androidx.compose.runtime.Composable
1315
import androidx.compose.runtime.DisposableEffect
1416
import androidx.compose.runtime.LaunchedEffect
@@ -27,6 +29,7 @@ import androidx.compose.ui.graphics.TransformOrigin
2729
import androidx.compose.ui.graphics.graphicsLayer
2830
import androidx.compose.ui.input.pointer.pointerInput
2931
import androidx.compose.ui.layout.Layout
32+
import androidx.compose.ui.layout.SubcomposeLayout
3033
import androidx.compose.ui.layout.layout
3134
import androidx.compose.ui.layout.onGloballyPositioned
3235
import androidx.compose.ui.layout.positionInWindow
@@ -43,6 +46,7 @@ import top.yukonga.miuix.kmp.utils.MiuixPopupUtil.Companion.dismissPopup
4346
import top.yukonga.miuix.kmp.utils.MiuixPopupUtil.Companion.showPopup
4447
import top.yukonga.miuix.kmp.utils.SmoothRoundedCornerShape
4548
import top.yukonga.miuix.kmp.utils.getWindowSize
49+
import kotlin.math.min
4650

4751
/**
4852
* A popup with a list of items.
@@ -52,7 +56,7 @@ import top.yukonga.miuix.kmp.utils.getWindowSize
5256
* @param popupPositionProvider The [PopupPositionProvider] of the [ListPopup].
5357
* @param alignment The alignment of the [ListPopup].
5458
* @param onDismissRequest The callback when the [ListPopup] is dismissed.
55-
* @param content The [Composable] content of the [ListPopup].
59+
* @param content The [Composable] content of the [ListPopup]. You should use the [ListPopupColumn] in general.
5660
*/
5761
@Composable
5862
fun ListPopup(
@@ -151,7 +155,7 @@ fun ListPopup(
151155
constraints.copy(
152156
minWidth = 200.dp.roundToPx(),
153157
minHeight = 50.dp.roundToPx(),
154-
maxHeight = windowBounds.height
158+
maxHeight = windowBounds.height - popupMargin.top - popupMargin.bottom
155159
)
156160
)
157161
popupContentSize = IntSize(placeable.width, placeable.height)
@@ -222,6 +226,46 @@ fun ListPopup(
222226
}
223227
}
224228

229+
/**
230+
* A column that automatically aligns the width to the widest item
231+
* @param content The items
232+
*/
233+
@Composable
234+
fun ListPopupColumn(
235+
content: @Composable () -> Unit
236+
) {
237+
SubcomposeLayout(
238+
modifier = Modifier.verticalScroll(rememberScrollState())
239+
) { constraints ->
240+
var itemCount = 0
241+
var listHeight = 0
242+
var visibleHeight = 0
243+
val listWidth = subcompose("miuixPopupListFake", content).map {
244+
it.measure(constraints.copy(
245+
minWidth = 200.dp.roundToPx(), maxWidth = 288.dp.roundToPx()
246+
))
247+
}.maxOf { it.width }.coerceIn(200.dp.roundToPx(), 288.dp.roundToPx())
248+
val placeables = subcompose("miuixPopupListReal", content).map {
249+
val placeable = it.measure(constraints.copy(
250+
minWidth = listWidth, maxWidth = listWidth
251+
))
252+
if (itemCount < 8) {
253+
visibleHeight += placeable.height
254+
}
255+
listHeight += placeable.height
256+
itemCount++
257+
placeable
258+
}
259+
layout(listWidth, min(constraints.maxHeight, listHeight)) {
260+
var height = 0
261+
placeables.forEach {
262+
it.place(0, height)
263+
height += it.height
264+
}
265+
}
266+
}
267+
}
268+
225269
interface PopupPositionProvider {
226270
/**
227271
* Calculate the position (offset) of Popup

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDropdown.kt

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import androidx.compose.foundation.layout.PaddingValues
99
import androidx.compose.foundation.layout.Row
1010
import androidx.compose.foundation.layout.padding
1111
import androidx.compose.foundation.layout.size
12-
import androidx.compose.foundation.layout.width
1312
import androidx.compose.foundation.layout.widthIn
14-
import androidx.compose.foundation.lazy.LazyColumn
1513
import androidx.compose.runtime.Composable
1614
import androidx.compose.runtime.DisposableEffect
1715
import androidx.compose.runtime.getValue
@@ -33,13 +31,13 @@ import androidx.compose.ui.platform.LocalHapticFeedback
3331
import androidx.compose.ui.text.font.FontWeight
3432
import androidx.compose.ui.text.style.TextAlign
3533
import androidx.compose.ui.text.style.TextOverflow
36-
import androidx.compose.ui.unit.Dp
3734
import androidx.compose.ui.unit.dp
3835
import kotlinx.coroutines.launch
3936
import top.yukonga.miuix.kmp.basic.BasicComponent
4037
import top.yukonga.miuix.kmp.basic.BasicComponentColors
4138
import top.yukonga.miuix.kmp.basic.BasicComponentDefaults
4239
import top.yukonga.miuix.kmp.basic.ListPopup
40+
import top.yukonga.miuix.kmp.basic.ListPopupColumn
4341
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
4442
import top.yukonga.miuix.kmp.basic.Text
4543
import top.yukonga.miuix.kmp.icon.MiuixIcons
@@ -139,10 +137,10 @@ fun SuperDropdown(
139137
isDropdownExpanded.value = false
140138
}
141139
) {
142-
LazyColumn {
143-
items(items.size) { index ->
140+
ListPopupColumn {
141+
items.forEachIndexed { index, string ->
144142
DropdownImpl(
145-
text = items[index],
143+
text = string,
146144
optionSize = items.size,
147145
isSelected = selectedIndex == index,
148146
onSelectedIndexChange = {
@@ -151,7 +149,6 @@ fun SuperDropdown(
151149
dismissPopup(showPopup)
152150
isDropdownExpanded.value = false
153151
},
154-
textWidthDp = 128.dp,
155152
index = index
156153
)
157154
}
@@ -205,16 +202,14 @@ fun SuperDropdown(
205202
* @param isSelected Whether the option is selected.
206203
* @param index The index of the current option in the options.
207204
* @param onSelectedIndexChange The callback when the index is selected.
208-
* @param textWidthDp The maximum width of text in options.
209205
*/
210206
@Composable
211207
fun DropdownImpl(
212208
text: String,
213209
optionSize: Int,
214210
isSelected: Boolean,
215211
index: Int,
216-
onSelectedIndexChange: (Int) -> Unit,
217-
textWidthDp: Dp?
212+
onSelectedIndexChange: (Int) -> Unit
218213
) {
219214
val additionalTopPadding = if (index == 0) 20f.dp else 12f.dp
220215
val additionalBottomPadding = if (index == optionSize - 1) 20f.dp else 12f.dp
@@ -241,12 +236,11 @@ fun DropdownImpl(
241236
onSelectedIndexChange(index)
242237
}
243238
.background(backgroundColor)
244-
.widthIn(200.dp, 288.dp)
245239
.padding(horizontal = 20.dp)
246240
.padding(top = additionalTopPadding, bottom = additionalBottomPadding)
247241
) {
248242
Text(
249-
modifier = Modifier.width(textWidthDp ?: 128.dp),
243+
modifier = Modifier.widthIn(max = 216.dp),
250244
text = text,
251245
fontSize = MiuixTheme.textStyles.body1.fontSize,
252246
fontWeight = FontWeight.Medium,

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperSpinner.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import top.yukonga.miuix.kmp.basic.BasicComponent
4747
import top.yukonga.miuix.kmp.basic.BasicComponentColors
4848
import top.yukonga.miuix.kmp.basic.BasicComponentDefaults
4949
import top.yukonga.miuix.kmp.basic.ListPopup
50+
import top.yukonga.miuix.kmp.basic.ListPopupColumn
5051
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
5152
import top.yukonga.miuix.kmp.basic.Text
5253
import top.yukonga.miuix.kmp.basic.TextButton
@@ -150,10 +151,10 @@ fun SuperSpinner(
150151
isDropdownExpanded.value = false
151152
}
152153
) {
153-
LazyColumn {
154-
items(items.size) { index ->
154+
ListPopupColumn {
155+
items.forEachIndexed { index, spinnerEntry ->
155156
SpinnerItemImpl(
156-
entry = items[index],
157+
entry = spinnerEntry,
157158
entryCount = items.size,
158159
isSelected = selectedIndex == index,
159160
index = index,
@@ -434,11 +435,12 @@ fun SpinnerItemImpl(
434435
.background(backgroundColor)
435436
.then(
436437
if (dialogMode) Modifier.heightIn(min = 56.dp).widthIn(min = 200.dp).fillMaxWidth().padding(horizontal = 28.dp)
437-
else Modifier.widthIn(200.dp, 288.dp).padding(horizontal = 20.dp)
438+
else Modifier.padding(horizontal = 20.dp)
438439
)
439440
.padding(top = additionalTopPadding, bottom = additionalBottomPadding)
440441
) {
441442
Row(
443+
modifier = if (dialogMode) Modifier else Modifier.widthIn(max = 216.dp),
442444
verticalAlignment = Alignment.CenterVertically,
443445
horizontalArrangement = Arrangement.Start
444446
) {

0 commit comments

Comments
 (0)