Skip to content

Commit 2981658

Browse files
authored
library: Refactor press feedback using custom pressable (#101)
* library: Refactor press feedback using custom pressable * docs: Update press feedback effects documentation * library: Fix PressFeedback interactionSource nullable issues
1 parent 8f3e4e2 commit 2981658

File tree

6 files changed

+665
-317
lines changed

6 files changed

+665
-317
lines changed

docs/guide/utils.md

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -127,81 +127,54 @@ LazyColumn(
127127

128128
* `hapticFeedbackType`: Specifies the type of haptic feedback to be performed when the scroll reaches its end. Defaults to `HapticFeedbackType.TextHandleMove`. You can use other types available in `androidx.compose.ui.hapticfeedback.HapticFeedbackType`.
129129

130-
## Press Feedback Effects
130+
## Press Feedback Effects (Modifier.pressable())
131131

132132
Miuix provides visual feedback effects to enhance user interaction experience, improving operability through tactile-like responses.
133133

134-
### Sink Effect
135-
136-
The `pressSink` modifier applies a "sink" visual feedback when the component is pressed, by smoothly scaling down the component.
134+
Supports use with responsive modifiers such as `Modifier.clickable()`, and `SinkFeedback` is the default effect.
137135

138136
```kotlin
139-
val interactionSource = remember { MutableInteractionSource() }
140-
141137
Box(
142138
modifier = Modifier
143-
.clickable(interactionSource = interactionSource, indication = null)
144-
.pressSink(interactionSource)
139+
.pressable()
145140
.background(Color.Blue)
146141
.size(100.dp)
147142
)
148143
```
149144

150-
### Tilt Effect
145+
### Sink Effect
151146

152-
The `pressTilt` modifier applies a "tilt" effect based on the position where the user pressed the component. The tilt direction is determined by touch offset, giving the effect that one corner "sinks" while the other "static".
147+
The `SinkFeedback` indication applies a "sink" visual when the component is pressed, which is achieved by smoothly scaling the component.
153148

154-
```kotlin
155-
val interactionSource = remember { MutableInteractionSource() }
149+
When `interactionSource` is `null`, internal `MutableInteractionSource` is lazily created only when needed, which reduces clickable performance costs during the combination.
156150

151+
```kotlin
157152
Box(
158153
modifier = Modifier
159-
.clickable(interactionSource = interactionSource, indication = null)
160-
.pressTilt(interactionSource)
161-
.background(Color.Green)
154+
.pressable(interactionSource = null, indication = SinkFeedback())
155+
.background(Color.Blue)
162156
.size(100.dp)
163157
)
164158
```
165159

166-
### Prerequisites for Triggering Press Feedback
167-
168-
Press feedback effects require detecting `interactionSource.collectIsPressedAsState()` to be triggered.
169-
170-
You can use responsive modifiers like `Modifier.clickable()` to add `PressInteraction.Press` to the `interactionSource` and trigger press feedback effects.
171-
172-
However, it's recommended to use the method below to add `PressInteraction.Press` to the `interactionSource` for faster response triggering of press feedback effects.
173-
174-
```kotlin
175-
val interactionSource = remember { MutableInteractionSource() }
176-
177-
Box(
178-
modifier = Modifier
179-
.pointerInput(Unit) {
180-
awaitEachGesture {
181-
val pressInteraction: PressInteraction.Press
182-
awaitFirstDown().also {
183-
pressInteraction = PressInteraction.Press(it.position)
184-
interactionSource.tryEmit(pressInteraction)
185-
}
186-
if (waitForUpOrCancellation() == null) {
187-
interactionSource.tryEmit(PressInteraction.Cancel(pressInteraction))
188-
} else {
189-
interactionSource.tryEmit(PressInteraction.Release(pressInteraction))
190-
}
191-
}
192-
}
193-
)
194-
```
160+
### Tilt Effect
195161

196-
If you want to use both `Modifier.clickable()` and instant feedback, you can pass the `immediate = true` parameter.
162+
The `TiltFeedback` indication applies a "tilt" effect based on the position of the user pressing the component. The tilt direction is determined by the touch offset, so that one corner "sinks" while the other corner remains "still".
197163

198164
```kotlin
199165
val interactionSource = remember { MutableInteractionSource() }
200166

201167
Box(
202168
modifier = Modifier
203-
.clickable(interactionSource = interactionSource, indication = null, onClick = {})
204-
.pressSink(interactionSource, immediate = true)
169+
.pressable(interactionSource = interactionSource, indication = TiltFeedback())
170+
.combinedClickable(
171+
interactionSource = interactionSource,
172+
indication = LocalIndication.current,
173+
onClick = {},
174+
onLongClick = {}
175+
)
176+
.background(Color.Green)
177+
.size(100.dp)
205178
)
206179
```
207180

docs/zh_CN/guide/utils.md

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -127,81 +127,54 @@ LazyColumn(
127127

128128
* `hapticFeedbackType`: 指定滚动到达末端时要执行的触觉反馈类型。默认为 `HapticFeedbackType.TextHandleMove`。您可以使用 `androidx.compose.ui.hapticfeedback.HapticFeedbackType` 中可用的其他类型。
129129

130-
## 按压反馈效果 (PressFeedback)
130+
## 按压反馈效果 (Modifier.pressable())
131131

132132
Miuix 提供了视觉反馈效果来增强用户交互体验,通过类似触觉的响应提升操作感。
133133

134-
### 下沉效果
135-
136-
`pressSink` 修饰符会在组件被按下时应用一种“下沉”视觉效果,通过平滑缩放组件实现。
134+
支持与 `Modifier.clickable()` 等响应式修饰符一起使用,`SinkFeedback` 为默认效果。
137135

138136
```kotlin
139-
val interactionSource = remember { MutableInteractionSource() }
140-
141137
Box(
142138
modifier = Modifier
143-
.clickable(interactionSource = interactionSource, indication = null)
144-
.pressSink(interactionSource)
139+
.pressable()
145140
.background(Color.Blue)
146141
.size(100.dp)
147142
)
148143
```
149144

150-
### 倾斜效果
145+
### 下沉效果
151146

152-
`pressTilt` 修饰符会根据用户按压组件的位置应用一种“倾斜”效果。倾斜方向由触摸偏移决定,使一角“下沉”而另一角保持“静止”
147+
`SinkFeedback` 指示会在组件被按下时应用一种“下沉”视觉效果,通过平滑缩放组件实现
153148

154-
```kotlin
155-
val interactionSource = remember { MutableInteractionSource() }
149+
`interactionSource``null` 时,仅在需要时才会懒惰地创建内部 `MutableInteractionSource`,这会降低组合过程中可点击的性能成本。
156150

151+
```kotlin
157152
Box(
158153
modifier = Modifier
159-
.clickable(interactionSource = interactionSource, indication = null)
160-
.pressTilt(interactionSource)
161-
.background(Color.Green)
154+
.pressable(interactionSource = null, indication = SinkFeedback())
155+
.background(Color.Blue)
162156
.size(100.dp)
163157
)
164158
```
165159

166-
### 触发按压反馈效果的前提
167-
168-
按压反馈效果需要检测 `interactionSource.collectIsPressedAsState()` 以触发。
169-
170-
可以使用 `Modifier.clickable()` 等响应式修饰符来为 `interactionSource` 添加 `PressInteraction.Press` 以触发按压反馈效果。
171-
172-
但更推荐使用下面的方法来为 `interactionSource` 添加 `PressInteraction.Press` 以获得更快响应的触发按压反馈效果。
173-
174-
```kotlin
175-
val interactionSource = remember { MutableInteractionSource() }
176-
177-
Box(
178-
modifier = Modifier
179-
.pointerInput(Unit) {
180-
awaitEachGesture {
181-
val pressInteraction: PressInteraction.Press
182-
awaitFirstDown().also {
183-
pressInteraction = PressInteraction.Press(it.position)
184-
interactionSource.tryEmit(pressInteraction)
185-
}
186-
if (waitForUpOrCancellation() == null) {
187-
interactionSource.tryEmit(PressInteraction.Cancel(pressInteraction))
188-
} else {
189-
interactionSource.tryEmit(PressInteraction.Release(pressInteraction))
190-
}
191-
}
192-
}
193-
)
194-
```
160+
### 倾斜效果
195161

196-
如果既想使用 `Modifier.clickable()` 又想要即时的反馈效果,可以传递 `immediate = true` 参数
162+
`TiltFeedback` 指示会根据用户按压组件的位置应用一种“倾斜”效果。倾斜方向由触摸偏移决定,使一角“下沉”而另一角保持“静止”
197163

198164
```kotlin
199165
val interactionSource = remember { MutableInteractionSource() }
200166

201167
Box(
202168
modifier = Modifier
203-
.clickable(interactionSource = interactionSource, indication = null, onClick = {})
204-
.pressSink(interactionSource, immediate = true)
169+
.pressable(interactionSource = interactionSource, indication = TiltFeedback())
170+
.combinedClickable(
171+
interactionSource = interactionSource,
172+
indication = LocalIndication.current,
173+
onClick = {},
174+
onLongClick = {}
175+
)
176+
.background(Color.Green)
177+
.size(100.dp)
205178
)
206179
```
207180

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

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme
2929
import top.yukonga.miuix.kmp.utils.CornerSmoothness
3030
import top.yukonga.miuix.kmp.utils.G2RoundedCornerShape
3131
import top.yukonga.miuix.kmp.utils.PressFeedbackType
32-
import top.yukonga.miuix.kmp.utils.pressSink
33-
import top.yukonga.miuix.kmp.utils.pressTilt
32+
import top.yukonga.miuix.kmp.utils.SinkFeedback
33+
import top.yukonga.miuix.kmp.utils.TiltFeedback
34+
import top.yukonga.miuix.kmp.utils.pressable
3435

3536
/**
3637
* A [Card] component with Miuix style.
@@ -47,9 +48,9 @@ import top.yukonga.miuix.kmp.utils.pressTilt
4748
@Composable
4849
fun Card(
4950
modifier: Modifier = Modifier,
50-
colors: CardColors = CardDefaults.defaultColors(),
5151
cornerRadius: Dp = CardDefaults.CornerRadius,
5252
insideMargin: PaddingValues = CardDefaults.InsideMargin,
53+
colors: CardColors = CardDefaults.defaultColors(),
5354
content: @Composable ColumnScope.() -> Unit,
5455
) {
5556
BasicCard(
@@ -94,16 +95,20 @@ fun Card(
9495
) {
9596
val interactionSource = remember { MutableInteractionSource() }
9697

97-
val pressFeedbackModifier = remember(pressFeedbackType, interactionSource) {
98+
val pressFeedback = remember(pressFeedbackType) {
9899
when (pressFeedbackType) {
99-
PressFeedbackType.None -> Modifier
100-
PressFeedbackType.Sink -> Modifier.pressSink(interactionSource, immediate = true)
101-
PressFeedbackType.Tilt -> Modifier.pressTilt(interactionSource, immediate = true)
100+
PressFeedbackType.None -> null
101+
PressFeedbackType.Sink -> SinkFeedback()
102+
PressFeedbackType.Tilt -> TiltFeedback()
102103
}
103104
}
104105

105106
BasicCard(
106-
modifier = modifier.then(pressFeedbackModifier),
107+
modifier = modifier.pressable(
108+
interactionSource = if (pressFeedback != null) interactionSource else null,
109+
indication = pressFeedback,
110+
delay = null
111+
),
107112
cornerRadius = cornerRadius,
108113
colors = colors
109114
) {
@@ -191,9 +196,8 @@ class CardColors(
191196
fun copy(
192197
color: Color = this.color,
193198
contentColor: Color = this.contentColor,
194-
) =
195-
CardColors(
196-
color.takeOrElse { this.color },
197-
contentColor.takeOrElse { this.contentColor },
198-
)
199+
) = CardColors(
200+
color.takeOrElse { this.color },
201+
contentColor.takeOrElse { this.contentColor },
202+
)
199203
}

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import androidx.compose.animation.core.FastOutSlowInEasing
99
import androidx.compose.animation.core.keyframes
1010
import androidx.compose.animation.core.tween
1111
import androidx.compose.foundation.Canvas
12-
import androidx.compose.foundation.interaction.MutableInteractionSource
1312
import androidx.compose.foundation.layout.Box
1413
import androidx.compose.foundation.layout.requiredSize
1514
import androidx.compose.foundation.layout.size
@@ -39,7 +38,7 @@ import androidx.compose.ui.unit.dp
3938
import kotlinx.coroutines.launch
4039
import top.yukonga.miuix.kmp.theme.MiuixTheme
4140
import top.yukonga.miuix.kmp.utils.CapsuleShape
42-
import top.yukonga.miuix.kmp.utils.pressSink
41+
import top.yukonga.miuix.kmp.utils.pressable
4342

4443
/**
4544
* A [Checkbox] component with Miuix style.
@@ -59,7 +58,6 @@ fun Checkbox(
5958
enabled: Boolean = true,
6059
) {
6160
val hapticFeedback = LocalHapticFeedback.current
62-
val interactionSource = remember { MutableInteractionSource() }
6361

6462
val backgroundColor by animateColorAsState(
6563
targetValue = if (checked) colors.checkedBackgroundColor(enabled) else colors.uncheckedBackgroundColor(enabled),
@@ -95,7 +93,7 @@ fun Checkbox(
9593
modifier = modifier
9694
.wrapContentSize(Alignment.Center)
9795
.requiredSize(25.5.dp)
98-
.pressSink(interactionSource, immediate = true)
96+
.pressable(enabled = enabled, delay = null)
9997
.clip(CapsuleShape)
10098
.drawBehind {
10199
drawCircle(backgroundColor)

0 commit comments

Comments
 (0)