Skip to content

Commit dc3664a

Browse files
add Color picker with gradient option
1 parent 44c6dd1 commit dc3664a

File tree

16 files changed

+360
-59
lines changed

16 files changed

+360
-59
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Bundle of Stylish customizable Color pickers, selectors, colorful sliders written with Jetpack
44
Compose enables users to choose from HSL, HSV or RGB color models to pick Solid colors or gradients.
55

6-
Inspired by mchome's (flutter_colorpicker)[https://github.com/mchome/flutter_colorpicker] for Flutter
6+
Inspired by [mchome's flutter_colorpicker for Flutter](https://github.com/mchome/flutter_colorpicker)
77

88
### Color Pickers
99

app/src/main/java/com/smarttoolfactory/composecolorpicker/demo/ColorPickerDemo.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ fun ColorPickerDemo() {
5858
}
5959
)
6060

61+
DialogRingGradientHSL(
62+
modifier = buttonModifier,
63+
color = color,
64+
onPreviousColorChange = {
65+
previousColor = it
66+
},
67+
onCurrentColorChange = {
68+
color = it
69+
}
70+
)
71+
6172
// Ring Hue, Rect Saturation-Value selector HSV Picker
6273
DialogRingHSV(
6374
modifier = buttonModifier,
@@ -182,6 +193,37 @@ private fun DialogRingHSL(
182193
}
183194
}
184195

196+
@Composable
197+
private fun DialogRingGradientHSL(
198+
modifier: Modifier,
199+
color: Color,
200+
onPreviousColorChange: (Color) -> Unit,
201+
onCurrentColorChange: (Color) -> Unit,
202+
203+
) {
204+
var showDialog by remember { mutableStateOf(false) }
205+
206+
OutlinedButton(
207+
modifier = modifier,
208+
onClick = { showDialog = !showDialog },
209+
colors = ButtonDefaults.outlinedButtonColors(
210+
backgroundColor = Color.Transparent
211+
)
212+
213+
) {
214+
Text(text = "Hue Ring-Diamond HSL Dialog")
215+
}
216+
217+
if (showDialog) {
218+
onPreviousColorChange(color.copy())
219+
220+
ColorPickerRingDiamondGradientHSLDialog(color) {
221+
showDialog = !showDialog
222+
// onCurrentColorChange(it)
223+
}
224+
}
225+
}
226+
185227
@Composable
186228
private fun DialogRingHSV(
187229
modifier: Modifier,

app/src/main/java/com/smarttoolfactory/composecolorpicker/demo/GradientSelectionDemo.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package com.smarttoolfactory.composecolorpicker.demo
22

3-
import androidx.compose.foundation.background
4-
import androidx.compose.foundation.layout.*
3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.fillMaxSize
5+
import androidx.compose.foundation.layout.padding
6+
import androidx.compose.foundation.layout.size
57
import androidx.compose.foundation.rememberScrollState
6-
import androidx.compose.foundation.shape.RoundedCornerShape
78
import androidx.compose.foundation.verticalScroll
89
import androidx.compose.material.Text
910
import androidx.compose.runtime.*
@@ -20,7 +21,6 @@ import com.smarttoolfactory.colorpicker.selector.gradient.BrushDisplay
2021
import com.smarttoolfactory.colorpicker.selector.gradient.GradientSelector
2122
import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplayHueHSL
2223
import com.smarttoolfactory.colorpicker.ui.Blue400
23-
import com.smarttoolfactory.colorpicker.widget.drawChecker
2424

2525

2626
/**
@@ -89,7 +89,11 @@ fun GradientSelectionDemo() {
8989
// .background(gradientColor.brushColor.activeBrush),
9090
// )
9191
BrushDisplay(gradientColor = gradientColor)
92-
GradientSelector(currentColor, gradientColor)
92+
GradientSelector(
93+
modifier = Modifier.padding(horizontal = 8.dp),
94+
color = currentColor,
95+
gradientColor = gradientColor
96+
)
9397
}
9498
}
9599
}

colorpicker/src/main/java/com/smarttoolfactory/colorpicker/dialog/ColorPickerDialog.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,61 @@ import androidx.compose.ui.graphics.Shape
1818
import androidx.compose.ui.unit.Dp
1919
import androidx.compose.ui.unit.dp
2020
import androidx.compose.ui.window.Dialog
21+
import com.smarttoolfactory.colorpicker.model.BrushColor
2122
import com.smarttoolfactory.colorpicker.picker.*
2223
import com.smarttoolfactory.colorpicker.ui.Blue400
2324

25+
@Composable
26+
fun ColorPickerRingDiamondGradientHSLDialog(
27+
initialColor: Color,
28+
ringOuterRadiusFraction: Float = .9f,
29+
ringInnerRadiusFraction: Float = .6f,
30+
ringBackgroundColor: Color = Color.Transparent,
31+
ringBorderStrokeColor: Color = Color.Black,
32+
ringBorderStrokeWidth: Dp = 4.dp,
33+
selectionRadius: Dp = 8.dp,
34+
onDismiss: (BrushColor) -> Unit
35+
) {
36+
37+
var color by remember { mutableStateOf(BrushColor(color = initialColor.copy())) }
38+
Dialog(
39+
onDismissRequest = {
40+
onDismiss(color)
41+
}
42+
) {
43+
Column(horizontalAlignment = Alignment.CenterHorizontally) {
44+
45+
ColorPickerRingDiamondGradientHSL(
46+
modifier = Modifier
47+
.fillMaxWidth()
48+
.weight(1f)
49+
.background(Color(0xcc212121), shape = RoundedCornerShape(5.dp))
50+
.padding(horizontal = 10.dp, vertical = 2.dp),
51+
initialColor = initialColor,
52+
ringOuterRadiusFraction = ringOuterRadiusFraction,
53+
ringInnerRadiusFraction = ringInnerRadiusFraction,
54+
ringBackgroundColor = ringBackgroundColor,
55+
ringBorderStrokeColor = ringBorderStrokeColor,
56+
ringBorderStrokeWidth = ringBorderStrokeWidth,
57+
selectionRadius = selectionRadius
58+
) {
59+
color = it
60+
}
61+
62+
FloatingActionButton(
63+
onClick = { onDismiss(color) },
64+
backgroundColor = Color.Black
65+
) {
66+
Icon(
67+
imageVector = Icons.Filled.Close,
68+
contentDescription = null,
69+
tint = Blue400
70+
)
71+
}
72+
}
73+
}
74+
}
75+
2476
@Composable
2577
fun ColorPickerRingDiamondHSLDialog(
2678
initialColor: Color,

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ class GradientColor(size: Size) {
3939
var size by mutableStateOf(size)
4040
val brushColor: BrushColor
4141
get() {
42-
val colorStops = colorStops.toTypedArray()
42+
43+
val colorStops = if (colorStops.size == 1) {
44+
listOf(colorStops.first(), colorStops.first()).toTypedArray()
45+
} else {
46+
colorStops.toTypedArray()
47+
}
48+
4349
val brush = when (gradientType) {
4450
GradientType.Linear -> Brush.linearGradient(
4551
colorStops = colorStops,
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package com.smarttoolfactory.colorpicker.picker
2+
3+
import androidx.compose.foundation.clickable
4+
import androidx.compose.foundation.layout.*
5+
import androidx.compose.foundation.rememberScrollState
6+
import androidx.compose.foundation.verticalScroll
7+
import androidx.compose.runtime.*
8+
import androidx.compose.ui.Alignment
9+
import androidx.compose.ui.Modifier
10+
import androidx.compose.ui.graphics.Color
11+
import androidx.compose.ui.unit.Dp
12+
import androidx.compose.ui.unit.dp
13+
import com.smarttoolfactory.colorpicker.model.BrushColor
14+
import com.smarttoolfactory.colorpicker.model.ColorHSL
15+
import com.smarttoolfactory.colorpicker.model.ColorModel
16+
import com.smarttoolfactory.colorpicker.model.rememberGradientColor
17+
import com.smarttoolfactory.colorpicker.selector.SelectorDiamondSaturationLightnessHSL
18+
import com.smarttoolfactory.colorpicker.selector.SelectorRingHue
19+
import com.smarttoolfactory.colorpicker.selector.gradient.BrushDisplay
20+
import com.smarttoolfactory.colorpicker.selector.gradient.GradientSelector
21+
import com.smarttoolfactory.colorpicker.slider.CompositeSliderPanel
22+
import com.smarttoolfactory.colorpicker.util.colorToHSL
23+
import com.smarttoolfactory.colorpicker.widget.ColorDisplayRoundedRect
24+
import com.smarttoolfactory.colorpicker.widget.ColorModelChangeTabRow
25+
import com.smarttoolfactory.colorpicker.widget.ColorWheel
26+
27+
/**
28+
* ColorPicker with [SelectorRingHue] hue selector and [SelectorDiamondSaturationLightnessHSL]
29+
* saturation lightness Selector uses [HSL](https://en.wikipedia.org/wiki/HSL_and_HSV)
30+
* color model as base.
31+
*
32+
* This color picker has tabs section that can be changed between
33+
* HSL, HSV and RGB color models and color can be set using [CompositeSliderPanel] which contains
34+
* sliders for each color models.
35+
*
36+
* @param initialColor color that is passed to this picker initially.
37+
* @param ringOuterRadiusFraction outer radius of [SelectorRingHue].
38+
* @param ringInnerRadiusFraction inner radius of [SelectorRingHue].
39+
* @param ringBackgroundColor background from center to inner radius of [SelectorRingHue].
40+
* @param ringBorderStrokeColor stroke color for drawing borders around inner or outer radius.
41+
* @param ringBorderStrokeWidth stroke width of borders.
42+
* @param selectionRadius radius of white and black circle selector.
43+
* @param onColorChange callback that is triggered when [Color] is changed using [SelectorRingHue],
44+
* [SelectorDiamondSaturationLightnessHSL] or [CompositeSliderPanel]
45+
*/
46+
@Composable
47+
fun ColorPickerRingDiamondGradientHSL(
48+
modifier: Modifier = Modifier,
49+
initialColor: Color,
50+
ringOuterRadiusFraction: Float = .9f,
51+
ringInnerRadiusFraction: Float = .6f,
52+
ringBackgroundColor: Color = Color.Black,
53+
ringBorderStrokeColor: Color = Color.Black,
54+
ringBorderStrokeWidth: Dp = 4.dp,
55+
selectionRadius: Dp = 8.dp,
56+
onColorChange: (BrushColor) -> Unit
57+
) {
58+
59+
val gradientColor = rememberGradientColor()
60+
var isGradientMode by remember { mutableStateOf(false) }
61+
62+
var inputColorModel by remember { mutableStateOf(ColorModel.HSL) }
63+
64+
val hslArray = colorToHSL(initialColor)
65+
66+
var hue by remember { mutableStateOf(hslArray[0]) }
67+
var saturation by remember { mutableStateOf(hslArray[1]) }
68+
var lightness by remember { mutableStateOf(hslArray[2]) }
69+
var alpha by remember { mutableStateOf(initialColor.alpha) }
70+
71+
val currentColor =
72+
Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = alpha)
73+
74+
gradientColor.brushColor.color = currentColor
75+
onColorChange(gradientColor.brushColor)
76+
77+
Column(
78+
modifier = modifier,
79+
horizontalAlignment = Alignment.CenterHorizontally,
80+
) {
81+
82+
Spacer(modifier = Modifier.height(10.dp))
83+
84+
// Initial and Current Colors
85+
Box(modifier=Modifier.height(80.dp), contentAlignment = Alignment.Center) {
86+
if (isGradientMode) {
87+
BrushDisplay(gradientColor = gradientColor)
88+
} else {
89+
ColorDisplayRoundedRect(
90+
modifier = Modifier
91+
.fillMaxWidth()
92+
.padding(horizontal = 50.dp, vertical = 10.dp),
93+
initialColor = initialColor,
94+
currentColor = currentColor
95+
)
96+
}
97+
}
98+
99+
Box(contentAlignment = Alignment.Center) {
100+
101+
// Ring Shaped Hue Selector
102+
SelectorRingHue(
103+
modifier = Modifier.fillMaxWidth(.9f),
104+
hue = hue,
105+
outerRadiusFraction = ringOuterRadiusFraction,
106+
innerRadiusFraction = ringInnerRadiusFraction,
107+
backgroundColor = ringBackgroundColor,
108+
borderStrokeColor = ringBorderStrokeColor,
109+
borderStrokeWidth = ringBorderStrokeWidth,
110+
selectionRadius = selectionRadius
111+
) { hueChange ->
112+
hue = hueChange
113+
}
114+
115+
// Diamond Shaped Saturation and Lightness Selector
116+
SelectorDiamondSaturationLightnessHSL(
117+
modifier = Modifier.fillMaxWidth(ringInnerRadiusFraction * .8f),
118+
hue = hue,
119+
saturation = saturation,
120+
lightness = lightness,
121+
selectionRadius = selectionRadius
122+
) { s, l ->
123+
saturation = s
124+
lightness = l
125+
}
126+
}
127+
128+
// HSL-HSV-RGB Color Model Change Tabs
129+
Row(
130+
modifier = Modifier.fillMaxWidth(),
131+
verticalAlignment = Alignment.CenterVertically
132+
) {
133+
ColorModelChangeTabRow(
134+
modifier = Modifier.weight(3f),
135+
colorModel = inputColorModel,
136+
onColorModelChange = {
137+
isGradientMode = false
138+
inputColorModel = it
139+
}
140+
)
141+
142+
Column(modifier = Modifier.weight(1f)) {
143+
ColorWheel(modifier = Modifier
144+
.size(16.dp)
145+
.clickable {
146+
isGradientMode = true
147+
})
148+
}
149+
}
150+
151+
// HSL-HSV-RGB Sliders
152+
if (!isGradientMode) {
153+
CompositeSliderPanel(
154+
compositeColor = ColorHSL(
155+
hue = hue,
156+
saturation = saturation,
157+
lightness = lightness,
158+
alpha = alpha
159+
),
160+
onColorChange = {
161+
(it as? ColorHSL)?.let { color ->
162+
hue = color.hue
163+
saturation = color.saturation
164+
lightness = color.lightness
165+
alpha = color.alpha
166+
}
167+
},
168+
showAlphaSlider = true,
169+
inputColorModel = inputColorModel,
170+
outputColorModel = ColorModel.HSL
171+
)
172+
173+
} else {
174+
Column(
175+
modifier = Modifier
176+
.fillMaxHeight()
177+
.verticalScroll(rememberScrollState())
178+
) {
179+
GradientSelector(color = currentColor, gradientColor = gradientColor)
180+
}
181+
}
182+
}
183+
}

colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/SelectorRingHue.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import androidx.compose.ui.platform.LocalDensity
1717
import androidx.compose.ui.unit.Dp
1818
import androidx.compose.ui.unit.dp
1919
import com.smarttoolfactory.colorpicker.ui.gradientColorScaleHSV
20+
import com.smarttoolfactory.colorpicker.ui.gradientColorScaleHSVReversed
2021
import com.smarttoolfactory.colorpicker.util.calculateAngleFomLocalCoordinates
2122
import com.smarttoolfactory.colorpicker.util.calculateDistanceFromCenter
2223
import com.smarttoolfactory.colorpicker.ui.gradientColorScaleRGB
@@ -149,7 +150,7 @@ private fun HueSelectorRingImpl(
149150

150151
// Draw hue selection circle with sweep gradient
151152
drawCircle(
152-
brush = Brush.sweepGradient(colors = gradientColorScaleHSV, center = center),
153+
brush = Brush.sweepGradient(colors = gradientColorScaleHSVReversed, center = center),
153154
radius = radiusInner + strokeWidth / 2,
154155
style = Stroke(
155156
width = strokeWidth

0 commit comments

Comments
 (0)