├── .gitignore
├── LICENSE.md
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── composecolorpicker
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── composecolorpicker
│ │ │ ├── MainActivity.kt
│ │ │ ├── demo
│ │ │ ├── ColorAndGradientPickerDemo.kt
│ │ │ ├── ColorPickerDemo.kt
│ │ │ ├── ColorfulSliderDemo.kt
│ │ │ ├── DoughnutDemo.kt
│ │ │ ├── GradientSelectionDemo.kt
│ │ │ ├── HSVHSLGradientDemo.kt
│ │ │ ├── HexConversionDemo.kt
│ │ │ └── SaturationSelectorDemo.kt
│ │ │ └── ui
│ │ │ ├── CanvasWithTitle.kt
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Shape.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── smarttoolfactory
│ └── composecolorpicker
│ └── ExampleUnitTest.kt
├── build.gradle
├── colorpicker
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── colorpicker
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── colorpicker
│ │ │ ├── dialog
│ │ │ ├── ColorPickerDialog.kt
│ │ │ └── GradientColorPickerDialog.kt
│ │ │ ├── model
│ │ │ ├── BrushColor.kt
│ │ │ ├── ColorModel.kt
│ │ │ ├── CompositeColor.kt
│ │ │ └── GradientColorState.kt
│ │ │ ├── picker
│ │ │ ├── ColorPickerCircleValueHSV.kt
│ │ │ ├── ColorPickerRectHueLightnessHSL.kt
│ │ │ ├── ColorPickerRectHueSaturationHSL.kt
│ │ │ ├── ColorPickerRectHueSaturationHSV.kt
│ │ │ ├── ColorPickerRectHueValueHSV.kt
│ │ │ ├── ColorPickerRectSaturationLightnessHSL.kt
│ │ │ ├── ColorPickerRectSaturationValueHSV.kt
│ │ │ ├── ColorPickerRingDiamondHEX.kt
│ │ │ ├── ColorPickerRingDiamondHSL.kt
│ │ │ ├── ColorPickerRingHEX.kt
│ │ │ ├── ColorPickerRingRectHSL.kt
│ │ │ ├── ColorPickerRingRectHSV.kt
│ │ │ ├── M2ColorPicker.kt
│ │ │ ├── M3ColorPicker.kt
│ │ │ └── gradient
│ │ │ │ ├── ColorPickerGradientRingDimondHSL.kt
│ │ │ │ ├── ColorPickerGradientRingRectHSL.kt
│ │ │ │ └── ColorPickerGradientRingRectHSV.kt
│ │ │ ├── selector
│ │ │ ├── DrawColorSelectionCircle.kt
│ │ │ ├── SelectorCircleHueSaturation.kt
│ │ │ ├── SelectorDiamondSaturationLightness.kt
│ │ │ ├── SelectorRectHueLightnessValue.kt
│ │ │ ├── SelectorRectSaturationLightnessValue.kt
│ │ │ ├── SelectorRingHue.kt
│ │ │ └── gradient
│ │ │ │ ├── SelectorGradient.kt
│ │ │ │ ├── SelectorGradientLinear.kt
│ │ │ │ ├── SelectorGradientRadial.kt
│ │ │ │ ├── SelectorGradientSweep.kt
│ │ │ │ └── SliderWithPercent.kt
│ │ │ ├── slider
│ │ │ ├── CompositeSliderPanel.kt
│ │ │ ├── Slider.kt
│ │ │ ├── SliderDisplay.kt
│ │ │ ├── SliderDisplayPanel.kt
│ │ │ ├── SliderPanel.kt
│ │ │ └── SliderWithCircleDisplay.kt
│ │ │ ├── ui
│ │ │ ├── Color.kt
│ │ │ ├── GradientColors.kt
│ │ │ ├── GradientOffset.kt
│ │ │ └── brush
│ │ │ │ ├── Brush.kt
│ │ │ │ ├── HSLSHVBrush.kt
│ │ │ │ └── SliderBrush.kt
│ │ │ ├── util
│ │ │ ├── DimensionUtil.kt
│ │ │ ├── DrawUtil.kt
│ │ │ ├── HexRegex.kt
│ │ │ └── HexVisualTransformation.kt
│ │ │ └── widget
│ │ │ ├── Checker.kt
│ │ │ ├── ColorDisplayExposedSelectionMenu.kt
│ │ │ ├── ColorDisplayRoundedRect.kt
│ │ │ ├── ColorDisplayWithClipboard.kt
│ │ │ ├── ColorModelChangeTabRow.kt
│ │ │ ├── ColorWheel.kt
│ │ │ ├── ExpandableColumn.kt
│ │ │ ├── ExposedSelectionMenu.kt
│ │ │ ├── HexTextField.kt
│ │ │ ├── HexTextFieldWithCircleDisplay.kt
│ │ │ └── HexTextFieldWithClipboard.kt
│ └── res
│ │ └── drawable
│ │ └── ic_baseline_content_copy_24.xml
│ └── test
│ └── java
│ └── com
│ └── smarttoolfactory
│ └── colorpicker
│ └── ExampleUnitTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── screenshots
├── colorful_sliders.gif
├── colorpicker
│ ├── cp_circle_hue_saturation_hsv.png
│ ├── cp_gradient_diamond_hsl.png
│ ├── cp_gradient_diamond_hsl2.png
│ ├── cp_gradient_rect_hsl.png
│ ├── cp_gradient_rect_hsv.png
│ ├── cp_rect_hue_lightness_hsl.png
│ ├── cp_rect_hue_saturation_hsl.png
│ ├── cp_rect_hue_saturation_hsv.png
│ ├── cp_rect_hue_value_hsv.png
│ ├── cp_rect_saturation_lightness_hsl.png
│ ├── cp_rect_saturation_value_hsv.png
│ ├── cp_ring_diamond_hex.png
│ ├── cp_ring_diamond_hsl.png
│ ├── cp_ring_rect_hex_hsv.png
│ ├── cp_ring_rect_hsl.png
│ └── cp_ring_rect_hsv.png
├── gradient_angle.gif
├── gradient_selection.gif
├── hsv_hsl_gradient.gif
├── intro.gif
└── saturation.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/*
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 33
8 |
9 | defaultConfig {
10 | applicationId "com.smarttoolfactory.composecolorpicker"
11 | minSdk 21
12 | targetSdk 33
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | vectorDrawables {
18 | useSupportLibrary true
19 | }
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 | kotlinOptions {
33 | jvmTarget = '1.8'
34 | }
35 | buildFeatures {
36 | compose true
37 | }
38 | composeOptions {
39 | kotlinCompilerExtensionVersion '1.5.0'
40 | }
41 | packagingOptions {
42 | resources {
43 | excludes += '/META-INF/{AL2.0,LGPL2.1}'
44 | }
45 | }
46 | namespace 'com.smarttoolfactory.composecolorpicker'
47 | }
48 |
49 | dependencies {
50 |
51 | implementation project(':colorpicker')
52 |
53 | implementation 'com.github.SmartToolFactory:Compose-Colorful-Sliders:1.2.0'
54 | implementation 'com.github.SmartToolFactory:Compose-Extended-Gestures:3.0.0'
55 | implementation 'com.github.SmartToolFactory:Compose-Extended-Colors:1.0.0-alpha07'
56 | implementation "androidx.compose.material3:material3:1.1.1"
57 |
58 | // Jetpack Compose
59 | implementation 'androidx.core:core-ktx:1.10.1'
60 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
61 | implementation 'androidx.activity:activity-compose:1.7.2'
62 |
63 | // Jetpack Compose
64 | implementation "androidx.compose.ui:ui:$compose_version"
65 | implementation "androidx.compose.material:material:$compose_version"
66 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
67 |
68 |
69 | def accompanist_version = "0.30.1"
70 | // Accompanist
71 | implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"
72 | implementation "com.google.accompanist:accompanist-pager:$accompanist_version"
73 | implementation "com.google.accompanist:accompanist-drawablepainter:$accompanist_version"
74 |
75 | testImplementation 'junit:junit:4.13.2'
76 | androidTestImplementation 'androidx.test.ext:junit:1.1.5'
77 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
78 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
79 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
80 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/smarttoolfactory/composecolorpicker/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.smarttoolfactory.composecolorpicker", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.animation.ExperimentalAnimationApi
7 | import androidx.compose.foundation.layout.Column
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.material.*
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.runtime.rememberCoroutineScope
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.unit.dp
15 | import com.google.accompanist.pager.ExperimentalPagerApi
16 | import com.google.accompanist.pager.HorizontalPager
17 | import com.google.accompanist.pager.PagerState
18 | import com.google.accompanist.pager.rememberPagerState
19 | import com.smarttoolfactory.composecolorpicker.demo.*
20 | import com.smarttoolfactory.composecolorpicker.ui.theme.ComposeColorPickerTheme
21 | import kotlinx.coroutines.launch
22 |
23 | @ExperimentalPagerApi
24 | class MainActivity : ComponentActivity() {
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | setContent {
28 | ComposeColorPickerTheme {
29 | // A surface container using the 'background' color from the theme
30 | Surface(color = MaterialTheme.colors.background) {
31 | Column(
32 | modifier = Modifier.fillMaxSize()
33 | ) {
34 | HomeContent()
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
42 | @ExperimentalPagerApi
43 | @Composable
44 | private fun HomeContent() {
45 |
46 | val pagerState: PagerState = rememberPagerState(initialPage = 0)
47 |
48 | val coroutineScope = rememberCoroutineScope()
49 |
50 | ScrollableTabRow(
51 | backgroundColor = Color(0xff00897B),
52 | contentColor = Color.White,
53 | edgePadding = 8.dp,
54 | // Our selected tab is our current page
55 | selectedTabIndex = pagerState.currentPage,
56 | // Override the indicator, using the provided pagerTabIndicatorOffset modifier
57 | indicator = {}
58 | ) {
59 | // Add tabs for all of our pages
60 | tabList.forEachIndexed { index, title ->
61 | Tab(
62 | text = { Text(title) },
63 | selected = pagerState.currentPage == index,
64 | onClick = {
65 | coroutineScope.launch {
66 | pagerState.animateScrollToPage(index)
67 | }
68 | }
69 | )
70 | }
71 | }
72 |
73 | HorizontalPager(
74 | state = pagerState,
75 | count = tabList.size
76 | ) { page: Int ->
77 |
78 | when (page) {
79 | 0 -> ColorPickerDemo()
80 | 1 -> ColorAndGradientPickerDemo()
81 | 2 -> GradientSelectionDemo()
82 | 3 -> HexConversionDemo()
83 | 4 -> SaturationSelectorDemo()
84 | 5 -> HSVHSLGradientDemo()
85 | else -> ColorfulSliderDemo()
86 | }
87 | }
88 | }
89 |
90 | internal val tabList =
91 | listOf(
92 | "Color Picker",
93 | "Gradient Color Picker",
94 | "Gradient Selection",
95 | "Hex Conversions",
96 | "Saturation Selector",
97 | "HSV&HSL Gradients",
98 | "Colorful Sliders"
99 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/demo/DoughnutDemo.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.demo
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.aspectRatio
9 | import androidx.compose.foundation.layout.fillMaxWidth
10 | import androidx.compose.foundation.layout.height
11 | import androidx.compose.foundation.layout.padding
12 | import androidx.compose.foundation.shape.RoundedCornerShape
13 | import androidx.compose.material3.MaterialTheme
14 | import androidx.compose.material3.Surface
15 | import androidx.compose.material3.Text
16 | import androidx.compose.runtime.Composable
17 | import androidx.compose.runtime.getValue
18 | import androidx.compose.runtime.mutableStateOf
19 | import androidx.compose.runtime.remember
20 | import androidx.compose.runtime.setValue
21 | import androidx.compose.ui.Alignment
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.draw.alpha
24 | import androidx.compose.ui.draw.clip
25 | import androidx.compose.ui.graphics.Brush
26 | import androidx.compose.ui.graphics.Color
27 | import androidx.compose.ui.text.font.FontWeight
28 | import androidx.compose.ui.tooling.preview.Preview
29 | import androidx.compose.ui.unit.Dp
30 | import androidx.compose.ui.unit.dp
31 | import com.smarttoolfactory.colorpicker.selector.SelectorRectHueLightnessHSLHorizontal
32 | import com.smarttoolfactory.colorpicker.widget.drawChecker
33 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
34 | import com.smarttoolfactory.extendedcolors.util.roundToTwoDigits
35 | import com.smarttoolfactory.slider.ColorfulSlider
36 | import com.smarttoolfactory.slider.MaterialSliderDefaults
37 | import com.smarttoolfactory.slider.SliderBrushColor
38 | import kotlin.math.roundToInt
39 |
40 | @Composable
41 | fun ColorPickerRectHueLightnessHSLHorizontal(
42 | modifier: Modifier = Modifier,
43 | selectionRadius: Dp = 8.dp,
44 | initialColor: Color,
45 | onColorChange: (Color, String) -> Unit
46 | ) {
47 | val hslArray = ColorUtil.colorToHSL(initialColor)
48 |
49 | var hue by remember { mutableStateOf(hslArray[0]) }
50 | val saturation by remember { mutableStateOf(hslArray[1]) }
51 | var lightness by remember { mutableStateOf(hslArray[2]) }
52 | var alpha by remember { mutableStateOf(initialColor.alpha) }
53 |
54 |
55 | val currentColor =
56 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = alpha)
57 |
58 |
59 | Column(
60 | modifier = modifier,
61 | horizontalAlignment = Alignment.Start
62 | ) {
63 | SelectorRectHueLightnessHSLHorizontal(
64 | modifier = Modifier
65 | .aspectRatio(1f, true)
66 | .fillMaxWidth(),
67 | hue = hue,
68 | lightness = lightness,
69 | selectionRadius = selectionRadius,
70 | onChange = { h, l ->
71 | hue = h
72 | lightness = l
73 |
74 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
75 | },
76 | )
77 |
78 | Text(
79 | text = "OPACITY",
80 | modifier = Modifier
81 | .padding(top = 8.dp)
82 | .alpha(0.38f),
83 | fontWeight = FontWeight.Bold,
84 | style = MaterialTheme.typography.labelSmall,
85 | )
86 | Slider(
87 | modifier = Modifier,
88 | value = alpha,
89 | onValueChange = { alpha = it },
90 | brush = Brush.linearGradient(
91 | colors = listOf(
92 | currentColor.copy(alpha = 0f),
93 | currentColor.copy(alpha = 1f),
94 | )
95 | ),
96 | drawChecker = true
97 | )
98 | }
99 | }
100 |
101 |
102 | @Composable
103 | @Preview
104 | private fun Preview() {
105 | MaterialTheme {
106 | Box(modifier = Modifier.background(Color.Red)) {
107 | Surface(
108 | modifier = Modifier
109 | .background(Color(0xFFF9F9F9))
110 | .padding(16.dp)
111 | ) {
112 | var color by remember {
113 | mutableStateOf(Color(0xFF00FFF7))
114 | }
115 | Column {
116 | ColorPickerRectHueLightnessHSLHorizontal(
117 | modifier = Modifier
118 | .clip(RoundedCornerShape(8.dp)),
119 | initialColor = color,
120 | onColorChange = { c, _ -> color = c.copy(alpha = color.alpha) }
121 | )
122 |
123 |
124 | }
125 | }
126 | }
127 | }
128 | }
129 |
130 | @Composable
131 | private fun Slider(
132 | modifier: Modifier = Modifier,
133 | value: Float,
134 | valueRange: ClosedFloatingPointRange = 0f..1f,
135 | onValueChange: (Float) -> Unit,
136 | brush: Brush,
137 | drawChecker: Boolean = false
138 | ) {
139 | Row(
140 | verticalAlignment = Alignment.CenterVertically,
141 | horizontalArrangement = Arrangement.spacedBy(16.dp),
142 | ) {
143 | Box(
144 | modifier = modifier.weight(1f),
145 | contentAlignment = Alignment.CenterStart
146 | ) {
147 | if (drawChecker) {
148 | Box(
149 | modifier = Modifier
150 | .fillMaxWidth()
151 | .height(36.dp)
152 | .drawChecker(shape = RoundedCornerShape(50))
153 | )
154 | }
155 |
156 | ColorfulSlider(
157 | value = value,
158 | modifier = Modifier,
159 | thumbRadius = 18.dp,
160 | trackHeight = 36.dp,
161 | onValueChange = { value ->
162 | onValueChange(value.roundToTwoDigits())
163 | },
164 | valueRange = valueRange,
165 | coerceThumbInTrack = true,
166 | colors = MaterialSliderDefaults.materialColors(
167 | activeTrackColor = SliderBrushColor(brush = brush),
168 | inactiveTrackColor = SliderBrushColor(color = Color.Transparent)
169 | ),
170 | drawInactiveTrack = false
171 | )
172 | }
173 |
174 | Surface(shape = MaterialTheme.shapes.small, color = Color.White) {
175 | Text(
176 | modifier = Modifier
177 | .padding(4.dp)
178 | .padding(horizontal = 16.dp),
179 | text = "${(value * 100).roundToInt()}%",
180 | fontWeight = FontWeight.Bold,
181 | )
182 | }
183 | }
184 |
185 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/demo/GradientSelectionDemo.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.demo
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.*
5 | import androidx.compose.foundation.rememberScrollState
6 | import androidx.compose.foundation.verticalScroll
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.*
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.text.font.FontWeight
13 | import androidx.compose.ui.unit.DpSize
14 | import androidx.compose.ui.unit.dp
15 | import androidx.compose.ui.unit.sp
16 | import com.smarttoolfactory.colorpicker.model.BrushColor
17 | import com.smarttoolfactory.colorpicker.model.rememberGradientColorState
18 | import com.smarttoolfactory.colorpicker.selector.SelectorDiamondSaturationLightnessHSL
19 | import com.smarttoolfactory.colorpicker.selector.gradient.GradientSelector
20 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplayHueHSL
21 | import com.smarttoolfactory.colorpicker.ui.Blue400
22 |
23 |
24 | /**
25 | * Demo for creating selecting gradient
26 | */
27 | @Composable
28 | fun GradientSelectionDemo() {
29 | Column(
30 | modifier = Modifier.fillMaxSize(),
31 | horizontalAlignment = Alignment.CenterHorizontally
32 | ) {
33 |
34 | var hue by remember { mutableStateOf(0f) }
35 | var saturation by remember { mutableStateOf(.5f) }
36 | var lightness by remember { mutableStateOf(.5f) }
37 | var alpha by remember { mutableStateOf(1f) }
38 |
39 | val currentColor = Color.hsl(hue, saturation, lightness, alpha)
40 |
41 | SelectorDiamondSaturationLightnessHSL(
42 | modifier = Modifier.size(150.dp),
43 | hue = hue,
44 | saturation = saturation,
45 | lightness = lightness,
46 | selectionRadius = 8.dp
47 | ) { s, l ->
48 | saturation = s
49 | lightness = l
50 | }
51 |
52 | SliderCircleColorDisplayHueHSL(
53 | modifier = Modifier.padding(8.dp),
54 | hue = hue,
55 | saturation = saturation,
56 | lightness = lightness,
57 | alpha = alpha,
58 | onHueChange = {
59 | hue = it
60 | },
61 | onAlphaChange = {
62 | alpha = it
63 | }
64 | )
65 |
66 | Text(
67 | text = "GRADIENT SELECTION",
68 | modifier = Modifier.padding(5.dp),
69 | color = Blue400,
70 | fontSize = 16.sp,
71 | fontWeight = FontWeight.Bold
72 | )
73 |
74 | val size = DpSize(150.dp, 100.dp)
75 | val gradientColor = rememberGradientColorState(size = size)
76 | var brushColor by remember { mutableStateOf(gradientColor.brushColor) }
77 |
78 | Box(
79 | modifier = Modifier
80 | .size(size)
81 | .background(brushColor.activeBrush)
82 | )
83 |
84 | Spacer(modifier = Modifier.height(10.dp))
85 | Column(
86 | modifier = Modifier
87 | .fillMaxSize()
88 | .verticalScroll(rememberScrollState()),
89 | horizontalAlignment = Alignment.CenterHorizontally
90 | ) {
91 | GradientSelector(
92 | modifier = Modifier.padding(horizontal = 8.dp),
93 | color = currentColor,
94 | gradientColorState = gradientColor
95 | ) {
96 | brushColor = BrushColor(brush = it)
97 | }
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/demo/HexConversionDemo.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.demo
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.*
5 | import androidx.compose.foundation.rememberScrollState
6 | import androidx.compose.foundation.verticalScroll
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.*
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.text.font.FontWeight
13 | import androidx.compose.ui.unit.dp
14 | import androidx.compose.ui.unit.sp
15 | import com.smarttoolfactory.colorpicker.widget.HexTextField
16 | import com.smarttoolfactory.colorpicker.widget.HexTextFieldWithClipboard
17 | import com.smarttoolfactory.colorpicker.widget.HexTextFieldWithLabelClipboard
18 | import com.smarttoolfactory.composecolorpicker.ui.theme.backgroundColor
19 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
20 |
21 | @Composable
22 | fun HexConversionDemo() {
23 | Column(
24 | modifier = Modifier
25 | .background(backgroundColor)
26 | .fillMaxSize()
27 | .padding(12.dp)
28 | .verticalScroll(rememberScrollState()),
29 | horizontalAlignment = Alignment.CenterHorizontally
30 | ) {
31 |
32 | var color1 by remember { mutableStateOf(Color.hsl(0f, 0.5f, 0.5f)) }
33 | var hexString1 by remember { mutableStateOf(ColorUtil.colorToHexAlpha(color1)) }
34 |
35 | var color2 by remember { mutableStateOf(Color.hsl(0f, 0.5f, 0.5f)) }
36 | var hexString2 by remember { mutableStateOf(ColorUtil.colorToHexAlpha(color2)) }
37 |
38 | var color3 by remember { mutableStateOf(Color.hsl(0f, 0.5f, 0.5f)) }
39 | var hexString3 by remember { mutableStateOf(ColorUtil.colorToHex(color3)) }
40 |
41 | Spacer(modifier = Modifier.height(50.dp))
42 |
43 | Text(
44 | "HEX: $hexString1",
45 | color = color1,
46 | fontWeight = FontWeight.Bold,
47 | fontSize = 30.sp
48 | )
49 |
50 | Spacer(modifier = Modifier.height(10.dp))
51 |
52 | HexTextField(
53 | hexString = hexString1,
54 | useAlpha = true,
55 | onTextChange = {
56 | hexString1 = it
57 | },
58 | onColorChange = {
59 | color1 = it
60 | }
61 | )
62 |
63 | Spacer(modifier = Modifier.height(30.dp))
64 | Text(
65 | "HEX: $hexString2",
66 | color = color2,
67 | fontWeight = FontWeight.Bold,
68 | fontSize = 30.sp
69 | )
70 |
71 | Spacer(modifier = Modifier.height(10.dp))
72 |
73 | HexTextFieldWithClipboard(
74 | hexString = hexString2,
75 | useAlpha = true,
76 | onTextChange = {
77 | hexString2 = it
78 | },
79 | onColorChange = {
80 | color2 = it
81 | }
82 | )
83 |
84 | Spacer(modifier = Modifier.height(30.dp))
85 | Text(
86 | "HEX: $hexString3",
87 | color = color3,
88 | fontWeight = FontWeight.Bold,
89 | fontSize = 30.sp
90 | )
91 |
92 | Spacer(modifier = Modifier.height(10.dp))
93 |
94 | HexTextFieldWithLabelClipboard(
95 | hexString = hexString3,
96 | onTextChange = {
97 | hexString3 = it
98 | },
99 | onColorChange = {
100 | color3 = it
101 | }
102 | )
103 | }
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/ui/CanvasWithTitle.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.ui
2 |
3 | import androidx.compose.foundation.Canvas
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.foundation.layout.wrapContentWidth
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.drawscope.DrawScope
11 | import androidx.compose.ui.text.font.FontWeight
12 | import androidx.compose.ui.unit.dp
13 | import androidx.compose.ui.unit.sp
14 | import com.smarttoolfactory.colorpicker.ui.Blue400
15 |
16 | @Composable
17 | fun CanvasWithTitle(
18 | modifier: Modifier = Modifier,
19 | text: String,
20 | onDraw: DrawScope.() -> Unit
21 | ) {
22 | Column(
23 | modifier = Modifier
24 | .wrapContentWidth()
25 | ) {
26 |
27 | Text(
28 | text = text,
29 | color = Blue400,
30 | modifier = Modifier
31 | .padding(8.dp),
32 | fontSize = 18.sp,
33 | fontWeight = FontWeight.Bold
34 | )
35 |
36 | Canvas(modifier = modifier, onDraw = onDraw)
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple200 = Color(0xFFBB86FC)
6 | val Purple500 = Color(0xFF6200EE)
7 | val Purple700 = Color(0xFF3700B3)
8 | val Teal200 = Color(0xFF03DAC5)
9 |
10 | val backgroundColor = Color(0xffECEFF1)
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.ui.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val Shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material.MaterialTheme
5 | import androidx.compose.material.darkColors
6 | import androidx.compose.material.lightColors
7 | import androidx.compose.runtime.Composable
8 |
9 | private val DarkColorPalette = darkColors(
10 | primary = Purple200,
11 | primaryVariant = Purple700,
12 | secondary = Teal200
13 | )
14 |
15 | private val LightColorPalette = lightColors(
16 | primary = Purple500,
17 | primaryVariant = Purple700,
18 | secondary = Teal200
19 |
20 | /* Other default colors to override
21 | background = Color.White,
22 | surface = Color.White,
23 | onPrimary = Color.White,
24 | onSecondary = Color.Black,
25 | onBackground = Color.Black,
26 | onSurface = Color.Black,
27 | */
28 | )
29 |
30 | @Composable
31 | fun ComposeColorPickerTheme(
32 | darkTheme: Boolean = isSystemInDarkTheme(),
33 | content: @Composable () -> Unit
34 | ) {
35 | val colors = if (darkTheme) {
36 | DarkColorPalette
37 | } else {
38 | LightColorPalette
39 | }
40 |
41 | MaterialTheme(
42 | colors = colors,
43 | typography = Typography,
44 | shapes = Shapes,
45 | content = content
46 | )
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/composecolorpicker/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker.ui.theme
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | /* Other default text styles to override
17 | button = TextStyle(
18 | fontFamily = FontFamily.Default,
19 | fontWeight = FontWeight.W500,
20 | fontSize = 14.sp
21 | ),
22 | caption = TextStyle(
23 | fontFamily = FontFamily.Default,
24 | fontWeight = FontWeight.Normal,
25 | fontSize = 12.sp
26 | )
27 | */
28 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 | #ff00897B
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Compose Color Picker
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
--------------------------------------------------------------------------------
/app/src/test/java/com/smarttoolfactory/composecolorpicker/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.composecolorpicker
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | compose_version = '1.4.3'
4 | }
5 | }// Top-level build file where you can add configuration options common to all sub-projects/modules.
6 | plugins {
7 | id 'com.android.application' version '8.0.2' apply false
8 | id 'com.android.library' version '8.0.2' apply false
9 | id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
10 | }
11 |
12 | task clean(type: Delete) {
13 | delete rootProject.buildDir
14 | }
--------------------------------------------------------------------------------
/colorpicker/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/colorpicker/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 33
8 |
9 | defaultConfig {
10 | minSdk 21
11 | targetSdk 33
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | kotlinOptions {
28 | jvmTarget = '1.8'
29 | }
30 | buildFeatures {
31 | compose true
32 | }
33 | composeOptions {
34 | kotlinCompilerExtensionVersion '1.5.0'
35 | }
36 |
37 | packagingOptions {
38 | resources {
39 | excludes += '/META-INF/{AL2.0,LGPL2.1}'
40 | }
41 | }
42 | namespace 'com.smarttoolfactory.colorpicker'
43 | }
44 |
45 | dependencies {
46 |
47 | implementation 'androidx.core:core-ktx:1.10.1'
48 |
49 | implementation 'com.github.SmartToolFactory:Compose-Extended-Gestures:3.0.0'
50 | implementation 'com.github.SmartToolFactory:Compose-Extended-Colors:1.0.0-alpha07'
51 | implementation 'com.github.SmartToolFactory:Compose-Screenshot:1.0.3'
52 | implementation 'com.github.SmartToolFactory:Compose-Color-Detector:1.0.0'
53 | implementation 'com.github.SmartToolFactory:Compose-Colorful-Sliders:1.2.0'
54 | // Jetpack Compose
55 | implementation "androidx.compose.ui:ui:$compose_version"
56 | implementation "androidx.compose.ui:ui-tooling:$compose_version"
57 | implementation "androidx.compose.material:material:$compose_version"
58 | implementation "androidx.compose.runtime:runtime:$compose_version"
59 | implementation "androidx.compose.material:material-icons-extended:$compose_version"
60 | implementation "androidx.compose.material3:material3:1.1.1"
61 |
62 | testImplementation 'junit:junit:4.13.2'
63 | androidTestImplementation 'androidx.test.ext:junit:1.1.5'
64 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
65 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
66 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
67 | }
--------------------------------------------------------------------------------
/colorpicker/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/colorpicker/consumer-rules.pro
--------------------------------------------------------------------------------
/colorpicker/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/colorpicker/src/androidTest/java/com/smarttoolfactory/colorpicker/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.smarttoolfactory.colorpicker.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/model/BrushColor.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.model
2 |
3 | import androidx.compose.ui.graphics.Brush
4 | import androidx.compose.ui.graphics.Color
5 | import androidx.compose.ui.graphics.SolidColor
6 |
7 |
8 | /**
9 | * Data class that contains [Brush] and [Color] and can return either based on user selection.
10 | */
11 | data class BrushColor(
12 | var color: Color = Color.Unspecified,
13 | var brush: Brush? = null
14 | ) {
15 | /**
16 | * [Brush] that is not **null** [brush] property or [SolidColor] that is not nullable and
17 | * contains [color] property as [SolidColor.value]
18 | */
19 | val activeBrush: Brush
20 | get() = brush ?: solidColor
21 |
22 | /**
23 | * [SolidColor] is a [Brush] that
24 | * wraps [color] property that is used for [activeBrush] if [brush] property is **null**
25 | */
26 | val solidColor = SolidColor(color)
27 | }
28 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/model/ColorModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.model
2 |
3 | /**
4 | * Color Model HSV(Hue-Saturation-Value), HSL(Hue-Saturation-Lightness), RGB(Red-Green-Blue)
5 | */
6 | enum class ColorModel {
7 | RGB, HSV, HSL
8 | }
9 |
10 | /**
11 | * Color Modes that contain HSL, HSV, RGB, and Gradient
12 | */
13 | enum class ColorMode {
14 | HSL, HSV, RGB, Gradient
15 | }
16 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/model/CompositeColor.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.model
2 |
3 | import androidx.annotation.FloatRange
4 | import androidx.compose.ui.graphics.Color
5 | import com.smarttoolfactory.extendedcolors.util.RGBUtil
6 |
7 | /**
8 | * Color in HSV color model
9 | */
10 | class ColorHSV(
11 | @FloatRange(from = 0.0, to = 360.0) val hue: Float,
12 | @FloatRange(from = 0.0, to = 1.0) val saturation: Float,
13 | @FloatRange(from = 0.0, to = 1.0) val value: Float,
14 | @FloatRange(from = 0.0, to = 1.0) val alpha: Float,
15 | ) : CompositeColor {
16 |
17 | override val color: Color
18 | get() = Color.hsv(hue, saturation, value, alpha)
19 |
20 | override val argbHexString: String
21 | get() = RGBUtil.argbToHex(color.alpha, color.red, color.green, color.blue)
22 |
23 | override val rgbHexString: String
24 | get() = RGBUtil.rgbToHex(color.red, color.green, color.blue)
25 |
26 | companion object {
27 | val Unspecified = ColorHSV(0f, 0f, 0f, 0f)
28 | }
29 |
30 | override fun toString(): String {
31 | return "ColorHSV(hash: ${hashCode()}," +
32 | " hue: $hue, saturation: $saturation, value: $value, alpha: $alpha)"
33 | }
34 | }
35 |
36 | /**
37 | * Color in HSL color model
38 | */
39 | class ColorHSL(
40 | @FloatRange(from = 0.0, to = 360.0) val hue: Float,
41 | @FloatRange(from = 0.0, to = 1.0) val saturation: Float,
42 | @FloatRange(from = 0.0, to = 1.0) val lightness: Float,
43 | @FloatRange(from = 0.0, to = 1.0) val alpha: Float,
44 | ) : CompositeColor {
45 |
46 | override val color: Color
47 | get() = Color.hsl(hue, saturation, lightness, alpha)
48 |
49 | override val argbHexString: String
50 | get() = RGBUtil.argbToHex(color.alpha, color.red, color.green, color.blue)
51 |
52 | override val rgbHexString: String
53 | get() = RGBUtil.rgbToHex(color.red, color.green, color.blue)
54 |
55 | companion object {
56 | val Unspecified: ColorHSL = ColorHSL(0f, 0f, 0f, 0f)
57 | }
58 |
59 | override fun toString(): String {
60 | return "ColorHSL(hash: ${hashCode()}," +
61 | " hue: $hue, saturation: $saturation, lightness: $lightness, alpha: $alpha)"
62 | }
63 | }
64 |
65 | /**
66 | * Color in RGB color model
67 | */
68 | class ColorRGB(
69 | @FloatRange(from = 0.0, to = 1.0) val red: Float,
70 | @FloatRange(from = 0.0, to = 1.0) val green: Float,
71 | @FloatRange(from = 0.0, to = 1.0) val blue: Float,
72 | @FloatRange(from = 0.0, to = 1.0) val alpha: Float,
73 | ) : CompositeColor {
74 |
75 | override val color: Color
76 | get() = Color(red, green, blue, alpha)
77 |
78 | override val argbHexString: String
79 | get() = RGBUtil.argbToHex(color.alpha, color.red, color.green, color.blue)
80 |
81 | override val rgbHexString: String
82 | get() = RGBUtil.rgbToHex(color.red, color.green, color.blue)
83 |
84 | companion object {
85 | val Unspecified = ColorRGB(0f, 0f, 0f, 0f)
86 | }
87 |
88 | override fun toString(): String {
89 | return "ColorRGB(hash: ${hashCode()}," +
90 | " hue: $red, saturation: $green, lightness: $blue, alpha: $alpha)"
91 | }
92 | }
93 |
94 | /**
95 | * Interface that can be polymorph into HSV, HSL or RGB color model
96 | */
97 | interface CompositeColor {
98 | val color: Color
99 | val argbHexString: String
100 | val rgbHexString: String
101 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/model/GradientColorState.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.model
2 |
3 | import androidx.compose.runtime.*
4 | import androidx.compose.ui.geometry.Offset
5 | import androidx.compose.ui.geometry.Size
6 | import androidx.compose.ui.graphics.Brush
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.graphics.TileMode
9 | import androidx.compose.ui.platform.LocalDensity
10 | import androidx.compose.ui.unit.DpSize
11 | import com.smarttoolfactory.colorpicker.selector.gradient.GradientType
12 | import com.smarttoolfactory.colorpicker.ui.GradientAngle
13 | import com.smarttoolfactory.colorpicker.ui.GradientOffset
14 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
15 |
16 | @Composable
17 | fun rememberGradientColorState(
18 | color: Color = Color.Unspecified,
19 | size: DpSize = DpSize.Zero
20 | ): GradientColorState {
21 |
22 | val density = LocalDensity.current
23 |
24 | return remember {
25 |
26 | val sizePx = if (size == DpSize.Zero) {
27 | Size.Zero
28 | } else {
29 | with(density) {
30 | Size(
31 | size.width.toPx(),
32 | size.height.toPx()
33 | )
34 | }
35 | }
36 | GradientColorState(color, sizePx)
37 | }
38 | }
39 |
40 | /**
41 | * Gradient and color state for setting and getting gradient color
42 | * with [gradientType] such as Linear, Radial or Sweep, [tileMode]s, and [colorStops].
43 | * * Linear gradient uses [gradientOffset] to set offset or angle.
44 | * * Radial gradient uses [centerFriction] and [radiusFriction]
45 | */
46 | class GradientColorState internal constructor(initialColor: Color, size: Size) {
47 |
48 | var size by mutableStateOf(size)
49 | var color: Color = initialColor
50 |
51 | val hexString: String
52 | get() {
53 | return ColorUtil.colorToHexAlpha(color)
54 | }
55 |
56 | val brush: Brush
57 | get() {
58 |
59 | val colorStops = if (colorStops.size == 1) {
60 | listOf(colorStops.first(), colorStops.first()).toTypedArray()
61 | } else {
62 | colorStops.toTypedArray()
63 | }
64 |
65 | val brush = when (gradientType) {
66 | GradientType.Linear -> Brush.linearGradient(
67 | colorStops = colorStops,
68 | start = gradientOffset.start,
69 | end = gradientOffset.end,
70 | tileMode = tileMode
71 | )
72 | GradientType.Radial -> Brush.radialGradient(
73 | colorStops = colorStops,
74 | center = Offset(
75 | x = size.width * centerFriction.x,
76 | y = size.height * centerFriction.y
77 | ),
78 | radius = ((size.width.coerceAtLeast(size.height)) / 2 * radiusFriction)
79 | .coerceAtLeast(0.01f),
80 | tileMode = tileMode
81 | )
82 | GradientType.Sweep -> Brush.sweepGradient(
83 | colorStops = colorStops,
84 | center = Offset(
85 | x = size.width * centerFriction.x,
86 | y = size.height * centerFriction.y
87 | ),
88 | )
89 | }
90 | return brush
91 | }
92 |
93 | val brushColor: BrushColor
94 | get() {
95 | return BrushColor(color = color, brush = brush)
96 | }
97 |
98 | var gradientType: GradientType by mutableStateOf(GradientType.Linear)
99 | var colorStops = mutableStateListOf(
100 | 0.0f to Color.Red,
101 | 0.3f to Color.Green,
102 | 1.0f to Color.Blue,
103 | )
104 | var tileMode by mutableStateOf(TileMode.Clamp)
105 | var gradientOffset by mutableStateOf(GradientOffset(GradientAngle.CW0))
106 | var centerFriction by mutableStateOf(Offset(.5f, .5f))
107 | var radiusFriction by mutableStateOf(.5f)
108 | }
109 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerCircleValueHSV.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.runtime.*
6 | import androidx.compose.ui.Alignment
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.Color
9 | import androidx.compose.ui.unit.Dp
10 | import androidx.compose.ui.unit.dp
11 | import com.smarttoolfactory.colorpicker.model.ColorModel
12 | import com.smarttoolfactory.colorpicker.selector.SelectorCircleHueSaturationHSV
13 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplayValueHSV
14 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayExposedSelectionMenu
15 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
16 |
17 |
18 | /**
19 | *
20 | */
21 | @Composable
22 | fun ColorPickerCircleValueHSV(
23 | modifier: Modifier = Modifier,
24 | selectionRadius: Dp = 8.dp,
25 | initialColor: Color,
26 | onColorChange: (Color, String) -> Unit
27 | ) {
28 |
29 | val hsvArray = ColorUtil.colorToHSV(initialColor)
30 |
31 | var hue by remember { mutableStateOf(hsvArray[0]) }
32 | var saturation by remember { mutableStateOf(hsvArray[1]) }
33 | var value by remember { mutableStateOf(hsvArray[2]) }
34 | var alpha by remember { mutableStateOf(initialColor.alpha) }
35 |
36 | val currentColor =
37 | Color.hsv(hue = hue, saturation = saturation, value = value, alpha = alpha)
38 |
39 | var colorModel by remember { mutableStateOf(ColorModel.HSV) }
40 |
41 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
42 |
43 | Column(
44 | modifier = modifier,
45 | horizontalAlignment = Alignment.CenterHorizontally
46 | ) {
47 | SelectorCircleHueSaturationHSV(
48 | modifier = Modifier.padding(8.dp),
49 | hue = hue,
50 | saturation = saturation,
51 | selectionRadius = selectionRadius
52 | ) { h, s ->
53 | hue = h
54 | saturation = s
55 |
56 | }
57 |
58 | Column(modifier = Modifier.padding(8.dp)) {
59 | SliderCircleColorDisplayValueHSV(
60 | hue = hue,
61 | saturation = saturation,
62 | value = value,
63 | alpha = alpha,
64 | onValueChange = {
65 | value = it
66 | },
67 | onAlphaChange = {
68 | alpha = it
69 | }
70 | )
71 |
72 | ColorDisplayExposedSelectionMenu(
73 | color = currentColor,
74 | colorModel = colorModel,
75 | onColorModelChange = {
76 | colorModel = it
77 | }
78 | )
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRectHueLightnessHSL.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
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.ColorModel
14 | import com.smarttoolfactory.colorpicker.selector.SelectorRectHueLightnessHSL
15 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplaySaturationHSL
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayExposedSelectionMenu
17 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
18 |
19 | @Composable
20 | fun ColorPickerRectHueLightnessHSL(
21 | modifier: Modifier = Modifier,
22 | selectionRadius: Dp = 8.dp,
23 | initialColor: Color,
24 | onColorChange: (Color, String) -> Unit
25 | ) {
26 |
27 | var colorModel by remember { mutableStateOf(ColorModel.HSL) }
28 |
29 | val hslArray = ColorUtil.colorToHSL(initialColor)
30 |
31 | var hue by remember { mutableStateOf(hslArray[0]) }
32 | var saturation by remember { mutableStateOf(hslArray[1]) }
33 | var lightness by remember { mutableStateOf(hslArray[2]) }
34 | var alpha by remember { mutableStateOf(initialColor.alpha) }
35 |
36 |
37 | val currentColor =
38 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = alpha)
39 |
40 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
41 |
42 | Column(
43 | modifier = modifier,
44 | horizontalAlignment = Alignment.CenterHorizontally
45 | ) {
46 | SelectorRectHueLightnessHSL(
47 | modifier = Modifier
48 | .fillMaxWidth()
49 | .aspectRatio(4 / 3f),
50 | hue = hue,
51 | lightness = lightness,
52 | selectionRadius = selectionRadius,
53 | onChange = { h, l ->
54 | hue = h
55 | lightness = l
56 | },
57 | )
58 |
59 | Column(modifier = Modifier.padding(8.dp)) {
60 | SliderCircleColorDisplaySaturationHSL(
61 | hue = hue,
62 | saturation = saturation,
63 | lightness = lightness,
64 | alpha = alpha,
65 | onSaturationChange = {
66 | saturation = it
67 | },
68 | onAlphaChange = {
69 | alpha = it
70 | }
71 | )
72 |
73 | ColorDisplayExposedSelectionMenu(
74 | color = currentColor,
75 | colorModel = colorModel,
76 | onColorModelChange = {
77 | colorModel = it
78 | }
79 | )
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRectHueSaturationHSL.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
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.ColorModel
14 | import com.smarttoolfactory.colorpicker.selector.SelectorRectHueSaturationHSL
15 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplayLightnessHSL
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayExposedSelectionMenu
17 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
18 |
19 | @Composable
20 | fun ColorPickerRectHueSaturationHSL(
21 | modifier: Modifier = Modifier,
22 | selectionRadius: Dp = 8.dp,
23 | initialColor: Color,
24 | onColorChange: (Color, String) -> Unit
25 | ) {
26 |
27 | var colorModel by remember { mutableStateOf(ColorModel.HSL) }
28 |
29 | val hslArray = ColorUtil.colorToHSL(initialColor)
30 |
31 | var hue by remember { mutableStateOf(hslArray[0]) }
32 | var saturation by remember { mutableStateOf(hslArray[1]) }
33 | var lightness by remember { mutableStateOf(hslArray[2]) }
34 | var alpha by remember { mutableStateOf(initialColor.alpha) }
35 |
36 | val currentColor =
37 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = alpha)
38 |
39 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
40 |
41 | Column(
42 | modifier = modifier,
43 | horizontalAlignment = Alignment.CenterHorizontally
44 | ) {
45 | SelectorRectHueSaturationHSL(
46 | modifier = Modifier
47 | .fillMaxWidth()
48 | .aspectRatio(4 / 3f),
49 | hue = hue,
50 | saturation = saturation,
51 | selectionRadius = selectionRadius,
52 | onChange = { h, s ->
53 | hue = h
54 | saturation = s
55 | }
56 | )
57 |
58 | Column(modifier = Modifier.padding(8.dp)) {
59 | SliderCircleColorDisplayLightnessHSL(
60 | hue = hue,
61 | saturation = saturation,
62 | lightness = lightness,
63 | alpha = alpha,
64 | onLightnessChange = {
65 | lightness = it
66 | },
67 | onAlphaChange = {
68 | alpha = it
69 | }
70 | )
71 |
72 | ColorDisplayExposedSelectionMenu(
73 | color = currentColor,
74 | colorModel = colorModel,
75 | onColorModelChange = {
76 | colorModel = it
77 | }
78 | )
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRectHueSaturationHSV.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
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.ColorModel
14 | import com.smarttoolfactory.colorpicker.selector.SelectorRectHueSaturationHSV
15 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplayValueHSV
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayExposedSelectionMenu
17 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
18 |
19 | @Composable
20 | fun ColorPickerRectHueSaturationHSV(
21 | modifier: Modifier = Modifier,
22 | selectionRadius: Dp = 8.dp,
23 | initialColor: Color,
24 | onColorChange: (Color, String) -> Unit
25 | ) {
26 |
27 | val hsvArray = ColorUtil.colorToHSV(initialColor)
28 |
29 | var hue by remember { mutableStateOf(hsvArray[0]) }
30 | var saturation by remember { mutableStateOf(hsvArray[1]) }
31 | var value by remember { mutableStateOf(hsvArray[2]) }
32 | var alpha by remember { mutableStateOf(initialColor.alpha) }
33 |
34 | val currentColor =
35 | Color.hsv(hue = hue, saturation = saturation, value = value, alpha = alpha)
36 |
37 | var colorModel by remember { mutableStateOf(ColorModel.HSV) }
38 |
39 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
40 |
41 | Column(
42 | modifier = modifier,
43 | horizontalAlignment = Alignment.CenterHorizontally
44 | ) {
45 | SelectorRectHueSaturationHSV(
46 | modifier = Modifier
47 | .fillMaxWidth()
48 | .aspectRatio(4 / 3f),
49 | hue = hue,
50 | saturation = saturation,
51 | selectionRadius = selectionRadius,
52 | onChange = { h, s ->
53 | hue = h
54 | saturation = s
55 | },
56 | )
57 |
58 | Column(modifier = Modifier.padding(8.dp)) {
59 | SliderCircleColorDisplayValueHSV(
60 | hue = hue,
61 | saturation = saturation,
62 | value = value,
63 | alpha = alpha,
64 | onValueChange = {
65 | value = it
66 | },
67 | onAlphaChange = {
68 | alpha = it
69 | }
70 | )
71 |
72 | ColorDisplayExposedSelectionMenu(
73 | color = currentColor,
74 | colorModel = colorModel,
75 | onColorModelChange = {
76 | colorModel = it
77 | }
78 | )
79 | }
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRectHueValueHSV.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
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.ColorModel
14 | import com.smarttoolfactory.colorpicker.selector.SelectorRectHueValueHSV
15 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplaySaturationHSV
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayExposedSelectionMenu
17 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
18 |
19 | @Composable
20 | fun ColorPickerRectHueValueHSV(
21 | modifier: Modifier = Modifier,
22 | selectionRadius: Dp = 8.dp,
23 | initialColor: Color,
24 | onColorChange: (Color, String) -> Unit
25 | ) {
26 |
27 | val hsvArray = ColorUtil.colorToHSV(initialColor)
28 |
29 | var hue by remember { mutableStateOf(hsvArray[0]) }
30 | var saturation by remember { mutableStateOf(hsvArray[1]) }
31 | var value by remember { mutableStateOf(hsvArray[2]) }
32 | var alpha by remember { mutableStateOf(initialColor.alpha) }
33 |
34 | val currentColor =
35 | Color.hsv(hue = hue, saturation = saturation, value = value, alpha = alpha)
36 |
37 | var colorModel by remember { mutableStateOf(ColorModel.HSV) }
38 |
39 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
40 |
41 | Column(
42 | modifier = modifier,
43 | horizontalAlignment = Alignment.CenterHorizontally
44 | ) {
45 | SelectorRectHueValueHSV(
46 | modifier = Modifier
47 | .fillMaxWidth()
48 | .aspectRatio(4 / 3f),
49 | hue = hue,
50 | value = value,
51 | selectionRadius = selectionRadius
52 | ) { h, v ->
53 | hue = h
54 | value = v
55 | }
56 |
57 | Column(modifier = Modifier.padding(8.dp)) {
58 | SliderCircleColorDisplaySaturationHSV(
59 | hue = hue,
60 | saturation = saturation,
61 | value = value,
62 | alpha = alpha,
63 | onSaturationChange = {
64 | saturation = it
65 | },
66 | onAlphaChange = {
67 | alpha = it
68 | }
69 | )
70 |
71 | ColorDisplayExposedSelectionMenu(
72 | color = currentColor,
73 | colorModel = colorModel,
74 | onColorModelChange = {
75 | colorModel = it
76 | }
77 | )
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRectSaturationLightnessHSL.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
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.ColorModel
14 | import com.smarttoolfactory.colorpicker.selector.SelectorRectSaturationLightnessHSL
15 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplayHueHSL
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayExposedSelectionMenu
17 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
18 |
19 | @Composable
20 | fun ColorPickerRectSaturationLightnessHSL(
21 | modifier: Modifier = Modifier,
22 | selectionRadius: Dp = 8.dp,
23 | initialColor: Color,
24 | onColorChange: (Color, String) -> Unit
25 | ) {
26 |
27 | var colorModel by remember { mutableStateOf(ColorModel.HSL) }
28 |
29 | val hslArray = ColorUtil.colorToHSL(initialColor)
30 |
31 | var hue by remember { mutableStateOf(hslArray[0]) }
32 | var saturation by remember { mutableStateOf(hslArray[1]) }
33 | var lightness by remember { mutableStateOf(hslArray[2]) }
34 | var alpha by remember { mutableStateOf(initialColor.alpha) }
35 |
36 | val currentColor =
37 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = alpha)
38 |
39 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
40 |
41 | Column(
42 | modifier = modifier,
43 | horizontalAlignment = Alignment.CenterHorizontally
44 | ) {
45 | SelectorRectSaturationLightnessHSL(
46 | modifier = Modifier
47 | .fillMaxWidth()
48 | .aspectRatio(4 / 3f),
49 | hue = hue,
50 | saturation = saturation,
51 | lightness = lightness,
52 | selectionRadius = selectionRadius
53 | ) { s, l ->
54 | saturation = s
55 | lightness = l
56 | }
57 |
58 | Column(modifier = Modifier.padding(8.dp)) {
59 | SliderCircleColorDisplayHueHSL(
60 | modifier = Modifier.padding(8.dp),
61 | hue = hue,
62 | saturation = saturation,
63 | lightness = lightness,
64 | alpha = alpha,
65 | onHueChange = {
66 | hue = it
67 | },
68 | onAlphaChange = {
69 | alpha = it
70 | }
71 | )
72 |
73 | ColorDisplayExposedSelectionMenu(
74 | color = currentColor,
75 | colorModel = colorModel,
76 | onColorModelChange = {
77 | colorModel = it
78 | }
79 | )
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRectSaturationValueHSV.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
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.ColorModel
14 | import com.smarttoolfactory.colorpicker.selector.SelectorRectSaturationValueHSV
15 | import com.smarttoolfactory.colorpicker.slider.SliderCircleColorDisplayHueHSV
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayExposedSelectionMenu
17 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
18 |
19 | @Composable
20 | fun ColorPickerRectSaturationValueHSV(
21 | modifier: Modifier = Modifier,
22 | selectionRadius: Dp = 8.dp,
23 | initialColor: Color,
24 | onColorChange: (Color, String) -> Unit
25 | ) {
26 |
27 | val hsvArray = ColorUtil.colorToHSV(initialColor)
28 |
29 | var hue by remember { mutableStateOf(hsvArray[0]) }
30 | var saturation by remember { mutableStateOf(hsvArray[1]) }
31 | var value by remember { mutableStateOf(hsvArray[2]) }
32 | var alpha by remember { mutableStateOf(initialColor.alpha) }
33 |
34 | val currentColor =
35 | Color.hsv(hue = hue, saturation = saturation, value = value, alpha = alpha)
36 |
37 | var colorModel by remember { mutableStateOf(ColorModel.HSV) }
38 |
39 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
40 |
41 | Column(
42 | modifier = modifier,
43 | horizontalAlignment = Alignment.CenterHorizontally
44 | ) {
45 | SelectorRectSaturationValueHSV(
46 | modifier = Modifier
47 | .fillMaxWidth()
48 | .aspectRatio(4 / 3f),
49 | hue = hue,
50 | saturation = saturation,
51 | value = value,
52 | selectionRadius = selectionRadius
53 | ) { s, v ->
54 | saturation = s
55 | value = v
56 | }
57 |
58 | Column(modifier = Modifier.padding(8.dp)) {
59 | SliderCircleColorDisplayHueHSV(
60 | hue = hue,
61 | saturation = saturation,
62 | value = value,
63 | alpha = alpha,
64 | onHueChange = {
65 | hue = it
66 | },
67 | onAlphaChange = {
68 | alpha = it
69 | }
70 | )
71 |
72 | ColorDisplayExposedSelectionMenu(
73 | color = currentColor,
74 | colorModel = colorModel,
75 | onColorModelChange = {
76 | colorModel = it
77 | }
78 | )
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRingDiamondHSL.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.unit.Dp
9 | import androidx.compose.ui.unit.dp
10 | import com.smarttoolfactory.colorpicker.model.ColorHSL
11 | import com.smarttoolfactory.colorpicker.model.ColorModel
12 | import com.smarttoolfactory.colorpicker.selector.SelectorDiamondSaturationLightnessHSL
13 | import com.smarttoolfactory.colorpicker.selector.SelectorRingHue
14 | import com.smarttoolfactory.colorpicker.slider.CompositeSliderPanel
15 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayRoundedRect
16 | import com.smarttoolfactory.colorpicker.widget.ColorModelChangeTabRow
17 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
18 |
19 | /**
20 | * ColorPicker with [SelectorRingHue] hue selector and [SelectorDiamondSaturationLightnessHSL]
21 | * saturation lightness Selector uses [HSL](https://en.wikipedia.org/wiki/HSL_and_HSV)
22 | * color model as base.
23 | *
24 | * This color picker has tabs section that can be changed between
25 | * HSL, HSV and RGB color models and color can be set using [CompositeSliderPanel] which contains
26 | * sliders for each color models.
27 | *
28 | * @param initialColor color that is passed to this picker initially.
29 | * @param ringOuterRadiusFraction outer radius of [SelectorRingHue].
30 | * @param ringInnerRadiusFraction inner radius of [SelectorRingHue].
31 | * @param ringBackgroundColor background from center to inner radius of [SelectorRingHue].
32 | * @param ringBorderStrokeColor stroke color for drawing borders around inner or outer radius.
33 | * @param ringBorderStrokeWidth stroke width of borders.
34 | * @param selectionRadius radius of white and black circle selector.
35 | * @param onColorChange callback that is triggered when [Color] is changed using [SelectorRingHue],
36 | * [SelectorDiamondSaturationLightnessHSL] or [CompositeSliderPanel]
37 | */
38 | @Composable
39 | fun ColorPickerRingDiamondHSL(
40 | modifier: Modifier = Modifier,
41 | initialColor: Color,
42 | ringOuterRadiusFraction: Float = .9f,
43 | ringInnerRadiusFraction: Float = .6f,
44 | ringBackgroundColor: Color = Color.Black,
45 | ringBorderStrokeColor: Color = Color.Black,
46 | ringBorderStrokeWidth: Dp = 4.dp,
47 | selectionRadius: Dp = 8.dp,
48 | onColorChange: (Color, String) -> Unit
49 | ) {
50 |
51 | var inputColorModel by remember { mutableStateOf(ColorModel.HSL) }
52 |
53 | val hslArray = ColorUtil.colorToHSL(initialColor)
54 |
55 | var hue by remember { mutableStateOf(hslArray[0]) }
56 | var saturation by remember { mutableStateOf(hslArray[1]) }
57 | var lightness by remember { mutableStateOf(hslArray[2]) }
58 | var alpha by remember { mutableStateOf(initialColor.alpha) }
59 |
60 | val currentColor =
61 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = alpha)
62 |
63 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
64 |
65 | Column(
66 | modifier = modifier,
67 | horizontalAlignment = Alignment.CenterHorizontally,
68 | verticalArrangement = Arrangement.Center
69 | ) {
70 |
71 | Spacer(modifier = Modifier.height(10.dp))
72 |
73 | // Initial and Current Colors
74 | ColorDisplayRoundedRect(
75 | modifier = Modifier
76 | .fillMaxWidth()
77 | .padding(horizontal = 50.dp, vertical = 10.dp),
78 | initialColor = initialColor,
79 | currentColor = currentColor
80 | )
81 |
82 | Box(contentAlignment = Alignment.Center) {
83 |
84 | // Ring Shaped Hue Selector
85 | SelectorRingHue(
86 | modifier = Modifier.fillMaxWidth(1f),
87 | hue = hue,
88 | outerRadiusFraction = ringOuterRadiusFraction,
89 | innerRadiusFraction = ringInnerRadiusFraction,
90 | backgroundColor = ringBackgroundColor,
91 | borderStrokeColor = ringBorderStrokeColor,
92 | borderStrokeWidth = ringBorderStrokeWidth,
93 | selectionRadius = selectionRadius
94 | ) { hueChange ->
95 | hue = hueChange
96 | }
97 |
98 | // Diamond Shaped Saturation and Lightness Selector
99 | SelectorDiamondSaturationLightnessHSL(
100 | modifier = Modifier.fillMaxWidth(ringInnerRadiusFraction * .9f),
101 | hue = hue,
102 | saturation = saturation,
103 | lightness = lightness,
104 | selectionRadius = selectionRadius
105 | ) { s, l ->
106 | saturation = s
107 | lightness = l
108 | }
109 | }
110 |
111 | // HSL-HSV-RGB Color Model Change Tabs
112 | ColorModelChangeTabRow(
113 | modifier = Modifier.width(350.dp),
114 | colorModel = inputColorModel,
115 | onColorModelChange = {
116 | inputColorModel = it
117 | }
118 | )
119 | // HSL-HSV-RGB Sliders
120 | CompositeSliderPanel(
121 | modifier = Modifier.padding(start = 10.dp, end = 7.dp),
122 | compositeColor = ColorHSL(
123 | hue = hue,
124 | saturation = saturation,
125 | lightness = lightness,
126 | alpha = alpha
127 | ),
128 | onColorChange = {
129 | (it as? ColorHSL)?.let { color ->
130 | hue = color.hue
131 | saturation = color.saturation
132 | lightness = color.lightness
133 | alpha = color.alpha
134 | }
135 | },
136 | showAlphaSlider = true,
137 | inputColorModel = inputColorModel,
138 | outputColorModel = ColorModel.HSL
139 | )
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRingHEX.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.unit.Dp
9 | import androidx.compose.ui.unit.dp
10 | import com.smarttoolfactory.colorpicker.selector.SelectorRectSaturationValueHSV
11 | import com.smarttoolfactory.colorpicker.selector.SelectorRingHue
12 | import com.smarttoolfactory.colorpicker.slider.CompositeSliderPanel
13 | import com.smarttoolfactory.colorpicker.widget.HexTextFieldWithCircleDisplay
14 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
15 |
16 |
17 | /**
18 | * ColorPicker with [SelectorRingHue] hue selector and [SelectorRectSaturationValueHSV]
19 | * saturation lightness Selector that uses [HSV](https://en.wikipedia.org/wiki/HSL_and_HSV)
20 | * color model as base.
21 | * This color picker has tabs section that can be changed between
22 | * HSL, HSV and RGB color models and color can be set using [CompositeSliderPanel] which contains
23 | * sliders for each color models.
24 | *
25 | * @param initialColor color that is passed to this picker initially.
26 | * @param selectionRadius radius of white and black circle selector.
27 | * @param onColorChange callback that is triggered when [Color] is changed using [SelectorRingHue],
28 | * [SelectorRectSaturationValueHSV] or [CompositeSliderPanel]
29 | */
30 | @Composable
31 | fun ColorPickerRingRectHex(
32 | modifier: Modifier = Modifier,
33 | selectionRadius: Dp = 8.dp,
34 | initialColor: Color,
35 | onColorChange: (Color, String) -> Unit
36 | ) {
37 | val hsvArray = ColorUtil.colorToHSV(initialColor)
38 |
39 | var hue by remember { mutableStateOf(hsvArray[0]) }
40 | var saturation by remember { mutableStateOf(hsvArray[1]) }
41 | var value by remember { mutableStateOf(hsvArray[2]) }
42 |
43 | val currentColor = Color.hsv(
44 | hue = hue,
45 | saturation = saturation,
46 | value = value
47 | )
48 |
49 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
50 |
51 | Column(
52 | modifier = modifier,
53 | horizontalAlignment = Alignment.CenterHorizontally
54 | ) {
55 |
56 | Box(contentAlignment = Alignment.Center) {
57 |
58 | // Ring Shaped Hue Selector
59 | SelectorRingHue(
60 | modifier = Modifier.fillMaxWidth(1f),
61 | hue = hue,
62 | outerRadiusFraction = .95f,
63 | innerRadiusFraction = .75f,
64 | backgroundColor = Color.Transparent,
65 | borderStrokeColor = Color.Transparent,
66 | borderStrokeWidth = 0.dp,
67 | selectionRadius = selectionRadius,
68 | ) { hueChange ->
69 | hue = hueChange
70 | }
71 |
72 | // Rect Shaped Saturation and Lightness Selector
73 | SelectorRectSaturationValueHSV(
74 | modifier = Modifier
75 | .fillMaxWidth(.5f)
76 | .aspectRatio(1f),
77 | hue = hue,
78 | saturation = saturation,
79 | value = value,
80 | selectionRadius = selectionRadius
81 | ) { s, v ->
82 | saturation = s
83 | value = v
84 | }
85 | }
86 |
87 | HexTextFieldWithCircleDisplay(
88 | modifier = Modifier.padding(8.dp),
89 | color = currentColor,
90 | onColorChange = {
91 | val hsvArrayNew = ColorUtil.colorToHSV(it)
92 | hue = hsvArrayNew[0]
93 | saturation = hsvArrayNew[1]
94 | value = hsvArrayNew[2]
95 | }
96 | )
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRingRectHSL.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.unit.Dp
9 | import androidx.compose.ui.unit.dp
10 | import com.smarttoolfactory.colorpicker.model.ColorHSL
11 | import com.smarttoolfactory.colorpicker.model.ColorModel
12 | import com.smarttoolfactory.colorpicker.selector.SelectorDiamondSaturationLightnessHSL
13 | import com.smarttoolfactory.colorpicker.selector.SelectorRectSaturationLightnessHSL
14 | import com.smarttoolfactory.colorpicker.selector.SelectorRingHue
15 | import com.smarttoolfactory.colorpicker.slider.CompositeSliderPanel
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayRoundedRect
17 | import com.smarttoolfactory.colorpicker.widget.ColorModelChangeTabRow
18 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
19 | import com.smarttoolfactory.extendedcolors.util.ColorUtil.colorToHSL
20 |
21 | /**
22 | * ColorPicker with [SelectorRingHue] hue selector and [SelectorRectSaturationLightnessHSL]
23 | * saturation lightness Selector that uses [HSL](https://en.wikipedia.org/wiki/HSL_and_HSV)
24 | * color model as base.
25 | * This color picker has tabs section that can be changed between
26 | * HSL, HSV and RGB color models and color can be set using [CompositeSliderPanel] which contains
27 | * sliders for each color models.
28 | *
29 | * @param initialColor color that is passed to this picker initially.
30 | * @param ringOuterRadiusFraction outer radius of [SelectorRingHue].
31 | * @param ringInnerRadiusFraction inner radius of [SelectorRingHue].
32 | * @param ringBackgroundColor background from center to inner radius of [SelectorRingHue].
33 | * @param ringBorderStrokeColor stroke color for drawing borders around inner or outer radius.
34 | * @param ringBorderStrokeWidth stroke width of borders.
35 | * @param selectionRadius radius of white and black circle selector.
36 | * @param onColorChange callback that is triggered when [Color] is changed using [SelectorRingHue],
37 | * [SelectorDiamondSaturationLightnessHSL] or [CompositeSliderPanel]
38 | */
39 | @Composable
40 | fun ColorPickerRingRectHSL(
41 | modifier: Modifier = Modifier,
42 | initialColor: Color,
43 | ringOuterRadiusFraction: Float = .9f,
44 | ringInnerRadiusFraction: Float = .6f,
45 | ringBackgroundColor: Color = Color.Transparent,
46 | ringBorderStrokeColor: Color = Color.Black,
47 | ringBorderStrokeWidth: Dp = 4.dp,
48 | selectionRadius: Dp = 8.dp,
49 | onColorChange: (Color, String) -> Unit
50 | ) {
51 |
52 | var inputColorModel by remember { mutableStateOf(ColorModel.HSL) }
53 |
54 | val hslArray = colorToHSL(initialColor)
55 |
56 | var hue by remember { mutableStateOf(hslArray[0]) }
57 | var saturation by remember { mutableStateOf(hslArray[1]) }
58 | var lightness by remember { mutableStateOf(hslArray[2]) }
59 | var alpha by remember { mutableStateOf(initialColor.alpha) }
60 |
61 | val currentColor =
62 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = alpha)
63 |
64 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
65 |
66 | Column(
67 | modifier = modifier,
68 | horizontalAlignment = Alignment.CenterHorizontally,
69 | verticalArrangement = Arrangement.Center
70 | ) {
71 |
72 | Spacer(modifier = Modifier.height(10.dp))
73 |
74 | // Initial and Current Colors
75 | ColorDisplayRoundedRect(
76 | modifier = Modifier
77 | .fillMaxWidth()
78 | .padding(horizontal = 50.dp, vertical = 10.dp),
79 | initialColor = initialColor,
80 | currentColor = currentColor
81 | )
82 |
83 | Box(contentAlignment = Alignment.Center) {
84 |
85 | // Ring Shaped Hue Selector
86 | SelectorRingHue(
87 | modifier = Modifier.fillMaxWidth(1f),
88 | hue = hue,
89 | outerRadiusFraction = ringOuterRadiusFraction,
90 | innerRadiusFraction = ringInnerRadiusFraction,
91 | backgroundColor = ringBackgroundColor,
92 | borderStrokeColor = ringBorderStrokeColor,
93 | borderStrokeWidth = ringBorderStrokeWidth,
94 | selectionRadius = selectionRadius,
95 | ) { hueChange ->
96 | hue = hueChange
97 | }
98 |
99 | // Rect Shaped Saturation and Lightness Selector
100 | SelectorRectSaturationLightnessHSL(
101 | modifier = Modifier
102 | .fillMaxWidth(ringInnerRadiusFraction * .65f)
103 | .aspectRatio(1f),
104 | hue = hue,
105 | saturation = saturation,
106 | lightness = lightness,
107 | selectionRadius = selectionRadius
108 | ) { s, l ->
109 | saturation = s
110 | lightness = l
111 | }
112 | }
113 |
114 | // HSL-HSV-RGB Color Model Change Tabs
115 | ColorModelChangeTabRow(
116 | modifier = Modifier.width(350.dp),
117 | colorModel = inputColorModel,
118 | onColorModelChange = {
119 | inputColorModel = it
120 | }
121 | )
122 |
123 | // HSL-HSV-RGB Sliders
124 | CompositeSliderPanel(
125 | modifier = Modifier.padding(start = 10.dp, end = 7.dp),
126 | compositeColor = ColorHSL(
127 | hue = hue,
128 | saturation = saturation,
129 | lightness = lightness,
130 | alpha = alpha
131 | ),
132 | onColorChange = {
133 | (it as? ColorHSL)?.let { color ->
134 | hue = color.hue
135 | saturation = color.saturation
136 | lightness = color.lightness
137 | alpha = color.alpha
138 | }
139 | },
140 | showAlphaSlider = true,
141 | inputColorModel = inputColorModel,
142 | outputColorModel = ColorModel.HSL
143 | )
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/ColorPickerRingRectHSV.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.unit.Dp
9 | import androidx.compose.ui.unit.dp
10 | import com.smarttoolfactory.colorpicker.model.ColorHSV
11 | import com.smarttoolfactory.colorpicker.model.ColorModel
12 | import com.smarttoolfactory.colorpicker.selector.SelectorRectSaturationValueHSV
13 | import com.smarttoolfactory.colorpicker.selector.SelectorRingHue
14 | import com.smarttoolfactory.colorpicker.slider.CompositeSliderPanel
15 |
16 | import com.smarttoolfactory.colorpicker.widget.ColorDisplayRoundedRect
17 | import com.smarttoolfactory.colorpicker.widget.ColorModelChangeTabRow
18 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
19 | import com.smarttoolfactory.extendedcolors.util.ColorUtil.colorToHSV
20 |
21 | /**
22 | * ColorPicker with [SelectorRingHue] hue selector and [SelectorRectSaturationValueHSV]
23 | * saturation lightness Selector that uses [HSV](https://en.wikipedia.org/wiki/HSL_and_HSV)
24 | * color model as base.
25 | * This color picker has tabs section that can be changed between
26 | * HSL, HSV and RGB color models and color can be set using [CompositeSliderPanel] which contains
27 | * sliders for each color models.
28 | *
29 | * @param initialColor color that is passed to this picker initially.
30 | * @param ringOuterRadiusFraction outer radius of [SelectorRingHue].
31 | * @param ringInnerRadiusFraction inner radius of [SelectorRingHue].
32 | * @param ringBackgroundColor background from center to inner radius of [SelectorRingHue].
33 | * @param ringBorderStrokeColor stroke color for drawing borders around inner or outer radius.
34 | * @param ringBorderStrokeWidth stroke width of borders.
35 | * @param selectionRadius radius of white and black circle selector.
36 | * @param onColorChange callback that is triggered when [Color] is changed using [SelectorRingHue],
37 | * [SelectorRectSaturationValueHSV] or [CompositeSliderPanel]
38 | */
39 | @Composable
40 | fun ColorPickerRingRectHSV(
41 | modifier: Modifier = Modifier,
42 | initialColor: Color,
43 | ringOuterRadiusFraction: Float = .9f,
44 | ringInnerRadiusFraction: Float = .6f,
45 | ringBackgroundColor: Color = Color.Transparent,
46 | ringBorderStrokeColor: Color = Color.Black,
47 | ringBorderStrokeWidth: Dp = 4.dp,
48 | selectionRadius: Dp = 8.dp,
49 | onColorChange: (Color, String) -> Unit
50 | ) {
51 |
52 | var inputColorModel by remember { mutableStateOf(ColorModel.HSV) }
53 |
54 | val hsvArray = colorToHSV(initialColor)
55 |
56 | var hue by remember { mutableStateOf(hsvArray[0]) }
57 | var saturation by remember { mutableStateOf(hsvArray[1]) }
58 | var value by remember { mutableStateOf(hsvArray[2]) }
59 | var alpha by remember { mutableStateOf(initialColor.alpha) }
60 |
61 | val currentColor =
62 | Color.hsv(hue = hue, saturation = saturation, value = value, alpha = alpha)
63 |
64 | onColorChange(currentColor, ColorUtil.colorToHexAlpha(currentColor))
65 |
66 | Column(
67 | modifier = modifier,
68 | horizontalAlignment = Alignment.CenterHorizontally,
69 | verticalArrangement = Arrangement.Center
70 | ) {
71 |
72 | Spacer(modifier = Modifier.height(10.dp))
73 |
74 | // Initial and Current Colors
75 | ColorDisplayRoundedRect(
76 | modifier = Modifier
77 | .fillMaxWidth()
78 | .padding(horizontal = 50.dp, vertical = 10.dp),
79 | initialColor = initialColor,
80 | currentColor = currentColor
81 | )
82 |
83 | Box(contentAlignment = Alignment.Center) {
84 |
85 | // Ring Shaped Hue Selector
86 | SelectorRingHue(
87 | modifier = Modifier.fillMaxWidth(1f),
88 | hue = hue,
89 | outerRadiusFraction = ringOuterRadiusFraction,
90 | innerRadiusFraction = ringInnerRadiusFraction,
91 | backgroundColor = ringBackgroundColor,
92 | borderStrokeColor = ringBorderStrokeColor,
93 | borderStrokeWidth = ringBorderStrokeWidth,
94 | selectionRadius = selectionRadius,
95 | ) { hueChange ->
96 | hue = hueChange
97 | }
98 |
99 | // Rect Shaped Saturation and Lightness Selector
100 | SelectorRectSaturationValueHSV(
101 | modifier = Modifier
102 | .fillMaxWidth(ringInnerRadiusFraction * .65f)
103 | .aspectRatio(1f),
104 | hue = hue,
105 | saturation = saturation,
106 | value = value,
107 | selectionRadius = selectionRadius
108 | ) { s, v ->
109 | saturation = s
110 | value = v
111 | }
112 | }
113 |
114 | // HSL-HSV-RGB Color Model Change Tabs
115 | ColorModelChangeTabRow(
116 | modifier = Modifier.width(350.dp),
117 | colorModel = inputColorModel,
118 | onColorModelChange = {
119 | inputColorModel = it
120 | }
121 | )
122 |
123 | // HSL-HSV-RGB Sliders
124 | CompositeSliderPanel(
125 | modifier = Modifier.padding(start = 10.dp, end = 7.dp),
126 | compositeColor = ColorHSV(
127 | hue = hue,
128 | saturation = saturation,
129 | value = value,
130 | alpha = alpha
131 | ),
132 | onColorChange = {
133 | (it as? ColorHSV)?.let { color ->
134 | hue = color.hue
135 | saturation = color.saturation
136 | value = color.value
137 | alpha = color.alpha
138 | }
139 |
140 | },
141 | showAlphaSlider = true,
142 | inputColorModel = inputColorModel,
143 | outputColorModel = ColorModel.HSV
144 | )
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/picker/M2ColorPicker.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.picker
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.foundation.lazy.LazyColumn
7 | import androidx.compose.foundation.lazy.itemsIndexed
8 | import androidx.compose.foundation.shape.CircleShape
9 | import androidx.compose.foundation.shape.RoundedCornerShape
10 | import androidx.compose.material.Divider
11 | import androidx.compose.material.Text
12 | import androidx.compose.runtime.*
13 | import androidx.compose.ui.Alignment
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.draw.clip
16 | import androidx.compose.ui.draw.shadow
17 | import androidx.compose.ui.graphics.Color
18 | import androidx.compose.ui.graphics.graphicsLayer
19 | import androidx.compose.ui.text.font.FontWeight
20 | import androidx.compose.ui.unit.dp
21 | import androidx.compose.ui.unit.sp
22 | import com.smarttoolfactory.extendedcolors.ColorSwatch
23 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
24 |
25 | /**
26 | * Color picker that displays header colors on left and material primary and if available
27 | * accent variants in a list with shade and hex display.
28 | * @param onColorChange callback that returns Color
29 | */
30 | @Composable
31 | fun M2ColorPicker(onColorChange: (Color) -> Unit) {
32 |
33 | var headerIndex by remember { mutableStateOf(0) }
34 | var selectedColorIndex by remember { mutableStateOf(-1) }
35 |
36 |
37 | Row(modifier = Modifier.fillMaxSize()) {
38 | LazyColumn(
39 | verticalArrangement = Arrangement.spacedBy(8.dp),
40 | contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp)
41 | ) {
42 | itemsIndexed(ColorSwatch.primaryHeaderColors) { index: Int, item: Color ->
43 | ColorDisplay(
44 | modifier = Modifier
45 | .padding(horizontal = 2.dp)
46 | .clip(CircleShape)
47 | .size(60.dp)
48 | .clickable {
49 | headerIndex = index
50 | }, color = item
51 | )
52 | }
53 | }
54 |
55 | val colorSwatch: LinkedHashMap = ColorSwatch.primaryColorSwatches[headerIndex]
56 |
57 | val keys: MutableList = colorSwatch.keys.toMutableList()
58 | val colors: MutableList = colorSwatch.values.toMutableList()
59 |
60 | val result: Result> =
61 | runCatching { ColorSwatch.accentColorSwatches[headerIndex] }
62 |
63 | if (result.isSuccess) {
64 | result.getOrNull()?.let { accentColorSwatch: LinkedHashMap ->
65 | val accentKeys = accentColorSwatch.keys.toList()
66 | val accentColors = accentColorSwatch.values.toList()
67 | keys.addAll(accentKeys)
68 | colors.addAll(accentColors)
69 | }
70 | }
71 | Divider(
72 | modifier = Modifier
73 | .fillMaxHeight()
74 | .width(1.dp)
75 | .background(Color.LightGray)
76 | )
77 |
78 | LazyColumn(
79 | modifier = Modifier.fillMaxWidth(),
80 | verticalArrangement = Arrangement.spacedBy(10.dp),
81 | contentPadding = PaddingValues(horizontal = 10.dp, vertical = 8.dp)
82 | ) {
83 | itemsIndexed(colors) { index: Int, item: Color ->
84 | Column {
85 | if (index == 0 || index == 10) {
86 | Text(
87 | modifier = Modifier.padding(8.dp),
88 | text = if (index == 0) "Primary" else "Accent",
89 | fontSize = 24.sp,
90 | fontWeight = FontWeight.Bold
91 | )
92 | }
93 | ColorRowWithInfo(
94 | modifier =
95 | Modifier
96 | .graphicsLayer {
97 | scaleY = if (selectedColorIndex == index) 1.03f else 1f
98 | scaleX = if (selectedColorIndex == index) 1.03f else 1f
99 | }
100 | .shadow(2.dp, RoundedCornerShape(4.dp))
101 | .fillMaxWidth()
102 | .clickable {
103 | selectedColorIndex = index
104 | onColorChange(item)
105 | },
106 | title = keys[index].toString(),
107 | color = item,
108 | textColor = if (index < 5 || index > 9) Color.Black else Color.White
109 | )
110 | }
111 | }
112 | }
113 | }
114 | }
115 |
116 | @Composable
117 | fun ColorDisplay(modifier: Modifier, color: Color) {
118 | Box(
119 | modifier = modifier
120 | .background(color)
121 | )
122 | }
123 |
124 | @Composable
125 | fun ColorRowWithInfo(
126 | modifier: Modifier,
127 | title: String,
128 | color: Color,
129 | textColor: Color
130 | ) {
131 | Row(
132 | modifier
133 | .background(color)
134 | .padding(16.dp),
135 | verticalAlignment = Alignment.CenterVertically
136 | ) {
137 | Text(text = title, color = textColor, fontSize = 22.sp)
138 | Spacer(modifier = Modifier.weight(1f))
139 | Text(
140 | text = ColorUtil.colorToHex(color),
141 | color = textColor,
142 | fontSize = 22.sp,
143 | fontWeight = FontWeight.Bold
144 | )
145 | }
146 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/DrawColorSelectionCircle.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.selector
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.graphics.Color
5 | import androidx.compose.ui.graphics.drawscope.DrawScope
6 | import androidx.compose.ui.graphics.drawscope.Stroke
7 |
8 | /**
9 | * Draw selection circle with white and black colors
10 | * Stroke draws half in and half out of the current radius.
11 | * With 200 radius 20 stroke width starts from 190 and ends at 210.
12 | * @param center center of the selection circle
13 | * @param radius radius of the selection circle
14 | */
15 | fun DrawScope.drawHueSelectionCircle(
16 | center: Offset,
17 | radius: Float
18 | ) {
19 | drawCircle(
20 | Color.White,
21 | radius = radius,
22 | center = center,
23 | style = Stroke(width = radius / 4)
24 | )
25 |
26 | drawCircle(
27 | Color.Black,
28 | radius = radius + radius / 8,
29 | center = center,
30 | style = Stroke(width = radius / 8)
31 | )
32 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/SelectorCircleHueSaturation.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.selector
2 |
3 | import androidx.compose.foundation.Canvas
4 | import androidx.compose.foundation.layout.BoxWithConstraints
5 | import androidx.compose.foundation.layout.aspectRatio
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.geometry.Offset
9 | import androidx.compose.ui.graphics.Brush
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.input.pointer.pointerInput
12 | import androidx.compose.ui.platform.LocalDensity
13 | import androidx.compose.ui.unit.Dp
14 | import com.smarttoolfactory.colorpicker.ui.gradientColorScaleHSVReversed
15 | import com.smarttoolfactory.colorpicker.util.calculateAngleFomLocalCoordinates
16 | import com.smarttoolfactory.colorpicker.util.calculateDistanceFromCenter
17 | import com.smarttoolfactory.colorpicker.util.calculatePositionFromAngleAndDistance
18 | import com.smarttoolfactory.gesture.detectMotionEvents
19 |
20 | /**
21 | * Circle Hue and Saturation picker for
22 | * [HSV](https://en.wikipedia.org/wiki/HSL_and_HSV) color model. Angle is between touch point
23 | * and center of this selector is returned as [hue], distance from center as [saturation].
24 | * @param hue is in [0f..360f] of HSV color
25 | * @param saturation is in [0f..1f] of HSV color
26 | * @param selectionRadius radius of selection circle that moves based on touch position
27 | * @param onChange callback that returns [hue] and [saturation]
28 | * when position of touch in this selector has changed.
29 | */
30 | @Composable
31 | fun SelectorCircleHueSaturationHSV(
32 | modifier: Modifier = Modifier,
33 | hue: Float,
34 | saturation: Float,
35 | selectionRadius: Dp = Dp.Unspecified,
36 | onChange: (Float, Float) -> Unit
37 | ) {
38 | BoxWithConstraints(modifier) {
39 |
40 | val density = LocalDensity.current.density
41 |
42 | // Check if user touches between the valid area of circle
43 | var isTouched by remember { mutableStateOf(false) }
44 |
45 | val width = constraints.maxWidth.toFloat()
46 | val radius = width / 2
47 |
48 | // Center position of color picker
49 | val center = Offset(radius, radius)
50 |
51 | var currentPosition by remember {
52 | mutableStateOf(center)
53 | }
54 |
55 | currentPosition = calculatePositionFromAngleAndDistance(
56 | distance = saturation * radius,
57 | angle = hue,
58 | center = center
59 | )
60 |
61 | val selectorRadius =
62 | if (selectionRadius != Dp.Unspecified) selectionRadius.value * density
63 | else width * .04f
64 |
65 |
66 | val canvasModifier = Modifier
67 | .pointerInput(Unit) {
68 | detectMotionEvents(
69 | onDown = {
70 | val position = it.position
71 |
72 | // Distance from center to touch point
73 | val distance =
74 | calculateDistanceFromCenter(center, position).coerceAtMost(radius)
75 |
76 | // if distance is between inner and outer radius then we touched valid area
77 | isTouched = (distance < radius)
78 |
79 | if (isTouched) {
80 | val hueChange = calculateAngleFomLocalCoordinates(center, position)
81 | val saturationChange = (distance / radius).coerceIn(0f, 1f)
82 | onChange(hueChange, saturationChange)
83 | it.consume()
84 | }
85 |
86 | },
87 | onMove = {
88 | if (isTouched) {
89 |
90 | val position = it.position
91 | val hueChange = calculateAngleFomLocalCoordinates(center, position)
92 | val distance =
93 | calculateDistanceFromCenter(center, position).coerceAtMost(radius)
94 |
95 | val saturationChange = (distance / radius).coerceIn(0f, 1f)
96 | onChange(hueChange, saturationChange)
97 | it.consume()
98 | }
99 |
100 | },
101 | onUp = {
102 | if (isTouched) {
103 | it.consume()
104 | }
105 | isTouched = false
106 |
107 | },
108 | delayAfterDownInMillis = 20
109 | )
110 | }
111 |
112 | SelectorCircleImpl(
113 | modifier = canvasModifier,
114 | selectorPosition = currentPosition,
115 | selectorRadius = selectorRadius
116 | )
117 | }
118 | }
119 |
120 | @Composable
121 | private fun SelectorCircleImpl(
122 | modifier: Modifier,
123 | selectorPosition: Offset,
124 | selectorRadius: Float
125 | ) {
126 | val colorScaleHSVSweep = remember { Brush.sweepGradient(gradientColorScaleHSVReversed) }
127 | val whiteToTransparentRadial = remember {
128 | Brush.radialGradient(
129 | colors = listOf(Color.White, Color(0x00FFFFFF))
130 | )
131 | }
132 | Canvas(
133 | modifier = modifier.aspectRatio(1f)
134 |
135 | ) {
136 | // Draw hue selection circle with sweep gradient
137 | drawCircle(colorScaleHSVSweep)
138 | drawCircle(whiteToTransparentRadial)
139 | drawHueSelectionCircle(center = selectorPosition, radius = selectorRadius)
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/SelectorRectSaturationLightnessValue.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.selector
2 |
3 | import androidx.compose.foundation.Canvas
4 | import androidx.compose.foundation.layout.BoxWithConstraints
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.geometry.Offset
9 | import androidx.compose.ui.graphics.BlendMode
10 | import androidx.compose.ui.graphics.Brush
11 | import androidx.compose.ui.input.pointer.pointerInput
12 | import androidx.compose.ui.platform.LocalDensity
13 | import androidx.compose.ui.unit.Dp
14 | import com.smarttoolfactory.colorpicker.model.ColorModel
15 | import com.smarttoolfactory.colorpicker.ui.brush.lightnessGradient
16 | import com.smarttoolfactory.colorpicker.ui.brush.saturationHSLGradient
17 | import com.smarttoolfactory.colorpicker.ui.brush.saturationHSVGradient
18 | import com.smarttoolfactory.colorpicker.ui.brush.valueGradient
19 | import com.smarttoolfactory.colorpicker.util.drawBlendingRectGradient
20 | import com.smarttoolfactory.gesture.detectMotionEvents
21 |
22 | /**
23 | * Rectangle Saturation and Lightness selector for
24 | * [HSL](https://en.wikipedia.org/wiki/HSL_and_HSV) color model
25 | * @param hue is in [0f..360f] of HSL color
26 | * @param lightness is in [0f..1f] of HSL color
27 | * @param selectionRadius radius of selection circle that moves based on touch position
28 | * @param onChange callback that returns [hue] and [lightness]
29 | * when position of touch in this selector has changed.
30 | */
31 | @Composable
32 | fun SelectorRectSaturationLightnessHSL(
33 | modifier: Modifier = Modifier,
34 | hue: Float,
35 | saturation: Float = 0.5f,
36 | lightness: Float = 0.5f,
37 | selectionRadius: Dp = Dp.Unspecified,
38 | onChange: (Float, Float) -> Unit
39 | ) {
40 | val lightnessGradient = lightnessGradient(hue)
41 | val hueSaturation = saturationHSLGradient(hue)
42 |
43 | SelectorRect(
44 | modifier = modifier,
45 | saturation = saturation,
46 | property = lightness,
47 | brushSrc = hueSaturation,
48 | brushDst = lightnessGradient,
49 | selectionRadius = selectionRadius,
50 | onChange = onChange,
51 | colorModel = ColorModel.HSL
52 | )
53 | }
54 |
55 | /**
56 | * Rectangle Saturation and Vale selector for
57 | * [HSV](https://en.wikipedia.org/wiki/HSL_and_HSV) color model
58 | * @param hue is in [0f..360f] of HSL color
59 | * @param value is in [0f..1f] of HSL color
60 | * @param selectionRadius radius of selection circle that moves based on touch position
61 | * @param onChange callback that returns [hue] and [value]
62 | * when position of touch in this selector has changed.
63 | */
64 | @Composable
65 | fun SelectorRectSaturationValueHSV(
66 | modifier: Modifier = Modifier,
67 | hue: Float,
68 | saturation: Float = 0.5f,
69 | value: Float = 0.5f,
70 | selectionRadius: Dp = Dp.Unspecified,
71 | onChange: (Float, Float) -> Unit
72 | ) {
73 |
74 | val valueGradient = valueGradient(hue)
75 | val hueSaturation = saturationHSVGradient(hue)
76 |
77 | SelectorRect(
78 | modifier = modifier,
79 | saturation = saturation,
80 | property = value,
81 | brushSrc = hueSaturation,
82 | brushDst = valueGradient,
83 | selectionRadius = selectionRadius,
84 | onChange = onChange,
85 | colorModel = ColorModel.HSV
86 | )
87 | }
88 |
89 | @Composable
90 | private fun SelectorRect(
91 | modifier: Modifier = Modifier,
92 | saturation: Float = 0.5f,
93 | property: Float = 0.5f,
94 | brushSrc: Brush,
95 | brushDst: Brush,
96 | selectionRadius: Dp = Dp.Unspecified,
97 | onChange: (Float, Float) -> Unit,
98 | colorModel: ColorModel
99 | ) {
100 |
101 | BoxWithConstraints(modifier) {
102 |
103 | val density = LocalDensity.current.density
104 | val width = constraints.maxWidth.toFloat()
105 | val height = constraints.maxHeight.toFloat()
106 |
107 | // Center position of color picker
108 | val center = Offset(width / 2, height / 2)
109 |
110 | /**
111 | * Circle selector radius for setting [saturation] and [property] by gesture
112 | */
113 | val selectorRadius =
114 | if (selectionRadius != Dp.Unspecified) selectionRadius.value * density
115 | else width.coerceAtMost(height) * .04f
116 |
117 | /**
118 | * Current position is initially set by [saturation] and [property] that is bound
119 | * in diamond since (1,1) points to bottom left corner of a rectangle but it's bounded
120 | * in diamond by [setSelectorPositionFromColorParams].
121 | * When user touches anywhere in diamond current position is updated and
122 | * this composable is recomposed
123 | */
124 | var currentPosition by remember {
125 | mutableStateOf(center)
126 | }
127 |
128 | val posX = saturation * width
129 | val posY = (1 - property) * height
130 | currentPosition = Offset(posX, posY)
131 |
132 |
133 | val canvasModifier = Modifier
134 | .fillMaxSize()
135 | .pointerInput(Unit) {
136 | detectMotionEvents(
137 | onDown = {
138 | val position = it.position
139 | val saturationChange = (position.x / width).coerceIn(0f, 1f)
140 | val valueChange = (1 - (position.y / height)).coerceIn(0f, 1f)
141 | onChange(saturationChange, valueChange)
142 | it.consume()
143 |
144 | },
145 | onMove = {
146 | val position = it.position
147 | val saturationChange = (position.x / width).coerceIn(0f, 1f)
148 | val valueChange = (1 - (position.y / height)).coerceIn(0f, 1f)
149 | onChange(saturationChange, valueChange)
150 | it.consume()
151 | },
152 | delayAfterDownInMillis = 20
153 | )
154 | }
155 |
156 | SelectorRectImpl(
157 | modifier = canvasModifier,
158 | brushSrc = brushSrc,
159 | brushDst = brushDst,
160 | selectorPosition = currentPosition,
161 | selectorRadius = selectorRadius,
162 | colorModel = colorModel
163 | )
164 | }
165 | }
166 |
167 | @Composable
168 | private fun SelectorRectImpl(
169 | modifier: Modifier,
170 | brushSrc: Brush,
171 | brushDst: Brush,
172 | selectorPosition: Offset,
173 | selectorRadius: Float,
174 | colorModel: ColorModel
175 | ) {
176 |
177 | Canvas(modifier = modifier) {
178 | drawBlendingRectGradient(
179 | dst = brushDst,
180 | src = brushSrc,
181 | blendMode = if (colorModel == ColorModel.HSV) BlendMode.Multiply else BlendMode.Overlay
182 | )
183 | // Saturation and Value/Lightness or value selector
184 | drawHueSelectionCircle(
185 | center = selectorPosition,
186 | radius = selectorRadius
187 | )
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/gradient/SelectorGradientRadial.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.selector.gradient
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.runtime.*
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.geometry.Offset
8 | import com.smarttoolfactory.colorpicker.model.GradientColorState
9 | import com.smarttoolfactory.colorpicker.ui.Pink400
10 | import com.smarttoolfactory.colorpicker.widget.ExpandableColumnWithTitle
11 |
12 |
13 | @Composable
14 | internal fun RadialGradientSelection(
15 | gradientColorState: GradientColorState,
16 | onRadialDimensionsChange: (Offset, Float) -> Unit
17 | ) {
18 |
19 | var centerX by remember { mutableStateOf(gradientColorState.centerFriction.x) }
20 | var centerY by remember { mutableStateOf(gradientColorState.centerFriction.y) }
21 | var radiusFriction by remember { mutableStateOf(gradientColorState.radiusFriction) }
22 |
23 | onRadialDimensionsChange(Offset(centerX, centerY), radiusFriction)
24 |
25 | ExpandableColumnWithTitle(
26 | title = "Gradient Center/Radius",
27 | color = Pink400,
28 | initialExpandState = false
29 | ) {
30 | Column {
31 | SliderWithPercent(
32 | modifier = Modifier.fillMaxWidth(),
33 | title = "CenterX",
34 | value = centerX
35 | ) {
36 | centerX = it
37 | }
38 |
39 | SliderWithPercent(
40 | modifier = Modifier.fillMaxWidth(),
41 | title = "CenterY",
42 | value = centerY
43 | ) {
44 | centerY = it
45 | }
46 |
47 | SliderWithPercent(
48 | modifier = Modifier.fillMaxWidth(),
49 | title = "Radius",
50 | value = radiusFriction
51 | ) {
52 | radiusFriction = it
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/gradient/SelectorGradientSweep.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.selector.gradient
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.runtime.*
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.geometry.Offset
8 | import com.smarttoolfactory.colorpicker.model.GradientColorState
9 | import com.smarttoolfactory.colorpicker.ui.Pink400
10 | import com.smarttoolfactory.colorpicker.widget.ExpandableColumnWithTitle
11 |
12 | @Composable
13 | internal fun SweepGradientSelection(
14 | gradientColorState: GradientColorState,
15 | onCenterChange: (Offset) -> Unit
16 | ) {
17 | var centerX by remember { mutableStateOf(gradientColorState.centerFriction.x) }
18 | var centerY by remember { mutableStateOf(gradientColorState.centerFriction.y) }
19 |
20 | onCenterChange(Offset(centerX, centerY))
21 |
22 | ExpandableColumnWithTitle(
23 | title = "Gradient Center",
24 | color = Pink400,
25 | initialExpandState = false
26 | ) {
27 | Column {
28 | SliderWithPercent(
29 | modifier = Modifier.fillMaxWidth(),
30 | title = "CenterX",
31 | value = centerX
32 | ) {
33 | centerX = it
34 | }
35 |
36 | SliderWithPercent(
37 | modifier = Modifier.fillMaxWidth(),
38 | title = "CenterY",
39 | value = centerY
40 | ) {
41 | centerY = it
42 | }
43 | }
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/selector/gradient/SliderWithPercent.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.selector.gradient
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.width
6 | import androidx.compose.material.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.text.font.FontWeight
11 | import androidx.compose.ui.unit.dp
12 | import androidx.compose.ui.unit.sp
13 | import com.smarttoolfactory.colorpicker.ui.Grey400
14 | import com.smarttoolfactory.extendedcolors.util.fractionToIntPercent
15 | import com.smarttoolfactory.slider.SliderBrushColor
16 | import com.smarttoolfactory.slider.ColorfulSlider
17 | import com.smarttoolfactory.slider.MaterialSliderDefaults
18 | import com.smarttoolfactory.slider.ui.InactiveTrackColor
19 |
20 | @Composable
21 | internal fun SliderWithPercent(
22 | modifier: Modifier = Modifier,
23 | title: String,
24 | value: Float,
25 | valueRange: ClosedFloatingPointRange = 0f..1f,
26 | onValueChange: (Float) -> Unit
27 | ) {
28 | Row(
29 | modifier = modifier,
30 | verticalAlignment = Alignment.CenterVertically
31 | ) {
32 |
33 | Text(
34 | modifier = Modifier.width(40.dp),
35 | text = title,
36 | color = Grey400,
37 | fontSize = 12.sp,
38 | fontWeight = FontWeight.Bold
39 | )
40 | Spacer(modifier = Modifier.width(10.dp))
41 | ColorfulSlider(
42 | modifier = Modifier.weight(1f),
43 | value = value,
44 | valueRange = valueRange,
45 | onValueChange = onValueChange,
46 | thumbRadius = 10.dp,
47 | trackHeight = 8.dp,
48 | colors = MaterialSliderDefaults.materialColors(
49 | inactiveTrackColor = SliderBrushColor(InactiveTrackColor)
50 | )
51 | )
52 | Spacer(modifier = Modifier.width(10.dp))
53 | Text(
54 | text = "${value.fractionToIntPercent()}%",
55 | fontSize = 12.sp,
56 | color = Grey400,
57 | fontWeight = FontWeight.Bold,
58 | modifier = Modifier.width(36.dp)
59 | )
60 | }
61 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/ui/Color.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.ui
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Orange400 = Color(0xffFFA726)
6 | val Blue400 = Color(0xff42A5F5)
7 | val Pink400 = Color(0xffEC407A)
8 | val Green400 = Color(0xff66BB6A)
9 | val Red400 = Color(0xffEF5350)
10 | val Yellow400 = Color(0xffFFEE58)
11 | val Purple400 = Color(0xffAB47BC)
12 | val Brown400 = Color(0xff8D6E63)
13 | val BlueGrey400 = Color(0xff78909C)
14 | val Grey400 = Color(0xffBDBDBD)
15 |
16 | val Grey600 = Color(0xff757575)
17 | val Grey800 = Color(0xff424242)
18 | val Grey900 = Color(0xff212121)
19 |
20 | val Gray = Color(0xff808080)
21 | val WhiteTransparent = Color(0x00ffffff)
22 |
23 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/ui/GradientColors.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.ui
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | fun gradientColorScaleRGB(alpha: Float = 1f) = listOf(
6 | Color.Red.copy(alpha = alpha),
7 | Color.Magenta.copy(alpha = alpha),
8 | Color.Blue.copy(alpha = alpha),
9 | Color.Cyan.copy(alpha = alpha),
10 | Color.Green.copy(alpha = alpha),
11 | Color.Yellow.copy(alpha = alpha),
12 | Color.Red.copy(alpha = alpha)
13 | )
14 |
15 | fun gradientColorScaleRGBReversed(alpha: Float = 1f) = listOf(
16 | Color.Red.copy(alpha = alpha),
17 | Color.Yellow.copy(alpha = alpha),
18 | Color.Green.copy(alpha = alpha),
19 | Color.Cyan.copy(alpha = alpha),
20 | Color.Blue.copy(alpha = alpha),
21 | Color.Magenta.copy(alpha = alpha),
22 | Color.Red.copy(alpha = alpha)
23 | )
24 |
25 | fun gradientColorScaleHSV(
26 | saturation: Float = 1f,
27 | value: Float = 1f,
28 | alpha: Float = 1f
29 | ) = listOf(
30 | Color.hsv(hue = 0f, saturation = saturation, value = value, alpha = alpha),
31 | Color.hsv(hue = 60f, saturation = saturation, value = value, alpha = alpha),
32 | Color.hsv(hue = 120f, saturation = saturation, value = value, alpha = alpha),
33 | Color.hsv(hue = 180f, saturation = saturation, value = value, alpha = alpha),
34 | Color.hsv(hue = 240f, saturation = saturation, value = value, alpha = alpha),
35 | Color.hsv(hue = 300f, saturation = saturation, value = value, alpha = alpha),
36 | Color.hsv(hue = 360f, saturation = saturation, value = value, alpha = alpha)
37 | )
38 |
39 | fun gradientColorScaleHSVReversed(
40 | saturation: Float = 1f,
41 | value: Float = 1f,
42 | alpha: Float = 1f
43 | ) = listOf(
44 | Color.hsv(hue = 360f, saturation = saturation, value = value, alpha = alpha),
45 | Color.hsv(hue = 300f, saturation = saturation, value = value, alpha = alpha),
46 | Color.hsv(hue = 240f, saturation = saturation, value = value, alpha = alpha),
47 | Color.hsv(hue = 180f, saturation = saturation, value = value, alpha = alpha),
48 | Color.hsv(hue = 120f, saturation = saturation, value = value, alpha = alpha),
49 | Color.hsv(hue = 60f, saturation = saturation, value = value, alpha = alpha),
50 | Color.hsv(hue = 0f, saturation = saturation, value = value, alpha = alpha)
51 | )
52 |
53 | fun gradientColorScaleHSL(
54 | saturation: Float = 1f,
55 | lightness: Float = .5f,
56 | alpha: Float = 1f
57 | ) = listOf(
58 | Color.hsl(hue = 0f, saturation = saturation, lightness = lightness, alpha = alpha),
59 | Color.hsl(hue = 60f, saturation = saturation, lightness = lightness, alpha = alpha),
60 | Color.hsl(hue = 120f, saturation = saturation, lightness = lightness, alpha = alpha),
61 | Color.hsl(hue = 180f, saturation = saturation, lightness = lightness, alpha = alpha),
62 | Color.hsl(hue = 240f, saturation = saturation, lightness = lightness, alpha = alpha),
63 | Color.hsl(hue = 300f, saturation = saturation, lightness = lightness, alpha = alpha),
64 | Color.hsl(hue = 360f, saturation = saturation, lightness = lightness, alpha = alpha)
65 | )
66 |
67 | fun gradientColorScaleHSLReversed(
68 | saturation: Float = 1f,
69 | lightness: Float = .5f,
70 | alpha: Float = 1f
71 | ) = listOf(
72 | Color.hsl(hue = 360f, saturation = saturation, lightness = lightness, alpha = alpha),
73 | Color.hsl(hue = 300f, saturation = saturation, lightness = lightness, alpha = alpha),
74 | Color.hsl(hue = 240f, saturation = saturation, lightness = lightness, alpha = alpha),
75 | Color.hsl(hue = 180f, saturation = saturation, lightness = lightness, alpha = alpha),
76 | Color.hsl(hue = 120f, saturation = saturation, lightness = lightness, alpha = alpha),
77 | Color.hsl(hue = 60f, saturation = saturation, lightness = lightness, alpha = alpha),
78 | Color.hsl(hue = 0f, saturation = saturation, lightness = lightness, alpha = alpha)
79 | )
80 |
81 | val gradientColorScaleHSV = listOf(
82 | Color.hsv(hue = 0f, saturation = 1f, value = 1f),
83 | Color.hsv(hue = 60f, saturation = 1f, value = 1f),
84 | Color.hsv(hue = 120f, saturation = 1f, value = 1f),
85 | Color.hsv(hue = 180f, saturation = 1f, value = 1f),
86 | Color.hsv(hue = 240f, saturation = 1f, value = 1f),
87 | Color.hsv(hue = 300f, saturation = 1f, value = 1f),
88 | Color.hsv(hue = 360f, saturation = 1f, value = 1f)
89 | )
90 |
91 | val gradientColorScaleRGB = listOf(
92 | Color.Red,
93 | Color.Magenta,
94 | Color.Blue,
95 | Color.Cyan,
96 | Color.Green,
97 | Color.Yellow,
98 | Color.Red
99 | )
100 |
101 | val gradientColorScaleRGBReversed = listOf(
102 | Color.Red,
103 | Color.Yellow,
104 | Color.Green,
105 | Color.Cyan,
106 | Color.Blue,
107 | Color.Magenta,
108 | Color.Red
109 | )
110 |
111 | val gradientColorScaleHSVReversed = listOf(
112 | Color.hsv(hue = 360f, saturation = 1f, value = 1f),
113 | Color.hsv(hue = 300f, saturation = 1f, value = 1f),
114 | Color.hsv(hue = 240f, saturation = 1f, value = 1f),
115 | Color.hsv(hue = 180f, saturation = 1f, value = 1f),
116 | Color.hsv(hue = 120f, saturation = 1f, value = 1f),
117 | Color.hsv(hue = 60f, saturation = 1f, value = 1f),
118 | Color.hsv(hue = 0f, saturation = 1f, value = 1f)
119 | )
120 |
121 | val gradientColorScaleHSL = listOf(
122 | Color.hsl(hue = 0f, saturation = 1f, lightness = .5f),
123 | Color.hsl(hue = 60f, saturation = 1f, lightness = .5f),
124 | Color.hsl(hue = 120f, saturation = 1f, lightness = .5f),
125 | Color.hsl(hue = 180f, saturation = 1f, lightness = .5f),
126 | Color.hsl(hue = 240f, saturation = 1f, lightness = .5f),
127 | Color.hsl(hue = 300f, saturation = 1f, lightness = .5f),
128 | Color.hsl(hue = 360f, saturation = 1f, lightness = .5f)
129 |
130 | )
131 |
132 | val gradientColorScaleHSLReversed = listOf(
133 | Color.hsl(hue = 360f, saturation = 1f, lightness = .5f),
134 | Color.hsl(hue = 300f, saturation = 1f, lightness = .5f),
135 | Color.hsl(hue = 240f, saturation = 1f, lightness = .5f),
136 | Color.hsl(hue = 180f, saturation = 1f, lightness = .5f),
137 | Color.hsl(hue = 120f, saturation = 1f, lightness = .5f),
138 | Color.hsl(hue = 60f, saturation = 1f, lightness = .5f),
139 | Color.hsl(hue = 0f, saturation = 1f, lightness = .5f)
140 | )
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/ui/GradientOffset.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.ui
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.graphics.Brush
5 |
6 |
7 | /**
8 | *
9 | * Get a [GradientOffset] that rotate a gradient clockwise with specified angle in degrees.
10 | * Default value for [GradientOffset] is [GradientAngle.CW0] which is 0 degrees
11 | * that returns a horizontal gradient.
12 | *
13 | * Get start and end offsets that are limited between [0f, Float.POSITIVE_INFINITY] in x and
14 | * y axes wrapped in [GradientOffset].
15 | * Infinity is converted to Composable width on x axis, height on y axis in shader.
16 | *
17 | * Default angle for [Brush.linearGradient] when no offset is 0 degrees in Compose ,
18 | * [Brush.verticalGradient] is [Brush.linearGradient] with 90 degrees.
19 | *
20 | * ```
21 | * 0 degrees
22 | * start = Offset(0f,0f),
23 | * end = Offset(Float.POSITIVE_INFINITY,0f)
24 | *
25 | * 45 degrees
26 | * start = Offset(0f, Float.POSITIVE_INFINITY),
27 | * end = Offset(Float.POSITIVE_INFINITY, 0f)
28 | *
29 | * 90 degrees
30 | * start = Offset(0f, Float.POSITIVE_INFINITY),
31 | * end = Offset.Zero
32 | *
33 | * 135 degrees
34 | * start = Offset.Infinity,
35 | * end = Offset.Zero
36 | *
37 | * 180 degrees
38 | * start = Offset(Float.POSITIVE_INFINITY, 0f),
39 | * end = Offset.Zero,
40 | *
41 | * ```
42 | */
43 | fun GradientOffset(angle: GradientAngle = GradientAngle.CW0): GradientOffset {
44 | return when (angle) {
45 | GradientAngle.CW45 -> GradientOffset(
46 | start = Offset.Zero,
47 | end = Offset.Infinite
48 | )
49 | GradientAngle.CW90 -> GradientOffset(
50 | start = Offset.Zero,
51 | end = Offset(0f, Float.POSITIVE_INFINITY)
52 | )
53 | GradientAngle.CW135 -> GradientOffset(
54 | start = Offset(Float.POSITIVE_INFINITY, 0f),
55 | end = Offset(0f, Float.POSITIVE_INFINITY)
56 | )
57 | GradientAngle.CW180 -> GradientOffset(
58 | start = Offset(Float.POSITIVE_INFINITY, 0f),
59 | end = Offset.Zero,
60 | )
61 | GradientAngle.CW225 -> GradientOffset(
62 | start = Offset.Infinite,
63 | end = Offset.Zero
64 | )
65 | GradientAngle.CW270 -> GradientOffset(
66 | start = Offset(0f, Float.POSITIVE_INFINITY),
67 | end = Offset.Zero
68 | )
69 | GradientAngle.CW315 -> GradientOffset(
70 | start = Offset(0f, Float.POSITIVE_INFINITY),
71 | end = Offset(Float.POSITIVE_INFINITY, 0f)
72 | )
73 | else -> GradientOffset(
74 | start = Offset.Zero,
75 | end = Offset(Float.POSITIVE_INFINITY, 0f)
76 | )
77 | }
78 | }
79 |
80 | /**
81 | * Offset for [Brush.linearGradient] to rotate gradient depending on [start] and [end] offsets.
82 | */
83 | data class GradientOffset(val start: Offset, val end: Offset)
84 |
85 | enum class GradientAngle {
86 | CW0, CW45, CW90, CW135, CW180, CW225, CW270, CW315
87 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/ui/brush/Brush.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.ui.brush
2 |
3 | import androidx.compose.ui.graphics.Brush
4 | import androidx.compose.ui.graphics.Color
5 | import com.smarttoolfactory.colorpicker.ui.Gray
6 | import com.smarttoolfactory.colorpicker.ui.WhiteTransparent
7 |
8 |
9 | /*
10 | Vertical Brushes for adding lightness or value to Rectangle Color pickers
11 | */
12 | fun transparentToBlackVerticalGradient(
13 | startY: Float = 0.0f,
14 | endY: Float = Float.POSITIVE_INFINITY
15 | ): Brush {
16 | return Brush.verticalGradient(
17 | colors = listOf(Color.Transparent, Color.Black),
18 | startY = startY,
19 | endY = endY
20 | )
21 | }
22 |
23 | fun transparentToWhiteVerticalGradient(
24 | startY: Float = 0.0f,
25 | endY: Float = Float.POSITIVE_INFINITY
26 | ): Brush {
27 | return Brush.verticalGradient(
28 | colors = listOf(Color.Transparent, Color.White),
29 | startY = startY,
30 | endY = endY
31 | )
32 | }
33 |
34 | fun transparentToGrayVerticalGradient(
35 | startY: Float = 0.0f,
36 | endY: Float = Float.POSITIVE_INFINITY
37 | ): Brush {
38 | return Brush.verticalGradient(
39 | colors = listOf(Color.Transparent, Gray),
40 | startY = startY,
41 | endY = endY
42 | )
43 | }
44 |
45 | fun whiteToTransparentToBlackVerticalGradient(
46 | startY: Float = 0.0f,
47 | endY: Float = Float.POSITIVE_INFINITY
48 | ): Brush {
49 | return Brush.verticalGradient(
50 | 0.0f to Color.White,
51 | 0.5f to WhiteTransparent,
52 | 0.5f to Color.Transparent,
53 | 1f to Color.Black,
54 | startY = startY,
55 | endY = endY
56 | )
57 | }
58 |
59 | fun whiteToTransparentToBlackHorizontalGradient(
60 | startX: Float = 0.0f,
61 | endX: Float = Float.POSITIVE_INFINITY
62 | ): Brush {
63 | return Brush.horizontalGradient(
64 | 0.0f to Color.White,
65 | 0.5f to WhiteTransparent,
66 | 0.5f to Color.Transparent,
67 | 1f to Color.Black,
68 | startX = startX,
69 | endX = endX
70 | )
71 | }
72 |
73 | fun whiteToBlackGradient(
74 | startY: Float = 0.0f,
75 | endY: Float = Float.POSITIVE_INFINITY
76 | ): Brush {
77 | return Brush.verticalGradient(
78 | colors = listOf(Color.White, Color.Black),
79 | startY = startY,
80 | endY = endY
81 | )
82 | }
83 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/ui/brush/HSLSHVBrush.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.ui.brush
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.graphics.Brush
5 | import androidx.compose.ui.graphics.Color
6 |
7 | /*
8 | HSV Gradients
9 | */
10 | /**
11 | * Gradient for creating HSV saturation change with 0 degree rotation by default.
12 | */
13 | fun saturationHSVGradient(
14 | hue: Float,
15 | value: Float = 1f,
16 | alpha: Float = 1f,
17 | start: Offset = Offset.Zero,
18 | end: Offset = Offset(Float.POSITIVE_INFINITY, 0f)
19 | ): Brush {
20 | return Brush.linearGradient(
21 | colors = listOf(
22 | Color.hsv(hue = hue, saturation = 0f, value = value, alpha = alpha),
23 | Color.hsv(hue = hue, saturation = 1f, value = value, alpha = alpha)
24 | ),
25 | start = start,
26 | end = end
27 | )
28 | }
29 |
30 | /**
31 | * Vertical gradient that goes from 1 value to 0 with 90 degree rotation by default.
32 | */
33 | fun valueGradient(
34 | hue: Float,
35 | alpha: Float = 1f,
36 | start:Offset = Offset.Zero,
37 | end:Offset = Offset(0f, Float.POSITIVE_INFINITY)
38 | ): Brush {
39 | return Brush.linearGradient(
40 | colors = listOf(
41 | Color.hsv(hue = hue, saturation = 0f, value = 1f, alpha = alpha),
42 | Color.hsv(hue = hue, saturation = 0f, value = 0f, alpha = alpha)
43 | ),
44 | start = start,
45 | end = end
46 | )
47 | }
48 |
49 | /*
50 | HSL Gradients
51 | */
52 | fun saturationHSLGradient(
53 | hue: Float,
54 | lightness: Float = .5f,
55 | alpha: Float = 1f,
56 | start: Offset = Offset.Zero,
57 | end: Offset = Offset(Float.POSITIVE_INFINITY, 0f)
58 | ): Brush {
59 | return Brush.linearGradient(
60 | colors = listOf(
61 | Color.hsl(hue = hue, saturation = 0f, lightness = lightness, alpha = alpha),
62 | Color.hsl(hue = hue, saturation = 1f, lightness = lightness, alpha = alpha),
63 | ),
64 | start = start,
65 | end = end
66 | )
67 | }
68 |
69 | fun lightnessGradient(
70 | hue: Float,
71 | alpha: Float = 1f,
72 | start:Offset = Offset.Zero,
73 | end:Offset = Offset(0f, Float.POSITIVE_INFINITY)
74 | ): Brush {
75 | return Brush.linearGradient(
76 | colors = listOf(
77 | Color.hsl(hue = hue, saturation = .5f, lightness = 1f, alpha = alpha),
78 | Color.hsl(hue = hue, saturation = .5f, lightness = 0f, alpha = alpha)
79 | ),
80 | start = start,
81 | end = end
82 | )
83 | }
84 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/ui/brush/SliderBrush.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.ui.brush
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.graphics.Brush
5 | import androidx.compose.ui.graphics.Color
6 | import com.smarttoolfactory.colorpicker.ui.gradientColorScaleHSL
7 | import com.smarttoolfactory.colorpicker.ui.gradientColorScaleHSV
8 |
9 |
10 | /*
11 | HSV Gradients
12 | */
13 | /**
14 | * Gradient for Slider for selecting Hue from HSV
15 | */
16 | fun sliderHueHSVGradient(
17 | saturation: Float = 1f,
18 | value: Float = 1f,
19 | alpha: Float = 1f,
20 | start: Offset = Offset.Zero,
21 | end: Offset = Offset.Infinite
22 | ): Brush {
23 | return Brush.linearGradient(
24 | colors = gradientColorScaleHSV(
25 | saturation = saturation,
26 | value = value,
27 | alpha = alpha,
28 | ),
29 | start = start,
30 | end = end
31 | )
32 | }
33 |
34 | fun sliderSaturationHSVGradient(
35 | hue: Float,
36 | value: Float = 1f,
37 | alpha: Float = 1f,
38 | start: Offset = Offset.Zero,
39 | end: Offset = Offset.Infinite
40 | ): Brush {
41 | return Brush.linearGradient(
42 | colors = listOf(
43 | Color.hsv(hue = hue, saturation = 0f, value = value, alpha = alpha),
44 | Color.hsv(hue = hue, saturation = 1f, value = value, alpha = alpha)
45 | ),
46 | start = start,
47 | end = end
48 | )
49 | }
50 |
51 | fun sliderValueGradient(
52 | hue: Float,
53 | alpha: Float = 1f,
54 | start: Offset = Offset.Zero,
55 | end: Offset = Offset.Infinite
56 | ): Brush {
57 | return Brush.linearGradient(
58 | colors = listOf(
59 | Color.hsv(hue = hue, saturation = 1f, value = 0f, alpha = alpha),
60 | Color.hsv(hue = hue, saturation = 1f, value = 1f, alpha = alpha)
61 | ),
62 | start = start,
63 | end = end
64 | )
65 | }
66 |
67 | fun sliderAlphaHSVGradient(
68 | hue: Float = 0f,
69 | saturation: Float = 1f,
70 | value: Float = 1f,
71 | start: Offset = Offset.Zero,
72 | end: Offset = Offset.Infinite
73 | ): Brush {
74 | return Brush.linearGradient(
75 | colors = listOf(
76 | Color.hsv(hue = hue, saturation = saturation, value = value, alpha = 0f),
77 | Color.hsv(hue = hue, saturation = saturation, value = value, alpha = 1f)
78 | ),
79 | start = start,
80 | end = end
81 | )
82 | }
83 |
84 |
85 | /*
86 | HSL Gradients
87 | */
88 | /**
89 | * Gradient for Slider for selecting Hue from HSL
90 | */
91 | fun sliderHueHSLGradient(
92 | saturation: Float = 1f,
93 | lightness: Float = .5f,
94 | alpha: Float = 1f,
95 | start: Offset = Offset.Zero,
96 | end: Offset = Offset.Infinite
97 | ): Brush {
98 | return Brush.linearGradient(
99 | colors = gradientColorScaleHSL(
100 | saturation = saturation,
101 | lightness = lightness,
102 | alpha = alpha,
103 | ),
104 | start = start,
105 | end = end
106 | )
107 | }
108 |
109 | fun sliderSaturationHSLGradient(
110 | hue: Float,
111 | lightness: Float = .5f,
112 | alpha: Float = 1f,
113 | start: Offset = Offset.Zero,
114 | end: Offset = Offset.Infinite
115 | ): Brush {
116 | return Brush.linearGradient(
117 | colors = listOf(
118 | Color.hsl(hue = hue, saturation = 0f, lightness = lightness, alpha = alpha),
119 | Color.hsl(hue = hue, saturation = 1f, lightness = lightness, alpha = alpha),
120 | ),
121 | start = start,
122 | end = end
123 | )
124 | }
125 |
126 | fun sliderLightnessGradient(
127 | hue: Float,
128 | saturation: Float = 0f,
129 | alpha: Float = 1f,
130 | start: Offset = Offset.Zero,
131 | end: Offset = Offset.Infinite
132 | ): Brush {
133 | return Brush.linearGradient(
134 | colors = listOf(
135 | Color.hsl(hue = hue, saturation = saturation, lightness = 0f, alpha = alpha),
136 | Color.hsl(hue = hue, saturation = saturation, lightness = .5f, alpha = alpha),
137 | Color.hsl(hue = hue, saturation = saturation, lightness = 1f, alpha = alpha)
138 | ),
139 | start = start,
140 | end = end
141 | )
142 | }
143 |
144 | fun sliderLightnessGradient3Stops(
145 | hue: Float,
146 | alpha: Float = 1f,
147 | start: Offset = Offset.Zero,
148 | end: Offset = Offset.Infinite
149 | ): Brush {
150 | return Brush.linearGradient(
151 | colors = listOf(
152 | Color.hsl(hue = hue, saturation = 1f, lightness = 0f, alpha = alpha),
153 | Color.hsl(hue = hue, saturation = 1f, lightness = .5f, alpha = alpha),
154 | Color.hsl(hue = hue, saturation = 1f, lightness = 1f, alpha = alpha)
155 | ),
156 | start = start,
157 | end = end
158 | )
159 | }
160 |
161 | fun sliderAlphaHSLGradient(
162 | hue: Float,
163 | saturation: Float = 1f,
164 | lightness: Float = .5f,
165 | start: Offset = Offset.Zero,
166 | end: Offset = Offset.Infinite
167 | ): Brush {
168 | return Brush.linearGradient(
169 | colors = listOf(
170 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = 0f),
171 | Color.hsl(hue = hue, saturation = saturation, lightness = lightness, alpha = 1f)
172 | ),
173 | start = start,
174 | end = end
175 | )
176 | }
177 |
178 | /*
179 | RGB Gradients
180 | */
181 | fun sliderRedGradient(
182 | alpha: Float = 1f,
183 | start: Offset = Offset.Zero,
184 | end: Offset = Offset.Infinite
185 | ): Brush {
186 | return Brush.linearGradient(
187 | colors = listOf(
188 | Color.hsl(hue = 0f, saturation = 1f, lightness = 0f, alpha = alpha),
189 | Color.hsl(hue = 0f, saturation = 1f, lightness = .5f, alpha = alpha)
190 | ),
191 | start = start,
192 | end = end
193 | )
194 | }
195 |
196 | fun sliderGreenGradient(
197 | alpha: Float = 1f,
198 | start: Offset = Offset.Zero,
199 | end: Offset = Offset.Infinite
200 | ): Brush {
201 | return Brush.linearGradient(
202 | colors = listOf(
203 | Color.hsl(hue = 120f, saturation = 1f, lightness = 0f, alpha = alpha),
204 | Color.hsl(hue = 120f, saturation = 1f, lightness = .5f, alpha = alpha)
205 | ),
206 | start = start,
207 | end = end
208 | )
209 | }
210 |
211 | fun sliderBlueGradient(
212 | alpha: Float = 1f,
213 | start: Offset = Offset.Zero,
214 | end: Offset = Offset.Infinite
215 | ): Brush {
216 | return Brush.linearGradient(
217 | colors = listOf(
218 | Color.hsl(hue = 240f, saturation = 1f, lightness = 0f, alpha = alpha),
219 | Color.hsl(hue = 240f, saturation = 1f, lightness = .5f, alpha = alpha)
220 | ),
221 | start = start,
222 | end = end
223 | )
224 | }
225 |
226 | fun sliderAlphaRGBGradient(
227 | red: Float,
228 | green: Float,
229 | blue: Float,
230 | start: Offset = Offset.Zero,
231 | end: Offset = Offset.Infinite
232 | ): Brush {
233 | return Brush.linearGradient(
234 | colors = listOf(
235 | Color(red,green, blue,0f),
236 | Color(red,green, blue,1f)
237 | ),
238 | start = start,
239 | end = end
240 | )
241 | }
242 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/util/DimensionUtil.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.util
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import kotlin.math.*
5 |
6 | /**
7 | * [Linear Interpolation](https://en.wikipedia.org/wiki/Linear_interpolation) function that moves
8 | * amount from it's current position to start and amount
9 | * @param start of interval
10 | * @param end of interval
11 | * @param amount e closed unit interval [0, 1]
12 | */
13 | fun lerp(start: Float, end: Float, amount: Float): Float {
14 | return (1 - amount) * start + amount * end
15 | }
16 |
17 | /**
18 | * Scale x1 from start1..end1 range to start2..end2 range
19 |
20 | */
21 | fun scale(start1: Float, end1: Float, pos: Float, start2: Float, end2: Float) =
22 | lerp(start2, end2, calculateFraction(start1, end1, pos))
23 |
24 | /**
25 | * Scale x.start, x.endInclusive from a1..b1 range to a2..b2 range
26 | */
27 | fun scale(
28 | start1: Float,
29 | end1: Float,
30 | range: ClosedFloatingPointRange,
31 | start2: Float,
32 | end2: Float
33 | ) =
34 | scale(start1, end1, range.start, start2, end2)..scale(
35 | start1,
36 | end1,
37 | range.endInclusive,
38 | start2,
39 | end2
40 | )
41 |
42 | /**
43 | * Calculate fraction for value between a range [end] and [start] coerced into 0f-1f range
44 | */
45 | fun calculateFraction(start: Float, end: Float, pos: Float) =
46 | (if (end - start == 0f) 0f else (pos - start) / (end - start)).coerceIn(0f, 1f)
47 |
48 |
49 | /**
50 | * Calculate distance from center to touch position
51 | */
52 | fun calculateDistanceFromCenter(center: Offset, position: Offset): Float {
53 | val dy = center.y - position.y
54 | val dx = position.x - center.x
55 | return sqrt(dx * dx + dy * dy)
56 | }
57 |
58 | /**
59 | * Get angle between 0 and 360 degrees from local coordinate system of a composable
60 | * Local coordinates of touch are equal to Composable position when in bounds, when
61 | * touch position is above this composable it returns minus in y axis.
62 | */
63 | fun calculateAngleFomLocalCoordinates(center: Offset, position: Offset): Float {
64 | if (center == Offset.Unspecified || position == Offset.Unspecified) return -1f
65 |
66 | val dy = center.y - position.y
67 | val dx = position.x - center.x
68 | return (((360 + ((atan2(dy, dx) * 180 / PI))) % 360).toFloat())
69 |
70 | }
71 |
72 | /**
73 | * Calculate touch position based on distance to center and angle relative
74 | * to center counter-clockwise.
75 | *
76 | * @param center center of Composable
77 | * @param distance from center
78 | * @param angle in degrees
79 | */
80 | fun calculatePositionFromAngleAndDistance(
81 | center: Offset,
82 | distance: Float,
83 | angle: Float
84 | ): Offset {
85 |
86 | val centerX = center.x
87 | val centerY = center.y
88 |
89 | val angleInRadian: Float = ((angle * Math.PI) / 180f).toFloat()
90 | val x = centerX + distance * cos(angleInRadian)
91 | val y = centerY - distance * sin(angleInRadian)
92 | return Offset(x, y)
93 | }
94 |
95 | /**
96 | * Get angle between 0 and 360 degrees using coordinates of the root composable. Root composable
97 | * needs to cover whole screen to return correct results.
98 | */
99 | fun calculateAngleFromRootCoordinates(center: Offset, position: Offset): Float {
100 | if (center == Offset.Unspecified || position == Offset.Unspecified) return -1f
101 |
102 | val dy = (position.y - center.y)
103 | val dx = (center.x - position.x)
104 | return ((360 + ((atan2(dy, dx) * 180 / PI))) % 360).toFloat()
105 | }
106 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/util/DrawUtil.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.util
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.geometry.Size
5 | import androidx.compose.ui.graphics.BlendMode
6 | import androidx.compose.ui.graphics.Brush
7 | import androidx.compose.ui.graphics.drawscope.DrawScope
8 | import androidx.compose.ui.graphics.nativeCanvas
9 |
10 |
11 | /**
12 | * Draw 2 rectangles that blend with [BlendMode.Multiply] to draw saturation picker
13 | */
14 | fun DrawScope.drawBlendingRectGradient(
15 | dst: Brush,
16 | dstTopLeft: Offset = Offset.Zero,
17 | dstSize: Size = this.size,
18 | src: Brush,
19 | srcTopLeft: Offset = Offset.Zero,
20 | srcSize: Size = this.size,
21 | blendMode: BlendMode = BlendMode.Multiply
22 | ) {
23 | with(drawContext.canvas.nativeCanvas) {
24 | val checkPoint = saveLayer(null, null)
25 | drawRect(dst, dstTopLeft, dstSize)
26 | drawRect(src, srcTopLeft, srcSize, blendMode = blendMode)
27 | restoreToCount(checkPoint)
28 | }
29 | }
30 |
31 | /**
32 | * Draw into layer
33 | */
34 | fun DrawScope.drawIntoLayer(
35 | content: DrawScope.() -> Unit
36 | ) {
37 | with(drawContext.canvas.nativeCanvas) {
38 | val checkPoint = saveLayer(null, null)
39 | content()
40 | restoreToCount(checkPoint)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/util/HexRegex.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.util
2 |
3 | // Regex for checking if this string is a 6 char hex or 8 char hex
4 | val hexWithAlphaRegex = "^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})\$".toRegex()
5 | val hexRegex = "^#?([0-9a-fA-F]{6})\$".toRegex()
6 |
7 | // Check only on char if it's in range of 0-9, a-f, A-F
8 | val hexRegexSingleChar = "[a-fA-F0-9]".toRegex()
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/util/HexVisualTransformation.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.util
2 |
3 | import androidx.compose.ui.text.AnnotatedString
4 | import androidx.compose.ui.text.input.OffsetMapping
5 | import androidx.compose.ui.text.input.TransformedText
6 | import androidx.compose.ui.text.input.VisualTransformation
7 |
8 | class HexVisualTransformation(private val useAlpha: Boolean) : VisualTransformation {
9 |
10 | override fun filter(text: AnnotatedString): TransformedText {
11 |
12 | val limit = if (useAlpha) 8 else 6
13 |
14 | val trimmed =
15 | if (text.text.length >= limit) text.text.substring(0 until limit) else text.text
16 |
17 | val output = if (trimmed.isEmpty()) {
18 | trimmed
19 | } else {
20 | if (!useAlpha || trimmed.length < 7) {
21 | "#${trimmed.uppercase()}"
22 | } else {
23 | "#${
24 | trimmed.substring(0, 2).lowercase() +
25 | trimmed.substring(2, trimmed.length).uppercase()
26 | }"
27 | }
28 | }
29 |
30 | return TransformedText(
31 | AnnotatedString(output),
32 | if (useAlpha) hexAlphaOffsetMapping else hexOffsetMapping
33 | )
34 | }
35 |
36 | private val hexOffsetMapping = object : OffsetMapping {
37 |
38 | override fun originalToTransformed(offset: Int): Int {
39 |
40 | // when empty return only 1 char for #
41 | if (offset == 0) return offset
42 | if (offset <= 5) return offset + 1
43 | return 7
44 | }
45 |
46 | override fun transformedToOriginal(offset: Int): Int {
47 | if (offset == 0) return offset
48 | // #ABCABC
49 | if (offset <= 6) return offset - 1
50 | return 6
51 | }
52 | }
53 |
54 | private val hexAlphaOffsetMapping = object : OffsetMapping {
55 |
56 | override fun originalToTransformed(offset: Int): Int {
57 |
58 | // when empty return only 1 char for #
59 | if (offset == 0) return offset
60 | if (offset <= 7) return offset + 1
61 | return 9
62 | }
63 |
64 | override fun transformedToOriginal(offset: Int): Int {
65 | if (offset == 0) return offset
66 | // #ffABCABC
67 | if (offset <= 8) return offset - 1
68 | return 8
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/Checker.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.draw.clip
7 | import androidx.compose.ui.draw.drawWithCache
8 | import androidx.compose.ui.geometry.Offset
9 | import androidx.compose.ui.geometry.Size
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.graphics.Shape
12 | import androidx.compose.ui.unit.DpSize
13 | import androidx.compose.ui.unit.dp
14 |
15 | /**
16 | * [Box] that draws checker pattern behind its [content] using [Modifier.drawChecker]
17 | * @param size size of the checker. If size is [DpSize.Unspecified] **10.dp** is used by
18 | * default.
19 | * @param shape shape of the checker.
20 | * @param content The content of the Box.
21 | */
22 | @Composable
23 | private fun CheckeredBox(
24 | modifier: Modifier,
25 | shape: Shape,
26 | size: DpSize = DpSize.Unspecified,
27 | content: @Composable () -> Unit
28 | ) {
29 | Box(modifier.drawChecker(shape, size)) {
30 | content()
31 | }
32 | }
33 |
34 | /**
35 | * Modify element to add checker with appearance specified with a [shape], and [size]
36 | * and clip it.
37 | * @param size size of the checker. If size is [DpSize.Unspecified] **10.dp** is used by
38 | * default.
39 | * @param shape shape of the checker.
40 | */
41 | fun Modifier.drawChecker(shape: Shape, size: DpSize = DpSize.Unspecified) = this
42 | .clip(shape)
43 | .then(
44 | drawWithCache {
45 | this.onDrawBehind {
46 | val width = this.size.width
47 | val height = this.size.height
48 |
49 | val checkerWidth =
50 | (if (size != DpSize.Unspecified) size.width.toPx() else 10.dp.toPx())
51 | .coerceAtMost(width / 2)
52 |
53 | val checkerHeight =
54 | (if (size != DpSize.Unspecified) size.height.toPx() else 10.dp.toPx())
55 | .coerceAtMost(height / 2)
56 |
57 | val horizontalSteps = (width / checkerWidth).toInt()
58 | val verticalSteps = (height / checkerHeight).toInt()
59 |
60 | for (y in 0..verticalSteps) {
61 | for (x in 0..horizontalSteps) {
62 | val isGrayTile = ((x + y) % 2 == 1)
63 | drawRect(
64 | color = if (isGrayTile) Color.LightGray else Color.White,
65 | topLeft = Offset(x * checkerWidth, y * checkerHeight),
66 | size = Size(checkerWidth, checkerHeight)
67 | )
68 | }
69 | }
70 | }
71 | }
72 | )
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ColorDisplayExposedSelectionMenu.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.*
5 | import androidx.compose.material.ExperimentalMaterialApi
6 | import androidx.compose.material.ExposedDropdownMenuDefaults
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.*
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.text.font.FontWeight
13 | import androidx.compose.ui.unit.dp
14 | import androidx.compose.ui.unit.sp
15 | import com.smarttoolfactory.colorpicker.model.ColorModel
16 | import com.smarttoolfactory.colorpicker.model.CompositeColor
17 | import com.smarttoolfactory.extendedcolors.util.*
18 |
19 | @Composable
20 | fun ColorDisplayExposedSelectionMenu(compositeColor: CompositeColor, colorModel: ColorModel) {
21 |
22 | val options = listOf("HEX", "HSV", "HSL")
23 | var index by remember { mutableStateOf(0) }
24 | Row(
25 | verticalAlignment = Alignment.CenterVertically,
26 | modifier = Modifier
27 | .fillMaxWidth()
28 | .padding(2.dp)
29 | ) {
30 | ExposedSelectionMenu(
31 | index = index,
32 | options = options,
33 | onSelected = {
34 | index = it
35 | }
36 | )
37 | }
38 | }
39 |
40 | /**
41 | * Selection menu that displays Color's components in Hex, HSL, or HSL
42 | */
43 | @OptIn(ExperimentalMaterialApi::class)
44 | @Composable
45 | fun ColorDisplayExposedSelectionMenu(
46 | color: Color,
47 | colorModel: ColorModel,
48 | onColorModelChange: (ColorModel) -> Unit
49 | ) {
50 |
51 | val options = listOf("HEX", "HSV", "HSL")
52 |
53 | var selectedIndex by remember {
54 | mutableStateOf(
55 | when (colorModel) {
56 | ColorModel.RGB -> 0
57 | ColorModel.HSV -> 1
58 | ColorModel.HSL -> 2
59 | }
60 | )
61 | }
62 |
63 | Row(
64 | verticalAlignment = Alignment.CenterVertically,
65 | modifier = Modifier
66 | .background(Color.White)
67 | .fillMaxWidth()
68 | .padding(2.dp)
69 | ) {
70 | ExposedSelectionMenu(
71 | modifier = Modifier.width(100.dp),
72 | index = selectedIndex,
73 | colors = ExposedDropdownMenuDefaults.textFieldColors(
74 | backgroundColor = Color.Transparent,
75 | focusedIndicatorColor = Color.Transparent,
76 | unfocusedIndicatorColor = Color.Transparent,
77 | disabledIndicatorColor = Color.Transparent,
78 | focusedLabelColor = Color.Black,
79 | unfocusedLabelColor = Color.Black,
80 | trailingIconColor = Color.Black,
81 | focusedTrailingIconColor = Color.Black,
82 | textColor = Color.Black,
83 | ),
84 | options = options,
85 | onSelected = {
86 | selectedIndex = it
87 | onColorModelChange(ColorModel.values()[selectedIndex])
88 | }
89 | )
90 |
91 | Row(
92 | modifier = Modifier
93 | .weight(1f),
94 | horizontalArrangement = Arrangement.Start
95 | ) {
96 |
97 | when (colorModel) {
98 | ColorModel.RGB -> {
99 | ColorText(
100 | title = "R",
101 | value = color.red.fractionToRGBString(),
102 | modifier = Modifier.weight(1f)
103 | )
104 | ColorText(
105 | title = "G", value = color.green.fractionToRGBString(),
106 | modifier = Modifier.weight(1f)
107 | )
108 | ColorText(
109 | title = "B", value = color.blue.fractionToRGBString(),
110 | modifier = Modifier.weight(1f)
111 | )
112 | ColorText(
113 | title = "A", value = "${color.alpha.fractionToPercent()}%",
114 | modifier = Modifier.weight(1f)
115 | )
116 | }
117 | ColorModel.HSV -> {
118 | val hsvArray = ColorUtil.colorToHSV(color)
119 | ColorText(
120 | title = "H", value = "${hsvArray[0].round()}°",
121 | modifier = Modifier.weight(1f)
122 | )
123 | ColorText(
124 | title = "S", value = "${hsvArray[1].fractionToPercent()}%",
125 | modifier = Modifier.weight(1f)
126 | )
127 | ColorText(
128 | title = "V", value = "${hsvArray[2].fractionToPercent()}%",
129 | modifier = Modifier.weight(1f)
130 | )
131 | ColorText(
132 | title = "A", value = "${color.alpha.fractionToPercent()}%",
133 | modifier = Modifier.weight(1f)
134 | )
135 | }
136 |
137 | ColorModel.HSL -> {
138 | val hslArray = ColorUtil.colorToHSL(color)
139 | ColorText(
140 | title = "H", value = "${hslArray[0].round()}°",
141 | modifier = Modifier.weight(1f)
142 | )
143 | ColorText(
144 | title = "S", value = "${hslArray[1].fractionToPercent()}%",
145 | modifier = Modifier.weight(1f)
146 | )
147 | ColorText(
148 | title = "L", value = "${hslArray[2].fractionToPercent()}%",
149 | modifier = Modifier.weight(1f)
150 | )
151 | ColorText(
152 | title = "A", value = "${color.alpha.fractionToPercent()}%",
153 | modifier = Modifier.weight(1f)
154 | )
155 | }
156 | }
157 | }
158 | }
159 | }
160 |
161 | @Composable
162 | private fun ColorText(modifier: Modifier = Modifier, title: String, value: String) {
163 | Column(
164 | modifier = modifier,
165 | horizontalAlignment = Alignment.CenterHorizontally
166 | ) {
167 | Text(
168 | text = title.substring(0, 1),
169 | fontWeight = FontWeight.Bold,
170 | fontSize = 16.sp
171 |
172 | )
173 | Spacer(modifier = Modifier.height(4.dp))
174 | Text(text = value, fontSize = 14.sp)
175 | }
176 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ColorDisplayRoundedRect.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.*
5 | import androidx.compose.foundation.shape.RoundedCornerShape
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.Brush
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.unit.DpSize
11 | import androidx.compose.ui.unit.dp
12 |
13 | /**
14 | * Color display that displays both colors horizontally aligned with half [RoundedCornerShape]
15 | * with [Modifier.drawChecker] to display checker pattern when any color's alpha is less than 1.0f
16 | * @param initialColor color that should be static
17 | * @param currentColor color that is changed based on user actions
18 | */
19 | @Composable
20 | fun ColorDisplayRoundedRect(
21 | modifier: Modifier = Modifier,
22 | initialColor: Color,
23 | currentColor: Color
24 | ) {
25 | Row(modifier = modifier.height(40.dp)) {
26 | Box(
27 | modifier = Modifier
28 | .weight(1f)
29 | .fillMaxHeight()
30 | .drawChecker(RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp))
31 | .background(
32 | initialColor,
33 | shape = RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp)
34 | )
35 | )
36 | Box(
37 | modifier = Modifier
38 | .weight(1f)
39 | .fillMaxHeight()
40 | .drawChecker(RoundedCornerShape(topEnd = 8.dp, bottomEnd = 8.dp))
41 | .background(
42 | currentColor,
43 | shape = RoundedCornerShape(topEnd = 8.dp, bottomEnd = 8.dp)
44 | )
45 | )
46 | }
47 | }
48 |
49 | /**
50 | * Color display that displays both colors horizontally aligned with half [RoundedCornerShape]
51 | * with [Modifier.drawChecker] to display checker pattern when any color's alpha is less than 1.0f
52 | * @param boxSize size of left [Box] with [initialBrush] and size of right [Box] with [currentBrush]
53 | * @param initialBrush [Brush] that should be static
54 | * @param currentBrush [Brush] that is changed based on user actions
55 | */
56 | @Composable
57 | fun ColorDisplayRoundedRect(
58 | modifier: Modifier,
59 | boxSize: DpSize,
60 | initialBrush: Brush,
61 | currentBrush: Brush
62 | ) {
63 | Row() {
64 | Box(
65 | modifier = Modifier
66 | .size(boxSize)
67 | .drawChecker(RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp))
68 | .background(
69 | initialBrush,
70 | shape = RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp)
71 | )
72 | )
73 | Box(
74 | modifier = Modifier
75 | .size(boxSize)
76 | .drawChecker(RoundedCornerShape(topEnd = 8.dp, bottomEnd = 8.dp))
77 | .background(
78 | currentBrush,
79 | shape = RoundedCornerShape(topEnd = 8.dp, bottomEnd = 8.dp)
80 | )
81 | )
82 | }
83 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ColorDisplayWithClipboard.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import android.widget.Toast
4 | import androidx.compose.animation.AnimatedVisibility
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.layout.*
8 | import androidx.compose.foundation.shape.RoundedCornerShape
9 | import androidx.compose.material.*
10 | import androidx.compose.runtime.*
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.draw.shadow
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.platform.LocalClipboardManager
15 | import androidx.compose.ui.platform.LocalContext
16 | import androidx.compose.ui.res.painterResource
17 | import androidx.compose.ui.text.AnnotatedString
18 | import androidx.compose.ui.unit.dp
19 | import androidx.compose.ui.unit.sp
20 | import com.smarttoolfactory.colordetector.ColorData
21 | import com.smarttoolfactory.colorpicker.R
22 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
23 | import com.smarttoolfactory.extendedcolors.util.fractionToIntPercent
24 | import kotlin.math.roundToInt
25 |
26 |
27 | @Composable
28 | fun ColorDisplayWithClipboard(
29 | modifier: Modifier = Modifier,
30 | colorData: ColorData
31 | ) {
32 |
33 | val clipboardManager = LocalClipboardManager.current
34 | val context = LocalContext.current
35 |
36 | val color = colorData.color
37 | val colorName = colorData.name
38 |
39 | val lightness = ColorUtil.colorToHSL(color)[2]
40 | val textColor = if (lightness < .6f) Color.White else Color.Black
41 |
42 | var expanded by remember { mutableStateOf(false) }
43 |
44 | val hexText = colorData.hexText
45 | Column(
46 | modifier = modifier
47 | .shadow(
48 | 2.dp, if (expanded) RoundedCornerShape(25)
49 | else RoundedCornerShape(50)
50 | )
51 | .width(200.dp)
52 | .background(color = color)
53 | .clickable {
54 | expanded = !expanded
55 | }
56 | .padding(start = 20.dp, end = 4.dp, top = 5.dp, bottom = 5.dp),
57 | ) {
58 |
59 | Row {
60 | Column {
61 | Text(text = colorName, fontSize = 10.sp, color = textColor)
62 | Text(
63 | text = hexText,
64 | fontSize = 20.sp,
65 | color = textColor
66 | )
67 | }
68 | Spacer(modifier = Modifier.weight(1f))
69 | IconButton(
70 | onClick = {
71 | Toast.makeText(context, "Copied $hexText", Toast.LENGTH_SHORT).show()
72 | clipboardManager.setText(AnnotatedString(hexText))
73 | }) {
74 | Icon(
75 | tint = textColor,
76 | painter = painterResource(id = R.drawable.ic_baseline_content_copy_24),
77 | contentDescription = "clipboard"
78 | )
79 | }
80 | }
81 |
82 | AnimatedVisibility(visible = expanded) {
83 | Column {
84 | Text(text = colorData.rgb, fontSize = 12.sp, color = textColor)
85 | Text(text = colorData.hslString, fontSize = 12.sp, color = textColor)
86 | Text(text = colorData.hsvString, fontSize = 12.sp, color = textColor)
87 | }
88 | }
89 | }
90 | }
91 |
92 | @Composable
93 | fun ColorDisplayWithClipboard(
94 | modifier: Modifier = Modifier,
95 | color: Color
96 | ) {
97 |
98 | val clipboardManager = LocalClipboardManager.current
99 | val context = LocalContext.current
100 |
101 |
102 | val lightness = ColorUtil.colorToHSL(color)[2]
103 | val textColor = if (lightness < .6f) Color.White else Color.Black
104 |
105 | var expanded by remember { mutableStateOf(false) }
106 |
107 | val hexText = ColorUtil.colorToHex(color)
108 | Column(
109 | modifier = modifier
110 | .shadow(
111 | 2.dp, if (expanded) RoundedCornerShape(25)
112 | else RoundedCornerShape(50)
113 | )
114 | .width(200.dp)
115 | .background(color = color)
116 | .clickable {
117 | expanded = !expanded
118 | }
119 | .padding(start = 16.dp, end = 2.dp, top = 2.dp, bottom = 2.dp),
120 | ) {
121 |
122 | Row {
123 | Column {
124 | Text(
125 | text = hexText,
126 | fontSize = 20.sp,
127 | color = textColor
128 | )
129 | }
130 | Spacer(modifier = Modifier.weight(1f))
131 | IconButton(
132 | onClick = {
133 | Toast.makeText(context, "Copied $hexText", Toast.LENGTH_SHORT).show()
134 | clipboardManager.setText(AnnotatedString(hexText))
135 | }) {
136 | Icon(
137 | tint = textColor,
138 | painter = painterResource(id = R.drawable.ic_baseline_content_copy_24),
139 | contentDescription = "clipboard"
140 | )
141 | }
142 | }
143 |
144 | AnimatedVisibility(visible = expanded) {
145 | Column {
146 |
147 | val rgb = ColorUtil.colorToRGBArray(color)
148 | val hslArray: FloatArray = ColorUtil.colorToHSL(color)
149 | val hsvArray: FloatArray = ColorUtil.colorToHSV(color)
150 |
151 | Text(
152 | text = "R: ${rgb[0]} G: ${rgb[1]} B: ${rgb[2]}",
153 | fontSize = 12.sp,
154 | color = textColor
155 | )
156 | Text(
157 | text = "H: ${hslArray[0].roundToInt()}° " +
158 | "S: ${hslArray[1].fractionToIntPercent()}% " +
159 | "L: ${hslArray[2].fractionToIntPercent()}%",
160 | fontSize = 12.sp,
161 | color = textColor
162 | )
163 | Text(
164 | text = "H: ${hsvArray[0].roundToInt()}° " +
165 | "S: ${hsvArray[1].fractionToIntPercent()}% " +
166 | "V: ${hsvArray[2].fractionToIntPercent()}%",
167 | fontSize = 12.sp,
168 | color = textColor
169 | )
170 | }
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ColorModelChangeTabRow.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.interaction.MutableInteractionSource
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.material.Text
7 | import androidx.compose.runtime.*
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.text.font.FontWeight
11 | import androidx.compose.ui.unit.dp
12 | import androidx.compose.ui.unit.sp
13 | import com.smarttoolfactory.colorpicker.model.ColorMode
14 | import com.smarttoolfactory.colorpicker.model.ColorModel
15 | import com.smarttoolfactory.colorpicker.ui.Grey400
16 | import com.smarttoolfactory.colorpicker.ui.Grey600
17 |
18 | /**
19 | * Tab Section for changing between HSL, HSV and RGB color models.
20 | * @param colorModel current color model.
21 | * @param onColorModelChange callback that is triggered when user touches buttons.
22 | */
23 | @Composable
24 | fun ColorModelChangeTabRow(
25 | modifier: Modifier = Modifier,
26 | colorModel: ColorModel,
27 | onColorModelChange: (ColorModel) -> Unit
28 | ) {
29 | var selectedIndex by remember {
30 | mutableStateOf(
31 | when (colorModel) {
32 | ColorModel.HSL -> 0
33 | ColorModel.HSV -> 1
34 | ColorModel.RGB -> 2
35 | }
36 | )
37 | }
38 |
39 | Row(
40 | modifier = modifier,
41 | horizontalArrangement = Arrangement.SpaceEvenly
42 | ) {
43 | modelList.forEachIndexed { index, s ->
44 | Box(modifier = Modifier
45 | .clickable(
46 | interactionSource = MutableInteractionSource(),
47 | indication = null,
48 | onClick = {
49 | selectedIndex = index
50 | val newColorModel = when (selectedIndex) {
51 | 0 -> ColorModel.HSL
52 | 1 -> ColorModel.HSV
53 | else -> ColorModel.RGB
54 | }
55 | onColorModelChange(newColorModel)
56 | }
57 | )
58 | .padding(10.dp)
59 | ) {
60 | Text(
61 | text = s,
62 | fontWeight = FontWeight.Bold,
63 | fontSize = 16.sp,
64 | color = if (index == selectedIndex) Grey400 else Grey600
65 | )
66 | }
67 | }
68 | }
69 | }
70 |
71 | /**
72 | * Tab Section for changing between HSL, HSV and RGB color models and Gradient color.
73 | * @param colorMode current color mode.
74 | * @param onColorModeChange callback that is triggered when user touches buttons.
75 | */
76 | @Composable
77 | fun ColorGradientModeChangeTabRow(
78 | modifier: Modifier = Modifier,
79 | colorMode: ColorMode,
80 | onColorModeChange: (ColorMode) -> Unit
81 | ) {
82 | var selectedIndex by remember {
83 | mutableStateOf(
84 | when (colorMode) {
85 | ColorMode.HSL -> 0
86 | ColorMode.HSV -> 1
87 | ColorMode.RGB -> 2
88 | ColorMode.Gradient -> 3
89 | }
90 | )
91 | }
92 |
93 | Row(
94 | modifier = modifier
95 | .fillMaxWidth()
96 | .padding(horizontal = 10.dp),
97 | horizontalArrangement = Arrangement.SpaceEvenly,
98 | verticalAlignment = Alignment.CenterVertically
99 | ) {
100 | modeList.forEachIndexed { index, s ->
101 | Box(
102 | modifier = Modifier
103 | .clickable(
104 | interactionSource = MutableInteractionSource(),
105 | indication = null,
106 | onClick = {
107 | selectedIndex = index
108 | val newColorMode = when (selectedIndex) {
109 | 0 -> ColorMode.HSL
110 | 1 -> ColorMode.HSV
111 | 2 -> ColorMode.RGB
112 | else -> ColorMode.Gradient
113 | }
114 | onColorModeChange(newColorMode)
115 | }
116 | )
117 | .padding(10.dp),
118 | contentAlignment = Alignment.Center
119 | ) {
120 | if (index != 3) {
121 | Text(
122 | text = s,
123 | fontWeight = FontWeight.Bold,
124 | fontSize = 18.sp,
125 | color = if (index == selectedIndex) Grey400 else Grey600
126 | )
127 | } else {
128 | ColorWheel(
129 | modifier = Modifier.size(18.dp),
130 | borderColor = if (index == selectedIndex) Grey400 else Grey600
131 | )
132 | }
133 |
134 | }
135 | }
136 | }
137 | }
138 |
139 | private val modelList = listOf("HSL", "HSV", "RGB")
140 | private val modeList = listOf("HSL", "HSV", "RGB", "Gradient")
141 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ColorWheel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.foundation.Canvas
4 | import androidx.compose.foundation.layout.sizeIn
5 | import androidx.compose.material.icons.filled.Brush
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.geometry.Offset
9 | import androidx.compose.ui.graphics.Brush
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.graphics.drawscope.Stroke
12 | import androidx.compose.ui.unit.dp
13 | import com.smarttoolfactory.colorpicker.ui.gradientColorScaleRGB
14 |
15 |
16 | /**
17 | * Simple circle with stroke to show rainbow colors as [Brush.sweepGradient]
18 | */
19 | @Composable
20 | fun ColorWheel(
21 | modifier: Modifier = Modifier,
22 | borderColor: Color
23 | ) {
24 |
25 | Canvas(modifier = modifier.sizeIn(minWidth = 24.dp, minHeight = 24.dp)) {
26 | val canvasWidth = size.width
27 | val canvasHeight = size.height
28 |
29 | require(canvasWidth == canvasHeight,
30 | lazyMessage = {
31 | print("Canvas dimensions should be equal to each other")
32 | }
33 | )
34 | val cX = canvasWidth / 2
35 | val cY = canvasHeight / 2
36 | val canvasRadius = canvasWidth.coerceAtMost(canvasHeight) / 2f
37 | val center = Offset(cX, cY)
38 |
39 | drawCircle(
40 | brush = Brush.sweepGradient(colors = gradientColorScaleRGB, center = center),
41 | radius = canvasRadius,
42 | center = center
43 | )
44 |
45 | drawCircle(
46 | color = borderColor,
47 | radius = canvasRadius,
48 | center = center,
49 | style = Stroke(width = 2.dp.toPx())
50 | )
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ExpandableColumn.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.material.Icon
7 | import androidx.compose.material.Text
8 | import androidx.compose.material.icons.Icons
9 | import androidx.compose.material.icons.filled.ExpandLess
10 | import androidx.compose.material.icons.filled.ExpandMore
11 | import androidx.compose.runtime.*
12 | import androidx.compose.ui.Alignment
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.text.font.FontWeight
16 | import androidx.compose.ui.unit.dp
17 | import androidx.compose.ui.unit.sp
18 |
19 | /**
20 | * Column with full width title and expand icon that can expand/shrink with [AnimatedVisibility].
21 | * @param title text on top of the column that is visible on both states.
22 | * @param color of [title].
23 | * @param initialExpandState whether this composable should be expanded initially.
24 | * @param content is the content that should be expended or hidden.
25 | */
26 | @Composable
27 | fun ExpandableColumnWithTitle(
28 | modifier: Modifier = Modifier,
29 | horizontalAlignment: Alignment.Horizontal = Alignment.Start,
30 | title: String,
31 | color: Color,
32 | initialExpandState: Boolean = true,
33 | content: @Composable () -> Unit
34 | ) {
35 | var expanded by remember { mutableStateOf(initialExpandState) }
36 | Column(
37 | modifier = modifier.fillMaxWidth(),
38 | horizontalAlignment = horizontalAlignment
39 | ) {
40 |
41 | Row(
42 | modifier = Modifier
43 | .clickable { expanded = !expanded },
44 | verticalAlignment = Alignment.CenterVertically
45 | ) {
46 | Text(
47 | modifier = Modifier.padding(vertical = 8.dp),
48 | text = title,
49 | fontSize = 16.sp,
50 | color = color,
51 | fontWeight = FontWeight.Bold
52 | )
53 |
54 | Spacer(modifier = Modifier.weight(1f))
55 |
56 | Icon(
57 | imageVector = if (expanded) Icons.Filled.ExpandLess
58 | else Icons.Filled.ExpandMore,
59 | contentDescription = null,
60 | tint = color
61 | )
62 | }
63 |
64 | AnimatedVisibility(visible = expanded) {
65 | content()
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/ExposedSelectionMenu.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.material.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.graphics.Color
7 | import androidx.compose.ui.text.TextStyle
8 | import androidx.compose.ui.text.font.FontWeight
9 | import androidx.compose.ui.unit.sp
10 | import com.smarttoolfactory.colorpicker.ui.Green400
11 | import com.smarttoolfactory.colorpicker.ui.Grey400
12 | import com.smarttoolfactory.colorpicker.ui.Orange400
13 | import com.smarttoolfactory.colorpicker.ui.Red400
14 |
15 | @OptIn(ExperimentalMaterialApi::class)
16 | @Composable
17 | fun ExposedSelectionMenu(
18 | modifier: Modifier = Modifier,
19 | index: Int,
20 | title: String? = null,
21 | textStyle: TextStyle = TextStyle(
22 | fontWeight = FontWeight.W600,
23 | fontSize = 14.sp
24 | ),
25 | colors: TextFieldColors = ExposedDropdownMenuDefaults.textFieldColors(
26 | backgroundColor = Color.Transparent,
27 | focusedIndicatorColor = Color.Transparent,
28 | unfocusedIndicatorColor = Color.Transparent,
29 | disabledIndicatorColor = Color.Transparent,
30 | focusedLabelColor = Grey400,
31 | unfocusedLabelColor = Grey400,
32 | trailingIconColor = Grey400,
33 | focusedTrailingIconColor = Grey400,
34 | textColor = Grey400,
35 | ),
36 | options: List,
37 | onSelected: (Int) -> Unit
38 | ) {
39 |
40 | var expanded by remember { mutableStateOf(false) }
41 | var selectedOptionText by remember { mutableStateOf(options[index]) }
42 |
43 | ExposedDropdownMenuBox(
44 | modifier = modifier,
45 | expanded = expanded,
46 | onExpandedChange = {
47 | expanded = !expanded
48 | }
49 | ) {
50 | TextField(
51 | modifier = modifier,
52 | readOnly = true,
53 | value = selectedOptionText,
54 | onValueChange = { },
55 | label = {
56 | title?.let {
57 | Text(it)
58 | }
59 | },
60 | trailingIcon = {
61 | ExposedDropdownMenuDefaults.TrailingIcon(
62 | expanded = expanded
63 | )
64 | },
65 | colors = colors,
66 | textStyle = textStyle
67 | )
68 | ExposedDropdownMenu(
69 | expanded = expanded,
70 | onDismissRequest = {
71 | expanded = false
72 |
73 | }
74 | ) {
75 | options.forEachIndexed { index: Int, selectionOption: String ->
76 | DropdownMenuItem(
77 | onClick = {
78 | selectedOptionText = selectionOption
79 | expanded = false
80 | onSelected(index)
81 | }
82 | ) {
83 | Text(text = selectionOption)
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/HexTextField.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.foundation.layout.widthIn
4 | import androidx.compose.foundation.shape.RoundedCornerShape
5 | import androidx.compose.material.*
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.draw.drawBehind
9 | import androidx.compose.ui.geometry.Offset
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.graphics.Shape
12 | import androidx.compose.ui.text.TextStyle
13 | import androidx.compose.ui.unit.dp
14 | import androidx.compose.ui.unit.sp
15 | import com.smarttoolfactory.colorpicker.ui.Grey400
16 | import com.smarttoolfactory.colorpicker.ui.Red400
17 | import com.smarttoolfactory.colorpicker.util.HexVisualTransformation
18 | import com.smarttoolfactory.colorpicker.util.hexRegex
19 | import com.smarttoolfactory.colorpicker.util.hexRegexSingleChar
20 | import com.smarttoolfactory.colorpicker.util.hexWithAlphaRegex
21 | import com.smarttoolfactory.extendedcolors.util.HexUtil
22 |
23 |
24 | /**
25 | * [TextField] that displays color in hex representation either with #RRGGBB or #AARRGGBB
26 | * depending on [useAlpha] flag.
27 | *
28 | * @param colors [TextFieldColors] that will be used to resolve color of the text and content
29 | * (including label, placeholder, leading and trailing icons, border) for this text field in
30 | * different states. See [TextFieldDefaults.outlinedTextFieldColors]
31 | * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
32 | * [LocalTextStyle] defined by the theme
33 | * @param label the optional label to be displayed inside the text field container. The default
34 | * text style for internal [Text] is [Typography.caption] when the text field is in focus and
35 | * [Typography.subtitle1] when the text field is not in focus
36 | * @param placeholder the optional placeholder to be displayed when the text field is in focus and
37 | * the input text is empty. The default text style for internal [Text] is [Typography.subtitle1]
38 | * @param shape of this [TextField].
39 | * @param hexString string in hex format.
40 | * @param useAlpha when set to true returns colors in #AARRGGBB format.
41 | * @param onTextChange this callback returns when the last char typed by user is an acceptable
42 | * HEX char between 0-9,a-f, or A-F.
43 | * @param onColorChange when user type valid 6 or 8 char hex returns a [Color] associated
44 | * with the hex string.
45 | */
46 | @Composable
47 | fun HexTextField(
48 | modifier: Modifier = Modifier,
49 | hexString: String,
50 | textStyle: TextStyle = TextStyle(fontSize = 24.sp),
51 | colors: TextFieldColors = TextFieldDefaults.textFieldColors(
52 | backgroundColor = Color.Transparent,
53 | focusedIndicatorColor = Color.Transparent,
54 | unfocusedIndicatorColor = Color.Transparent
55 | ),
56 | label: @Composable () -> Unit = {
57 | Text("Hex", color = Grey400)
58 | },
59 | placeholder: @Composable () -> Unit = {
60 | Text("Enter a color", fontSize = textStyle.fontSize)
61 | },
62 | shape: Shape = RoundedCornerShape(25),
63 | useAlpha: Boolean = false,
64 | onTextChange: (String) -> Unit,
65 | onColorChange: (Color) -> Unit
66 | ) {
67 | val currentRegex = if (useAlpha) hexWithAlphaRegex else hexRegex
68 | OutlinedTextField(
69 | modifier = modifier
70 | .widthIn(min = 80.dp)
71 | .drawBehind {
72 | drawLine(
73 | if (currentRegex.matches(hexString)) Grey400 else Red400,
74 | start = Offset(0f, size.height),
75 | end = Offset(size.width, size.height),
76 | strokeWidth = 5f
77 | )
78 | },
79 | visualTransformation = HexVisualTransformation(useAlpha),
80 | textStyle = textStyle,
81 | colors = colors,
82 | shape = shape,
83 | label = label,
84 | placeholder = placeholder,
85 | value = hexString.removePrefix("#"),
86 | onValueChange = {
87 |
88 | if (it.length <= if (useAlpha) 8 else 6) {
89 | var validHex = true
90 |
91 | for (index in it.indices) {
92 | validHex = hexRegexSingleChar.matches(it[index].toString())
93 | if (!validHex) break
94 | }
95 |
96 | if (validHex) {
97 | onTextChange("#$it")
98 | // Hex String with 6 or 8 chars matches a Color
99 | if (currentRegex.matches(it)) {
100 | onColorChange(HexUtil.hexToColor(it))
101 | }
102 | }
103 | }
104 | }
105 | )
106 | }
107 |
--------------------------------------------------------------------------------
/colorpicker/src/main/java/com/smarttoolfactory/colorpicker/widget/HexTextFieldWithCircleDisplay.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker.widget
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.unit.dp
9 | import com.smarttoolfactory.colorpicker.slider.CircleDisplay
10 | import com.smarttoolfactory.extendedcolors.util.ColorUtil
11 |
12 |
13 | @Composable
14 | fun HexTextFieldWithCircleDisplay(
15 | modifier: Modifier = Modifier,
16 | circleModifier: Modifier = Modifier,
17 | color: Color,
18 | useAlpha: Boolean = false,
19 | onColorChange: (Color) -> Unit
20 | ) {
21 |
22 | var hexString by remember(color) {
23 | mutableStateOf(
24 | if (useAlpha) {
25 | ColorUtil.colorToHexAlpha(color)
26 | } else {
27 | ColorUtil.colorToHex(color)
28 | }
29 | )
30 | }
31 |
32 | Row(
33 | modifier = modifier.requiredHeightIn(min = 100.dp),
34 | verticalAlignment = Alignment.CenterVertically
35 | ) {
36 |
37 | CircleDisplay(
38 | modifier = circleModifier
39 | .widthIn(min = 70.dp)
40 | .heightIn(min = 70.dp),
41 | color = color
42 | )
43 | Spacer(modifier = Modifier.width(10.dp))
44 | HexTextField(
45 | modifier = Modifier.weight(1f),
46 | useAlpha = useAlpha,
47 | hexString = hexString,
48 | onTextChange = {
49 | hexString = it
50 | },
51 | onColorChange = onColorChange
52 | )
53 | }
54 | }
--------------------------------------------------------------------------------
/colorpicker/src/main/res/drawable/ic_baseline_content_copy_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/colorpicker/src/test/java/com/smarttoolfactory/colorpicker/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.colorpicker
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jul 25 14:46:27 AEST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk17
--------------------------------------------------------------------------------
/screenshots/colorful_sliders.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorful_sliders.gif
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_circle_hue_saturation_hsv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_circle_hue_saturation_hsv.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_gradient_diamond_hsl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_gradient_diamond_hsl.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_gradient_diamond_hsl2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_gradient_diamond_hsl2.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_gradient_rect_hsl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_gradient_rect_hsl.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_gradient_rect_hsv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_gradient_rect_hsv.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_rect_hue_lightness_hsl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_rect_hue_lightness_hsl.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_rect_hue_saturation_hsl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_rect_hue_saturation_hsl.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_rect_hue_saturation_hsv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_rect_hue_saturation_hsv.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_rect_hue_value_hsv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_rect_hue_value_hsv.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_rect_saturation_lightness_hsl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_rect_saturation_lightness_hsl.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_rect_saturation_value_hsv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_rect_saturation_value_hsv.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_ring_diamond_hex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_ring_diamond_hex.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_ring_diamond_hsl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_ring_diamond_hsl.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_ring_rect_hex_hsv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_ring_rect_hex_hsv.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_ring_rect_hsl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_ring_rect_hsl.png
--------------------------------------------------------------------------------
/screenshots/colorpicker/cp_ring_rect_hsv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/colorpicker/cp_ring_rect_hsv.png
--------------------------------------------------------------------------------
/screenshots/gradient_angle.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/gradient_angle.gif
--------------------------------------------------------------------------------
/screenshots/gradient_selection.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/gradient_selection.gif
--------------------------------------------------------------------------------
/screenshots/hsv_hsl_gradient.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/hsv_hsl_gradient.gif
--------------------------------------------------------------------------------
/screenshots/intro.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/intro.gif
--------------------------------------------------------------------------------
/screenshots/saturation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Compose-Color-Picker-Bundle/a027bbadc0542494214ce59c399dcfe9cd0862ef/screenshots/saturation.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | maven { url 'https://jitpack.io'}
14 | }
15 | }
16 | rootProject.name = "Compose Color Picker"
17 | include ':app'
18 | include ':colorpicker'
19 |
--------------------------------------------------------------------------------