Skip to content

Commit 32dea1e

Browse files
HowieHChenYuKongA
andauthored
library: Add new component ListPopup (#43)
* feat: new component ListPopup; refactor: refactor SuperDropdown and SuperSpinner using ListPopup * fix: fix popup can't appear above anchor * misc: cleanup * fix: fix window size not updating immediately after rotating the screen; fix: fix the button in Spinner's dialog is too small in some cases; --------- Co-authored-by: YuKongA <[email protected]>
1 parent de380be commit 32dea1e

File tree

12 files changed

+1078
-626
lines changed

12 files changed

+1078
-626
lines changed

composeApp/src/commonMain/kotlin/UITest.kt

Lines changed: 131 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,11 @@ import androidx.compose.foundation.layout.captionBarPadding
1111
import androidx.compose.foundation.layout.fillMaxSize
1212
import androidx.compose.foundation.layout.imePadding
1313
import androidx.compose.foundation.layout.padding
14+
import androidx.compose.foundation.layout.size
1415
import androidx.compose.foundation.layout.statusBarsPadding
16+
import androidx.compose.foundation.lazy.LazyColumn
1517
import androidx.compose.foundation.pager.PagerState
1618
import androidx.compose.foundation.pager.rememberPagerState
17-
import androidx.compose.material.icons.Icons
18-
import androidx.compose.material.icons.rounded.Favorite
19-
import androidx.compose.material.icons.rounded.Home
20-
import androidx.compose.material.icons.rounded.Menu
21-
import androidx.compose.material.icons.rounded.Settings
2219
import androidx.compose.runtime.Composable
2320
import androidx.compose.runtime.LaunchedEffect
2421
import androidx.compose.runtime.MutableState
@@ -40,16 +37,26 @@ import top.yukonga.miuix.kmp.basic.FloatingActionButton
4037
import top.yukonga.miuix.kmp.basic.HorizontalPager
4138
import top.yukonga.miuix.kmp.basic.Icon
4239
import top.yukonga.miuix.kmp.basic.IconButton
40+
import top.yukonga.miuix.kmp.basic.ListPopup
41+
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
4342
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
4443
import top.yukonga.miuix.kmp.basic.NavigationBar
4544
import top.yukonga.miuix.kmp.basic.NavigationItem
45+
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
4646
import top.yukonga.miuix.kmp.basic.Scaffold
4747
import top.yukonga.miuix.kmp.basic.ScrollBehavior
4848
import top.yukonga.miuix.kmp.basic.SmallTopAppBar
4949
import top.yukonga.miuix.kmp.basic.TopAppBar
5050
import top.yukonga.miuix.kmp.basic.rememberTopAppBarState
51+
import top.yukonga.miuix.kmp.extra.DropdownImpl
5152
import top.yukonga.miuix.kmp.icon.MiuixIcons
5253
import top.yukonga.miuix.kmp.icon.icons.GitHub
54+
import top.yukonga.miuix.kmp.icon.icons.ImmersionMore
55+
import top.yukonga.miuix.kmp.icon.icons.Info
56+
import top.yukonga.miuix.kmp.icon.icons.More
57+
import top.yukonga.miuix.kmp.icon.icons.NavigatorSwitch
58+
import top.yukonga.miuix.kmp.icon.icons.Settings
59+
import top.yukonga.miuix.kmp.utils.MiuixPopupUtil.Companion.dismissPopup
5360
import utils.FPSMonitor
5461

5562
@OptIn(FlowPreview::class)
@@ -76,9 +83,10 @@ fun UITest(
7683
}
7784

7885
val items = listOf(
79-
NavigationItem("HomePage", Icons.Rounded.Home),
80-
NavigationItem("DropDown", Icons.Rounded.Favorite),
81-
NavigationItem("Settings", Icons.Rounded.Settings)
86+
NavigationItem("HomePage", MiuixIcons.NavigatorSwitch),
87+
NavigationItem("DropDown", MiuixIcons.Info),
88+
NavigationItem("Settings", MiuixIcons.Settings),
89+
NavigationItem("More", MiuixIcons.More)
8290
)
8391

8492
LaunchedEffect(pagerState) {
@@ -93,6 +101,11 @@ fun UITest(
93101
val showFloatingActionButton = remember { mutableStateOf(true) }
94102
val enablePageUserScroll = remember { mutableStateOf(false) }
95103

104+
val isTopPopupExpanded = remember { mutableStateOf(false) }
105+
val showTopPopup = remember { mutableStateOf(false) }
106+
val isBottomPopupExpanded = remember { mutableStateOf(false) }
107+
val showBottomPopup = remember { mutableStateOf(false) }
108+
96109
val uriHandler = LocalUriHandler.current
97110

98111
Scaffold(
@@ -109,12 +122,45 @@ fun UITest(
109122
title = "Miuix",
110123
scrollBehavior = currentScrollBehavior,
111124
actions = {
125+
if (isTopPopupExpanded.value) {
126+
ListPopup(
127+
show = showTopPopup,
128+
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
129+
alignment = PopupPositionProvider.Align.TopRight,
130+
onDismissRequest = {
131+
isTopPopupExpanded.value = false
132+
}
133+
) {
134+
LazyColumn {
135+
items(items.take(3).size) { index ->
136+
DropdownImpl(
137+
text = items[index].label,
138+
optionSize = items.take(3).size,
139+
isSelected = items[index] == items[targetPage],
140+
onSelectedIndexChange = {
141+
targetPage = index
142+
coroutineScope.launch {
143+
pagerState.animateScrollToPage(index)
144+
}
145+
dismissPopup(showTopPopup)
146+
isTopPopupExpanded.value = false
147+
},
148+
textWidthDp = 100.dp,
149+
index = index
150+
)
151+
}
152+
}
153+
}
154+
showTopPopup.value = true
155+
}
112156
IconButton(
113-
modifier = Modifier.padding(end = 12.dp),
114-
onClick = { }
157+
modifier = Modifier.padding(end = 21.dp).size(40.dp),
158+
onClick = {
159+
isTopPopupExpanded.value = true
160+
}
115161
) {
116162
Icon(
117-
imageVector = Icons.Rounded.Menu,
163+
imageVector = MiuixIcons.ImmersionMore,
118164
contentDescription = "Menu"
119165
)
120166
}
@@ -125,12 +171,45 @@ fun UITest(
125171
title = "Miuix",
126172
scrollBehavior = currentScrollBehavior,
127173
actions = {
174+
if (isTopPopupExpanded.value) {
175+
ListPopup(
176+
show = showTopPopup,
177+
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
178+
alignment = PopupPositionProvider.Align.TopRight,
179+
onDismissRequest = {
180+
isTopPopupExpanded.value = false
181+
}
182+
) {
183+
LazyColumn {
184+
items(items.take(3).size) { index ->
185+
DropdownImpl(
186+
text = items[index].label,
187+
optionSize = items.take(3).size,
188+
isSelected = items[index] == items[targetPage],
189+
onSelectedIndexChange = {
190+
targetPage = index
191+
coroutineScope.launch {
192+
pagerState.animateScrollToPage(index)
193+
}
194+
dismissPopup(showTopPopup)
195+
isTopPopupExpanded.value = false
196+
},
197+
textWidthDp = 100.dp,
198+
index = index
199+
)
200+
}
201+
}
202+
}
203+
showTopPopup.value = true
204+
}
128205
IconButton(
129-
modifier = Modifier.padding(end = 12.dp),
130-
onClick = { }
206+
modifier = Modifier.padding(end = 21.dp).size(40.dp),
207+
onClick = {
208+
isTopPopupExpanded.value = true
209+
}
131210
) {
132211
Icon(
133-
imageVector = Icons.Rounded.Menu,
212+
imageVector = MiuixIcons.ImmersionMore,
134213
contentDescription = "Menu"
135214
)
136215
}
@@ -146,13 +225,48 @@ fun UITest(
146225
enter = fadeIn() + expandVertically(),
147226
exit = fadeOut() + shrinkVertically()
148227
) {
228+
if (isBottomPopupExpanded.value) {
229+
ListPopup(
230+
show = showBottomPopup,
231+
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
232+
alignment = PopupPositionProvider.Align.BottomRight,
233+
onDismissRequest = {
234+
isBottomPopupExpanded.value = false
235+
}
236+
) {
237+
LazyColumn {
238+
items(items.take(3).size) { index ->
239+
DropdownImpl(
240+
text = items[index].label,
241+
optionSize = items.take(3).size,
242+
isSelected = items[index] == items[targetPage],
243+
onSelectedIndexChange = {
244+
targetPage = index
245+
coroutineScope.launch {
246+
pagerState.animateScrollToPage(index)
247+
}
248+
dismissPopup(showBottomPopup)
249+
isBottomPopupExpanded.value = false
250+
},
251+
textWidthDp = 100.dp,
252+
index = index
253+
)
254+
}
255+
}
256+
}
257+
showBottomPopup.value = true
258+
}
149259
NavigationBar(
150260
items = items,
151261
selected = targetPage,
152262
onClick = { index ->
153-
targetPage = index
154-
coroutineScope.launch {
155-
pagerState.animateScrollToPage(index)
263+
if (index in 0..2) {
264+
targetPage = index
265+
coroutineScope.launch {
266+
pagerState.animateScrollToPage(index)
267+
}
268+
} else {
269+
isBottomPopupExpanded.value = true
156270
}
157271
}
158272
)

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,7 @@ fun dialog2(showDialog: MutableState<Boolean>) {
449449
title = "Dropdown",
450450
items = dropdownOptions,
451451
selectedIndex = dropdownSelectedOption.value,
452-
onSelectedIndexChange = { newOption -> dropdownSelectedOption.value = newOption },
453-
defaultWindowInsetsPadding = false
452+
onSelectedIndexChange = { newOption -> dropdownSelectedOption.value = newOption }
454453
)
455454
SuperSwitch(
456455
title = "Switch",

miuix/src/androidMain/kotlin/top/yukonga/miuix/kmp/utils/Utils.android.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,29 @@ import android.annotation.SuppressLint
44
import android.os.Build
55
import android.view.RoundedCorner
66
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.platform.LocalConfiguration
78
import androidx.compose.ui.platform.LocalContext
89
import androidx.compose.ui.platform.LocalDensity
910
import androidx.compose.ui.platform.LocalView
1011
import androidx.compose.ui.unit.Dp
1112
import androidx.compose.ui.unit.dp
1213
import androidx.window.layout.WindowMetricsCalculator
14+
import kotlin.math.max
15+
import kotlin.math.min
1316

1417
@Composable
1518
actual fun getWindowSize(): WindowSize {
19+
val configuration = LocalConfiguration.current
20+
val screenWidthDp = configuration.screenWidthDp
21+
val screenHeightDp = configuration.screenHeightDp
1622
val context = LocalContext.current
1723
val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
1824
val widthPx = windowMetrics.bounds.width()
1925
val heightPx = windowMetrics.bounds.height()
20-
return WindowSize(widthPx, heightPx)
26+
return if (screenWidthDp > screenHeightDp)
27+
WindowSize(max(widthPx, heightPx), min(widthPx, heightPx))
28+
else
29+
WindowSize(min(widthPx, heightPx), max(widthPx, heightPx))
2130
}
2231

2332
actual fun platform(): Platform = Platform.Android

0 commit comments

Comments
 (0)