From 5f92b72c3e1c9a7c83d3d8e4aad7644ef39476a0 Mon Sep 17 00:00:00 2001 From: YuKongA <70465933+YuKongA@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:30:14 +0800 Subject: [PATCH] library: Fix dropdown popup offset --- composeApp/src/commonMain/kotlin/MainPage.kt | 242 +++++++++++++----- .../yukonga/miuix/kmp/extra/SuperDropdown.kt | 44 ++-- 2 files changed, 195 insertions(+), 91 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/MainPage.kt b/composeApp/src/commonMain/kotlin/MainPage.kt index d15e976..8d4e564 100644 --- a/composeApp/src/commonMain/kotlin/MainPage.kt +++ b/composeApp/src/commonMain/kotlin/MainPage.kt @@ -1,10 +1,20 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column 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.WindowInsetsSides +import androidx.compose.foundation.layout.displayCutout import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -35,80 +45,182 @@ fun MainPage( var miuixSearchValue by remember { mutableStateOf("") } var expanded by rememberSaveable { mutableStateOf(false) } - LazyColumn( - contentPadding = PaddingValues(top = padding.calculateTopPadding()), - topAppBarScrollBehavior = topAppBarScrollBehavior, + BoxWithConstraints( + modifier = Modifier.fillMaxSize() ) { - item { - SearchBar( - modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp), - inputField = { - InputField( - query = miuixSearchValue, - onQueryChange = { miuixSearchValue = it }, - onSearch = { expanded = false }, - expanded = expanded, - onExpandedChange = { expanded = it }, - label = "Search", - leadingIcon = { - Image( - modifier = Modifier.padding(horizontal = 16.dp), - imageVector = MiuixIcons.Search, - colorFilter = BlendModeColorFilter( - MiuixTheme.colorScheme.onSurfaceContainer, - BlendMode.SrcIn - ), - contentDescription = "Search" + if (maxWidth < 840.dp) { + LazyColumn( + contentPadding = PaddingValues(top = padding.calculateTopPadding()), + topAppBarScrollBehavior = topAppBarScrollBehavior, + ) { + item { + SearchBar( + modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp), + inputField = { + InputField( + query = miuixSearchValue, + onQueryChange = { miuixSearchValue = it }, + onSearch = { expanded = false }, + expanded = expanded, + onExpandedChange = { expanded = it }, + label = "Search", + leadingIcon = { + Image( + modifier = Modifier.padding(horizontal = 16.dp), + imageVector = MiuixIcons.Search, + colorFilter = BlendModeColorFilter( + MiuixTheme.colorScheme.onSurfaceContainer, + BlendMode.SrcIn + ), + contentDescription = "Search" + ) + }, ) }, - ) - }, - outsideRightAction = { - Text( - modifier = Modifier - .padding(start = 12.dp) - .clickable( - interactionSource = null, - indication = null - ) { - expanded = false - miuixSearchValue = "" - }, - text = "Cancel", - color = MiuixTheme.colorScheme.primary - ) - }, - expanded = expanded, - onExpandedChange = { expanded = it } + outsideRightAction = { + Text( + modifier = Modifier + .padding(start = 12.dp) + .clickable( + interactionSource = null, + indication = null + ) { + expanded = false + miuixSearchValue = "" + }, + text = "Cancel", + color = MiuixTheme.colorScheme.primary + ) + }, + expanded = expanded, + onExpandedChange = { expanded = it } + ) { + Column( + Modifier.fillMaxSize() + ) { + repeat(4) { idx -> + val resultText = "Suggestion $idx" + BasicComponent( + title = resultText, + modifier = Modifier + .fillMaxWidth(), + onClick = { + miuixSearchValue = resultText + expanded = false + } + ) + } + } + } + } + if (!expanded) { + item( + key = "text" + ) { + TextComponent() + } + item( + key = "other" + ) { + OtherComponent(padding) + } + } + } + } else { + Row( + modifier = Modifier + .windowInsetsPadding(WindowInsets.displayCutout.only(WindowInsetsSides.Horizontal)) + .windowInsetsPadding(WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)) ) { - Column( - Modifier.fillMaxSize() + LazyColumn( + modifier = Modifier + .padding(start = 12.dp) + .weight(1f), + contentPadding = PaddingValues(top = padding.calculateTopPadding()), + topAppBarScrollBehavior = topAppBarScrollBehavior, ) { - repeat(4) { idx -> - val resultText = "Suggestion $idx" - BasicComponent( - title = resultText, + item { + SearchBar( modifier = Modifier - .fillMaxWidth(), - onClick = { - miuixSearchValue = resultText - expanded = false + .padding(horizontal = 12.dp, vertical = 10.dp), + inputField = { + InputField( + query = miuixSearchValue, + onQueryChange = { miuixSearchValue = it }, + onSearch = { expanded = false }, + expanded = expanded, + onExpandedChange = { expanded = it }, + label = "Search", + leadingIcon = { + Image( + modifier = Modifier.padding(horizontal = 16.dp), + imageVector = MiuixIcons.Search, + colorFilter = BlendModeColorFilter( + MiuixTheme.colorScheme.onSurfaceContainer, + BlendMode.SrcIn + ), + contentDescription = "Search" + ) + }, + ) + }, + outsideRightAction = { + Text( + modifier = Modifier + .padding(start = 12.dp) + .clickable( + interactionSource = null, + indication = null + ) { + expanded = false + miuixSearchValue = "" + }, + text = "Cancel", + color = MiuixTheme.colorScheme.primary + ) + }, + expanded = expanded, + onExpandedChange = { expanded = it } + ) { + Column( + Modifier.fillMaxSize() + ) { + repeat(4) { idx -> + val resultText = "Suggestion $idx" + BasicComponent( + title = resultText, + modifier = Modifier + .fillMaxWidth(), + onClick = { + miuixSearchValue = resultText + expanded = false + } + ) + } } - ) + } + } + if (!expanded) { + item( + key = "text" + ) { + OtherComponent(padding) + Spacer(modifier = Modifier.height(6.dp)) + } + } + } + LazyColumn( + modifier = Modifier + .padding(end = 12.dp) + .weight(1f), + contentPadding = PaddingValues(top = padding.calculateTopPadding()), + topAppBarScrollBehavior = topAppBarScrollBehavior + ) { + item { + TextComponent() + Spacer(modifier = Modifier.height(12.dp + padding.calculateBottomPadding())) } } - } - } - if (!expanded) { - item( - key = "text" - ) { - TextComponent() - } - item( - key = "other" - ) { - OtherComponent(padding) } } } diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDropdown.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDropdown.kt index 645ebaa..afb107f 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDropdown.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDropdown.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf @@ -54,9 +53,7 @@ import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.max import androidx.compose.ui.unit.sp import top.yukonga.miuix.kmp.basic.BasicComponent import top.yukonga.miuix.kmp.basic.Box @@ -110,7 +107,8 @@ fun SuperDropdown( val hapticFeedback = LocalHapticFeedback.current val actionColor = if (enabled) MiuixTheme.colorScheme.onSurfaceVariantActions else MiuixTheme.colorScheme.disabledOnSecondaryVariant var alignLeft by rememberSaveable { mutableStateOf(true) } - var dropdownOffsetPx by remember { mutableStateOf(0) } + var dropdownOffsetXPx by remember { mutableStateOf(0) } + var dropdownOffsetYPx by remember { mutableStateOf(0) } var componentHeightPx by remember { mutableStateOf(0) } var componentWidthPx by remember { mutableStateOf(0) } @@ -130,7 +128,8 @@ fun SuperDropdown( .onGloballyPositioned { coordinates -> if (isDropdownExpanded.value) { val positionInWindow = coordinates.positionInWindow() - dropdownOffsetPx = positionInWindow.y.toInt() + dropdownOffsetXPx = positionInWindow.x.toInt() + dropdownOffsetYPx = positionInWindow.y.toInt() componentHeightPx = coordinates.size.height componentWidthPx = coordinates.size.width } @@ -174,12 +173,12 @@ fun SuperDropdown( } val density = LocalDensity.current - var offsetPx by remember { mutableStateOf(0) } + var offsetXPx by remember { mutableStateOf(0) } + var offsetYPx by remember { mutableStateOf(0) } val textMeasurer = rememberTextMeasurer() val textStyle = remember { TextStyle(fontWeight = FontWeight.Medium, fontSize = 17.sp) } val textWidthDp = remember(items) { items.maxOfOrNull { with(density) { textMeasurer.measure(text = it, style = textStyle).size.width.toDp() } } } val getWindowSize = rememberUpdatedState(getWindowSize()) - val windowWeightPx by rememberUpdatedState(getWindowSize.value.width) val windowHeightPx by rememberUpdatedState(getWindowSize.value.height) val statusBarPx by rememberUpdatedState( with(density) { WindowInsets.statusBars.asPaddingValues().calculateTopPadding().toPx() }.roundToInt() @@ -191,17 +190,7 @@ fun SuperDropdown( with(density) { WindowInsets.captionBar.asPaddingValues().calculateBottomPadding().toPx() }.roundToInt() ) val insideHeightPx by rememberUpdatedState(with(density) { insideMargin.height.toPx() }.roundToInt()) - val displayCutoutSize = - WindowInsets.displayCutout.asPaddingValues(density).calculateLeftPadding(LayoutDirection.Ltr) + WindowInsets.displayCutout.asPaddingValues(density) - .calculateRightPadding(LayoutDirection.Ltr) - val popupPadding by rememberUpdatedState { - derivedStateOf { - max( - horizontalPadding + (windowWeightPx.dp - componentWidthPx.dp) / 2 / density.density - - if (defaultWindowInsetsPadding) displayCutoutSize / 2 else 0.dp, 0.dp - ) - } - } + val paddingPx by rememberUpdatedState(with(density) { horizontalPadding.toPx() }.roundToInt()) BackHandler(enabled = isDropdownExpanded.value) { dismissPopup(isDropdownExpanded) } @@ -222,17 +211,19 @@ fun SuperDropdown( } ) } - .offset(y = offsetPx.dp / density.density) + .offset(x = offsetXPx.dp / density.density, y = offsetYPx.dp / density.density) ) { LazyColumn( modifier = Modifier - .padding( - horizontal = popupPadding.invoke().value - ) .onGloballyPositioned { layoutCoordinates -> - offsetPx = calculateOffsetPx( + offsetXPx = if (alwaysRight || !alignLeft) { + dropdownOffsetXPx + componentWidthPx - layoutCoordinates.size.width - paddingPx + } else { + dropdownOffsetXPx + paddingPx + } + offsetYPx = calculateOffsetYPx( windowHeightPx, - dropdownOffsetPx, + dropdownOffsetYPx, layoutCoordinates.size.height, componentHeightPx, insideHeightPx, @@ -241,7 +232,7 @@ fun SuperDropdown( captionBarPx ) } - .align(if (alignLeft && !alwaysRight) AbsoluteAlignment.TopLeft else AbsoluteAlignment.TopRight) + .align(AbsoluteAlignment.TopLeft) .graphicsLayer( shadowElevation = 18f, shape = RoundedCornerShape(18.dp) @@ -348,7 +339,7 @@ fun DropdownImpl( * @param captionBarPx The height of the caption bar padding. * @return The offset of the current dropdown. */ -fun calculateOffsetPx( +fun calculateOffsetYPx( windowHeightPx: Int, dropdownOffsetPx: Int, dropdownHeightPx: Int, @@ -364,6 +355,7 @@ fun calculateOffsetPx( val offset = dropdownOffsetPx - dropdownHeightPx / 2 + componentHeightPx / 2 if (offset > insideHeightPx + statusBarPx) offset else insideHeightPx + statusBarPx } + } /**