From f3674828aaf251b983574bc3dcc0fe0fa04ffc15 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:03:46 +0800 Subject: [PATCH] library: Optimize some components --------- Co-Authored-By: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-Authored-By: YuKongA <70465933+YuKongA@users.noreply.github.com> --- example/src/commonMain/kotlin/UITest.kt | 5 +- iosApp/iosApp/Info.plist | 2 +- .../top/yukonga/miuix/kmp/basic/Button.kt | 6 +- .../top/yukonga/miuix/kmp/basic/Card.kt | 18 +-- .../top/yukonga/miuix/kmp/basic/Checkbox.kt | 27 ++-- .../top/yukonga/miuix/kmp/basic/Component.kt | 21 +-- .../top/yukonga/miuix/kmp/basic/IconButton.kt | 3 +- .../top/yukonga/miuix/kmp/basic/ListPopup.kt | 134 +++++++++--------- .../yukonga/miuix/kmp/basic/NavigationBar.kt | 11 +- .../yukonga/miuix/kmp/basic/PullToRefresh.kt | 7 +- .../top/yukonga/miuix/kmp/basic/Slider.kt | 5 +- .../top/yukonga/miuix/kmp/basic/Switch.kt | 48 +++---- .../yukonga/miuix/kmp/extra/SuperDialog.kt | 4 +- .../yukonga/miuix/kmp/extra/SuperDropdown.kt | 6 +- .../miuix/kmp/utils/MiuixPopupUtils.kt | 23 ++- .../yukonga/miuix/kmp/utils/PressFeedback.kt | 8 +- 16 files changed, 174 insertions(+), 154 deletions(-) diff --git a/example/src/commonMain/kotlin/UITest.kt b/example/src/commonMain/kotlin/UITest.kt index 9e17b351..a531db00 100644 --- a/example/src/commonMain/kotlin/UITest.kt +++ b/example/src/commonMain/kotlin/UITest.kt @@ -87,6 +87,7 @@ import top.yukonga.miuix.kmp.icon.icons.useful.NavigatorSwitch import top.yukonga.miuix.kmp.icon.icons.useful.Order import top.yukonga.miuix.kmp.icon.icons.useful.Settings import top.yukonga.miuix.kmp.theme.MiuixTheme +import top.yukonga.miuix.kmp.utils.WindowSize import top.yukonga.miuix.kmp.utils.getWindowSize import top.yukonga.miuix.kmp.utils.overScrollVertical import top.yukonga.miuix.kmp.utils.scrollEndHaptic @@ -229,7 +230,7 @@ private fun WideScreenLayout( pagerState: PagerState, topAppBarScrollBehaviorList: List, currentScrollBehavior: ScrollBehavior, - windowSize: Any, + windowSize: WindowSize, colorMode: MutableState ) { val layoutDirection = LocalLayoutDirection.current @@ -274,7 +275,7 @@ private fun WideScreenPanel( onPageSelected: (Int) -> Unit, barScrollBehavior: ScrollBehavior, uiState: UIState, - windowSize: Any, + windowSize: WindowSize, layoutDirection: LayoutDirection ) { Scaffold( diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 392ed4c7..78b0590c 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -17,7 +17,7 @@ CFBundleShortVersionString 1.0.4 CFBundleVersion - 500 + 501 LSRequiresIPhoneOS CADisableMinimumFrameDurationOnPhone diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Button.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Button.kt index c8bc14f5..e33d4db8 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Button.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Button.kt @@ -51,7 +51,7 @@ fun Button( content: @Composable RowScope.() -> Unit ) { val shape = remember(cornerRadius) { SmoothRoundedCornerShape(cornerRadius) } - val color by rememberUpdatedState(if (enabled) colors.color else colors.disabledColor) + val color = if (enabled) colors.color else colors.disabledColor Surface( onClick = onClick, enabled = enabled, @@ -96,8 +96,8 @@ fun TextButton( insideMargin: PaddingValues = ButtonDefaults.InsideMargin ) { val shape = remember(cornerRadius) { SmoothRoundedCornerShape(cornerRadius) } - val color by rememberUpdatedState(if (enabled) colors.color else colors.disabledColor) - val textColor by rememberUpdatedState(if (enabled) colors.textColor else colors.disabledTextColor) + val color = if (enabled) colors.color else colors.disabledColor + val textColor = if (enabled) colors.textColor else colors.disabledTextColor Surface( onClick = onClick, enabled = enabled, diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Card.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Card.kt index 140cf8b6..5b62c66a 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Card.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Card.kt @@ -102,21 +102,21 @@ fun Card( content: @Composable ColumnScope.() -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } - val currentOnClick by rememberUpdatedState(onClick) - val currentOnLongPress by rememberUpdatedState(onLongPress) - val pressFeedbackModifier = when (pressFeedbackType) { - PressFeedbackType.None -> Modifier - PressFeedbackType.Sink -> Modifier.pressSink(interactionSource) - PressFeedbackType.Tilt -> Modifier.pressTilt(interactionSource) + val pressFeedbackModifier = remember(pressFeedbackType, interactionSource) { + when (pressFeedbackType) { + PressFeedbackType.None -> Modifier + PressFeedbackType.Sink -> Modifier.pressSink(interactionSource) + PressFeedbackType.Tilt -> Modifier.pressTilt(interactionSource) + } } BasicCard( modifier = modifier - .pointerInput(Unit) { + .pointerInput(onClick, onLongPress) { detectTapGestures( - onTap = { currentOnClick?.invoke() }, - onLongPress = { currentOnLongPress?.invoke() } + onTap = { onClick?.invoke() }, + onLongPress = { onLongPress?.invoke() } ) } .pointerInput(interactionSource) { diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Checkbox.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Checkbox.kt index c624e08c..9f806239 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Checkbox.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Checkbox.kt @@ -28,7 +28,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -65,9 +64,6 @@ fun Checkbox( colors: CheckboxColors = CheckboxDefaults.checkboxColors(), enabled: Boolean = true, ) { - val isChecked by rememberUpdatedState(checked) - val currentOnCheckedChange by rememberUpdatedState(onCheckedChange) - val hapticFeedback = LocalHapticFeedback.current val interactionSource = remember { MutableInteractionSource() } @@ -75,10 +71,12 @@ fun Checkbox( val isDragged by interactionSource.collectIsDraggedAsState() val isHovered by interactionSource.collectIsHoveredAsState() - val springSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessMedium - ) + val springSpec = remember { + spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessMedium + ) + } val size by animateFloatAsState( targetValue = if (!enabled) 1f else if (isPressed || isDragged || isHovered) 0.95f else 1f, @@ -86,25 +84,24 @@ fun Checkbox( ) val backgroundColor by animateColorAsState( - targetValue = if (isChecked) colors.checkedBackgroundColor(enabled) else colors.uncheckedBackgroundColor(enabled), + targetValue = if (checked) colors.checkedBackgroundColor(enabled) else colors.uncheckedBackgroundColor(enabled), animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing) ) val foregroundColor by animateColorAsState( - targetValue = if (isChecked) colors.checkedForegroundColor(enabled) else colors.uncheckedForegroundColor(enabled), + targetValue = if (checked) colors.checkedForegroundColor(enabled) else colors.uncheckedForegroundColor(enabled), animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing) ) - val checkmarkAnim = rememberCheckmarkAnimationState(isChecked) + val checkmarkAnim = rememberCheckmarkAnimationState(checked) val finalModifier = if (onCheckedChange != null) { Modifier.toggleable( - value = isChecked, + value = checked, onValueChange = { - if (currentOnCheckedChange == null) return@toggleable - currentOnCheckedChange?.invoke(!isChecked) + onCheckedChange.invoke(!checked) hapticFeedback.performHapticFeedback( - if (isChecked) HapticFeedbackType.ToggleOn else HapticFeedbackType.ToggleOff + if (checked) HapticFeedbackType.ToggleOn else HapticFeedbackType.ToggleOff ) }, enabled = enabled, diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Component.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Component.kt index 788c2ccd..ff288d94 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Component.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Component.kt @@ -61,6 +61,7 @@ fun BasicComponent( ) { @Suppress("NAME_SHADOWING") val interactionSource = interactionSource ?: remember { MutableInteractionSource() } + val indication = LocalIndication.current val holdDown = remember { mutableStateOf(null) } LaunchedEffect(holdDownState) { @@ -76,19 +77,21 @@ fun BasicComponent( } } + val clickableModifier = remember(onClick, enabled, interactionSource) { + if (onClick != null && enabled) { + Modifier.clickable( + indication = indication, + interactionSource = interactionSource, + onClick = onClick + ) + } else Modifier + } + SubcomposeLayout( modifier = modifier .heightIn(min = 56.dp) .fillMaxWidth() - .then( - if (onClick != null && enabled) { - Modifier.clickable( - indication = LocalIndication.current, - interactionSource = interactionSource, - onClick = { onClick.invoke() } - ) - } else Modifier - ) + .then(clickableModifier) .padding(insideMargin) ) { constraints -> val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0) diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/IconButton.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/IconButton.kt index 213f689b..1b89655e 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/IconButton.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/IconButton.kt @@ -53,7 +53,6 @@ fun IconButton( minWidth: Dp = IconButtonDefaults.MinWidth, content: @Composable () -> Unit ) { - val currentOnClick by rememberUpdatedState(onClick) val shape = remember(cornerRadius) { SmoothRoundedCornerShape(cornerRadius) } val interactionSource = remember { MutableInteractionSource() } val holdDown = remember { mutableStateOf(null) } @@ -81,7 +80,7 @@ fun IconButton( role = Role.Button, indication = LocalIndication.current, interactionSource = interactionSource, - onClick = currentOnClick + onClick = onClick ), contentAlignment = Alignment.Center ) { diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ListPopup.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ListPopup.kt index 69c9b7f9..cc13b017 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ListPopup.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ListPopup.kt @@ -43,7 +43,6 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.utils.BackHandler import top.yukonga.miuix.kmp.utils.MiuixPopupUtils.Companion.PopupLayout import top.yukonga.miuix.kmp.utils.SmoothRoundedCornerShape -import top.yukonga.miuix.kmp.utils.WindowSize import top.yukonga.miuix.kmp.utils.getWindowSize import kotlin.math.min @@ -76,9 +75,6 @@ fun ListPopup( ) { if (!show.value) return - val density = LocalDensity.current - val layoutDirection = LocalLayoutDirection.current - val windowSize by rememberUpdatedState(getWindowSize()) var parentBounds by remember { mutableStateOf(IntRect.Zero) } @@ -96,8 +92,16 @@ fun ListPopup( } } ) { _, _ -> layout(0, 0) {} } + if (parentBounds == IntRect.Zero) return - val popupMargin = remember { + val density = LocalDensity.current + val layoutDirection = LocalLayoutDirection.current + val displayCutout = WindowInsets.displayCutout.asPaddingValues() + val statusBars = WindowInsets.statusBars.asPaddingValues() + val navigationBars = WindowInsets.navigationBars.asPaddingValues() + val captionBar = WindowInsets.captionBar.asPaddingValues() + + val popupMargin = remember(windowSize, density) { with(density) { IntRect( left = popupPositionProvider.getMargins().calculateLeftPadding(layoutDirection).roundToPx(), @@ -108,17 +112,19 @@ fun ListPopup( } } - val windowBounds = with(density) { - IntRect( - left = WindowInsets.displayCutout.asPaddingValues().calculateLeftPadding(layoutDirection).roundToPx(), - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding().roundToPx(), - right = windowSize.width - WindowInsets.displayCutout.asPaddingValues().calculateRightPadding(layoutDirection).roundToPx(), - bottom = windowSize.height - WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() - .roundToPx() - WindowInsets.captionBar.asPaddingValues().calculateBottomPadding().roundToPx() - ) + val windowBounds = remember(windowSize, density) { + with(density) { + IntRect( + left = displayCutout.calculateLeftPadding(layoutDirection).roundToPx(), + top = statusBars.calculateTopPadding().roundToPx(), + right = windowSize.width - displayCutout.calculateRightPadding(layoutDirection).roundToPx(), + bottom = windowSize.height - navigationBars.calculateBottomPadding() + .roundToPx() - captionBar.calculateBottomPadding().roundToPx() + ) + } } - val transformOrigin = remember(parentBounds, popupMargin, windowSize, alignment, density) { + val transformOrigin = remember(windowSize, alignment, density) { val xInWindow = when (alignment) { PopupPositionProvider.Align.Right, PopupPositionProvider.Align.TopRight, @@ -133,63 +139,61 @@ fun ListPopup( ) } - if (parentBounds != IntRect.Zero && windowSize != WindowSize(0, 0)) { - PopupLayout( - visible = show, - enableWindowDim = enableWindowDim, - transformOrigin = { transformOrigin }, - ) { - val shape = remember { SmoothRoundedCornerShape(16.dp) } - val elevationPx = with(density) { shadowElevation.toPx() } + PopupLayout( + visible = show, + enableWindowDim = enableWindowDim, + transformOrigin = { transformOrigin }, + ) { + val shape = remember { SmoothRoundedCornerShape(16.dp) } + val elevationPx = with(density) { shadowElevation.toPx() } - Box( - modifier = popupModifier - .pointerInput(onDismissRequest) { - detectTapGestures( - onTap = { onDismissRequest?.invoke() } - ) - } - .layout { measurable, constraints -> - val placeable = measurable.measure( - constraints.copy( - minWidth = if (minWidth.roundToPx() <= windowSize.width) minWidth.roundToPx() else windowSize.width, - minHeight = if (50.dp.roundToPx() <= windowSize.height) 50.dp.roundToPx() else windowSize.height, - maxHeight = maxHeight?.roundToPx()?.coerceAtLeast(50.dp.roundToPx()) - ?: (windowBounds.height - popupMargin.top - popupMargin.bottom).coerceAtLeast( - 50.dp.roundToPx() - ), - maxWidth = if (minWidth.roundToPx() <= windowSize.width) windowSize.width else minWidth.roundToPx() - ) + Box( + modifier = popupModifier + .pointerInput(onDismissRequest) { + detectTapGestures( + onTap = { onDismissRequest?.invoke() } + ) + } + .layout { measurable, constraints -> + val placeable = measurable.measure( + constraints.copy( + minWidth = if (minWidth.roundToPx() <= windowSize.width) minWidth.roundToPx() else windowSize.width, + minHeight = if (50.dp.roundToPx() <= windowSize.height) 50.dp.roundToPx() else windowSize.height, + maxHeight = maxHeight?.roundToPx()?.coerceAtLeast(50.dp.roundToPx()) + ?: (windowBounds.height - popupMargin.top - popupMargin.bottom).coerceAtLeast( + 50.dp.roundToPx() + ), + maxWidth = if (minWidth.roundToPx() <= windowSize.width) windowSize.width else minWidth.roundToPx() ) - val measuredSize = IntSize(placeable.width, placeable.height) + ) + val measuredSize = IntSize(placeable.width, placeable.height) - val calculatedOffset = popupPositionProvider.calculatePosition( - parentBounds, - windowBounds, - layoutDirection, - measuredSize, - popupMargin, - alignment - ) + val calculatedOffset = popupPositionProvider.calculatePosition( + parentBounds, + windowBounds, + layoutDirection, + measuredSize, + popupMargin, + alignment + ) - layout(constraints.maxWidth, constraints.maxHeight) { - placeable.place(calculatedOffset) - } + layout(constraints.maxWidth, constraints.maxHeight) { + placeable.place(calculatedOffset) } - ) { - Box( - modifier = Modifier - .graphicsLayer( - clip = true, - shape = shape, - shadowElevation = elevationPx, - ambientShadowColor = MiuixTheme.colorScheme.windowDimming, - spotShadowColor = MiuixTheme.colorScheme.windowDimming - ) - .background(MiuixTheme.colorScheme.surface) - ) { - content() } + ) { + Box( + modifier = Modifier + .graphicsLayer( + clip = true, + shape = shape, + shadowElevation = elevationPx, + ambientShadowColor = MiuixTheme.colorScheme.windowDimming, + spotShadowColor = MiuixTheme.colorScheme.windowDimming + ) + .background(MiuixTheme.colorScheme.surface) + ) { + content() } } } diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/NavigationBar.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/NavigationBar.kt index 325b1356..a3957bda 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/NavigationBar.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/NavigationBar.kt @@ -76,8 +76,6 @@ fun NavigationBar( ) { require(items.size in 2..5) { "BottomBar must have between 2 and 5 items" } - val currentOnClick by rememberUpdatedState(onClick) - val captionBarPaddings = WindowInsets.captionBar.only(WindowInsetsSides.Bottom).asPaddingValues() val captionBarBottomPaddingValue = captionBarPaddings.calculateBottomPadding() @@ -130,14 +128,14 @@ fun NavigationBar( modifier = Modifier .height(itemHeight) .weight(itemWeight) - .pointerInput(currentOnClick, index) { + .pointerInput(onClick, index) { detectTapGestures( onPress = { isPressed = true tryAwaitRelease() isPressed = false }, - onTap = { currentOnClick(index) } + onTap = { onClick(index) } ) }, horizontalAlignment = CenterHorizontally @@ -204,7 +202,6 @@ fun FloatingNavigationBar( ) { require(items.size in 2..5) { "FloatingNavigationBar must have between 2 and 5 items" } - val currentOnClick by rememberUpdatedState(onClick) val density = LocalDensity.current val platformValue = remember { platform() } @@ -294,14 +291,14 @@ fun FloatingNavigationBar( Column( modifier = Modifier - .pointerInput(currentOnClick, index) { + .pointerInput(onClick, index) { detectTapGestures( onPress = { isPressed = true tryAwaitRelease() isPressed = false }, - onTap = { currentOnClick(index) } + onTap = { onClick(index) } ) }, horizontalAlignment = CenterHorizontally diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/PullToRefresh.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/PullToRefresh.kt index 9f37eb2a..e47e5fa6 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/PullToRefresh.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/PullToRefresh.kt @@ -33,6 +33,7 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment @@ -191,9 +192,9 @@ fun rememberPullToRefreshState(): PullToRefreshState { } // Update context-dependent properties on the state instance to ensure it's always current. - val currentWindowSize = getWindowSize() - state.maxDragDistancePx = currentWindowSize.height.toFloat()// * maxDragRatio - state.refreshThresholdOffset = currentWindowSize.height.toFloat() * maxDragRatio * thresholdRatio + val windowSize by rememberUpdatedState(getWindowSize()) + state.maxDragDistancePx = windowSize.height.toFloat()// * maxDragRatio + state.refreshThresholdOffset = windowSize.height.toFloat() * maxDragRatio * thresholdRatio return state } diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Slider.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Slider.kt index 7b3ddd6e..b3b2df45 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Slider.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Slider.kt @@ -74,7 +74,6 @@ fun Slider( val hapticFeedback = LocalHapticFeedback.current var dragOffset by remember { mutableStateOf(0f) } var isDragging by remember { mutableStateOf(false) } - val updatedOnProgressChange by rememberUpdatedState(onProgressChange) val factor = remember(decimalPlaces) { 10f.pow(decimalPlaces) } val hapticState = remember { SliderHapticState() } val interactionSource = remember { MutableInteractionSource() } @@ -97,13 +96,13 @@ fun Slider( isDragging = true dragOffset = offset.x val calculatedValue = calculateProgress(dragOffset, size.width) - updatedOnProgressChange(calculatedValue) + onProgressChange(calculatedValue) hapticState.reset(calculatedValue) }, onHorizontalDrag = { _, dragAmount -> dragOffset = (dragOffset + dragAmount).coerceIn(0f, size.width.toFloat()) val calculatedValue = calculateProgress(dragOffset, size.width) - updatedOnProgressChange(calculatedValue) + onProgressChange(calculatedValue) hapticState.handleHapticFeedback(calculatedValue, hapticEffect, hapticFeedback) }, onDragEnd = { diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Switch.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Switch.kt index d6155633..1aa0ab49 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Switch.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/Switch.kt @@ -32,7 +32,6 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -66,9 +65,6 @@ fun Switch( colors: SwitchColors = SwitchDefaults.switchColors(), enabled: Boolean = true ) { - val isChecked by rememberUpdatedState(checked) - val currentOnCheckedChange by rememberUpdatedState(onCheckedChange) - val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() val isDragged by interactionSource.collectIsDraggedAsState() @@ -78,14 +74,16 @@ fun Switch( var hasVibrated by remember { mutableStateOf(false) } var hasVibratedOnce by remember { mutableStateOf(false) } - val springSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessMedium - ) + val springSpec = remember { + spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessMedium + ) + } var dragOffset by remember { mutableStateOf(0f) } val thumbOffset by animateDpAsState( - targetValue = if (isChecked) { + targetValue = if (checked) { if (!enabled) 26.dp else if (isPressed || isDragged || isHovered) 24.dp else 26.dp } else { if (!enabled) 4.dp else if (isPressed || isDragged || isHovered) 3.dp else 4.dp @@ -99,11 +97,11 @@ fun Switch( ) val thumbColor by animateColorAsState( - if (isChecked) colors.checkedThumbColor(enabled) else colors.uncheckedThumbColor(enabled) + if (checked) colors.checkedThumbColor(enabled) else colors.uncheckedThumbColor(enabled) ) val backgroundColor by animateColorAsState( - if (isChecked) colors.checkedTrackColor(enabled) else colors.uncheckedTrackColor(enabled), + if (checked) colors.checkedTrackColor(enabled) else colors.uncheckedTrackColor(enabled), animationSpec = tween(durationMillis = 200) ) @@ -143,21 +141,21 @@ fun Switch( } while (event.changes.all { it.pressed }) if (validHorizontalDrag && !isPressed && !isDragged) { - currentOnCheckedChange?.invoke(!isChecked) + onCheckedChange?.invoke(!checked) hapticFeedback.performHapticFeedback( - if (isChecked) HapticFeedbackType.ToggleOff + if (checked) HapticFeedbackType.ToggleOff else HapticFeedbackType.ToggleOn ) } } } .toggleable( - value = isChecked, + value = checked, onValueChange = { - if (currentOnCheckedChange == null) return@toggleable - currentOnCheckedChange?.invoke(!isChecked) + if (onCheckedChange == null) return@toggleable + onCheckedChange.invoke(!checked) hapticFeedback.performHapticFeedback( - if (isChecked) HapticFeedbackType.ToggleOn + if (checked) HapticFeedbackType.ToggleOn else HapticFeedbackType.ToggleOff ) }, @@ -175,7 +173,7 @@ fun Switch( .drawBehind { drawCircle(color = thumbColor) } - .pointerInput(Unit) { + .pointerInput(checked) { if (!enabled) return@pointerInput awaitEachGesture { val pressInteraction: PressInteraction.Press @@ -191,7 +189,7 @@ fun Switch( } } } - .pointerInput(Unit) { + .pointerInput(checked) { if (!enabled) return@pointerInput val dragInteraction: DragInteraction.Start = DragInteraction.Start() detectHorizontalDragGestures( @@ -201,11 +199,11 @@ fun Switch( hasVibratedOnce = false }, onDragEnd = { - if (dragOffset.absoluteValue > 21f / 2) currentOnCheckedChange?.invoke(!isChecked) + if (dragOffset.absoluteValue > 21f / 2) onCheckedChange?.invoke(!checked) if (!hasVibratedOnce && dragOffset.absoluteValue >= 1f) { - if ((isChecked && dragOffset <= -11f) || (!isChecked && dragOffset <= 10f)) { + if ((checked && dragOffset <= -11f) || (!checked && dragOffset <= 10f)) { hapticFeedback.performHapticFeedback(HapticFeedbackType.ToggleOff) - } else if ((isChecked && dragOffset >= -10f) || (!isChecked && dragOffset >= 11f)) { + } else if ((checked && dragOffset >= -10f) || (!checked && dragOffset >= 11f)) { hapticFeedback.performHapticFeedback(HapticFeedbackType.ToggleOn) } } @@ -218,18 +216,18 @@ fun Switch( } ) { _, dragAmount -> dragOffset = (dragOffset + dragAmount / 2).let { - if (isChecked) it.coerceIn(-21f, 0f) else it.coerceIn(0f, 21f) + if (checked) it.coerceIn(-21f, 0f) else it.coerceIn(0f, 21f) } if (dragOffset in -11f..-10f || dragOffset in 10f..11f) { hasVibratedOnce = false } else if (dragOffset in -20f..-1f || dragOffset in 1f..20f) { hasVibrated = false } else if (!hasVibrated) { - if ((isChecked && dragOffset == -21f) || (!isChecked && dragOffset == 0f)) { + if ((checked && dragOffset == -21f) || (!checked && dragOffset == 0f)) { hapticFeedback.performHapticFeedback(HapticFeedbackType.ToggleOff) hasVibrated = true hasVibratedOnce = true - } else if ((isChecked && dragOffset == 0f) || (!isChecked && dragOffset == 21f)) { + } else if ((checked && dragOffset == 0f) || (!checked && dragOffset == 21f)) { hapticFeedback.performHapticFeedback(HapticFeedbackType.ToggleOn) hasVibrated = true hasVibratedOnce = true diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDialog.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDialog.kt index fcc88dd7..7e5641bc 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDialog.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/extra/SuperDialog.kt @@ -70,6 +70,7 @@ fun SuperDialog( content: @Composable () -> Unit ) { if (!show.value) return + DialogLayout( visible = show, enableWindowDim = enableWindowDim, @@ -109,9 +110,10 @@ private fun SuperDialogContent( content: @Composable () -> Unit ) { val density = LocalDensity.current - val windowSize by rememberUpdatedState(getWindowSize()) + val roundedCorner by rememberUpdatedState(getRoundedCorner()) + val windowSize by rememberUpdatedState(getWindowSize()) val windowWidth by remember(windowSize, density) { derivedStateOf { windowSize.width.dp / density.density } } 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 c40ed618..de6c92e2 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 @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable +import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -27,6 +28,7 @@ import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.BlendModeColorFilter import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.hapticfeedback.HapticFeedback import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.pointerInput @@ -167,12 +169,12 @@ fun SuperDropdown( private fun SuperDropdownPopup( items: List, selectedIndex: Int, - isDropdownExpanded: androidx.compose.runtime.MutableState, + isDropdownExpanded: MutableState, mode: DropDownMode, alignLeft: Boolean, maxHeight: Dp?, dropdownColors: DropdownColors, - hapticFeedback: androidx.compose.ui.hapticfeedback.HapticFeedback, + hapticFeedback: HapticFeedback, onSelectedIndexChange: ((Int) -> Unit)? ) { ListPopup( diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/MiuixPopupUtils.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/MiuixPopupUtils.kt index d809adb2..e51424f3 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/MiuixPopupUtils.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/MiuixPopupUtils.kt @@ -152,7 +152,7 @@ class MiuixPopupUtils { */ @Composable fun DialogLayout( - visible: MutableState = mutableStateOf(true), + visible: MutableState, enterTransition: EnterTransition? = null, exitTransition: ExitTransition? = null, enableWindowDim: Boolean = true, @@ -324,10 +324,16 @@ class MiuixPopupUtils { @Composable fun MiuixPopupHost() { val density = LocalDensity.current - val getWindowSize by rememberUpdatedState(getWindowSize()) - val windowWidth by rememberUpdatedState(getWindowSize.width.dp / density.density) - val windowHeight by rememberUpdatedState(getWindowSize.height.dp / density.density) - val largeScreen by remember { derivedStateOf { (windowHeight >= 480.dp && windowWidth >= 840.dp) } } + val windowSize by rememberUpdatedState(getWindowSize()) + val windowWidth by remember(windowSize, density) { + derivedStateOf { windowSize.width.dp / density.density } + } + val windowHeight by remember(windowSize, density) { + derivedStateOf { windowSize.height.dp / density.density } + } + val largeScreen by remember(windowWidth, windowHeight) { + derivedStateOf { (windowHeight >= 480.dp && windowWidth >= 840.dp) } + } val dialogStates = LocalDialogStates.current val popupStates = LocalPopupStates.current @@ -356,6 +362,13 @@ class MiuixPopupUtils { } } } + + DisposableEffect(Unit) { + onDispose { + dialogStates.clear() + popupStates.clear() + } + } } } } diff --git a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/PressFeedback.kt b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/PressFeedback.kt index 6e1618e0..388801f1 100644 --- a/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/PressFeedback.kt +++ b/miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/PressFeedback.kt @@ -49,7 +49,9 @@ fun Modifier.pressSink( ): Modifier = composed { val isPressed by interactionSource.collectIsPressedAsState() - val animationSpec = spring(dampingRatio = dampingRatio, stiffness = stiffness) + val animationSpec = remember(dampingRatio, stiffness) { + spring(dampingRatio = dampingRatio, stiffness = stiffness) + } val scale by animateFloatAsState( targetValue = if (isPressed) sinkAmount else 1f, @@ -78,7 +80,9 @@ fun Modifier.pressTilt( ): Modifier = composed { val isPressed by interactionSource.collectIsPressedAsState() - val animationSpec = spring(dampingRatio = dampingRatio, stiffness = stiffness) + val animationSpec = remember(dampingRatio, stiffness) { + spring(dampingRatio = dampingRatio, stiffness = stiffness) + } var targetX by remember { mutableStateOf(0f) } var targetY by remember { mutableStateOf(0f) }