Skip to content

Commit

Permalink
library: Fix dropdown popup offset
Browse files Browse the repository at this point in the history
  • Loading branch information
YuKongA committed Oct 15, 2024
1 parent 0167e79 commit 5f92b72
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 91 deletions.
242 changes: 177 additions & 65 deletions composeApp/src/commonMain/kotlin/MainPage.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) }

Expand All @@ -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
}
Expand Down Expand Up @@ -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()
Expand All @@ -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) }

Expand All @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -364,6 +355,7 @@ fun calculateOffsetPx(
val offset = dropdownOffsetPx - dropdownHeightPx / 2 + componentHeightPx / 2
if (offset > insideHeightPx + statusBarPx) offset else insideHeightPx + statusBarPx
}

}

/**
Expand Down

0 comments on commit 5f92b72

Please sign in to comment.