Skip to content

Commit f7c427a

Browse files
add primary/accent selection to M3ColorPicker demo
1 parent e35d150 commit f7c427a

File tree

3 files changed

+192
-91
lines changed

3 files changed

+192
-91
lines changed

app/src/main/java/com/smarttoolfactory/composecolorsextended/M3ColorPicker.kt

Lines changed: 190 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ import androidx.compose.foundation.lazy.grid.GridCells
88
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
99
import androidx.compose.foundation.lazy.grid.itemsIndexed
1010
import androidx.compose.foundation.shape.RoundedCornerShape
11-
import androidx.compose.material.Icon
12-
import androidx.compose.material.IconButton
13-
import androidx.compose.material.Text
11+
import androidx.compose.material.*
1412
import androidx.compose.material.icons.Icons
1513
import androidx.compose.material.icons.filled.Check
1614
import androidx.compose.runtime.*
@@ -27,7 +25,6 @@ import androidx.compose.ui.text.style.TextAlign
2725
import androidx.compose.ui.unit.dp
2826
import androidx.compose.ui.unit.sp
2927
import com.smarttoolfactory.extendedcolors.ColorSwatch
30-
import com.smarttoolfactory.extendedcolors.MaterialColor
3128
import com.smarttoolfactory.extendedcolors.parser.rememberColorParser
3229
import com.smarttoolfactory.extendedcolors.util.colorToHSL
3330
import com.smarttoolfactory.extendedcolors.util.colorToHex
@@ -38,6 +35,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
3835
import kotlinx.coroutines.flow.flowOn
3936
import kotlinx.coroutines.flow.mapLatest
4037

38+
4139
@Composable
4240
fun M3ColorPicker(onColorChange: (Color) -> Unit) {
4341

@@ -53,24 +51,44 @@ fun M3ColorPicker(onColorChange: (Color) -> Unit) {
5351

5452
val colorNameParser = rememberColorParser()
5553

56-
var colorSwatchIndex by remember { mutableStateOf(0) }
57-
var color by remember { mutableStateOf(MaterialColor.Red500) }
58-
var md3Tones by remember { mutableStateOf(getColorTonesList(color)) }
54+
// Show primary or accent colors
55+
var primaryAccentSelection by remember {
56+
mutableStateOf(0)
57+
}
5958

59+
// Index of selected list as main and index of color in that list
6060
val colorSelectionIndex =
6161
remember { ColorSelectionIndex(mainSelection = 0, subSelection = 0) }
6262

63+
// This is the selected color swatch in top grid which is swatch/list of colors
64+
var colorSwatchIndex by remember { mutableStateOf(0) }
65+
6366
val colorSwatch: LinkedHashMap<Int, Color> =
64-
remember(colorSwatchIndex) { ColorSwatch.primaryColorSwatches[colorSwatchIndex] }
67+
remember(
68+
colorSwatchIndex,
69+
primaryAccentSelection
70+
) {
71+
if (primaryAccentSelection == 0)
72+
ColorSwatch.primaryColorSwatches[colorSwatchIndex] else
73+
ColorSwatch.accentColorSwatches[colorSwatchIndex]
74+
}
6575

66-
val keys: MutableList<Int> = colorSwatch.keys.toMutableList()
67-
val colors: MutableList<Color> = colorSwatch.values.toMutableList()
76+
// Get keys and colors for second grid to display Material Design 2 keys
77+
// and shades like 100, 200, 900
78+
val m2ColorIndices: List<Int> = colorSwatch.keys.toList()
79+
val m2ColorList: List<Color> = colorSwatch.values.toList()
6880

81+
// Current color picked by user or initial color as Red500
82+
var currentColor by remember { mutableStateOf(ColorSwatch.primaryHeaderColors.first()) }
83+
// Always get Material Design3 tonal palette from color picked recently from
84+
// first or second grid
85+
var m3Tones by remember { mutableStateOf(getColorTonesList(currentColor)) }
86+
// Name of the color that is currently picked
6987
var colorName by remember { mutableStateOf("") }
7088

7189
LaunchedEffect(key1 = colorNameParser) {
7290

73-
snapshotFlow { color }
91+
snapshotFlow { currentColor }
7492
.distinctUntilChanged()
7593
.mapLatest { color: Color ->
7694
colorNameParser.parseColorName(color)
@@ -81,116 +99,120 @@ fun M3ColorPicker(onColorChange: (Color) -> Unit) {
8199
}
82100
}
83101

84-
LazyVerticalGrid(
85-
columns = GridCells.Fixed(6),
102+
Spacer(modifier = Modifier.height(20.dp))
103+
PrimaryAccentSelectionTab(
104+
modifier = Modifier.width(300.dp),
105+
selectedIndex = primaryAccentSelection
106+
) {
107+
primaryAccentSelection = it
108+
colorSelectionIndex.mainSelection = 0
109+
colorSelectionIndex.subSelection = 0
110+
colorSwatchIndex = 0
111+
currentColor = ColorSwatch.primaryHeaderColors.first()
112+
}
113+
114+
Spacer(modifier = Modifier.height(10.dp))
115+
116+
// Color Swatch Selection
117+
ColorSelectionGrid(
118+
columns = GridCells.Fixed(8),
86119
contentPadding = PaddingValues(8.dp),
87120
verticalArrangement = Arrangement.spacedBy(4.dp),
88121
horizontalArrangement = Arrangement.spacedBy(4.dp),
89-
) {
90-
itemsIndexed(ColorSwatch.primaryHeaderColors) { index: Int, item: Color ->
91-
92-
ColorDisplayWithIcon(
93-
modifier = Modifier
94-
.clip(RoundedCornerShape(8.dp))
95-
.aspectRatio(1f)
96-
.clickable {
97-
color = item
98-
onColorChange(item)
99-
colorSwatchIndex = index
100-
colorSelectionIndex.mainSelection = 0
101-
colorSelectionIndex.subSelection = index
102-
md3Tones = getColorTonesList(item)
103-
},
104-
selected = (colorSelectionIndex.mainSelection == 0 &&
105-
colorSelectionIndex.subSelection == index) || (
106-
colorSelectionIndex.mainSelection == 1 &&
107-
colorSelectionIndex.subSelection == 5 &&
108-
index == colorSwatchIndex
109-
),
110-
backgroundColor = item
111-
)
112-
}
122+
colorSelectionList = if (primaryAccentSelection == 0)
123+
ColorSwatch.primaryHeaderColors else ColorSwatch.accentHeaderColors,
124+
selected = { index ->
125+
(colorSelectionIndex.mainSelection == 0 &&
126+
colorSelectionIndex.subSelection == index) || (
127+
primaryAccentSelection == 0 && colorSelectionIndex.mainSelection == 1 &&
128+
colorSelectionIndex.subSelection == 5 &&
129+
index == colorSwatchIndex
130+
)
131+
},
132+
tint = { Color.Unspecified },
133+
) { index: Int, item: Color ->
134+
currentColor = item
135+
onColorChange(item)
136+
colorSelectionIndex.mainSelection = 0
137+
colorSelectionIndex.subSelection = index
138+
colorSwatchIndex = index
139+
m3Tones = getColorTonesList(item)
113140
}
114141

115142
Text(
116143
text = "Material Design2 Shade",
117144
modifier = Modifier
118145
.fillMaxWidth()
119-
.padding(2.dp),
146+
.padding(5.dp),
120147
textAlign = TextAlign.Center,
121148
fontSize = 16.sp,
122149
fontWeight = FontWeight.Bold,
123150
color = Color.White
124151
)
125152

126-
LazyVerticalGrid(
127-
columns = GridCells.Fixed(8),
153+
// Primary/Accent Color Selection
154+
ColorSelectionGrid(
155+
modifier = Modifier.fillMaxWidth(if (primaryAccentSelection == 0) 1f else .5f),
156+
columns = GridCells.Fixed(if (primaryAccentSelection == 0) 8 else 4),
128157
contentPadding = PaddingValues(8.dp),
129158
verticalArrangement = Arrangement.spacedBy(4.dp),
130159
horizontalArrangement = Arrangement.spacedBy(4.dp),
131-
) {
132-
itemsIndexed(colors) { index: Int, item: Color ->
133-
ColorDisplayWithTitle(
134-
modifier = Modifier
135-
.clip(RoundedCornerShape(8.dp))
136-
.aspectRatio(1f)
137-
.clickable {
138-
color = item
139-
colorSelectionIndex.mainSelection = 1
140-
colorSelectionIndex.subSelection = index
141-
onColorChange(item)
142-
md3Tones = getColorTonesList(item)
143-
},
144-
selected = (colorSelectionIndex.mainSelection == 0 && index == 5)
145-
|| (colorSelectionIndex.mainSelection == 1
146-
&& colorSelectionIndex.subSelection == index),
147-
backgroundColor = item,
148-
contentColor = if (index < 5) Color.Black else Color.White,
149-
title = keys[index].toString(),
150-
)
151-
}
160+
colorSelectionList = m2ColorList,
161+
colorKeys = m2ColorIndices,
162+
selected = { index ->
163+
(primaryAccentSelection == 0 && colorSelectionIndex.mainSelection == 0 && index == 5)
164+
|| (colorSelectionIndex.mainSelection == 1
165+
&& colorSelectionIndex.subSelection == index)
166+
},
167+
tint = { index ->
168+
if (index < 5) Color.Black else Color.White
169+
},
170+
171+
) { index: Int, item: Color ->
172+
currentColor = item
173+
colorSelectionIndex.mainSelection = 1
174+
colorSelectionIndex.subSelection = index
175+
onColorChange(item)
176+
m3Tones = getColorTonesList(item)
152177
}
153178

154179
Text(
155180
text = "Material Design3 Tonal Palette",
156181
modifier = Modifier
157182
.fillMaxWidth()
158-
.padding(2.dp),
183+
.padding(5.dp),
159184
textAlign = TextAlign.Center,
160185
fontSize = 16.sp,
161186
fontWeight = FontWeight.Bold,
162187
color = Color.White
163188
)
164189

165-
LazyVerticalGrid(
190+
191+
// M3 Tone Selection
192+
ColorSelectionGrid(
166193
columns = GridCells.Fixed(8),
167194
contentPadding = PaddingValues(8.dp),
168195
verticalArrangement = Arrangement.spacedBy(4.dp),
169196
horizontalArrangement = Arrangement.spacedBy(4.dp),
170-
) {
171-
itemsIndexed(md3Tones) { index: Int, item: Color ->
172-
ColorDisplayWithTitle(
173-
modifier = Modifier
174-
.aspectRatio(1f)
175-
.clip(RoundedCornerShape(8.dp))
176-
.aspectRatio(1f)
177-
.clickable {
178-
colorSelectionIndex.mainSelection = 2
179-
colorSelectionIndex.subSelection = index
180-
color = item
181-
onColorChange(item)
182-
},
183-
backgroundColor = item,
184-
selected = (colorSelectionIndex.mainSelection == 2
185-
&& colorSelectionIndex.subSelection == index),
186-
contentColor = if (index < 6) Color.White else Color.Black,
187-
title = material3ToneRange[index].toString()
188-
)
189-
}
197+
colorSelectionList = m3Tones,
198+
colorKeys = material3ToneRange,
199+
selected = { index ->
200+
(colorSelectionIndex.mainSelection == 2
201+
&& colorSelectionIndex.subSelection == index)
202+
},
203+
tint = { index ->
204+
if (index < 6) Color.White else Color.Black
205+
},
206+
) { index: Int, item: Color ->
207+
colorSelectionIndex.mainSelection = 2
208+
colorSelectionIndex.subSelection = index
209+
currentColor = item
210+
onColorChange(item)
190211
}
191212

213+
192214
Spacer(modifier = Modifier.height(30.dp))
193-
val lightness = colorToHSL(color)[2]
215+
val lightness = colorToHSL(currentColor)[2]
194216
val textColor = if (lightness < .6f) Color.White else Color.Black
195217

196218

@@ -209,12 +231,12 @@ fun M3ColorPicker(onColorChange: (Color) -> Unit) {
209231
Row(
210232
modifier = Modifier
211233
.padding(8.dp)
212-
.background(color = color, RoundedCornerShape(50))
234+
.background(color = currentColor, RoundedCornerShape(50))
213235
.padding(horizontal = 20.dp, vertical = 10.dp),
214236
verticalAlignment = Alignment.CenterVertically
215237
) {
216238

217-
val hexText = colorToHex(color = color)
239+
val hexText = colorToHex(color = currentColor)
218240
Text(
219241
text = hexText,
220242
fontSize = 24.sp,
@@ -232,16 +254,93 @@ fun M3ColorPicker(onColorChange: (Color) -> Unit) {
232254
)
233255
}
234256
}
257+
}
258+
}
259+
260+
@Composable
261+
fun ColorSelectionGrid(
262+
modifier: Modifier = Modifier,
263+
columns: GridCells = GridCells.Fixed(8),
264+
contentPadding: PaddingValues = PaddingValues(8.dp),
265+
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(4.dp),
266+
horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(4.dp),
267+
colorSelectionList: List<Color>,
268+
colorKeys: List<Int>? = null,
269+
selected: (Int) -> Boolean,
270+
tint: (Int) -> Color,
271+
onClick: (Int, Color) -> Unit,
272+
) {
273+
LazyVerticalGrid(
274+
modifier = modifier,
275+
columns = columns,
276+
contentPadding = contentPadding,
277+
verticalArrangement = verticalArrangement,
278+
horizontalArrangement = horizontalArrangement,
279+
) {
280+
itemsIndexed(colorSelectionList) { index: Int, item: Color ->
281+
282+
ColorDisplayWithTitle(
283+
modifier = Modifier
284+
.aspectRatio(1f)
285+
.clip(RoundedCornerShape(8.dp))
286+
.aspectRatio(1f)
287+
.clickable {
288+
onClick(index, item)
289+
},
290+
backgroundColor = item,
291+
selected = selected(index),
292+
tint = tint(index),
293+
title = colorKeys?.get(index)?.toString() ?: ""
294+
)
295+
}
296+
}
297+
}
235298

299+
@Composable
300+
fun PrimaryAccentSelectionTab(
301+
modifier: Modifier = Modifier,
302+
selectedIndex: Int,
303+
onTabChange: (Int) -> Unit
304+
) {
305+
306+
val list = listOf("Primary", "Accent")
307+
308+
TabRow(selectedTabIndex = selectedIndex,
309+
backgroundColor = Color.DarkGray,
310+
modifier = modifier.clip(RoundedCornerShape(10.dp)),
311+
indicator = {}
312+
) {
313+
list.forEachIndexed { index, text ->
314+
val selected = selectedIndex == index
315+
Tab(
316+
modifier = if (selected) Modifier
317+
.clip(RoundedCornerShape(10.dp))
318+
.background(Color(0xff6FAAEE))
319+
else Modifier
320+
.clip(RoundedCornerShape(10.dp))
321+
.background(Color.DarkGray),
322+
selected = selected,
323+
onClick = {
324+
onTabChange(index)
325+
},
326+
text = {
327+
Text(
328+
text = text, color = if (selected) Color.White
329+
else Color.White.copy(.5f)
330+
)
331+
}
332+
)
333+
}
236334
}
237335
}
238336

337+
239338
@Composable
240339
fun ColorDisplayWithTitle(
241340
modifier: Modifier,
242341
title: String = "",
243342
selected: Boolean,
244-
contentColor: Color = Color.Unspecified,
343+
tint: Color = Color.Unspecified,
245344
backgroundColor: Color
246345
) {
247346
Box(
@@ -252,12 +351,14 @@ fun ColorDisplayWithTitle(
252351
.background(backgroundColor)
253352
)
254353

255-
Text(text = title, color = contentColor, fontSize = 16.sp)
354+
if (title.isNotEmpty()) {
355+
Text(text = title, color = tint, fontSize = 16.sp)
356+
}
256357

257358
if (selected) {
258359
Icon(
259360
modifier = modifier
260-
.background(contentColor.copy(alpha = .5f))
361+
.background(tint.copy(alpha = .5f))
261362
.padding(4.dp),
262363
imageVector = Icons.Default.Check,
263364
contentDescription = "check",

0 commit comments

Comments
 (0)