Skip to content

Commit 1df5bb7

Browse files
committed
library: ColorPicker: Don't use Paint
1 parent e5dda44 commit 1df5bb7

File tree

2 files changed

+57
-67
lines changed

2 files changed

+57
-67
lines changed

iosApp/iosApp/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<key>CFBundleShortVersionString</key>
1818
<string>1.0.4</string>
1919
<key>CFBundleVersion</key>
20-
<string>522</string>
20+
<string>526</string>
2121
<key>LSRequiresIPhoneOS</key>
2222
<true/>
2323
<key>CADisableMinimumFrameDurationOnPhone</key>

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

Lines changed: 56 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,17 @@ import androidx.compose.ui.Alignment
2323
import androidx.compose.ui.Modifier
2424
import androidx.compose.ui.draw.clip
2525
import androidx.compose.ui.draw.drawBehind
26+
import androidx.compose.ui.draw.drawWithCache
27+
import androidx.compose.ui.geometry.Rect
2628
import androidx.compose.ui.graphics.Brush
2729
import androidx.compose.ui.graphics.Color
28-
import androidx.compose.ui.graphics.ImageBitmap
29-
import androidx.compose.ui.graphics.ImageShader
30-
import androidx.compose.ui.graphics.Paint
31-
import androidx.compose.ui.graphics.ShaderBrush
30+
import androidx.compose.ui.graphics.Path
3231
import androidx.compose.ui.graphics.TileMode
33-
import androidx.compose.ui.graphics.drawscope.DrawScope
3432
import androidx.compose.ui.graphics.drawscope.Stroke
3533
import androidx.compose.ui.input.pointer.pointerInput
3634
import androidx.compose.ui.layout.onGloballyPositioned
3735
import androidx.compose.ui.platform.LocalDensity
3836
import androidx.compose.ui.platform.LocalHapticFeedback
39-
import androidx.compose.ui.unit.Density
4037
import androidx.compose.ui.unit.Dp
4138
import androidx.compose.ui.unit.dp
4239
import top.yukonga.miuix.kmp.utils.CapsuleShape
@@ -45,6 +42,8 @@ import top.yukonga.miuix.kmp.utils.Hsv
4542
import top.yukonga.miuix.kmp.utils.OkLab
4643
import top.yukonga.miuix.kmp.utils.toHsv
4744
import top.yukonga.miuix.kmp.utils.toOkLab
45+
import kotlin.math.ceil
46+
import kotlin.math.min
4847

4948
/**
5049
* A [ColorPicker] component with Miuix style that supports multiple color spaces.
@@ -299,26 +298,20 @@ fun HsvAlphaSlider(
299298
onAlphaChanged: (Float) -> Unit,
300299
hapticEffect: SliderDefaults.SliderHapticEffect = SliderDefaults.DefaultHapticEffect
301300
) {
302-
val density = LocalDensity.current
303-
304301
val alphaColors = remember(currentHue, currentSaturation, currentValue) {
305302
val baseColor = Hsv(currentHue.toDouble(), (currentValue * 100.0), (currentSaturation * 100.0)).toColor()
306303
listOf(baseColor.copy(alpha = 0f), baseColor.copy(alpha = 1f))
307304
}
308305

309-
val checkerBrush = remember(density) {
310-
createCheckerboardBrush(density)
311-
}
312306

313307
ColorSlider(
314308
value = currentAlpha,
315309
onValueChanged = onAlphaChanged,
316310
drawBrushColors = alphaColors,
317-
modifier = Modifier.fillMaxWidth(),
318-
hapticEffect = hapticEffect,
319-
drawBackground = { _, _ ->
320-
drawRect(brush = checkerBrush)
321-
}
311+
modifier = Modifier
312+
.fillMaxWidth()
313+
.drawCheckerboard(),
314+
hapticEffect = hapticEffect
322315
)
323316
}
324317

@@ -527,26 +520,19 @@ fun OkHsvAlphaSlider(
527520
onAlphaChanged: (Float) -> Unit,
528521
hapticEffect: SliderDefaults.SliderHapticEffect = SliderDefaults.DefaultHapticEffect
529522
) {
530-
val density = LocalDensity.current
531-
532523
val alphaColors = remember(currentH, currentS, currentV) {
533524
val baseColor = ColorUtils.okhsvToColor(currentH, currentS, currentV)
534525
listOf(baseColor.copy(alpha = 0f), baseColor.copy(alpha = 1f))
535526
}
536527

537-
val checkerBrush = remember(density) {
538-
createCheckerboardBrush(density)
539-
}
540-
541528
ColorSlider(
542529
value = currentAlpha,
543530
onValueChanged = onAlphaChanged,
544531
drawBrushColors = alphaColors,
545-
modifier = Modifier.fillMaxWidth(),
546-
hapticEffect = hapticEffect,
547-
drawBackground = { _, _ ->
548-
drawRect(brush = checkerBrush)
549-
}
532+
modifier = Modifier
533+
.fillMaxWidth()
534+
.drawCheckerboard(),
535+
hapticEffect = hapticEffect
550536
)
551537
}
552538

@@ -766,8 +752,6 @@ fun OkLabAlphaSlider(
766752
onAlphaChanged: (Float) -> Unit,
767753
hapticEffect: SliderDefaults.SliderHapticEffect = SliderDefaults.DefaultHapticEffect
768754
) {
769-
val density = LocalDensity.current
770-
771755
val alphaColors = remember(currentL, currentA, currentB) {
772756
val baseColor = OkLab(
773757
l = currentL * 100.0,
@@ -777,43 +761,50 @@ fun OkLabAlphaSlider(
777761
listOf(baseColor.copy(alpha = 0f), baseColor.copy(alpha = 1f))
778762
}
779763

780-
val checkerBrush = remember(density) {
781-
createCheckerboardBrush(density)
782-
}
783-
784764
ColorSlider(
785765
value = currentAlpha,
786766
onValueChanged = onAlphaChanged,
787767
drawBrushColors = alphaColors,
788-
modifier = Modifier.fillMaxWidth(),
768+
modifier = Modifier.fillMaxWidth()
769+
.drawCheckerboard(),
789770
hapticEffect = hapticEffect,
790-
drawBackground = { _, _ ->
791-
drawRect(brush = checkerBrush)
792-
}
793771
)
794772
}
795773

796-
private fun createCheckerboardBrush(density: Density): ShaderBrush {
797-
val lightColor = Color(0xFFCCCCCC)
798-
val darkColor = Color(0xFFAAAAAA)
799-
val checkerSizeDp = 3.dp
800-
801-
val pixelSize = with(density) { checkerSizeDp.toPx() }
802-
val tileBitmapSideLengthPx = (2 * pixelSize).toInt().coerceAtLeast(1)
803-
804-
val imageBitmap = ImageBitmap(tileBitmapSideLengthPx, tileBitmapSideLengthPx)
805-
val canvasForBitmap = androidx.compose.ui.graphics.Canvas(imageBitmap)
806-
807-
val lightPaint = Paint().apply { color = lightColor }
808-
val darkPaint = Paint().apply { color = darkColor }
809-
810-
canvasForBitmap.drawRect(0f, 0f, tileBitmapSideLengthPx.toFloat(), tileBitmapSideLengthPx.toFloat(), lightPaint)
811-
canvasForBitmap.drawRect(pixelSize, 0f, 2 * pixelSize, pixelSize, darkPaint)
812-
canvasForBitmap.drawRect(0f, pixelSize, pixelSize, 2 * pixelSize, darkPaint)
774+
fun Modifier.drawCheckerboard(
775+
cellSizeDp: Dp = 3.dp,
776+
lightColor: Color = Color(0xFFCCCCCC),
777+
darkColor: Color = Color(0xFFAAAAAA)
778+
): Modifier = this.then(
779+
Modifier.drawWithCache {
780+
val cell = cellSizeDp.toPx().coerceAtLeast(1f)
781+
val rows = ceil(size.height / cell).toInt().coerceAtLeast(1)
782+
783+
val darkPath = Path().apply {
784+
var y = 0f
785+
for (row in 0 until rows) {
786+
var x = if ((row and 1) == 0) cell else 0f
787+
while (x < size.width) {
788+
addRect(
789+
Rect(
790+
x,
791+
y,
792+
min(x + cell, size.width),
793+
min(y + cell, size.height)
794+
)
795+
)
796+
x += cell * 2f
797+
}
798+
y += cell
799+
}
800+
}
813801

814-
val shader = ImageShader(imageBitmap, TileMode.Repeated, TileMode.Repeated)
815-
return ShaderBrush(shader)
816-
}
802+
onDrawBehind {
803+
drawRect(color = lightColor)
804+
drawPath(path = darkPath, color = darkColor)
805+
}
806+
}
807+
)
817808

818809
/**
819810
* Generic slider component for color selection.
@@ -825,7 +816,6 @@ private fun ColorSlider(
825816
drawBrushColors: List<Color>,
826817
modifier: Modifier = Modifier,
827818
hapticEffect: SliderDefaults.SliderHapticEffect = SliderDefaults.DefaultHapticEffect,
828-
drawBackground: (DrawScope.(width: Float, height: Float) -> Unit)? = null
829819
) {
830820
val density = LocalDensity.current
831821
var sliderWidth by remember { mutableStateOf(0.dp) }
@@ -847,17 +837,15 @@ private fun ColorSlider(
847837
}
848838

849839
Box(
850-
modifier = modifier
851-
.height(sliderHeightDp)
840+
modifier = Modifier
852841
.clip(CapsuleShape())
842+
.then(modifier)
843+
.height(sliderHeightDp)
853844
.onGloballyPositioned { coordinates ->
854845
sliderWidth = with(density) { coordinates.size.width.toDp() }
855846
}
856847
.drawBehind {
857-
drawBackground?.invoke(this, size.width, size.height)
858-
drawRect(
859-
brush = gradientBrush,
860-
)
848+
drawRect(brush = gradientBrush)
861849
drawRect(
862850
color = Color.Gray.copy(0.1f),
863851
style = Stroke(width = with(density) { 0.5.dp.toPx() }),
@@ -867,7 +855,10 @@ private fun ColorSlider(
867855
detectHorizontalDragGestures(
868856
onDragStart = { offset ->
869857
val newValue =
870-
handleSliderInteraction(offset.x, size.width.toFloat(), with(density) { sliderHeightDp.toPx() }).coerceIn(
858+
handleSliderInteraction(
859+
offset.x,
860+
size.width.toFloat(),
861+
with(density) { sliderHeightDp.toPx() }).coerceIn(
871862
0f,
872863
1f
873864
)
@@ -916,7 +907,6 @@ private fun SliderIndicator(
916907
modifier = modifier
917908
.offset(x = indicatorOffsetXDp)
918909
.size(indicatorSize)
919-
.clip(CapsuleShape())
920910
.border(6.dp, Color.White, CapsuleShape())
921911
.background(Color.Transparent, CapsuleShape())
922912
)

0 commit comments

Comments
 (0)