Skip to content

Commit 3380efc

Browse files
add sweep and radial gradient selectionsa
1 parent e49dab7 commit 3380efc

File tree

5 files changed

+260
-74
lines changed

5 files changed

+260
-74
lines changed

colorpicker/src/main/java/com/smarttoolfactory/colorpicker/model/GradientColor.kt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
package com.smarttoolfactory.colorpicker.model
22

3+
import androidx.compose.runtime.*
34
import androidx.compose.ui.graphics.Brush
45
import androidx.compose.ui.graphics.Color
56
import androidx.compose.ui.graphics.SolidColor
7+
import androidx.compose.ui.graphics.TileMode
8+
import androidx.compose.ui.unit.DpOffset
9+
import androidx.compose.ui.unit.DpSize
10+
import com.smarttoolfactory.colorpicker.selector.gradient.GradientType
11+
import com.smarttoolfactory.colorpicker.ui.GradientAngle
12+
import com.smarttoolfactory.colorpicker.ui.GradientOffset
613

714

815
class GradientColor {
9-
val brushColor = BrushColor()
10-
11-
}
12-
13-
class GradientMeta {
1416

17+
var dpSize: DpSize by mutableStateOf(DpSize.Zero)
18+
var brushColor = BrushColor()
19+
var gradientType: GradientType by mutableStateOf( GradientType.Linear)
20+
val colorStop = mutableStateListOf<Pair<Float, Color>>()
21+
var tileMode by mutableStateOf(TileMode.Clamp)
22+
var gradientOffset by mutableStateOf(GradientOffset(GradientAngle.CW0))
23+
var center = derivedStateOf { DpOffset(dpSize.width / 2, dpSize.height / 2) }
24+
var radiusFraction: Float = .5f
1525
}
1626

1727
/**

colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/gradient/SelectorGradient.kt

Lines changed: 142 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import androidx.compose.ui.geometry.Size
1616
import androidx.compose.ui.graphics.Brush
1717
import androidx.compose.ui.graphics.Color
1818
import androidx.compose.ui.graphics.TileMode
19-
import androidx.compose.ui.layout.onSizeChanged
19+
import androidx.compose.ui.platform.LocalDensity
20+
import androidx.compose.ui.unit.DpSize
2021
import androidx.compose.ui.unit.dp
2122
import androidx.compose.ui.unit.sp
2223
import com.smarttoolfactory.colorpicker.ui.GradientAngle
@@ -30,18 +31,57 @@ import com.smarttoolfactory.slider.ColorfulSlider
3031
import com.smarttoolfactory.slider.MaterialSliderDefaults
3132
import com.smarttoolfactory.slider.ui.InactiveTrackColor
3233

34+
enum class GradientType {
35+
Linear, Radial, Sweep
36+
}
37+
3338
internal val gradientOptions = listOf(
3439
"Linear",
3540
"Radial",
3641
"Sweep"
3742
)
43+
3844
internal val gradientTileModeOptions = listOf("Clamp", "Repeated", "Mirror", "Decal")
3945

4046
@Composable
41-
fun GradientSelector(color: Color, size: Size = Size.Unspecified) {
47+
fun GradientSelector(
48+
color: Color,
49+
dpSize: DpSize,
50+
onGradientChange: (Brush) -> Unit
51+
) {
52+
53+
val size = with(LocalDensity.current) {
54+
Size(
55+
dpSize.width.toPx(),
56+
dpSize.width.toPx()
57+
)
58+
}
4259

43-
var gradientSelection by remember { mutableStateOf(0) }
60+
// Gradient type
61+
var gradientType by remember { mutableStateOf(GradientType.Linear) }
62+
63+
// Tile mode
4464
var tileModeSelection by remember { mutableStateOf(0) }
65+
val tileMode = when (tileModeSelection) {
66+
0 -> TileMode.Clamp
67+
1 -> TileMode.Repeated
68+
2 -> TileMode.Mirror
69+
else -> TileMode.Decal
70+
}
71+
72+
// Color Stops
73+
val colorStops = remember {
74+
mutableStateListOf(
75+
0.0f to Color.Red,
76+
0.3f to Color.Green,
77+
1.0f to Color.Blue,
78+
)
79+
}
80+
81+
// Offset for Linear Gradient
82+
var gradientOffset by remember {
83+
mutableStateOf(GradientOffset(GradientAngle.CW0))
84+
}
4585

4686
Column(
4787
modifier = Modifier
@@ -51,14 +91,16 @@ fun GradientSelector(color: Color, size: Size = Size.Unspecified) {
5191
) {
5292
ExposedSelectionMenu(
5393
modifier = Modifier.fillMaxWidth(),
54-
index = gradientSelection,
94+
index = gradientType.ordinal,
5595
title = "Gradient",
5696
options = gradientOptions,
5797
onSelected = {
58-
gradientSelection = it
98+
gradientType = GradientType.values()[it]
5999
}
60100
)
101+
61102
Spacer(modifier = Modifier.height(10.dp))
103+
62104
ExposedSelectionMenu(
63105
modifier = Modifier.fillMaxWidth(),
64106
index = tileModeSelection,
@@ -69,80 +111,117 @@ fun GradientSelector(color: Color, size: Size = Size.Unspecified) {
69111
}
70112
)
71113

72-
val tileMode = when (tileModeSelection) {
73-
0 -> TileMode.Clamp
74-
1 -> TileMode.Repeated
75-
2 -> TileMode.Mirror
76-
else -> TileMode.Decal
114+
// Display Brush
115+
BrushDisplay(
116+
gradientType = gradientType,
117+
colorStops = colorStops,
118+
gradientOffset = gradientOffset,
119+
size = size,
120+
tileMode = tileMode
121+
) { brush: Brush ->
122+
onGradientChange(brush)
123+
}
124+
125+
// Gradient type selection
126+
when (gradientType) {
127+
GradientType.Linear -> LinearGradientSelection(
128+
size,
129+
) { offset: GradientOffset ->
130+
gradientOffset = offset
131+
}
132+
GradientType.Radial -> RadialGradientSelection(
133+
size,
134+
) { offset: GradientOffset ->
135+
gradientOffset = offset
136+
}
137+
GradientType.Sweep -> SweepGradientSelection(
138+
size,
139+
) { offset: GradientOffset ->
140+
gradientOffset = offset
141+
}
77142
}
78-
LinearGradientSelection(currentColor = color, tileMode = tileMode)
143+
144+
// Color Stops and Colors
145+
ColorStopSelection(
146+
color = color,
147+
colorStops = colorStops,
148+
onRemoveClick = { index: Int ->
149+
if (colorStops.size > 2) {
150+
colorStops.removeAt(index)
151+
}
152+
},
153+
onValueChange = { index: Int, pair: Pair<Float, Color> ->
154+
colorStops[index] = pair.copy()
155+
},
156+
onAddColorStop = { pair: Pair<Float, Color> ->
157+
colorStops.add(pair)
158+
}
159+
)
79160
}
80161
}
81162

82163
@Composable
83-
internal fun LinearGradientSelection(currentColor: Color, tileMode: TileMode) {
164+
internal fun BrushDisplay(
165+
gradientType: GradientType,
166+
colorStops: List<Pair<Float, Color>>,
167+
gradientOffset: GradientOffset,
168+
size: Size,
169+
tileMode: TileMode,
170+
onBrushChange: (Brush) -> Unit
171+
) {
172+
val brush = when (gradientType) {
173+
GradientType.Linear -> {
174+
if (colorStops.size == 1) {
175+
val brushColor = colorStops.first().second
176+
Brush.linearGradient(listOf(brushColor, brushColor))
177+
} else {
178+
Brush.linearGradient(
179+
colorStops = colorStops.toTypedArray(),
180+
start = gradientOffset.start,
181+
end = gradientOffset.end,
182+
tileMode = tileMode
183+
)
184+
}
185+
}
186+
GradientType.Radial -> {
187+
Brush.radialGradient(
188+
colorStops = colorStops.toTypedArray(),
189+
center = gradientOffset.start,
190+
radius = size.width
191+
)
192+
}
84193

85-
val colorStops = remember {
86-
mutableStateListOf(
87-
0.0f to Color.Red,
88-
0.3f to Color.Green,
89-
1.0f to Color.Blue,
90-
)
91-
}
194+
GradientType.Sweep -> {
195+
Brush.sweepGradient(
196+
colorStops = colorStops.toTypedArray(),
197+
center = gradientOffset.start
198+
)
199+
}
92200

93-
var gradientOffset by remember {
94-
mutableStateOf(
95-
GradientOffset(GradientAngle.CW0)
96-
)
97201
}
98202

99-
val brush = if (colorStops.size == 1) {
100-
val color = colorStops.first().second
101-
Brush.linearGradient(listOf(color, color))
102-
} else {
103-
Brush.linearGradient(
104-
colorStops = colorStops.toTypedArray(),
105-
start = gradientOffset.start,
106-
end = gradientOffset.end,
107-
tileMode = tileMode
108-
)
109-
}
203+
val contentWidth = size.width
204+
val contentHeight = size.height
205+
206+
onBrushChange(brush)
110207

111-
var size by remember {
112-
mutableStateOf(Size.Zero)
113-
}
114208
// Display Brush
115-
Box(
209+
BoxWithConstraints(
116210
modifier = Modifier
117211
.fillMaxWidth()
118-
.height(150.dp)
119-
.background(brush)
120-
.onSizeChanged {
121-
size = Size(it.width.toFloat(), it.height.toFloat())
122-
}
123-
)
212+
.height(150.dp),
213+
contentAlignment = Alignment.Center
214+
) {
215+
Box(
216+
modifier = Modifier
217+
.height(maxHeight)
218+
.aspectRatio(contentWidth / contentHeight)
219+
.background(brush)
220+
) {
124221

125-
GradientOffsetSelection(
126-
size = size
127-
) { offset ->
128-
gradientOffset = offset
222+
}
129223
}
130224

131-
ColorStopSelection(
132-
color = currentColor,
133-
colorStops = colorStops,
134-
onRemoveClick = { index: Int ->
135-
if (colorStops.size > 2) {
136-
colorStops.removeAt(index)
137-
}
138-
},
139-
onValueChange = { index: Int, pair: Pair<Float, Color> ->
140-
colorStops[index] = pair.copy()
141-
},
142-
onAddColorStop = { pair: Pair<Float, Color> ->
143-
colorStops.add(pair)
144-
}
145-
)
146225
}
147226

148227
@Composable
@@ -152,7 +231,6 @@ internal fun ColorStopSelection(
152231
onRemoveClick: (Int) -> Unit,
153232
onAddColorStop: (Pair<Float, Color>) -> Unit,
154233
onValueChange: (Int, Pair<Float, Color>) -> Unit
155-
156234
) {
157235

158236
ExpandableColumn(title = "Colors and Stops", color = Orange400) {

colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/gradient/SelectorGradientLinear.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,19 @@ import com.smarttoolfactory.slider.ui.ThumbColor
2020
import kotlin.math.roundToInt
2121

2222
@Composable
23-
internal fun GradientOffsetSelection(
23+
internal fun LinearGradientSelection(
24+
size: Size,
25+
onGradientOffsetChange: (GradientOffset) -> Unit
26+
) {
27+
28+
GradientOffsetSelection(
29+
size = size,
30+
onGradientOffsetChange = onGradientOffsetChange
31+
)
32+
}
33+
34+
@Composable
35+
private fun GradientOffsetSelection(
2436
size: Size,
2537
onGradientOffsetChange: (GradientOffset) -> Unit
2638
) {
Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,56 @@
11
package com.smarttoolfactory.colorpicker.selector.gradient
22

3-
import androidx.compose.runtime.Composable
3+
import androidx.compose.foundation.layout.fillMaxWidth
4+
import androidx.compose.runtime.*
5+
import androidx.compose.ui.Modifier
6+
import androidx.compose.ui.geometry.Offset
7+
import androidx.compose.ui.geometry.Size
8+
import com.smarttoolfactory.colorpicker.ui.GradientOffset
49

510
@Composable
6-
private fun RadialGradientSelection() {
11+
internal fun RadialGradientSelection(
12+
size: Size,
13+
onGradientOffsetChange: (GradientOffset) -> Unit
14+
) {
715

16+
var centerX by remember { mutableStateOf(.5f) }
17+
var centerY by remember { mutableStateOf(.5f) }
18+
var radius by remember { mutableStateOf(.5f) }
19+
20+
onGradientOffsetChange(
21+
GradientOffset(
22+
start = Offset(
23+
x = size.width * centerX,
24+
y = size.height * centerY
25+
),
26+
end = Offset(
27+
x = size.width * centerX,
28+
y = size.height * centerY
29+
)
30+
)
31+
)
32+
33+
SliderWithPercent(
34+
modifier = Modifier.fillMaxWidth(),
35+
title = "CenterX",
36+
value = centerX
37+
) {
38+
centerX = it
39+
}
40+
41+
SliderWithPercent(
42+
modifier = Modifier.fillMaxWidth(),
43+
title = "CenterY",
44+
value = centerY
45+
) {
46+
centerY = it
47+
}
48+
49+
SliderWithPercent(
50+
modifier = Modifier.fillMaxWidth(),
51+
title = "Radius",
52+
value = radius
53+
) {
54+
radius = it
55+
}
856
}

0 commit comments

Comments
 (0)