From c52abda1c47115641e1fcbad23276a200547c51d Mon Sep 17 00:00:00 2001 From: N7k Date: Sun, 22 Mar 2020 21:04:23 +0200 Subject: [PATCH 01/34] Add RGBColorPickerSeekBar class --- .../codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt new file mode 100644 index 0000000..39a5830 --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt @@ -0,0 +1,5 @@ +package codes.side.andcolorpicker.rgb + +class RGBColorPickerSeekBar { + +} From 97c90196f1cb58c90ecc136b340d07b87801b934 Mon Sep 17 00:00:00 2001 From: N7k Date: Tue, 24 Mar 2020 01:28:54 +0200 Subject: [PATCH 02/34] Add RGBColorFactory --- .../side/andcolorpicker/model/IntegerRGBColor.kt | 6 ++++++ .../andcolorpicker/model/factory/RGBColorFactory.kt | 13 +++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/RGBColorFactory.kt diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt index 3566945..51ec220 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt @@ -10,4 +10,10 @@ class IntegerRGBColor : IntegerColor(COMPONENTS_COUNT) { override val alpha: Float get() = TODO("Not yet implemented") + + override fun clone(): IntegerRGBColor { + return IntegerRGBColor().also { + it.setFrom(this) + } + } } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/RGBColorFactory.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/RGBColorFactory.kt new file mode 100644 index 0000000..b8dcbaf --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/RGBColorFactory.kt @@ -0,0 +1,13 @@ +package codes.side.andcolorpicker.model.factory + +import codes.side.andcolorpicker.model.IntegerRGBColor + +class RGBColorFactory : ColorFactory() { + override fun create(): IntegerRGBColor { + return IntegerRGBColor() + } + + override fun createColorFrom(color: IntegerRGBColor): IntegerRGBColor { + return color.clone() + } +} From 8a3bf4fb7c9db4438439cd3e6ee9cfa523e63b72 Mon Sep 17 00:00:00 2001 From: N7k Date: Thu, 26 Mar 2020 02:21:30 +0200 Subject: [PATCH 03/34] Describe IntegerRGBColor model --- .../andcolorpicker/model/IntegerRGBColor.kt | 144 +++++++++++++++++- 1 file changed, 138 insertions(+), 6 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt index 51ec220..2e42a80 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt @@ -1,19 +1,151 @@ package codes.side.andcolorpicker.model -class IntegerRGBColor : IntegerColor(COMPONENTS_COUNT) { +class IntegerRGBColor : IntegerColor( + COMPONENTS_COUNT, + DEFAULT_RGB_VALUES +) { companion object { private const val TAG = "IntegerRGBColor" - private const val COMPONENTS_COUNT = 3 + private val COMPONENTS_COUNT = Component.values().size + + private val DEFAULT_RGB_VALUES = Component + .values().map { it.defaultValue }.toIntArray() } override val colorKey = ColorKey.RGB override val alpha: Float - get() = TODO("Not yet implemented") + get() { + return intA / Component.A.maxValue.toFloat() + } + var intA: Int + get() { + return intValues[Component.A.ordinal] + } + set(value) { + setValue( + Component.A.ordinal, + value, + Component.A.minValue, + Component.A.maxValue + ) + } - override fun clone(): IntegerRGBColor { - return IntegerRGBColor().also { - it.setFrom(this) + var floatR: Float + get() { + return intR.toFloat() + } + set(value) { + intR = value.toInt() + } + var intR: Int + get() { + return intValues[Component.R.ordinal] + } + set(value) { + setValue( + Component.R.ordinal, + value, + Component.R.minValue, + Component.R.maxValue + ) + } + + var floatG: Float + get() { + return intG.toFloat() + } + set(value) { + intG = value.toInt() + } + var intG: Int + get() { + return intValues[Component.G.ordinal] + } + set(value) { + setValue( + Component.G.ordinal, + value, + Component.G.minValue, + Component.G.maxValue + ) + } + + var floatB: Float + get() { + return intB.toFloat() } + set(value) { + intB = value.toInt() + } + var intB: Int + get() { + return intValues[Component.B.ordinal] + } + set(value) { + setValue( + Component.B.ordinal, + value, + Component.B.minValue, + Component.B.maxValue + ) + } + + override fun clone(): IntegerRGBColor { + return super.clone() as IntegerRGBColor + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + if (!super.equals(other)) return false + + other as IntegerRGBColor + + if (colorKey != other.colorKey) return false + + return true + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + colorKey.hashCode() + return result + } + + // TODO: Make Component top-level? + // TODO: Make tree? + // TODO: Use range? + enum class Component( + val defaultValue: Int, + val minValue: Int, + val maxValue: Int + ) { + R( + 0, + 0, + 255 + ), + G( + 0, + 0, + 255 + ), + B( + 0, + 0, + 255 + ), + A( + 255, + 0, + 255 + ); + + // TODO: Adapt for non-zero min values + val normalizedDefaultValue: Float + get() { + return defaultValue / maxValue.toFloat() + } } } From 4ec1f4facf8a21fdc7ae3a8801d9f54803f9cf71 Mon Sep 17 00:00:00 2001 From: bayramcicek Date: Thu, 26 Mar 2020 12:04:31 +0300 Subject: [PATCH 04/34] SwatchView component added --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index bebf6fd..2585348 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,14 @@ Alpha component: android:layout_height="wrap_content" /> ``` +SwatchView component: +```xml + +``` + #### Kotlin Snippet ```kotlin // Configure picker color model programmatically From 8d87a9ced5c3dc3878021170803b9855c5f3532c Mon Sep 17 00:00:00 2001 From: N7k Date: Thu, 26 Mar 2020 22:34:55 +0200 Subject: [PATCH 05/34] Add minor fixes --- .../andcolorpicker/model/IntegerHSLColor.kt | 2 +- .../andcolorpicker/model/IntegerRGBColor.kt | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt index 1fc837a..82f4efb 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt @@ -174,7 +174,7 @@ class IntegerHSLColor : IntegerColor( return ordinal } - // TODO: Adapt for non-zero min valies + // TODO: Adapt for non-zero min values val normalizedDefaultValue: Float get() { return defaultValue / maxValue.toFloat() diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt index 2e42a80..dea62e6 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerRGBColor.kt @@ -20,11 +20,11 @@ class IntegerRGBColor : IntegerColor( } var intA: Int get() { - return intValues[Component.A.ordinal] + return intValues[Component.A.index] } set(value) { setValue( - Component.A.ordinal, + Component.A.index, value, Component.A.minValue, Component.A.maxValue @@ -40,11 +40,11 @@ class IntegerRGBColor : IntegerColor( } var intR: Int get() { - return intValues[Component.R.ordinal] + return intValues[Component.R.index] } set(value) { setValue( - Component.R.ordinal, + Component.R.index, value, Component.R.minValue, Component.R.maxValue @@ -60,11 +60,11 @@ class IntegerRGBColor : IntegerColor( } var intG: Int get() { - return intValues[Component.G.ordinal] + return intValues[Component.G.index] } set(value) { setValue( - Component.G.ordinal, + Component.G.index, value, Component.G.minValue, Component.G.maxValue @@ -80,11 +80,11 @@ class IntegerRGBColor : IntegerColor( } var intB: Int get() { - return intValues[Component.B.ordinal] + return intValues[Component.B.index] } set(value) { setValue( - Component.B.ordinal, + Component.B.index, value, Component.B.minValue, Component.B.maxValue @@ -142,6 +142,12 @@ class IntegerRGBColor : IntegerColor( 255 ); + // TODO: Review approach + val index: Int + get() { + return ordinal + } + // TODO: Adapt for non-zero min values val normalizedDefaultValue: Float get() { From 4d1d2f67b1d39e4ed5db97ae420c999047109df0 Mon Sep 17 00:00:00 2001 From: N7k Date: Fri, 27 Mar 2020 01:40:11 +0200 Subject: [PATCH 06/34] Add IntegerRGBColorConverter --- .../converter/IntegerRGBColorConverter.kt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt new file mode 100644 index 0000000..70dabb2 --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt @@ -0,0 +1,37 @@ +package codes.side.andcolorpicker.converter + +import codes.side.andcolorpicker.model.Color +import codes.side.andcolorpicker.model.IntegerRGBColor + +class IntegerRGBColorConverter : ColorConverter { + + override fun convertToOpaqueColorInt(color: Color): Int { + TODO("Not yet implemented") + } + + override fun convertToColorInt(color: Color): Int { + require(color is IntegerRGBColor) { "Unsupported color type supplied" } + + return android.graphics.Color.rgb( + color.floatR.toInt(), + color.floatG.toInt(), + color.floatB.toInt() + ) + } + + override fun convertToPureHueColorInt(color: Color): Int { + TODO("Not yet implemented") + } + + override fun setFromColorInt(color: Color, value: Int) { + require(color is IntegerRGBColor) { "Unsupported color type supplied" } + + color.copyValuesFrom( + intArrayOf( + android.graphics.Color.red(value), + android.graphics.Color.green(value), + android.graphics.Color.blue(value) + ) + ) + } +} From 53c19ef76a019d3b928e49ecc056263d6cd3bea7 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Fri, 27 Mar 2020 02:07:30 +0200 Subject: [PATCH 07/34] Move Swatches to separate README section --- README.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2585348..5d8d230 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,6 @@ Alpha component: android:layout_height="wrap_content" /> ``` -SwatchView component: -```xml - -``` - #### Kotlin Snippet ```kotlin // Configure picker color model programmatically @@ -154,6 +146,34 @@ Supported `cmykColoringMode` values: - `pure` (default) - `output` +### Swatches + +SwatchView component: +```xml + +``` + +#### Kotlin Snippet: +```kotlin +swatchView.setSwatchPatternTint( + Color.LTGRAY +) + +swatchView.setSwatchColor( + IntegerHSLColor().also { + it.setFromColorInt( + ColorUtils.setAlphaComponent( + Color.MAGENTA, + 128 + ) + ) + } +) +``` + ## :rocket: Roadmap - [ ] Add more picker types From 4232af303391be05a946c7f36b2d6c19a9c13276 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Fri, 27 Mar 2020 17:22:25 +0200 Subject: [PATCH 08/34] Prepare LAB color model --- .../side/andcolorpicker/model/ColorKey.kt | 3 +- .../andcolorpicker/model/IntegerLABColor.kt | 160 ++++++++++++++++++ 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/ColorKey.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/ColorKey.kt index cff2e55..51d33ff 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/ColorKey.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/ColorKey.kt @@ -3,5 +3,6 @@ package codes.side.andcolorpicker.model enum class ColorKey { HSL, RGB, - CMYK + CMYK, + LAB } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt new file mode 100644 index 0000000..964b368 --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt @@ -0,0 +1,160 @@ +package codes.side.andcolorpicker.model + +// TODO: Provide precision options +// TODO: Remove float properties? Move to converters? +class IntegerLABColor : IntegerColor( + COMPONENTS_COUNT, + DEFAULT_HSL_VALUES +) { + companion object { + private const val TAG = "IntegerLABColor" + private val COMPONENTS_COUNT = Component.values().size + + private val DEFAULT_HSL_VALUES = Component + .values().map { it.defaultValue }.toIntArray() + } + + override val colorKey = ColorKey.HSL + + override val alpha: Float + get() { + return intAlpha / Component.ALPHA.maxValue.toFloat() + } + + var intAlpha: Int + get() { + return intValues[Component.ALPHA.index] + } + set(value) { + setValue( + Component.ALPHA.index, + value, + Component.ALPHA.minValue, + Component.ALPHA.maxValue + ) + } + + var floatL: Float + get() { + return intL.toFloat() + } + set(value) { + intL = value.toInt() + } + var intL: Int + get() { + return intValues[Component.L.index] + } + set(value) { + setValue( + Component.L.index, + value, + Component.L.minValue, + Component.L.maxValue + ) + } + var floatA: Float + get() { + return intA / Component.A.maxValue.toFloat() + } + set(value) { + intA = (value * Component.A.maxValue).toInt() + } + var intA: Int + get() { + return intValues[Component.A.index] + } + set(value) { + setValue( + Component.A.index, + value, + Component.A.minValue, + Component.A.maxValue + ) + } + var floatB: Float + get() { + return intB / Component.B.maxValue.toFloat() + } + set(value) { + intB = (value * Component.B.maxValue).toInt() + } + var intB: Int + get() { + return intValues[Component.B.index] + } + set(value) { + setValue( + Component.B.index, + value, + Component.B.minValue, + Component.B.maxValue + ) + } + + override fun clone(): IntegerLABColor { + return IntegerLABColor().also { + it.setFrom(this) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + if (!super.equals(other)) return false + + other as IntegerLABColor + + if (colorKey != other.colorKey) return false + + return true + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + colorKey.hashCode() + return result + } + + // TODO: Make Component top-level? + // TODO: Make tree? + // TODO: Use range? + enum class Component( + val defaultValue: Int, + val minValue: Int, + val maxValue: Int + ) { + L( + 50, + 0, + 100 + ), + A( + 0, + -128, + 127 + ), + B( + 0, + -128, + 127 + ), + ALPHA( + 255, + 0, + 255 + ); + + // TODO: Review approach + val index: Int + get() { + return ordinal + } + + // TODO: Adapt for non-zero min valies + val normalizedDefaultValue: Float + get() { + return defaultValue / maxValue.toFloat() + } + } +} From 9634ac8dd246ab1ded287fa0699db88ef3c9a135 Mon Sep 17 00:00:00 2001 From: N7k Date: Fri, 27 Mar 2020 21:22:57 +0200 Subject: [PATCH 09/34] Implement RGBColorPickerSeekBar --- .../converter/ColorConverterHub.kt | 4 + .../rgb/RGBColorPickerSeekBar.kt | 325 +++++++++++++++++- andcolorpicker/src/main/res/values/attrs.xml | 12 + .../src/main/res/values/strings.xml | 3 + 4 files changed, 343 insertions(+), 1 deletion(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt index 1960d74..b70b626 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt @@ -14,6 +14,10 @@ object ColorConverterHub { ColorKey.CMYK, IntegerCMYKColorConverter() ) + registerConverter( + ColorKey.RGB, + IntegerRGBColorConverter() + ) } fun getConverterByKey(key: ColorKey): ColorConverter { diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt index 39a5830..21188f0 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt @@ -1,5 +1,328 @@ package codes.side.andcolorpicker.rgb -class RGBColorPickerSeekBar { +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.LayerDrawable +import android.util.AttributeSet +import androidx.core.graphics.ColorUtils +import codes.side.andcolorpicker.R +import codes.side.andcolorpicker.converter.IntegerRGBColorConverter +import codes.side.andcolorpicker.model.IntegerRGBColor +import codes.side.andcolorpicker.model.factory.RGBColorFactory +import codes.side.andcolorpicker.view.picker.GradientColorSeekBar +class RGBColorPickerSeekBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = androidx.appcompat.R.attr.seekBarStyle +) : + GradientColorSeekBar( + RGBColorFactory(), + context, + attrs, + defStyle + ) { + companion object { + private const val TAG = "RGBColorPickerSeekBar" + + private val DEFAULT_MODE = Mode.MODE_R + private val DEFAULT_COLORING_MODE = ColoringMode.PURE_COLOR + + private val PURE_RED_COLOR_CHECKPOINTS = intArrayOf( + Color.BLACK, + Color.RED + ) + private val PURE_GREEN_COLOR_CHECKPOINTS = intArrayOf( + Color.BLACK, + Color.GREEN + ) + private val PURE_BLUE_COLOR_CHECKPOINTS = intArrayOf( + Color.BLACK, + Color.BLUE + ) + + private val PLAIN_RED_COLOR_CHECKPOINTS = intArrayOf( + Color.RED, + Color.RED + ) + private val PLAIN_GREEN_COLOR_CHECKPOINTS = intArrayOf( + Color.GREEN, + Color.GREEN + ) + private val PLAIN_BLUE_COLOR_CHECKPOINTS = intArrayOf( + Color.BLUE, + Color.BLUE + ) + } + + override val colorConverter: IntegerRGBColorConverter + get() = super.colorConverter as IntegerRGBColorConverter + + private var modeInitialized = false + private var _mode: RGBColorPickerSeekBar.Mode? = null + var mode: RGBColorPickerSeekBar.Mode + get() { + return requireNotNull(_mode) { "Mode is not initialized yet" } + } + set(value) { + modeInitialized = true + if (_mode == value) { + return + } + _mode = value + refreshProperties() + refreshProgressFromCurrentColor() + refreshProgressDrawable() + refreshThumb() + } + + private var coloringModeInitialized = false + private var _coloringMode: ColoringMode? = null + var coloringMode: ColoringMode + get() { + return requireNotNull(_coloringMode) { "Coloring mode is not initialized yet" } + } + set(value) { + coloringModeInitialized = true + if (_coloringMode == value) { + return + } + _coloringMode = value + refreshProgressDrawable() + refreshThumb() + } + + init { + init(attrs) + } + + private fun init(attrs: AttributeSet? = null) { + val typedArray = context.theme.obtainStyledAttributes( + attrs, + R.styleable.RGBColorPickerSeekBar, + 0, + 0 + ) + + mode = Mode.values()[typedArray.getInteger( + R.styleable.RGBColorPickerSeekBar_rgbMode, + DEFAULT_MODE.ordinal + )] + coloringMode = ColoringMode.values()[typedArray.getInteger( + R.styleable.RGBColorPickerSeekBar_rgbColoringMode, + DEFAULT_COLORING_MODE.ordinal + )] + + typedArray.recycle() + } + + override fun setMin(min: Int) { + if (modeInitialized && min != mode.minProgress) { + throw IllegalArgumentException("Current mode supports ${mode.minProgress} min value only") + } + super.setMin(min) + } + + override fun setMax(max: Int) { + if (modeInitialized && max != mode.maxProgress) { + throw IllegalArgumentException("Current mode supports ${mode.maxProgress} max value only") + } + super.setMax(max) + } + + override fun updateInternalPickedColorFrom(value: IntegerRGBColor) { + super.updateInternalPickedColorFrom(value) + internalPickedColor.setFrom(value) + } + + override fun refreshProperties() { + super.refreshProperties() + if (!modeInitialized) { + return + } + max = mode.maxProgress + } + + override fun refreshProgressDrawable() { + super.refreshProgressDrawable() + + if (!coloringModeInitialized || !modeInitialized) { + return + } + + ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { + Mode.MODE_R -> { + when (coloringMode) { + ColoringMode.PURE_COLOR -> PURE_RED_COLOR_CHECKPOINTS + ColoringMode.OUTPUT_COLOR -> TODO() + ColoringMode.PLAIN_COLOR -> PLAIN_RED_COLOR_CHECKPOINTS + } + } + Mode.MODE_G -> { + when (coloringMode) { + ColoringMode.PURE_COLOR -> PURE_GREEN_COLOR_CHECKPOINTS + ColoringMode.OUTPUT_COLOR -> TODO() + ColoringMode.PLAIN_COLOR -> PLAIN_GREEN_COLOR_CHECKPOINTS + } + } + Mode.MODE_B -> { + when (coloringMode) { + ColoringMode.PURE_COLOR -> PURE_BLUE_COLOR_CHECKPOINTS + ColoringMode.OUTPUT_COLOR -> TODO() + ColoringMode.PLAIN_COLOR -> PLAIN_BLUE_COLOR_CHECKPOINTS + } + } + } + } + + override fun refreshThumb() { + super.refreshThumb() + + coloringDrawables.forEach { + when (it) { + is GradientDrawable -> { + paintThumbStroke(it) + } + is LayerDrawable -> { + paintThumbStroke(it.getDrawable(0) as GradientDrawable) + } + } + } + } + + override fun refreshInternalPickedColorFromProgress() { + super.refreshInternalPickedColorFromProgress() + + if (!modeInitialized) { + return + } + + val currentProgress = progress + // TODO: Use Atomic and compare/set? + val changed = when (mode) { + Mode.MODE_R -> { + val currentH = internalPickedColor.intR + if (currentH != currentProgress) { + internalPickedColor.intR = currentProgress + true + } else { + false + } + } + Mode.MODE_G -> { + val currentS = internalPickedColor.intG + if (currentS != currentProgress) { + internalPickedColor.intG = currentProgress + true + } else { + false + } + } + Mode.MODE_B -> { + val currentL = internalPickedColor.intB + if (currentL != currentProgress) { + internalPickedColor.intB = currentProgress + true + } else { + false + } + } + } + + if (changed) { + notifyListenersOnColorChanged() + } + } + + override fun refreshProgressFromCurrentColor() { + super.refreshProgressFromCurrentColor() + + if (!modeInitialized) { + return + } + + progress = when (mode) { + Mode.MODE_R -> { + internalPickedColor.intR + } + Mode.MODE_G -> { + internalPickedColor.intG + } + Mode.MODE_B -> { + internalPickedColor.intB + } + } + } + + // TODO: Refactor + private fun paintThumbStroke(drawable: GradientDrawable) { + if (!coloringModeInitialized || !modeInitialized) { + return + } + + val currentProgress = progress + drawable.setStroke( + thumbStrokeWidthPx, + when (mode) { + Mode.MODE_R -> { + when (coloringMode) { + ColoringMode.PURE_COLOR -> ColorUtils.blendARGB( + Color.BLACK, + Color.RED, + currentProgress / mode.maxProgress.toFloat() + ) + ColoringMode.OUTPUT_COLOR -> TODO() + ColoringMode.PLAIN_COLOR -> Color.RED + } + } + Mode.MODE_G -> { + when (coloringMode) { + ColoringMode.PURE_COLOR -> ColorUtils.blendARGB( + Color.BLACK, + Color.GREEN, + currentProgress / mode.maxProgress.toFloat() + ) + ColoringMode.OUTPUT_COLOR -> TODO() + ColoringMode.PLAIN_COLOR -> Color.GREEN + } + } + Mode.MODE_B -> { + when (coloringMode) { + ColoringMode.PURE_COLOR -> ColorUtils.blendARGB( + Color.BLACK, + Color.BLUE, + currentProgress / mode.maxProgress.toFloat() + ) + ColoringMode.OUTPUT_COLOR -> TODO() + ColoringMode.PLAIN_COLOR -> Color.BLUE + } + } + } + ) + } + + enum class ColoringMode { + PURE_COLOR, + OUTPUT_COLOR, + PLAIN_COLOR, + } + + enum class Mode( + val minProgress: Int, + val maxProgress: Int + ) { + MODE_R( + IntegerRGBColor.Component.R.minValue, + IntegerRGBColor.Component.R.maxValue + ), + MODE_G( + IntegerRGBColor.Component.G.minValue, + IntegerRGBColor.Component.G.maxValue + ), + MODE_B( + IntegerRGBColor.Component.B.minValue, + IntegerRGBColor.Component.B.maxValue + ), + } } diff --git a/andcolorpicker/src/main/res/values/attrs.xml b/andcolorpicker/src/main/res/values/attrs.xml index 5814363..4e105f3 100644 --- a/andcolorpicker/src/main/res/values/attrs.xml +++ b/andcolorpicker/src/main/res/values/attrs.xml @@ -23,4 +23,16 @@ + + + + + + + + + + + + diff --git a/andcolorpicker/src/main/res/values/strings.xml b/andcolorpicker/src/main/res/values/strings.xml index e7ef7b8..22fc4d8 100644 --- a/andcolorpicker/src/main/res/values/strings.xml +++ b/andcolorpicker/src/main/res/values/strings.xml @@ -8,6 +8,9 @@ Magenta Yellow Key + Red + Green + Blue [WIP] Pick a color Pick Cancel From bccd3ccaf827dee9ac6fdabd6991d93ba7d6e85d Mon Sep 17 00:00:00 2001 From: N7k Date: Fri, 27 Mar 2020 21:23:52 +0200 Subject: [PATCH 10/34] Add RGBSeekBarFragment --- .../app/activity/MainActivity.kt | 2 +- .../app/fragment/RGBSeekBarFragment.kt | 56 +++++++++++++ .../main/res/layout/fragment_rgb_seek_bar.xml | 82 +++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt create mode 100644 app/src/main/res/layout/fragment_rgb_seek_bar.xml diff --git a/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt b/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt index 51d15fc..dfa1cf0 100644 --- a/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt +++ b/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt @@ -211,7 +211,7 @@ class MainActivity : AppCompatActivity(), RGB_SEEK_BAR( "RGB SeekBar", MaterialDesignDx.Icon.gmf_space_bar, - { WipFragment() } + { RGBSeekBarFragment() } ), RGB_PLANE( "RGB Plane", diff --git a/app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt b/app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt new file mode 100644 index 0000000..1aeba66 --- /dev/null +++ b/app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt @@ -0,0 +1,56 @@ +package codes.side.andcolorpicker.app.fragment + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import codes.side.andcolorpicker.app.R +import codes.side.andcolorpicker.group.PickerGroup +import codes.side.andcolorpicker.group.registerPickers +import codes.side.andcolorpicker.model.IntegerRGBColor +import codes.side.andcolorpicker.view.picker.ColorSeekBar +import kotlinx.android.synthetic.main.fragment_rgb_seek_bar.* + +class RGBSeekBarFragment : Fragment(R.layout.fragment_rgb_seek_bar) { + + companion object { + private const val TAG = "RGBSeekBarFragment" + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated( + view, + savedInstanceState + ) + + val pickerGroup = PickerGroup().also { + it.registerPickers( + redRGBColorPickerSeekBar, + greenRGBColorPickerSeekBar, + blueRGBColorPickerSeekBar + ) + } + + pickerGroup.addListener( + object : + ColorSeekBar.DefaultOnColorPickListener, IntegerRGBColor>() { + override fun onColorChanged( + picker: ColorSeekBar, + color: IntegerRGBColor, + value: Int + ) { + swatchView.setSwatchColor( + color + ) + } + } + ) + + pickerGroup.setColor( + IntegerRGBColor().also { + it.intR = 30 + it.intG = 130 + it.intB = 230 + } + ) + } +} diff --git a/app/src/main/res/layout/fragment_rgb_seek_bar.xml b/app/src/main/res/layout/fragment_rgb_seek_bar.xml new file mode 100644 index 0000000..a72d452 --- /dev/null +++ b/app/src/main/res/layout/fragment_rgb_seek_bar.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + From 564f2097ac86521401bff1c483c7ff3b8a7ff1e9 Mon Sep 17 00:00:00 2001 From: N7k Date: Fri, 27 Mar 2020 21:25:33 +0200 Subject: [PATCH 11/34] Fix CMYKColorPickerSeekBar setMin/setMax mode checks --- .../side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt index 5d5b31d..632b2b8 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt @@ -95,15 +95,15 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( } override fun setMin(min: Int) { - if (min != 0) { - throw IllegalArgumentException("Current mode supports 0 min value only") + if (modeInitialized && min != mode.minProgress) { + throw IllegalArgumentException("Current mode supports ${mode.minProgress} min value only") } super.setMin(min) } override fun setMax(max: Int) { - if (max != 100) { - throw IllegalArgumentException("Current mode supports 100 max value only") + if (modeInitialized && max != mode.maxProgress) { + throw IllegalArgumentException("Current mode supports ${mode.maxProgress} max value only") } super.setMax(max) } From a652be04b3fd5cbdb00e3546a503860300ef0e54 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Fri, 27 Mar 2020 22:04:09 +0200 Subject: [PATCH 12/34] Extend LAB color model ecosystem --- .../converter/ColorConverterHub.kt | 6 + .../converter/IntegerLABColorConverter.kt | 30 ++ .../hsl/HSLColorPickerSeekBar.kt | 2 +- .../lab/LABColorPickerSeekBar.kt | 314 ++++++++++++++++++ .../andcolorpicker/model/IntegerLABColor.kt | 2 +- .../model/factory/LABColorFactory.kt | 13 + .../app/activity/MainActivity.kt | 25 +- .../app/fragment/LABSeekBarFragment.kt | 56 ++++ .../main/res/layout/fragment_lab_seek_bar.xml | 81 +++++ app/src/main/res/values/strings.xml | 3 + 10 files changed, 520 insertions(+), 12 deletions(-) create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerLABColorConverter.kt create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/LABColorFactory.kt create mode 100644 app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt create mode 100644 app/src/main/res/layout/fragment_lab_seek_bar.xml diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt index 1960d74..b274467 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/ColorConverterHub.kt @@ -14,12 +14,18 @@ object ColorConverterHub { ColorKey.CMYK, IntegerCMYKColorConverter() ) + registerConverter( + ColorKey.LAB, + IntegerLABColorConverter() + ) } + @Suppress("MemberVisibilityCanBePrivate") fun getConverterByKey(key: ColorKey): ColorConverter { return requireNotNull(map[key]) } + @Suppress("MemberVisibilityCanBePrivate") fun registerConverter(key: ColorKey, converter: ColorConverter) { map[key] = converter } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerLABColorConverter.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerLABColorConverter.kt new file mode 100644 index 0000000..9371477 --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerLABColorConverter.kt @@ -0,0 +1,30 @@ +package codes.side.andcolorpicker.converter + +import androidx.core.graphics.ColorUtils +import codes.side.andcolorpicker.model.Color +import codes.side.andcolorpicker.model.IntegerLABColor + +class IntegerLABColorConverter : ColorConverter { + override fun convertToOpaqueColorInt(color: Color): Int { + TODO("Not yet implemented") + } + + override fun convertToColorInt(color: Color): Int { + require(color is IntegerLABColor) { "Unsupported color type supplied" } + + return ColorUtils.LABToColor( + color.intL.toDouble(), + color.intA.toDouble(), + color.intB.toDouble() + ) + } + + override fun convertToPureHueColorInt(color: Color): Int { + TODO("Not yet implemented") + } + + override fun setFromColorInt(color: Color, value: Int) { + require(color is IntegerLABColor) { "Unsupported color type supplied" } + TODO("Not yet implemented") + } +} diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt index 224b595..c36014d 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt @@ -30,7 +30,7 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( defStyle ) { companion object { - private const val TAG = "AndColorPickerSeekBar" + private const val TAG = "HSLColorPickerSeekBar" private val DEFAULT_MODE = Mode.MODE_HUE private val DEFAULT_COLORING_MODE = ColoringMode.PURE_COLOR diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt new file mode 100644 index 0000000..e08875f --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -0,0 +1,314 @@ +package codes.side.andcolorpicker.lab + +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.LayerDrawable +import android.util.AttributeSet +import codes.side.andcolorpicker.converter.IntegerLABColorConverter +import codes.side.andcolorpicker.model.IntegerLABColor +import codes.side.andcolorpicker.model.factory.LABColorFactory +import codes.side.andcolorpicker.view.picker.ColorSeekBar +import codes.side.andcolorpicker.view.picker.GradientColorSeekBar + +// TODO: Minimize resource reads +// TODO: Add logger solution +// TODO: Add call flow diagram +// TODO: Add checks and reduce calls count +// TODO: Limit used SDK properties usage +class LABColorPickerSeekBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = androidx.appcompat.R.attr.seekBarStyle +) : + GradientColorSeekBar( + LABColorFactory(), + context, + attrs, + defStyle + ) { + companion object { + private const val TAG = "LABColorPickerSeekBar" + + private val DEFAULT_MODE = Mode.MODE_L + private val DEFAULT_COLORING_MODE = ColoringMode.OUTPUT_COLOR + } + + override val colorConverter: IntegerLABColorConverter + get() = super.colorConverter as IntegerLABColorConverter + + private var modeInitialized = false + private var _mode: Mode? = null + var mode: Mode + get() { + return requireNotNull(_mode) { "Mode is not initialized yet" } + } + set(value) { + modeInitialized = true + if (_mode == value) { + return + } + _mode = value + refreshProperties() + refreshProgressFromCurrentColor() + refreshProgressDrawable() + refreshThumb() + } + + private var coloringModeInitialized = false + private var _coloringMode: ColoringMode? = null + var coloringMode: ColoringMode + get() { + return requireNotNull(_coloringMode) { "Coloring mode is not initialized yet" } + } + set(value) { + coloringModeInitialized = true + if (_coloringMode == value) { + return + } + _coloringMode = value + refreshProgressDrawable() + refreshThumb() + } + + init { + init(attrs) + } + + private fun init(attrs: AttributeSet? = null) { + mode = DEFAULT_MODE + coloringMode = DEFAULT_COLORING_MODE + } + + override fun setMin(min: Int) { + if (modeInitialized && min != mode.minProgress) { + throw IllegalArgumentException("Current mode supports ${mode.minProgress} min value only") + } + super.setMin(min) + } + + override fun setMax(max: Int) { + if (modeInitialized && max != mode.maxProgress) { + throw IllegalArgumentException("Current mode supports ${mode.maxProgress} max value only") + } + super.setMax(max) + } + + override fun updateInternalPickedColorFrom(value: IntegerLABColor) { + super.updateInternalPickedColorFrom(value) + internalPickedColor.setFrom(value) + } + + override fun refreshProperties() { + super.refreshProperties() + if (!modeInitialized) { + return + } + max = mode.maxProgress - mode.minProgress + } + + override fun refreshProgressDrawable() { + super.refreshProgressDrawable() + + if (!coloringModeInitialized || !modeInitialized) { + return + } + + ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { + Mode.MODE_L -> { + when (coloringMode) { + ColoringMode.OUTPUT_COLOR -> intArrayOf( + Color.MAGENTA, + Color.MAGENTA + ) + } + } + Mode.MODE_A -> { + when (coloringMode) { + ColoringMode.OUTPUT_COLOR -> intArrayOf( + Color.MAGENTA, + Color.MAGENTA + ) + } + } + Mode.MODE_B -> { + when (coloringMode) { + ColoringMode.OUTPUT_COLOR -> intArrayOf( + Color.MAGENTA, + Color.MAGENTA + ) + } + } + } + } + + override fun refreshThumb() { + super.refreshThumb() + + coloringDrawables.forEach { + when (it) { + is GradientDrawable -> { + paintThumbStroke(it) + } + is LayerDrawable -> { + paintThumbStroke(it.getDrawable(0) as GradientDrawable) + } + } + } + } + + override fun refreshInternalPickedColorFromProgress() { + super.refreshInternalPickedColorFromProgress() + + if (!modeInitialized) { + return + } + + val currentProgress = mode.minProgress + progress + // TODO: Use Atomic and compare/set? + val changed = when (mode) { + Mode.MODE_L -> { + val currentH = internalPickedColor.intL + if (currentH != currentProgress) { + internalPickedColor.intL = currentProgress + true + } else { + false + } + } + Mode.MODE_A -> { + val currentS = internalPickedColor.intA + if (currentS != currentProgress) { + internalPickedColor.intA = currentProgress + true + } else { + false + } + } + Mode.MODE_B -> { + val currentL = internalPickedColor.intB + if (currentL != currentProgress) { + internalPickedColor.intB = currentProgress + true + } else { + false + } + } + } + + if (changed) { + notifyListenersOnColorChanged() + } + } + + override fun refreshProgressFromCurrentColor() { + super.refreshProgressFromCurrentColor() + + if (!modeInitialized) { + return + } + + progress = when (mode) { + Mode.MODE_L -> { + internalPickedColor.intL + } + Mode.MODE_A -> { + internalPickedColor.intA + } + Mode.MODE_B -> { + internalPickedColor.intB + } + } + } + + // TODO: Deal with int arrays + private fun paintThumbStroke(drawable: GradientDrawable) { + if (!coloringModeInitialized || !modeInitialized) { + return + } + + drawable.setStroke( + thumbStrokeWidthPx, + when (mode) { + Mode.MODE_L -> { + when (coloringMode) { + ColoringMode.OUTPUT_COLOR -> { + colorConverter.convertToColorInt(internalPickedColor) + } + } + } + Mode.MODE_A -> { + when (coloringMode) { + ColoringMode.OUTPUT_COLOR -> { + colorConverter.convertToColorInt(internalPickedColor) + } + } + } + Mode.MODE_B -> { + when (coloringMode) { + ColoringMode.OUTPUT_COLOR -> { + colorConverter.convertToColorInt(internalPickedColor) + } + } + } + } + ) + } + + override fun toString(): String { + return "LABColorPickerSeekBar(tag = $tag, _mode=${if (modeInitialized) mode else null}, _currentColor=$internalPickedColor)" + } + + enum class ColoringMode { + OUTPUT_COLOR + } + + enum class Mode( + val minProgress: Int, + val maxProgress: Int + ) { + MODE_L( + IntegerLABColor.Component.L.minValue, + IntegerLABColor.Component.L.maxValue + ), + MODE_A( + IntegerLABColor.Component.A.minValue, + IntegerLABColor.Component.A.maxValue + ), + MODE_B( + IntegerLABColor.Component.B.minValue, + IntegerLABColor.Component.B.maxValue + ) + } + + interface OnColorPickListener : + ColorSeekBar.OnColorPickListener, IntegerLABColor> + + open class DefaultOnColorPickListener : OnColorPickListener { + override fun onColorPicking( + picker: ColorSeekBar, + color: IntegerLABColor, + value: Int, + fromUser: Boolean + ) { + + } + + override fun onColorPicked( + picker: ColorSeekBar, + color: IntegerLABColor, + value: Int, + fromUser: Boolean + ) { + + } + + override fun onColorChanged( + picker: ColorSeekBar, + color: IntegerLABColor, + value: Int + ) { + + } + } +} diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt index 964b368..69c9aea 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt @@ -14,7 +14,7 @@ class IntegerLABColor : IntegerColor( .values().map { it.defaultValue }.toIntArray() } - override val colorKey = ColorKey.HSL + override val colorKey = ColorKey.LAB override val alpha: Float get() { diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/LABColorFactory.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/LABColorFactory.kt new file mode 100644 index 0000000..9a6e0cb --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/factory/LABColorFactory.kt @@ -0,0 +1,13 @@ +package codes.side.andcolorpicker.model.factory + +import codes.side.andcolorpicker.model.IntegerLABColor + +class LABColorFactory : ColorFactory() { + override fun create(): IntegerLABColor { + return IntegerLABColor() + } + + override fun createColorFrom(color: IntegerLABColor): IntegerLABColor { + return color.clone() + } +} diff --git a/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt b/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt index e634b15..448445e 100644 --- a/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt +++ b/app/src/main/java/codes/side/andcolorpicker/app/activity/MainActivity.kt @@ -203,16 +203,6 @@ class MainActivity : AppCompatActivity(), MaterialDesignDx.Icon.gmf_fullscreen, { WipFragment() } ), - CMYK_SEEK_BAR( - "CMYK SeekBar", - MaterialDesignDx.Icon.gmf_print, - { CMYKSeekBarFragment() } - ), - CMYK_SEEK_BAR_GITHUB( - "CMYK SeekBar GitHub", - FontAwesome.Icon.faw_github, - { CMYKSeekBarGithubSampleFragment() } - ), RGB_SEEK_BAR( "RGB SeekBar", MaterialDesignDx.Icon.gmf_space_bar, @@ -228,6 +218,21 @@ class MainActivity : AppCompatActivity(), MaterialDesignDx.Icon.gmf_lens, { WipFragment() } ), + LAB_SEEK_BAR( + "LAB SeekBar", + FontAwesome.Icon.faw_flask, + { LABSeekBarFragment() } + ), + CMYK_SEEK_BAR( + "CMYK SeekBar", + MaterialDesignDx.Icon.gmf_print, + { CMYKSeekBarFragment() } + ), + CMYK_SEEK_BAR_GITHUB( + "CMYK SeekBar GitHub", + FontAwesome.Icon.faw_github, + { CMYKSeekBarGithubSampleFragment() } + ), SWATCHES( "Swatches", MaterialDesignDx.Icon.gmf_view_comfy, diff --git a/app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt b/app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt new file mode 100644 index 0000000..46dd47a --- /dev/null +++ b/app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt @@ -0,0 +1,56 @@ +package codes.side.andcolorpicker.app.fragment + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import codes.side.andcolorpicker.app.R +import codes.side.andcolorpicker.group.PickerGroup +import codes.side.andcolorpicker.group.registerPickers +import codes.side.andcolorpicker.model.IntegerLABColor +import codes.side.andcolorpicker.view.picker.ColorSeekBar +import kotlinx.android.synthetic.main.fragment_cmyk_seek_bar.swatchView +import kotlinx.android.synthetic.main.fragment_lab_seek_bar.* + +class LABSeekBarFragment : Fragment(R.layout.fragment_lab_seek_bar) { + companion object { + private const val TAG = "LABSeekBarFragment" + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated( + view, + savedInstanceState + ) + + val pickerGroup = PickerGroup().also { + it.registerPickers( + lLABColorPickerSeekBar, + aLABColorPickerSeekBar, + bLABColorPickerSeekBar + ) + } + + pickerGroup.addListener( + object : + ColorSeekBar.DefaultOnColorPickListener, IntegerLABColor>() { + override fun onColorChanged( + picker: ColorSeekBar, + color: IntegerLABColor, + value: Int + ) { + swatchView.setSwatchColor( + color + ) + } + } + ) + + pickerGroup.setColor( + IntegerLABColor().also { + it.intL = 50 + it.intA = 0 + it.intB = 0 + } + ) + } +} diff --git a/app/src/main/res/layout/fragment_lab_seek_bar.xml b/app/src/main/res/layout/fragment_lab_seek_bar.xml new file mode 100644 index 0000000..ae3771e --- /dev/null +++ b/app/src/main/res/layout/fragment_lab_seek_bar.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d6fb9ce..b281d98 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,4 +9,7 @@ Randomize Work In Progress :D Show dialog + L + A + B From aeac3b7b3dfb4f114ead8564fff5c9dd33aaefd7 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sat, 28 Mar 2020 16:53:19 +0200 Subject: [PATCH 13/34] Extend LABColorPickerSeekBar implementation --- .../lab/LABColorPickerSeekBar.kt | 92 +++++++++++++------ andcolorpicker/src/main/res/values/attrs.xml | 10 ++ .../main/res/layout/fragment_lab_seek_bar.xml | 3 + 3 files changed, 78 insertions(+), 27 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index e08875f..dbd4616 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -1,10 +1,11 @@ package codes.side.andcolorpicker.lab import android.content.Context -import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.util.AttributeSet +import androidx.core.graphics.ColorUtils +import codes.side.andcolorpicker.R import codes.side.andcolorpicker.converter.IntegerLABColorConverter import codes.side.andcolorpicker.model.IntegerLABColor import codes.side.andcolorpicker.model.factory.LABColorFactory @@ -32,6 +33,8 @@ class LABColorPickerSeekBar @JvmOverloads constructor( private val DEFAULT_MODE = Mode.MODE_L private val DEFAULT_COLORING_MODE = ColoringMode.OUTPUT_COLOR + + private const val PROGRESS_SAMPLING_PERIOD = 10 } override val colorConverter: IntegerLABColorConverter @@ -76,20 +79,35 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } private fun init(attrs: AttributeSet? = null) { - mode = DEFAULT_MODE - coloringMode = DEFAULT_COLORING_MODE + val typedArray = context.theme.obtainStyledAttributes( + attrs, + R.styleable.LABColorPickerSeekBar, + 0, + 0 + ) + + mode = Mode.values()[typedArray.getInteger( + R.styleable.LABColorPickerSeekBar_labMode, + DEFAULT_MODE.ordinal + )] + coloringMode = ColoringMode.values()[typedArray.getInteger( + R.styleable.LABColorPickerSeekBar_labColoringMode, + DEFAULT_COLORING_MODE.ordinal + )] + + typedArray.recycle() } override fun setMin(min: Int) { - if (modeInitialized && min != mode.minProgress) { - throw IllegalArgumentException("Current mode supports ${mode.minProgress} min value only") + if (modeInitialized && min != 0) { + throw IllegalArgumentException("Current mode supports 0 min value only, but given $min") } super.setMin(min) } override fun setMax(max: Int) { - if (modeInitialized && max != mode.maxProgress) { - throw IllegalArgumentException("Current mode supports ${mode.maxProgress} max value only") + if (modeInitialized && max != mode.absoluteProgress) { + throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, but given $max") } super.setMax(max) } @@ -117,26 +135,41 @@ class LABColorPickerSeekBar @JvmOverloads constructor( ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { Mode.MODE_L -> { when (coloringMode) { - ColoringMode.OUTPUT_COLOR -> intArrayOf( - Color.MAGENTA, - Color.MAGENTA - ) + ColoringMode.OUTPUT_COLOR -> { + (mode.minProgress..mode.maxProgress).step(PROGRESS_SAMPLING_PERIOD).map { + ColorUtils.LABToColor( + it.toDouble(), + internalPickedColor.intA.toDouble(), + internalPickedColor.intB.toDouble() + ) + }.toIntArray() + } } } Mode.MODE_A -> { when (coloringMode) { - ColoringMode.OUTPUT_COLOR -> intArrayOf( - Color.MAGENTA, - Color.MAGENTA - ) + ColoringMode.OUTPUT_COLOR -> { + (mode.minProgress..mode.maxProgress).step(PROGRESS_SAMPLING_PERIOD).map { + ColorUtils.LABToColor( + internalPickedColor.intL.toDouble(), + it.toDouble(), + internalPickedColor.intB.toDouble() + ) + }.toIntArray() + } } } Mode.MODE_B -> { when (coloringMode) { - ColoringMode.OUTPUT_COLOR -> intArrayOf( - Color.MAGENTA, - Color.MAGENTA - ) + ColoringMode.OUTPUT_COLOR -> { + (mode.minProgress..mode.maxProgress).step(PROGRESS_SAMPLING_PERIOD).map { + ColorUtils.LABToColor( + internalPickedColor.intL.toDouble(), + internalPickedColor.intA.toDouble(), + it.toDouble() + ) + }.toIntArray() + } } } } @@ -168,8 +201,8 @@ class LABColorPickerSeekBar @JvmOverloads constructor( // TODO: Use Atomic and compare/set? val changed = when (mode) { Mode.MODE_L -> { - val currentH = internalPickedColor.intL - if (currentH != currentProgress) { + val currentValue = internalPickedColor.intL + if (currentValue != currentProgress) { internalPickedColor.intL = currentProgress true } else { @@ -177,8 +210,8 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } } Mode.MODE_A -> { - val currentS = internalPickedColor.intA - if (currentS != currentProgress) { + val currentValue = internalPickedColor.intA + if (currentValue != currentProgress) { internalPickedColor.intA = currentProgress true } else { @@ -186,8 +219,8 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } } Mode.MODE_B -> { - val currentL = internalPickedColor.intB - if (currentL != currentProgress) { + val currentValue = internalPickedColor.intB + if (currentValue != currentProgress) { internalPickedColor.intB = currentProgress true } else { @@ -208,7 +241,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( return } - progress = when (mode) { + progress = -mode.minProgress + when (mode) { Mode.MODE_L -> { internalPickedColor.intL } @@ -278,7 +311,12 @@ class LABColorPickerSeekBar @JvmOverloads constructor( MODE_B( IntegerLABColor.Component.B.minValue, IntegerLABColor.Component.B.maxValue - ) + ); + + val absoluteProgress: Int + get() { + return maxProgress - minProgress + } } interface OnColorPickListener : diff --git a/andcolorpicker/src/main/res/values/attrs.xml b/andcolorpicker/src/main/res/values/attrs.xml index 5814363..974c654 100644 --- a/andcolorpicker/src/main/res/values/attrs.xml +++ b/andcolorpicker/src/main/res/values/attrs.xml @@ -23,4 +23,14 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_lab_seek_bar.xml b/app/src/main/res/layout/fragment_lab_seek_bar.xml index ae3771e..720e0db 100644 --- a/app/src/main/res/layout/fragment_lab_seek_bar.xml +++ b/app/src/main/res/layout/fragment_lab_seek_bar.xml @@ -22,6 +22,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginHorizontal="8dp" + app:labMode="l" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/lTitleTextView" /> @@ -44,6 +45,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="8dp" app:cmykMode="magenta" + app:labMode="a" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/aTitleTextView" /> @@ -66,6 +68,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="8dp" app:cmykMode="yellow" + app:labMode="b" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/bTitleTextView" /> From ad332c7b1950c24fcbb18c8506bcb219df2f346a Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sat, 28 Mar 2020 17:09:18 +0200 Subject: [PATCH 14/34] Speed LABColorPickerSeekBar progress drawable checkpoint operations up --- .../lab/LABColorPickerSeekBar.kt | 18 ++++++++++++------ .../andcolorpicker/util/ArrayExtensions.kt | 11 +++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index dbd4616..c1feed2 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -9,6 +9,7 @@ import codes.side.andcolorpicker.R import codes.side.andcolorpicker.converter.IntegerLABColorConverter import codes.side.andcolorpicker.model.IntegerLABColor import codes.side.andcolorpicker.model.factory.LABColorFactory +import codes.side.andcolorpicker.util.mapToIntArray import codes.side.andcolorpicker.view.picker.ColorSeekBar import codes.side.andcolorpicker.view.picker.GradientColorSeekBar @@ -74,6 +75,8 @@ class LABColorPickerSeekBar @JvmOverloads constructor( refreshThumb() } + private val tempSampledRangeCheckpointsIntArray = IntArray(3) + init { init(attrs) } @@ -136,39 +139,39 @@ class LABColorPickerSeekBar @JvmOverloads constructor( Mode.MODE_L -> { when (coloringMode) { ColoringMode.OUTPUT_COLOR -> { - (mode.minProgress..mode.maxProgress).step(PROGRESS_SAMPLING_PERIOD).map { + mode.sampledRange.mapToIntArray(tempSampledRangeCheckpointsIntArray) { ColorUtils.LABToColor( it.toDouble(), internalPickedColor.intA.toDouble(), internalPickedColor.intB.toDouble() ) - }.toIntArray() + } } } } Mode.MODE_A -> { when (coloringMode) { ColoringMode.OUTPUT_COLOR -> { - (mode.minProgress..mode.maxProgress).step(PROGRESS_SAMPLING_PERIOD).map { + mode.sampledRange.mapToIntArray(tempSampledRangeCheckpointsIntArray) { ColorUtils.LABToColor( internalPickedColor.intL.toDouble(), it.toDouble(), internalPickedColor.intB.toDouble() ) - }.toIntArray() + } } } } Mode.MODE_B -> { when (coloringMode) { ColoringMode.OUTPUT_COLOR -> { - (mode.minProgress..mode.maxProgress).step(PROGRESS_SAMPLING_PERIOD).map { + mode.sampledRange.mapToIntArray(tempSampledRangeCheckpointsIntArray) { ColorUtils.LABToColor( internalPickedColor.intL.toDouble(), internalPickedColor.intA.toDouble(), it.toDouble() ) - }.toIntArray() + } } } } @@ -317,6 +320,9 @@ class LABColorPickerSeekBar @JvmOverloads constructor( get() { return maxProgress - minProgress } + + val range by lazy { minProgress..maxProgress } + val sampledRange by lazy { range.step(PROGRESS_SAMPLING_PERIOD) } } interface OnColorPickListener : diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt new file mode 100644 index 0000000..7f50ee3 --- /dev/null +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt @@ -0,0 +1,11 @@ +package codes.side.andcolorpicker.util + +public inline fun Iterable.mapToIntArray( + destination: IntArray, + transform: (T) -> Int +): IntArray { + forEachIndexed { index, t -> + destination[index] = transform(t) + } + return destination +} From 0336d5eae5bcb024bf3216f1971d57455c9dc37f Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sat, 28 Mar 2020 21:08:03 +0200 Subject: [PATCH 15/34] Fix LABColorPickerSeekBar optimizations --- .../side/andcolorpicker/lab/LABColorPickerSeekBar.kt | 12 +++++++----- .../side/andcolorpicker/util/ArrayExtensions.kt | 4 ++-- .../app/fragment/LABSeekBarFragment.kt | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index c1feed2..260940b 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -75,8 +75,6 @@ class LABColorPickerSeekBar @JvmOverloads constructor( refreshThumb() } - private val tempSampledRangeCheckpointsIntArray = IntArray(3) - init { init(attrs) } @@ -135,11 +133,14 @@ class LABColorPickerSeekBar @JvmOverloads constructor( return } + val sampledRangeIntArray = mode.sampledRangeIntArray + val outputIntArray = IntArray(sampledRangeIntArray.size) + ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { Mode.MODE_L -> { when (coloringMode) { ColoringMode.OUTPUT_COLOR -> { - mode.sampledRange.mapToIntArray(tempSampledRangeCheckpointsIntArray) { + sampledRangeIntArray.mapToIntArray(outputIntArray) { ColorUtils.LABToColor( it.toDouble(), internalPickedColor.intA.toDouble(), @@ -152,7 +153,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( Mode.MODE_A -> { when (coloringMode) { ColoringMode.OUTPUT_COLOR -> { - mode.sampledRange.mapToIntArray(tempSampledRangeCheckpointsIntArray) { + sampledRangeIntArray.mapToIntArray(outputIntArray) { ColorUtils.LABToColor( internalPickedColor.intL.toDouble(), it.toDouble(), @@ -165,7 +166,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( Mode.MODE_B -> { when (coloringMode) { ColoringMode.OUTPUT_COLOR -> { - mode.sampledRange.mapToIntArray(tempSampledRangeCheckpointsIntArray) { + sampledRangeIntArray.mapToIntArray(outputIntArray) { ColorUtils.LABToColor( internalPickedColor.intL.toDouble(), internalPickedColor.intA.toDouble(), @@ -323,6 +324,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( val range by lazy { minProgress..maxProgress } val sampledRange by lazy { range.step(PROGRESS_SAMPLING_PERIOD) } + val sampledRangeIntArray by lazy { sampledRange.map { it }.toIntArray() } } interface OnColorPickListener : diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt index 7f50ee3..5b67915 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/util/ArrayExtensions.kt @@ -1,8 +1,8 @@ package codes.side.andcolorpicker.util -public inline fun Iterable.mapToIntArray( +inline fun IntArray.mapToIntArray( destination: IntArray, - transform: (T) -> Int + transform: (Int) -> Int ): IntArray { forEachIndexed { index, t -> destination[index] = transform(t) diff --git a/app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt b/app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt index 46dd47a..e1c117c 100644 --- a/app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt +++ b/app/src/main/java/codes/side/andcolorpicker/app/fragment/LABSeekBarFragment.kt @@ -47,9 +47,9 @@ class LABSeekBarFragment : Fragment(R.layout.fragment_lab_seek_bar) { pickerGroup.setColor( IntegerLABColor().also { - it.intL = 50 - it.intA = 0 - it.intB = 0 + it.intL = 60 + it.intA = 50 + it.intB = 90 } ) } From 219c2a7faea887e3b08a2256d092a2c70492b11b Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 16:10:41 +0300 Subject: [PATCH 16/34] Refine ColorSeekBar min/max pipelines --- .../alpha/HSLAlphaColorPickerSeekBar.kt | 10 ++++++++++ .../cmyk/CMYKColorPickerSeekBar.kt | 20 +++++++------------ .../hsl/HSLColorPickerSeekBar.kt | 19 ++++++------------ .../lab/LABColorPickerSeekBar.kt | 20 ++++--------------- .../view/picker/ColorSeekBar.kt | 13 ++++++++++++ 5 files changed, 40 insertions(+), 42 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt index 7234fc9..1ca677b 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt @@ -20,11 +20,21 @@ class HSLAlphaColorPickerSeekBar @JvmOverloads constructor( defStyle ) { + private var isInitialized = false + override val colorConverter: IntegerHSLColorConverter get() = super.colorConverter as IntegerHSLColorConverter init { refreshProperties() + isInitialized = true + } + + override fun setMax(max: Int) { + if (isInitialized && max != IntegerHSLColor.Component.A.maxValue) { + throw IllegalArgumentException("Current mode supports ${IntegerHSLColor.Component.A.maxValue} max value only, but given $max") + } + super.setMax(max) } override fun refreshProperties() { diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt index 5d5b31d..055a556 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt @@ -10,6 +10,7 @@ import codes.side.andcolorpicker.R import codes.side.andcolorpicker.converter.IntegerCMYKColorConverter import codes.side.andcolorpicker.model.IntegerCMYKColor import codes.side.andcolorpicker.model.factory.CMYKColorFactory +import codes.side.andcolorpicker.view.picker.ColorSeekBar import codes.side.andcolorpicker.view.picker.GradientColorSeekBar class CMYKColorPickerSeekBar @JvmOverloads constructor( @@ -94,16 +95,9 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( typedArray.recycle() } - override fun setMin(min: Int) { - if (min != 0) { - throw IllegalArgumentException("Current mode supports 0 min value only") - } - super.setMin(min) - } - override fun setMax(max: Int) { - if (max != 100) { - throw IllegalArgumentException("Current mode supports 100 max value only") + if (modeInitialized && max != mode.absoluteProgress) { + throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, but given $max") } super.setMax(max) } @@ -118,7 +112,7 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( if (!modeInitialized) { return } - max = mode.maxProgress + max = mode.absoluteProgress } override fun refreshProgressDrawable() { @@ -307,10 +301,10 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( } enum class Mode( - val minProgress: Int, - val maxProgress: Int, + override val minProgress: Int, + override val maxProgress: Int, val checkpoints: IntArray - ) { + ) : ColorSeekBar.Mode { MODE_C( IntegerCMYKColor.Component.C.minValue, IntegerCMYKColor.Component.C.maxValue, diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt index c36014d..5b03955 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt @@ -132,16 +132,9 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( typedArray.recycle() } - override fun setMin(min: Int) { - if (modeInitialized && min != mode.minProgress) { - throw IllegalArgumentException("Current mode supports ${mode.minProgress} min value only") - } - super.setMin(min) - } - override fun setMax(max: Int) { - if (modeInitialized && max != mode.maxProgress) { - throw IllegalArgumentException("Current mode supports ${mode.maxProgress} max value only") + if (modeInitialized && max != mode.absoluteProgress) { + throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, but given $max") } super.setMax(max) } @@ -156,7 +149,7 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( if (!modeInitialized) { return } - max = mode.maxProgress + max = mode.absoluteProgress } // TODO: Get rid of toIntArray allocations @@ -388,9 +381,9 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( } enum class Mode( - val minProgress: Int, - val maxProgress: Int - ) { + override val minProgress: Int, + override val maxProgress: Int + ) : ColorSeekBar.Mode { // H from HSV/HSL/HSI/HSB MODE_HUE( IntegerHSLColor.Component.H.minValue, diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index 260940b..4cfa8d2 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -99,13 +99,6 @@ class LABColorPickerSeekBar @JvmOverloads constructor( typedArray.recycle() } - override fun setMin(min: Int) { - if (modeInitialized && min != 0) { - throw IllegalArgumentException("Current mode supports 0 min value only, but given $min") - } - super.setMin(min) - } - override fun setMax(max: Int) { if (modeInitialized && max != mode.absoluteProgress) { throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, but given $max") @@ -123,7 +116,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( if (!modeInitialized) { return } - max = mode.maxProgress - mode.minProgress + max = mode.absoluteProgress } override fun refreshProgressDrawable() { @@ -301,9 +294,9 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } enum class Mode( - val minProgress: Int, - val maxProgress: Int - ) { + override val minProgress: Int, + override val maxProgress: Int + ) : ColorSeekBar.Mode { MODE_L( IntegerLABColor.Component.L.minValue, IntegerLABColor.Component.L.maxValue @@ -317,11 +310,6 @@ class LABColorPickerSeekBar @JvmOverloads constructor( IntegerLABColor.Component.B.maxValue ); - val absoluteProgress: Int - get() { - return maxProgress - minProgress - } - val range by lazy { minProgress..maxProgress } val sampledRange by lazy { range.step(PROGRESS_SAMPLING_PERIOD) } val sampledRangeIntArray by lazy { sampledRange.map { it }.toIntArray() } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt index d3e77b2..c0ecf5d 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt @@ -205,6 +205,9 @@ abstract class ColorSeekBar @JvmOverloads constructor( } override fun setMin(min: Int) { + if (min != 0) { + throw IllegalArgumentException("Current mode supports 0 min value only, but given $min") + } ::minUpdating.marker { super.setMin(min) } @@ -422,6 +425,16 @@ abstract class ColorSeekBar @JvmOverloads constructor( notifyListenersOnColorPicked(true) } + interface Mode { + val minProgress: Int + val maxProgress: Int + } + + val Mode.absoluteProgress: Int + get() { + return maxProgress - minProgress + } + // TODO: Rename interface OnColorPickListener, C : Color> { fun onColorPicking( From ae6d0e383be3a33170ce6722f0124f3eef11a802 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 16:12:24 +0300 Subject: [PATCH 17/34] Update dependencies --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index eb78f39..3fa7feb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,9 +54,9 @@ dependencies { implementation "androidx.fragment:fragment-ktx:1.3.0-alpha02" implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta4" implementation "com.google.android.material:material:1.2.0-alpha05" - implementation "com.mikepenz:materialdrawer:8.0.0-rc02" + implementation "com.mikepenz:materialdrawer:8.0.0" - implementation "com.mikepenz:iconics-core:5.0.0" + implementation "com.mikepenz:iconics-core:5.0.1" implementation "com.mikepenz:material-design-icons-dx-typeface:5.0.1.0-kotlin@aar" implementation "com.mikepenz:fontawesome-typeface:5.9.0.0-kotlin@aar" From 2a2da73f82906e95194abed630df41508c7c93d6 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 16:19:47 +0300 Subject: [PATCH 18/34] Make updateInternalPickedColorFrom templated --- .../andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt | 5 ++--- .../side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt | 5 ++--- .../side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt | 5 ++--- .../side/andcolorpicker/lab/LABColorPickerSeekBar.kt | 5 ++--- .../side/andcolorpicker/view/picker/ColorSeekBar.kt | 10 ++++++++-- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt index 1ca677b..5a4f021 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt @@ -42,9 +42,8 @@ class HSLAlphaColorPickerSeekBar @JvmOverloads constructor( max = IntegerHSLColor.Component.A.maxValue } - override fun updateInternalPickedColorFrom(value: IntegerHSLColor) { - super.updateInternalPickedColorFrom(value) - internalPickedColor.setFrom(value) + override fun updateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { + color.setFrom(value) } override fun refreshInternalPickedColorFromProgress() { diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt index 055a556..f61ed56 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt @@ -102,9 +102,8 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun updateInternalPickedColorFrom(value: IntegerCMYKColor) { - super.updateInternalPickedColorFrom(value) - internalPickedColor.setFrom(value) + override fun updateColorFrom(color: IntegerCMYKColor, value: IntegerCMYKColor) { + color.setFrom(value) } override fun refreshProperties() { diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt index 5b03955..2884c65 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt @@ -139,9 +139,8 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun updateInternalPickedColorFrom(value: IntegerHSLColor) { - super.updateInternalPickedColorFrom(value) - internalPickedColor.setFrom(value) + override fun updateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { + color.setFrom(value) } override fun refreshProperties() { diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index 4cfa8d2..231414f 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -106,9 +106,8 @@ class LABColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun updateInternalPickedColorFrom(value: IntegerLABColor) { - super.updateInternalPickedColorFrom(value) - internalPickedColor.setFrom(value) + override fun updateColorFrom(color: IntegerLABColor, value: IntegerLABColor) { + color.setFrom(value) } override fun refreshProperties() { diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt index c0ecf5d..c8eaadd 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt @@ -220,18 +220,24 @@ abstract class ColorSeekBar @JvmOverloads constructor( } /* - * Silently updates internal picked color from value provided. + * Silently updates internal picked color from provided value. */ // TODO: Make abstract? Make template? - protected open fun updateInternalPickedColorFrom(value: C) { + private fun updateInternalPickedColorFrom(value: C) { if (DEBUG) { Log.d( TAG, "updateInternalCurrentColorFrom() called on $this" ) } + updateColorFrom( + internalPickedColor, + value + ) } + abstract fun updateColorFrom(color: C, value: C) + /* * Refresh or set basic SeekBar properties like min/max. */ From 978958d9a75e93835cfcdbb63460e302101b1cbe Mon Sep 17 00:00:00 2001 From: N7k Date: Sun, 29 Mar 2020 16:54:56 +0300 Subject: [PATCH 19/34] Refactor RGBColorPickerSeekBar --- .../rgb/RGBColorPickerSeekBar.kt | 144 +++++++----------- 1 file changed, 57 insertions(+), 87 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt index 21188f0..ff1a4cb 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt @@ -28,40 +28,14 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( private val DEFAULT_MODE = Mode.MODE_R private val DEFAULT_COLORING_MODE = ColoringMode.PURE_COLOR - - private val PURE_RED_COLOR_CHECKPOINTS = intArrayOf( - Color.BLACK, - Color.RED - ) - private val PURE_GREEN_COLOR_CHECKPOINTS = intArrayOf( - Color.BLACK, - Color.GREEN - ) - private val PURE_BLUE_COLOR_CHECKPOINTS = intArrayOf( - Color.BLACK, - Color.BLUE - ) - - private val PLAIN_RED_COLOR_CHECKPOINTS = intArrayOf( - Color.RED, - Color.RED - ) - private val PLAIN_GREEN_COLOR_CHECKPOINTS = intArrayOf( - Color.GREEN, - Color.GREEN - ) - private val PLAIN_BLUE_COLOR_CHECKPOINTS = intArrayOf( - Color.BLUE, - Color.BLUE - ) } override val colorConverter: IntegerRGBColorConverter get() = super.colorConverter as IntegerRGBColorConverter private var modeInitialized = false - private var _mode: RGBColorPickerSeekBar.Mode? = null - var mode: RGBColorPickerSeekBar.Mode + private var _mode: Mode? = null + var mode: Mode get() { return requireNotNull(_mode) { "Mode is not initialized yet" } } @@ -151,29 +125,15 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( return } - ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { - Mode.MODE_R -> { - when (coloringMode) { - ColoringMode.PURE_COLOR -> PURE_RED_COLOR_CHECKPOINTS - ColoringMode.OUTPUT_COLOR -> TODO() - ColoringMode.PLAIN_COLOR -> PLAIN_RED_COLOR_CHECKPOINTS + ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = + when (coloringMode) { + ColoringMode.PURE_COLOR, ColoringMode.PLAIN_COLOR -> mode.coloringModeCheckpointsMap[coloringMode] + ColoringMode.OUTPUT_COLOR -> when (mode) { + Mode.MODE_R -> TODO() + Mode.MODE_G -> TODO() + Mode.MODE_B -> TODO() } } - Mode.MODE_G -> { - when (coloringMode) { - ColoringMode.PURE_COLOR -> PURE_GREEN_COLOR_CHECKPOINTS - ColoringMode.OUTPUT_COLOR -> TODO() - ColoringMode.PLAIN_COLOR -> PLAIN_GREEN_COLOR_CHECKPOINTS - } - } - Mode.MODE_B -> { - when (coloringMode) { - ColoringMode.PURE_COLOR -> PURE_BLUE_COLOR_CHECKPOINTS - ColoringMode.OUTPUT_COLOR -> TODO() - ColoringMode.PLAIN_COLOR -> PLAIN_BLUE_COLOR_CHECKPOINTS - } - } - } } override fun refreshThumb() { @@ -255,7 +215,6 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( } } - // TODO: Refactor private fun paintThumbStroke(drawable: GradientDrawable) { if (!coloringModeInitialized || !modeInitialized) { return @@ -264,39 +223,19 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( val currentProgress = progress drawable.setStroke( thumbStrokeWidthPx, - when (mode) { - Mode.MODE_R -> { - when (coloringMode) { - ColoringMode.PURE_COLOR -> ColorUtils.blendARGB( - Color.BLACK, - Color.RED, - currentProgress / mode.maxProgress.toFloat() - ) - ColoringMode.OUTPUT_COLOR -> TODO() - ColoringMode.PLAIN_COLOR -> Color.RED - } + when (coloringMode) { + ColoringMode.PURE_COLOR, ColoringMode.PLAIN_COLOR -> { + val checkpoints = requireNotNull(mode.coloringModeCheckpointsMap[coloringMode]) + ColorUtils.blendARGB( + checkpoints.first(), + checkpoints.last(), + currentProgress / mode.maxProgress.toFloat() + ) } - Mode.MODE_G -> { - when (coloringMode) { - ColoringMode.PURE_COLOR -> ColorUtils.blendARGB( - Color.BLACK, - Color.GREEN, - currentProgress / mode.maxProgress.toFloat() - ) - ColoringMode.OUTPUT_COLOR -> TODO() - ColoringMode.PLAIN_COLOR -> Color.GREEN - } - } - Mode.MODE_B -> { - when (coloringMode) { - ColoringMode.PURE_COLOR -> ColorUtils.blendARGB( - Color.BLACK, - Color.BLUE, - currentProgress / mode.maxProgress.toFloat() - ) - ColoringMode.OUTPUT_COLOR -> TODO() - ColoringMode.PLAIN_COLOR -> Color.BLUE - } + ColoringMode.OUTPUT_COLOR -> when (mode) { + Mode.MODE_R -> TODO() + Mode.MODE_G -> TODO() + Mode.MODE_B -> TODO() } } ) @@ -310,19 +249,50 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( enum class Mode( val minProgress: Int, - val maxProgress: Int + val maxProgress: Int, + val coloringModeCheckpointsMap: HashMap ) { MODE_R( IntegerRGBColor.Component.R.minValue, - IntegerRGBColor.Component.R.maxValue + IntegerRGBColor.Component.R.maxValue, + hashMapOf( + ColoringMode.PURE_COLOR to intArrayOf( + Color.BLACK, + Color.RED + ), + ColoringMode.PLAIN_COLOR to intArrayOf( + Color.RED, + Color.RED + ) + ) ), MODE_G( IntegerRGBColor.Component.G.minValue, - IntegerRGBColor.Component.G.maxValue + IntegerRGBColor.Component.G.maxValue, + hashMapOf( + ColoringMode.PURE_COLOR to intArrayOf( + Color.BLACK, + Color.GREEN + ), + ColoringMode.PLAIN_COLOR to intArrayOf( + Color.GREEN, + Color.GREEN + ) + ) ), MODE_B( IntegerRGBColor.Component.B.minValue, - IntegerRGBColor.Component.B.maxValue - ), + IntegerRGBColor.Component.B.maxValue, + hashMapOf( + ColoringMode.PURE_COLOR to intArrayOf( + Color.BLACK, + Color.BLUE + ), + ColoringMode.PLAIN_COLOR to intArrayOf( + Color.BLUE, + Color.BLUE + ) + ) + ); } } From c928ad6ed2c6a553390850569e01b3e8421683b1 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 17:06:59 +0300 Subject: [PATCH 20/34] Refine ColorSeekBar template methods --- .../alpha/HSLAlphaColorPickerSeekBar.kt | 26 ++---- .../cmyk/CMYKColorPickerSeekBar.kt | 86 ++++++++----------- .../hsl/HSLColorPickerSeekBar.kt | 74 +++++++--------- .../lab/LABColorPickerSeekBar.kt | 74 +++++++--------- .../view/picker/ColorSeekBar.kt | 36 +++++--- 5 files changed, 135 insertions(+), 161 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt index 5a4f021..7df8b3b 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt @@ -37,34 +37,26 @@ class HSLAlphaColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun refreshProperties() { - super.refreshProperties() + override fun updateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { + color.setFrom(value) + } + + override fun onRefreshProperties() { max = IntegerHSLColor.Component.A.maxValue } - override fun updateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { - color.setFrom(value) + override fun calculateProgressFromColor(color: IntegerHSLColor): Int? { + return color.intA } - override fun refreshInternalPickedColorFromProgress() { - super.refreshInternalPickedColorFromProgress() - val currentProgress = progress + override fun refreshColorFromProgress(color: IntegerHSLColor, progress: Int): Boolean { val currentA = internalPickedColor.intA - val changed = if (currentA != currentProgress) { + return if (currentA != progress) { internalPickedColor.intA = progress true } else { false } - - if (changed) { - notifyListenersOnColorChanged() - } - } - - override fun refreshProgressFromCurrentColor() { - super.refreshProgressFromCurrentColor() - progress = internalPickedColor.intA } interface OnColorPickListener : diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt index f61ed56..c385d75 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt @@ -106,14 +106,34 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( color.setFrom(value) } - override fun refreshProperties() { - super.refreshProperties() + override fun onRefreshProperties() { if (!modeInitialized) { return } max = mode.absoluteProgress } + override fun calculateProgressFromColor(color: IntegerCMYKColor): Int? { + if (!modeInitialized) { + return null + } + + return -mode.minProgress + when (mode) { + Mode.MODE_C -> { + internalPickedColor.intC + } + Mode.MODE_M -> { + internalPickedColor.intM + } + Mode.MODE_Y -> { + internalPickedColor.intY + } + Mode.MODE_K -> { + internalPickedColor.intK + } + } + } + override fun refreshProgressDrawable() { super.refreshProgressDrawable() @@ -164,80 +184,50 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshInternalPickedColorFromProgress() { - super.refreshInternalPickedColorFromProgress() - + override fun refreshColorFromProgress(color: IntegerCMYKColor, progress: Int): Boolean { if (!modeInitialized) { - return + return false } - val currentProgress = progress - // TODO: Use Atomic and compare/set? - val changed = when (mode) { + val unmaskedProgress = mode.minProgress + progress + return when (mode) { Mode.MODE_C -> { - val currentH = internalPickedColor.intC - if (currentH != currentProgress) { - internalPickedColor.intC = currentProgress + val currentH = color.intC + if (currentH != unmaskedProgress) { + color.intC = unmaskedProgress true } else { false } } Mode.MODE_M -> { - val currentS = internalPickedColor.intM - if (currentS != currentProgress) { - internalPickedColor.intM = currentProgress + val currentS = color.intM + if (currentS != unmaskedProgress) { + color.intM = unmaskedProgress true } else { false } } Mode.MODE_Y -> { - val currentL = internalPickedColor.intY - if (currentL != currentProgress) { - internalPickedColor.intY = currentProgress + val currentL = color.intY + if (currentL != unmaskedProgress) { + color.intY = unmaskedProgress true } else { false } } Mode.MODE_K -> { - val currentL = internalPickedColor.intK - if (currentL != currentProgress) { - internalPickedColor.intK = currentProgress + val currentL = color.intK + if (currentL != unmaskedProgress) { + color.intK = unmaskedProgress true } else { false } } } - - if (changed) { - notifyListenersOnColorChanged() - } - } - - override fun refreshProgressFromCurrentColor() { - super.refreshProgressFromCurrentColor() - - if (!modeInitialized) { - return - } - - progress = when (mode) { - Mode.MODE_C -> { - internalPickedColor.intC - } - Mode.MODE_M -> { - internalPickedColor.intM - } - Mode.MODE_Y -> { - internalPickedColor.intY - } - Mode.MODE_K -> { - internalPickedColor.intK - } - } } // TODO: Refactor diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt index 2884c65..17ac7d4 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt @@ -143,14 +143,31 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( color.setFrom(value) } - override fun refreshProperties() { - super.refreshProperties() + override fun onRefreshProperties() { if (!modeInitialized) { return } max = mode.absoluteProgress } + override fun calculateProgressFromColor(color: IntegerHSLColor): Int? { + if (!modeInitialized) { + return null + } + + return -mode.minProgress + when (mode) { + Mode.MODE_HUE -> { + internalPickedColor.intH + } + Mode.MODE_SATURATION -> { + internalPickedColor.intS + } + Mode.MODE_LIGHTNESS -> { + internalPickedColor.intL + } + } + } + // TODO: Get rid of toIntArray allocations private fun createHueOutputColorCheckpoints(): IntArray { return HUE_COLOR_CHECKPOINTS @@ -233,68 +250,41 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshInternalPickedColorFromProgress() { - super.refreshInternalPickedColorFromProgress() - + override fun refreshColorFromProgress(color: IntegerHSLColor, progress: Int): Boolean { if (!modeInitialized) { - return + return false } - val currentProgress = progress - // TODO: Use Atomic and compare/set? - val changed = when (mode) { + val unmaskedProgress = mode.minProgress + progress + return when (mode) { Mode.MODE_HUE -> { - val currentH = internalPickedColor.intH - if (currentH != currentProgress) { - internalPickedColor.intH = currentProgress + val currentH = color.intH + if (currentH != unmaskedProgress) { + color.intH = unmaskedProgress true } else { false } } Mode.MODE_SATURATION -> { - val currentS = internalPickedColor.intS - if (currentS != currentProgress) { - internalPickedColor.intS = currentProgress + val currentS = color.intS + if (currentS != unmaskedProgress) { + color.intS = unmaskedProgress true } else { false } } Mode.MODE_LIGHTNESS -> { - val currentL = internalPickedColor.intL - if (currentL != currentProgress) { - internalPickedColor.intL = currentProgress + val currentL = color.intL + if (currentL != unmaskedProgress) { + color.intL = unmaskedProgress true } else { false } } } - - if (changed) { - notifyListenersOnColorChanged() - } - } - - override fun refreshProgressFromCurrentColor() { - super.refreshProgressFromCurrentColor() - - if (!modeInitialized) { - return - } - - progress = when (mode) { - Mode.MODE_HUE -> { - internalPickedColor.intH - } - Mode.MODE_SATURATION -> { - internalPickedColor.intS - } - Mode.MODE_LIGHTNESS -> { - internalPickedColor.intL - } - } } // TODO: Deal with int arrays diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index 231414f..c6ef994 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -110,14 +110,31 @@ class LABColorPickerSeekBar @JvmOverloads constructor( color.setFrom(value) } - override fun refreshProperties() { - super.refreshProperties() + override fun onRefreshProperties() { if (!modeInitialized) { return } max = mode.absoluteProgress } + override fun calculateProgressFromColor(color: IntegerLABColor): Int? { + if (!modeInitialized) { + return null + } + + return -mode.minProgress + when (mode) { + Mode.MODE_L -> { + internalPickedColor.intL + } + Mode.MODE_A -> { + internalPickedColor.intA + } + Mode.MODE_B -> { + internalPickedColor.intB + } + } + } + override fun refreshProgressDrawable() { super.refreshProgressDrawable() @@ -186,68 +203,41 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshInternalPickedColorFromProgress() { - super.refreshInternalPickedColorFromProgress() - + override fun refreshColorFromProgress(color: IntegerLABColor, progress: Int): Boolean { if (!modeInitialized) { - return + return false } - val currentProgress = mode.minProgress + progress - // TODO: Use Atomic and compare/set? - val changed = when (mode) { + val unmaskedProgress = mode.minProgress + progress + return when (mode) { Mode.MODE_L -> { - val currentValue = internalPickedColor.intL - if (currentValue != currentProgress) { - internalPickedColor.intL = currentProgress + val currentValue = color.intL + if (currentValue != unmaskedProgress) { + color.intL = unmaskedProgress true } else { false } } Mode.MODE_A -> { - val currentValue = internalPickedColor.intA - if (currentValue != currentProgress) { - internalPickedColor.intA = currentProgress + val currentValue = color.intA + if (currentValue != unmaskedProgress) { + color.intA = unmaskedProgress true } else { false } } Mode.MODE_B -> { - val currentValue = internalPickedColor.intB - if (currentValue != currentProgress) { - internalPickedColor.intB = currentProgress + val currentValue = color.intB + if (currentValue != unmaskedProgress) { + color.intB = unmaskedProgress true } else { false } } } - - if (changed) { - notifyListenersOnColorChanged() - } - } - - override fun refreshProgressFromCurrentColor() { - super.refreshProgressFromCurrentColor() - - if (!modeInitialized) { - return - } - - progress = -mode.minProgress + when (mode) { - Mode.MODE_L -> { - internalPickedColor.intL - } - Mode.MODE_A -> { - internalPickedColor.intA - } - Mode.MODE_B -> { - internalPickedColor.intB - } - } } // TODO: Deal with int arrays diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt index c8eaadd..fe641a2 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt @@ -222,7 +222,6 @@ abstract class ColorSeekBar @JvmOverloads constructor( /* * Silently updates internal picked color from provided value. */ - // TODO: Make abstract? Make template? private fun updateInternalPickedColorFrom(value: C) { if (DEBUG) { Log.d( @@ -241,47 +240,60 @@ abstract class ColorSeekBar @JvmOverloads constructor( /* * Refresh or set basic SeekBar properties like min/max. */ - // TODO: Make abstract? Make template? - protected open fun refreshProperties() { + protected fun refreshProperties() { if (DEBUG) { Log.d( TAG, "refreshProperties() called on $this" ) } + onRefreshProperties() } + abstract fun onRefreshProperties() + /* * Synchronizes progress from picked color. */ - // TODO: Provide color as a parameter? - // TODO: Make abstract? Make template? - protected open fun refreshProgressFromCurrentColor() { + protected fun refreshProgressFromCurrentColor() { if (DEBUG) { Log.d( TAG, "refreshProgressFromCurrentColor() called on $this" ) } + + val result = calculateProgressFromColor(internalPickedColor) + result?.let { + progress = it + } } + abstract fun calculateProgressFromColor(color: C): Int? + /* * Synchronizes internal picked color from progress while bypassing public API * pickedColor setter and consequent calls. - * CONTRACT: Derived class is responsible for notifying listeners on color change - * if needed. */ - // TODO: Provide color as a parameter? - // TODO: Make abstract? Make template? - protected open fun refreshInternalPickedColorFromProgress() { + private fun refreshInternalPickedColorFromProgress() { if (DEBUG) { Log.d( TAG, "refreshInternalCurrentColorFromProgress() called on $this" ) } + val changed = refreshColorFromProgress( + internalPickedColor, + progress + ) + + if (changed) { + notifyListenersOnColorChanged() + } } + abstract fun refreshColorFromProgress(color: C, progress: Int): Boolean + /* * Refreshes SeekBar's progress drawable according to derived class details. * CONTRACT: Derived class is responsible for progressDrawable changes if needed. @@ -332,7 +344,7 @@ abstract class ColorSeekBar @JvmOverloads constructor( super.setOnSeekBarChangeListener(l) } - protected fun notifyListenersOnColorChanged() { + private fun notifyListenersOnColorChanged() { if (!notifyListeners) { if (DEBUG) { Log.d( From a214066a062b82ac9eafa7774e682ab217b53b9b Mon Sep 17 00:00:00 2001 From: N7k Date: Sun, 29 Mar 2020 17:16:30 +0300 Subject: [PATCH 21/34] Update RGBColorPickerSeekBar implementation --- .../rgb/RGBColorPickerSeekBar.kt | 87 +++++++------------ 1 file changed, 33 insertions(+), 54 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt index ff1a4cb..367dbf8 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/rgb/RGBColorPickerSeekBar.kt @@ -2,6 +2,7 @@ package codes.side.andcolorpicker.rgb import android.content.Context import android.graphics.Color +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.util.AttributeSet @@ -10,6 +11,7 @@ import codes.side.andcolorpicker.R import codes.side.andcolorpicker.converter.IntegerRGBColorConverter import codes.side.andcolorpicker.model.IntegerRGBColor import codes.side.andcolorpicker.model.factory.RGBColorFactory +import codes.side.andcolorpicker.view.picker.ColorSeekBar import codes.side.andcolorpicker.view.picker.GradientColorSeekBar class RGBColorPickerSeekBar @JvmOverloads constructor( @@ -91,41 +93,30 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( typedArray.recycle() } - override fun setMin(min: Int) { - if (modeInitialized && min != mode.minProgress) { - throw IllegalArgumentException("Current mode supports ${mode.minProgress} min value only") - } - super.setMin(min) - } - override fun setMax(max: Int) { - if (modeInitialized && max != mode.maxProgress) { - throw IllegalArgumentException("Current mode supports ${mode.maxProgress} max value only") + if (modeInitialized && max != mode.absoluteProgress) { + throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, was $max") } super.setMax(max) } - override fun updateInternalPickedColorFrom(value: IntegerRGBColor) { - super.updateInternalPickedColorFrom(value) - internalPickedColor.setFrom(value) + override fun onUpdateColorFrom(color: IntegerRGBColor, value: IntegerRGBColor) { + color.setFrom(value) } - override fun refreshProperties() { - super.refreshProperties() + override fun onRefreshProperties() { if (!modeInitialized) { return } max = mode.maxProgress } - override fun refreshProgressDrawable() { - super.refreshProgressDrawable() - + override fun onRefreshProgressDrawable(progressDrawable: LayerDrawable) { if (!coloringModeInitialized || !modeInitialized) { return } - ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = + (progressDrawable.getDrawable(0) as GradientDrawable).colors = when (coloringMode) { ColoringMode.PURE_COLOR, ColoringMode.PLAIN_COLOR -> mode.coloringModeCheckpointsMap[coloringMode] ColoringMode.OUTPUT_COLOR -> when (mode) { @@ -136,10 +127,8 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshThumb() { - super.refreshThumb() - - coloringDrawables.forEach { + override fun onRefreshThumb(thumbColoringDrawables: Set) { + thumbColoringDrawables.forEach { when (it) { is GradientDrawable -> { paintThumbStroke(it) @@ -151,66 +140,56 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshInternalPickedColorFromProgress() { - super.refreshInternalPickedColorFromProgress() - + override fun onRefreshColorFromProgress(color: IntegerRGBColor, progress: Int): Boolean { if (!modeInitialized) { - return + return false } - val currentProgress = progress - // TODO: Use Atomic and compare/set? - val changed = when (mode) { + return when (mode) { Mode.MODE_R -> { - val currentH = internalPickedColor.intR - if (currentH != currentProgress) { - internalPickedColor.intR = currentProgress + val currentH = color.intR + if (currentH != progress) { + color.intR = progress true } else { false } } Mode.MODE_G -> { - val currentS = internalPickedColor.intG - if (currentS != currentProgress) { - internalPickedColor.intG = currentProgress + val currentS = color.intG + if (currentS != progress) { + color.intG = progress true } else { false } } Mode.MODE_B -> { - val currentL = internalPickedColor.intB - if (currentL != currentProgress) { - internalPickedColor.intB = currentProgress + val currentL = color.intB + if (currentL != progress) { + color.intB = progress true } else { false } } } - - if (changed) { - notifyListenersOnColorChanged() - } } - override fun refreshProgressFromCurrentColor() { - super.refreshProgressFromCurrentColor() - + override fun onRefreshProgressFromColor(color: IntegerRGBColor): Int? { if (!modeInitialized) { - return + return null } - progress = when (mode) { + return when (mode) { Mode.MODE_R -> { - internalPickedColor.intR + color.intR } Mode.MODE_G -> { - internalPickedColor.intG + color.intG } Mode.MODE_B -> { - internalPickedColor.intB + color.intB } } } @@ -248,10 +227,10 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( } enum class Mode( - val minProgress: Int, - val maxProgress: Int, + override val minProgress: Int, + override val maxProgress: Int, val coloringModeCheckpointsMap: HashMap - ) { + ) : ColorSeekBar.Mode { MODE_R( IntegerRGBColor.Component.R.minValue, IntegerRGBColor.Component.R.maxValue, @@ -293,6 +272,6 @@ class RGBColorPickerSeekBar @JvmOverloads constructor( Color.BLUE ) ) - ); + ) } } From 3e73a831d2e8e7e02d480928449718eb5e26a674 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 17:21:37 +0300 Subject: [PATCH 22/34] Refine ColorSeekBar template methods --- .../alpha/AlphaColorPickerSeekBar.kt | 12 ++---- .../alpha/HSLAlphaColorPickerSeekBar.kt | 6 +-- .../cmyk/CMYKColorPickerSeekBar.kt | 19 ++++------ .../hsl/HSLColorPickerSeekBar.kt | 19 ++++------ .../lab/LABColorPickerSeekBar.kt | 19 ++++------ .../view/picker/ColorSeekBar.kt | 37 +++++++++++-------- 6 files changed, 53 insertions(+), 59 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/AlphaColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/AlphaColorPickerSeekBar.kt index f5d7141..6032166 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/AlphaColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/AlphaColorPickerSeekBar.kt @@ -85,20 +85,16 @@ abstract class AlphaColorPickerSeekBar @JvmOverloads constructor( return layerList.toTypedArray() } - override fun refreshProgressDrawable() { - super.refreshProgressDrawable() - - ((progressDrawable as LayerDrawable).getDrawable(1) as GradientDrawable).colors = + override fun onRefreshProgressDrawable(progressDrawable: LayerDrawable) { + (progressDrawable.getDrawable(1) as GradientDrawable).colors = intArrayOf( android.graphics.Color.TRANSPARENT, colorConverter.convertToOpaqueColorInt(internalPickedColor) ) } - override fun refreshThumb() { - super.refreshThumb() - - coloringDrawables.forEach { + override fun onRefreshThumb(thumbColoringDrawables: Set) { + thumbColoringDrawables.forEach { when (it) { is GradientDrawable -> { paintThumbStroke(it) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt index 7df8b3b..4ff9560 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt @@ -37,7 +37,7 @@ class HSLAlphaColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun updateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { + override fun onUpdateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { color.setFrom(value) } @@ -45,11 +45,11 @@ class HSLAlphaColorPickerSeekBar @JvmOverloads constructor( max = IntegerHSLColor.Component.A.maxValue } - override fun calculateProgressFromColor(color: IntegerHSLColor): Int? { + override fun onRefreshProgressFromColor(color: IntegerHSLColor): Int? { return color.intA } - override fun refreshColorFromProgress(color: IntegerHSLColor, progress: Int): Boolean { + override fun onRefreshColorFromProgress(color: IntegerHSLColor, progress: Int): Boolean { val currentA = internalPickedColor.intA return if (currentA != progress) { internalPickedColor.intA = progress diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt index c385d75..9d66171 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt @@ -2,6 +2,7 @@ package codes.side.andcolorpicker.cmyk import android.content.Context import android.graphics.Color +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.util.AttributeSet @@ -102,7 +103,7 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun updateColorFrom(color: IntegerCMYKColor, value: IntegerCMYKColor) { + override fun onUpdateColorFrom(color: IntegerCMYKColor, value: IntegerCMYKColor) { color.setFrom(value) } @@ -113,7 +114,7 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( max = mode.absoluteProgress } - override fun calculateProgressFromColor(color: IntegerCMYKColor): Int? { + override fun onRefreshProgressFromColor(color: IntegerCMYKColor): Int? { if (!modeInitialized) { return null } @@ -134,14 +135,12 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshProgressDrawable() { - super.refreshProgressDrawable() - + override fun onRefreshProgressDrawable(progressDrawable: LayerDrawable) { if (!coloringModeInitialized || !modeInitialized) { return } - ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { + (progressDrawable.getDrawable(0) as GradientDrawable).colors = when (mode) { Mode.MODE_C -> { when (coloringMode) { ColoringMode.PURE_COLOR -> Mode.MODE_C.checkpoints @@ -169,10 +168,8 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshThumb() { - super.refreshThumb() - - coloringDrawables.forEach { + override fun onRefreshThumb(thumbColoringDrawables: Set) { + thumbColoringDrawables.forEach { when (it) { is GradientDrawable -> { paintThumbStroke(it) @@ -184,7 +181,7 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshColorFromProgress(color: IntegerCMYKColor, progress: Int): Boolean { + override fun onRefreshColorFromProgress(color: IntegerCMYKColor, progress: Int): Boolean { if (!modeInitialized) { return false } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt index 17ac7d4..97a189f 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt @@ -2,6 +2,7 @@ package codes.side.andcolorpicker.hsl import android.content.Context import android.graphics.Color +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.util.AttributeSet @@ -139,7 +140,7 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun updateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { + override fun onUpdateColorFrom(color: IntegerHSLColor, value: IntegerHSLColor) { color.setFrom(value) } @@ -150,7 +151,7 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( max = mode.absoluteProgress } - override fun calculateProgressFromColor(color: IntegerHSLColor): Int? { + override fun onRefreshProgressFromColor(color: IntegerHSLColor): Int? { if (!modeInitialized) { return null } @@ -188,14 +189,12 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( zeroSaturationOutputColorHSLCache[2] = internalPickedColor.floatL } - override fun refreshProgressDrawable() { - super.refreshProgressDrawable() - + override fun onRefreshProgressDrawable(progressDrawable: LayerDrawable) { if (!coloringModeInitialized || !modeInitialized) { return } - ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { + (progressDrawable.getDrawable(0) as GradientDrawable).colors = when (mode) { Mode.MODE_HUE -> { when (coloringMode) { ColoringMode.PURE_COLOR -> HUE_COLOR_CHECKPOINTS @@ -235,10 +234,8 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshThumb() { - super.refreshThumb() - - coloringDrawables.forEach { + override fun onRefreshThumb(thumbColoringDrawables: Set) { + thumbColoringDrawables.forEach { when (it) { is GradientDrawable -> { paintThumbStroke(it) @@ -250,7 +247,7 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshColorFromProgress(color: IntegerHSLColor, progress: Int): Boolean { + override fun onRefreshColorFromProgress(color: IntegerHSLColor, progress: Int): Boolean { if (!modeInitialized) { return false } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index c6ef994..a6093ca 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -1,6 +1,7 @@ package codes.side.andcolorpicker.lab import android.content.Context +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.util.AttributeSet @@ -106,7 +107,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( super.setMax(max) } - override fun updateColorFrom(color: IntegerLABColor, value: IntegerLABColor) { + override fun onUpdateColorFrom(color: IntegerLABColor, value: IntegerLABColor) { color.setFrom(value) } @@ -117,7 +118,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( max = mode.absoluteProgress } - override fun calculateProgressFromColor(color: IntegerLABColor): Int? { + override fun onRefreshProgressFromColor(color: IntegerLABColor): Int? { if (!modeInitialized) { return null } @@ -135,9 +136,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshProgressDrawable() { - super.refreshProgressDrawable() - + override fun onRefreshProgressDrawable(progressDrawable: LayerDrawable) { if (!coloringModeInitialized || !modeInitialized) { return } @@ -145,7 +144,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( val sampledRangeIntArray = mode.sampledRangeIntArray val outputIntArray = IntArray(sampledRangeIntArray.size) - ((progressDrawable as LayerDrawable).getDrawable(0) as GradientDrawable).colors = when (mode) { + (progressDrawable.getDrawable(0) as GradientDrawable).colors = when (mode) { Mode.MODE_L -> { when (coloringMode) { ColoringMode.OUTPUT_COLOR -> { @@ -188,10 +187,8 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshThumb() { - super.refreshThumb() - - coloringDrawables.forEach { + override fun onRefreshThumb(thumbColoringDrawables: Set) { + thumbColoringDrawables.forEach { when (it) { is GradientDrawable -> { paintThumbStroke(it) @@ -203,7 +200,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( } } - override fun refreshColorFromProgress(color: IntegerLABColor, progress: Int): Boolean { + override fun onRefreshColorFromProgress(color: IntegerLABColor, progress: Int): Boolean { if (!modeInitialized) { return false } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt index fe641a2..a51a5a3 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt @@ -91,7 +91,7 @@ abstract class ColorSeekBar @JvmOverloads constructor( private lateinit var thumbObjectAnimator: ObjectAnimator // TODO: Rename - protected val coloringDrawables = hashSetOf() + private val thumbColoringDrawables = hashSetOf() protected val thumbStrokeWidthPx by lazy { resources.getDimensionPixelOffset(R.dimen.acp_thumb_stroke_width) @@ -176,7 +176,7 @@ abstract class ColorSeekBar @JvmOverloads constructor( ) } - coloringDrawables.add(thumbDrawable) + thumbColoringDrawables.add(thumbDrawable) thumb = ScaleDrawable( StateListDrawable().also { @@ -229,13 +229,14 @@ abstract class ColorSeekBar @JvmOverloads constructor( "updateInternalCurrentColorFrom() called on $this" ) } - updateColorFrom( + + onUpdateColorFrom( internalPickedColor, value ) } - abstract fun updateColorFrom(color: C, value: C) + protected abstract fun onUpdateColorFrom(color: C, value: C) /* * Refresh or set basic SeekBar properties like min/max. @@ -247,10 +248,11 @@ abstract class ColorSeekBar @JvmOverloads constructor( "refreshProperties() called on $this" ) } + onRefreshProperties() } - abstract fun onRefreshProperties() + protected abstract fun onRefreshProperties() /* * Synchronizes progress from picked color. @@ -263,13 +265,13 @@ abstract class ColorSeekBar @JvmOverloads constructor( ) } - val result = calculateProgressFromColor(internalPickedColor) + val result = onRefreshProgressFromColor(internalPickedColor) result?.let { progress = it } } - abstract fun calculateProgressFromColor(color: C): Int? + protected abstract fun onRefreshProgressFromColor(color: C): Int? /* * Synchronizes internal picked color from progress while bypassing public API @@ -282,7 +284,8 @@ abstract class ColorSeekBar @JvmOverloads constructor( "refreshInternalCurrentColorFromProgress() called on $this" ) } - val changed = refreshColorFromProgress( + + val changed = onRefreshColorFromProgress( internalPickedColor, progress ) @@ -292,38 +295,42 @@ abstract class ColorSeekBar @JvmOverloads constructor( } } - abstract fun refreshColorFromProgress(color: C, progress: Int): Boolean + protected abstract fun onRefreshColorFromProgress(color: C, progress: Int): Boolean /* * Refreshes SeekBar's progress drawable according to derived class details. * CONTRACT: Derived class is responsible for progressDrawable changes if needed. */ - // TODO: Provide drawable as a parameter? - // TODO: Make abstract? Make template? - protected open fun refreshProgressDrawable() { + protected fun refreshProgressDrawable() { if (DEBUG) { Log.d( TAG, "refreshProgressDrawable() called on $this" ) } + + onRefreshProgressDrawable(progressDrawable as LayerDrawable) } + protected abstract fun onRefreshProgressDrawable(progressDrawable: LayerDrawable) + /** * Refreshes SeekBar's thumb drawable according to derived class details. * CONTRACT: Should paint GradientDrawable and first layer of LayerDrawable */ - // TODO: Make abstract? Make template? - // TODO: Pass ready-to-paint drawables list? - protected open fun refreshThumb() { + protected fun refreshThumb() { if (DEBUG) { Log.d( TAG, "refreshThumb() called on $this" ) } + + onRefreshThumb(thumbColoringDrawables) } + protected abstract fun onRefreshThumb(thumbColoringDrawables: Set) + fun addListener(listener: OnColorPickListener, C>) { colorPickListeners.add(listener) } From 4a3a5f9d51719f6eb56b03e1839254ad0d8075a3 Mon Sep 17 00:00:00 2001 From: N7k Date: Sun, 29 Mar 2020 17:23:57 +0300 Subject: [PATCH 23/34] Make use of alpha in IntegerRGBColorConverter --- .../converter/IntegerRGBColorConverter.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt index 70dabb2..e5346db 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/converter/IntegerRGBColorConverter.kt @@ -12,10 +12,11 @@ class IntegerRGBColorConverter : ColorConverter { override fun convertToColorInt(color: Color): Int { require(color is IntegerRGBColor) { "Unsupported color type supplied" } - return android.graphics.Color.rgb( - color.floatR.toInt(), - color.floatG.toInt(), - color.floatB.toInt() + return android.graphics.Color.argb( + color.intA, + color.intR, + color.intG, + color.intB ) } @@ -28,6 +29,7 @@ class IntegerRGBColorConverter : ColorConverter { color.copyValuesFrom( intArrayOf( + android.graphics.Color.alpha(value), android.graphics.Color.red(value), android.graphics.Color.green(value), android.graphics.Color.blue(value) From f1bc66b346163fc32495b76042214ebbdbdf7580 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 17:31:03 +0300 Subject: [PATCH 24/34] Polish min/max validation messages --- .../side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt | 2 +- .../codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt | 2 +- .../java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt | 2 +- .../java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt | 2 +- .../java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt index 4ff9560..64a949c 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/alpha/HSLAlphaColorPickerSeekBar.kt @@ -32,7 +32,7 @@ class HSLAlphaColorPickerSeekBar @JvmOverloads constructor( override fun setMax(max: Int) { if (isInitialized && max != IntegerHSLColor.Component.A.maxValue) { - throw IllegalArgumentException("Current mode supports ${IntegerHSLColor.Component.A.maxValue} max value only, but given $max") + throw IllegalArgumentException("Current mode supports ${IntegerHSLColor.Component.A.maxValue} max value only, was $max") } super.setMax(max) } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt index 9d66171..6c508a0 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/cmyk/CMYKColorPickerSeekBar.kt @@ -98,7 +98,7 @@ class CMYKColorPickerSeekBar @JvmOverloads constructor( override fun setMax(max: Int) { if (modeInitialized && max != mode.absoluteProgress) { - throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, but given $max") + throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, was $max") } super.setMax(max) } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt index 97a189f..34d2716 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/hsl/HSLColorPickerSeekBar.kt @@ -135,7 +135,7 @@ class HSLColorPickerSeekBar @JvmOverloads constructor( override fun setMax(max: Int) { if (modeInitialized && max != mode.absoluteProgress) { - throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, but given $max") + throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, was $max") } super.setMax(max) } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index a6093ca..092f6b7 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -102,7 +102,7 @@ class LABColorPickerSeekBar @JvmOverloads constructor( override fun setMax(max: Int) { if (modeInitialized && max != mode.absoluteProgress) { - throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, but given $max") + throw IllegalArgumentException("Current mode supports ${mode.absoluteProgress} max value only, was $max") } super.setMax(max) } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt index a51a5a3..2d25f2f 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/view/picker/ColorSeekBar.kt @@ -206,7 +206,7 @@ abstract class ColorSeekBar @JvmOverloads constructor( override fun setMin(min: Int) { if (min != 0) { - throw IllegalArgumentException("Current mode supports 0 min value only, but given $min") + throw IllegalArgumentException("Current mode supports 0 min value only, was $min") } ::minUpdating.marker { super.setMin(min) From ee6f0f125404823bd43c30d2b13eb83d2ac12b11 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 17:41:30 +0300 Subject: [PATCH 25/34] Simplify LABColorPickerSeekBar paintThumbStroke --- .../lab/LABColorPickerSeekBar.kt | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt index 092f6b7..142386e 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/lab/LABColorPickerSeekBar.kt @@ -245,27 +245,9 @@ class LABColorPickerSeekBar @JvmOverloads constructor( drawable.setStroke( thumbStrokeWidthPx, - when (mode) { - Mode.MODE_L -> { - when (coloringMode) { - ColoringMode.OUTPUT_COLOR -> { - colorConverter.convertToColorInt(internalPickedColor) - } - } - } - Mode.MODE_A -> { - when (coloringMode) { - ColoringMode.OUTPUT_COLOR -> { - colorConverter.convertToColorInt(internalPickedColor) - } - } - } - Mode.MODE_B -> { - when (coloringMode) { - ColoringMode.OUTPUT_COLOR -> { - colorConverter.convertToColorInt(internalPickedColor) - } - } + when (coloringMode) { + ColoringMode.OUTPUT_COLOR -> { + colorConverter.convertToColorInt(internalPickedColor) } } ) From 12ec5558fec8e702c1b29e241fa64a6be896cabd Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 19:00:56 +0300 Subject: [PATCH 26/34] Fix IntegerLABColor typo --- .../java/codes/side/andcolorpicker/model/IntegerLABColor.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt index 69c9aea..aa85b3b 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt @@ -4,13 +4,13 @@ package codes.side.andcolorpicker.model // TODO: Remove float properties? Move to converters? class IntegerLABColor : IntegerColor( COMPONENTS_COUNT, - DEFAULT_HSL_VALUES + DEFAULT_VALUES ) { companion object { private const val TAG = "IntegerLABColor" private val COMPONENTS_COUNT = Component.values().size - private val DEFAULT_HSL_VALUES = Component + private val DEFAULT_VALUES = Component .values().map { it.defaultValue }.toIntArray() } From b121021c57ac302cb9ceb8282c57bb5c856e91f6 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 19:02:20 +0300 Subject: [PATCH 27/34] Simplify DEFAULT_VALUES Color constant naming --- .../java/codes/side/andcolorpicker/model/IntegerCMYKColor.kt | 4 ++-- .../java/codes/side/andcolorpicker/model/IntegerHSLColor.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerCMYKColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerCMYKColor.kt index 0fb4b1f..273ec92 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerCMYKColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerCMYKColor.kt @@ -3,13 +3,13 @@ package codes.side.andcolorpicker.model // TODO: Remove float properties? Move to converters? class IntegerCMYKColor : IntegerColor( COMPONENTS_COUNT, - DEFAULT_CMYK_VALUES + DEFAULT_VALUES ) { companion object { private const val TAG = "IntegerCMYKColor" private val COMPONENTS_COUNT = Component.values().size - private val DEFAULT_CMYK_VALUES = Component + private val DEFAULT_VALUES = Component .values().map { it.defaultValue }.toIntArray() } diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt index 1fc837a..7255970 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt @@ -6,13 +6,13 @@ import kotlin.random.Random // TODO: Remove float properties? Move to converters? class IntegerHSLColor : IntegerColor( COMPONENTS_COUNT, - DEFAULT_HSL_VALUES + DEFAULT_VALUES ) { companion object { private const val TAG = "IntegerHSLColor" private val COMPONENTS_COUNT = Component.values().size - private val DEFAULT_HSL_VALUES = Component + private val DEFAULT_VALUES = Component .values().map { it.defaultValue }.toIntArray() fun createRandomColor(pure: Boolean = false): IntegerHSLColor { From b11999bc6c3d25204095f406389bbaf98ae14434 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 19:03:08 +0300 Subject: [PATCH 28/34] Fix TODOs typos --- .../java/codes/side/andcolorpicker/model/IntegerHSLColor.kt | 2 +- .../java/codes/side/andcolorpicker/model/IntegerLABColor.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt index 7255970..6ab5019 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerHSLColor.kt @@ -174,7 +174,7 @@ class IntegerHSLColor : IntegerColor( return ordinal } - // TODO: Adapt for non-zero min valies + // TODO: Adapt for non-zero min values val normalizedDefaultValue: Float get() { return defaultValue / maxValue.toFloat() diff --git a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt index aa85b3b..5dd63d0 100644 --- a/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt +++ b/andcolorpicker/src/main/java/codes/side/andcolorpicker/model/IntegerLABColor.kt @@ -151,7 +151,7 @@ class IntegerLABColor : IntegerColor( return ordinal } - // TODO: Adapt for non-zero min valies + // TODO: Adapt for non-zero min values val normalizedDefaultValue: Float get() { return defaultValue / maxValue.toFloat() From 94a05144721ffb50ff004e333325ccf96fbe5c71 Mon Sep 17 00:00:00 2001 From: N7k Date: Sun, 29 Mar 2020 20:31:48 +0300 Subject: [PATCH 29/34] Extend RGBSeekBarFragment with ColoringMode.PLAIN_COLOR --- .../app/fragment/RGBSeekBarFragment.kt | 26 +++++++++---- .../main/res/layout/fragment_rgb_seek_bar.xml | 38 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt b/app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt index 1aeba66..fa85f8a 100644 --- a/app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt +++ b/app/src/main/java/codes/side/andcolorpicker/app/fragment/RGBSeekBarFragment.kt @@ -7,6 +7,7 @@ import codes.side.andcolorpicker.app.R import codes.side.andcolorpicker.group.PickerGroup import codes.side.andcolorpicker.group.registerPickers import codes.side.andcolorpicker.model.IntegerRGBColor +import codes.side.andcolorpicker.rgb.RGBColorPickerSeekBar import codes.side.andcolorpicker.view.picker.ColorSeekBar import kotlinx.android.synthetic.main.fragment_rgb_seek_bar.* @@ -16,19 +17,19 @@ class RGBSeekBarFragment : Fragment(R.layout.fragment_rgb_seek_bar) { private const val TAG = "RGBSeekBarFragment" } + private val pickerGroup = PickerGroup() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated( view, savedInstanceState ) - val pickerGroup = PickerGroup().also { - it.registerPickers( - redRGBColorPickerSeekBar, - greenRGBColorPickerSeekBar, - blueRGBColorPickerSeekBar - ) - } + pickerGroup.registerPickers( + redRGBColorPickerSeekBar, + greenRGBColorPickerSeekBar, + blueRGBColorPickerSeekBar + ) pickerGroup.addListener( object : @@ -52,5 +53,16 @@ class RGBSeekBarFragment : Fragment(R.layout.fragment_rgb_seek_bar) { it.intB = 230 } ) + + val radioColoringModesMap = hashMapOf( + R.id.pureRadioButton to RGBColorPickerSeekBar.ColoringMode.PURE_COLOR, + R.id.plainRadioButton to RGBColorPickerSeekBar.ColoringMode.PLAIN_COLOR + ) + coloringModeRadioGroup.setOnCheckedChangeListener { _, checkedId -> + pickerGroup.forEach { + (it as? RGBColorPickerSeekBar)?.coloringMode = + requireNotNull(radioColoringModesMap[checkedId]) + } + } } } diff --git a/app/src/main/res/layout/fragment_rgb_seek_bar.xml b/app/src/main/res/layout/fragment_rgb_seek_bar.xml index a72d452..7b5d9c1 100644 --- a/app/src/main/res/layout/fragment_rgb_seek_bar.xml +++ b/app/src/main/res/layout/fragment_rgb_seek_bar.xml @@ -79,4 +79,42 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/blueRGBColorPickerSeekBar" /> + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b281d98..8d1f01f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ Coloring mode Pure Output + Plain Randomize Work In Progress :D Show dialog From 37de6cc56d1e76522e6c4aa3aa3560af5f6c769e Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 21:02:30 +0300 Subject: [PATCH 30/34] Add basic RGB & LAB readme sections --- README.md | 12 ++++++++++-- github/seek_bar_cmyk_pure.png | Bin 0 -> 12892 bytes github/seek_bar_hsl_pure.png | Bin 0 -> 19871 bytes github/seek_bar_lab_output.png | Bin 0 -> 10935 bytes github/seek_bar_rgb_pure.png | Bin 0 -> 10574 bytes 5 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 github/seek_bar_cmyk_pure.png create mode 100644 github/seek_bar_hsl_pure.png create mode 100644 github/seek_bar_lab_output.png create mode 100644 github/seek_bar_rgb_pure.png diff --git a/README.md b/README.md index 5d8d230..58e2f78 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ implementation "codes.side:andcolorpicker:0.2.0" - *Add color model description* -![](github/type_hsl.png) +![](github/seek_bar_hsl_pure.png) #### Layout XML Snippet @@ -121,9 +121,17 @@ pickerGroup.setColor( hueColorPickerSeekBar.progress = 50 ``` +### RGB (red, green, blue) + +![](github/seek_bar_rgb_pure.png) + +### LAB + +![](github/seek_bar_lab_output.png) + ### CMYK (cyan, magenta, yellow, key) -![](github/type_cmyk.png) +![](github/seek_bar_cmyk_pure.png) #### Layout XML Snippet diff --git a/github/seek_bar_cmyk_pure.png b/github/seek_bar_cmyk_pure.png new file mode 100644 index 0000000000000000000000000000000000000000..7c9d6a6582c07b72348413878cf3604e94c74be1 GIT binary patch literal 12892 zcmbVy2UwHYwsw#~Y=E6XX@f&idT-LihDcFFK$;i=LV(bc(5s4qfFlS30#TGsA|Nd^ z9h%h81B8HdNC=^YUjCgqbLO78_uTUQJ`eKoeS5Fn)?Vvf?^-X64R3H9Id=pCfpF;E zym}V`VOa$~oQDpAH^HM*4Djo)`%Mck2;}G~<_`-b`Q6VD2-}j=J#%k!gWGCQgsYUD zJ>n5e%E#3mw1z+~Yx%g_LE$iO!ACGhCpQftdS$JUpp(6ZkeQ-^jDfo@?6K2LKTnv6 zpW!{I9~`P`FQla@c-cn{2ylgY+X?!(y103%`Dh6JAy*ANGha&!3I5T<8?GUw!yHi1 z+`w2+7vTvLRFqPZgvu(Y2r8>e$to$x$SX+*%E`#8O3SE7%gRg2$g0UGsVOQ8{`pS` zjOJt927d0#_;W&F7z|F*T)Jzsa2^j(-20_h2bT|t0pV+x28a4?Crem{@2F#P&EgHr>h-Uu#>BuBTU-e%~443 zuWeM*MYte5!N6d4^8fl?PgmF26XD?G0zP=%y>UfQ@0zZXtg4cdq@0xOAJa83P}6hs z^0sq>!t}0c2!YL!a&od)gR01@Dy!JTBxQhlNh;YXC`j5tm7tQ!P+12Rn2NHzqJrH& z`(H&skxUg}_W#Fiutz|FjQ^OP986J8McGbXQq@7#P7-Rb0F_j6P?42XQj(ETf;vDI zWnfDGlxF1V1TLhV%fCux&dMH0lv8z3R92KxmV_#R{~kFgC`sDMsLDtxLY3v93JUhh zs&ev7wf{qt)NVR?fiw2~^CFqRJpO!gaT5GPdDQHn%uAsm1Z8Ri%wFiv*G~UtKmJc6 z|Jfh?7zTv?4_fk%(Y+83-e@~dn2saZyZ==SrT@Fhd)cA>=c)hSj`AOK`gc?Ro7VpS zO#RPofj+i#bA$m4D=oxaj5O00{o_!i|MPkM@$Fyi)<2sAW5|5`%L;=({<7mRHz3** zSm}(P(IOCt#0kBtI`@2rXyn83r~An3OZ&&RYcAzI#~iwJ;1Y|n_KA$EUFG#Rmy>UV z%g%HW-})52s-mypf8~!BasJRm7P}s;8TsLPY-D)@AKSMx*VYW|`PN`t1*0@eUHP_d zeWVd%ard1U;WpZAG&cu37rcug`{(_7LIwgc?!7h027w&!WC2c!?_VAcs6ilt7*+^` z`H+3Ovvq(20y$kGC+7`;Q1qU0LLh7XA$#r0qXR=F@WF5hq+Yx3_NYwYmUqf!-y+&@ zMNdm>Yk_VAC+Zs;w|P?x-(U*pulsCojf5b9ZZ0?YeLwDwbJu>>m#U~QMU1);tkYk<+Bb#l0BKFL~r$D7`rfG**al)R&au+AWGWcdyaq zrFspU%HZ1=Z_XKudO{A&h%HJ5t_0Ph-Y)BS-!cQL5fnz z{elOyr6G(?`21oW7rHj~XW8Ft_O{@bwL#VX`Nyn6H#a6?(XqCH3{&xo7Yp;S_!hRg zR;kLiG@tRqmR}ybpxtA%4KY@#U^Z9sKSGlL+=c;q$t?F30nD;X_DuL(LY z4i3P{J!zTZ=G&WDX=JZSU@AI@|;p3kXP@s^!R?9?yS45))!}_Y0-7j@EOPX>EuC2 zLK-a$>8xE##a3F$?|e$Man{=3B+$mAq)UO2O6p3Ej;%1m!&MR}-0{wy&SFO+;_1Bs zXe@1QC)YI3<@YCt`?K$4M9%mzC{xJ;h-$`tv=F8P@t&Z$-FA?R-)d>N2-fPtp!8vZ zHbd-1GhV>r4MyGTINT(*zJ{_|HNAE`| z+kmB~U@4i89w*Bok&z}nK{tt{J`%VP_X@t5nprDqZVdjK`jqs=3EIT(pAVNymv@Xg zI@`pDewT_K@%5L;6Y-QpFI3DdBm@e0e*a~`Z}Wyg;AC=l(@UNSxve!C$#SM6mFz{i zmqlG1Lwa$9lN{3n^NrFq10F#j^G=p}$F&t}@=bdcwe~h9N&_}MD|eP#2iqZ#Nwd{5 zqrR8!`x{MKRwg^MT^WN7;38>HvRD)GiiK^2pf!2*#jmV`bP4=^@+H9DVe@=)uN*yzz)TquFHE32OD$Hn!- zJrTwu%b^b}d@_=2>I2+ICs~6dFzKG9Yz{&`$O zq#Z9xSDtEK%65prNUR6c`2IndxpB};v0n(7G~05J@!lk>*EZ(Y06|+?Fp8sPPZKK5 z2o1C_KFfBk%&nWTl29X6jn*^fF7cgv-eF>PI-`OVu1B`8Htc$_sMRAsD@t=$q zLj!F|NOEA1vD6}xg`tX(FFY2a#<*!beehF8a8)Dw%6_7bjPm}TEgLLf826sOO32Fz z`XKDz%F5tj;nPmNbALDBmW){`_62kgVa(ZD)RF$v)G>Wj{wd@;5`X-@13Duu$~Dga zcW(VnC)3MjJ@B)3d@mHrq)D{woIOff-W>h=8eP18ugyDs+ADaPy3s=1Yg_ZVSdX$d ziBS8}uD5xhTams$r7z{}x*Kd+#8X?KrPqn)DiDflZrXb}?(@P~K$Nn%Z)?DCSV7-2 zyHT<4`$Ws>vS85C;tCOOr5BlX9FpQe=bIj^<$iR)~78LMpz z*7UIZlpKhk+^oD1xnG-0A$(c5vJ>p+d5p-drYTT58NlvwWVSHnA~u(+=GQv4Tk<D{U?dIAoOt{3RDH;4s!a)9F)u$a@o zOT=?Ue-7#+5`#B0Mc9QIGSI7I@gILp3VtzIdZLr1UZ`9=YT=|uHD#EcuG~RS zh9=M|6x*ONYZSIZqqhxZOjFK5ux_m&dh6t@(Q>hdS1_O0y*-DR(MbAp&x zI2(N_^|Jde@10F4rhxtBF8aArCDCzYfK%n@TVdXV*ZX5!o;JN&;BohDE0sjm^>_1J zx!*_dZW?o_w8Bn2g`ZzZT6o*Jd~99Mp!Iq+z2(AQRX5-MyY`x5-u(pcCQrv@o_dBk zp=sv$UOM$?cC@g1L(b+h4fBq>3*9Uc9M-8R90KRvI@lV>>-R2};TeVE^}{!9ja-du zy-bOfJm7cEaY60oxqE7_+H%voMB|7>7TC=7NjT}lhQG6=(kPX%K4bcP_ulh~PuNoV zxT)nkbbp=A&R|)}T2?ySl(2rx4MN|kJx5l7_}-8Ey80cwW+%hbciXzp+q*2E-QEE~e%MfocX%rLAnd8lq=#j&tbN1HulXY&QuBy|T$ZVk{e z-isAZ`5`ZRQB0ZbQ;x8D-9?NvM~jTUA=}Yj$HHpTxsd9nFBmICGbvk%5o+E(87=9n z5%|$G7%gr+u_Gu(_z8v=Rv&zNs`v5019pAGuE9|9%dLW!A)JC|`OEB0d__r$y)IE) z^E3%n>Fv;js#5+KOCs%lZIL}<%~Rlu{_f##M{EhvI&;l`SfL{`7X^A#He57#$1~MG zI=#cI5on98l+Ej!Q3$8X>kaYS+O_GzxQ0`0%$2BN3TM|3!rlw3mwuA3_4iS~kNjxD zUOo}FoPM^}<97hczK$^3?VCL&_e<@2DzI_Nj3j?uQlGyy&-Ph?`ZqZOyzwUCa$(ko zfIX{^z^22;lLix9t`jE=G|waEYs-5TM&-pu54S*2Jcnj~@`fyBQfKo5w9QZ>V!{~K z&qdGXSs@MD?KJB_4(*b+=76SPBEG+%AM-(5n;ilHUH{_&xCjW-P(%KD0DAr(C~}gw z6W}suBMnA806_&qFyQ1J0V+vvPuEJ3qG>)K+TY67F+nGMz@134^%-L$1@G@#e;qCNfHtfE zG*}GKYkdvClI!KZE7Pd4r`!(`rK~R{BxwfXcd54fMUBztEk|ntt*eEFg;9H(GxXB7 z`WL6j%~zb9a;)9Iu`!|I`cS?_@jSr7U&1-nzH4s$VC8kAB37Hx{edLwRD8aSL)*{lYXeYKIS7OyuWJ@!UFMd`7r5F5K+*+&uxY5H zLoGo!qR&kwm^7Zp161fb-Ij!E7ET<%1@FSqcQKiCne$)^aWq8mc1korrPU)iTP<{I zQ1ffyGe|MPyd2waYv|hTR6c-aP%G`qm9)b|+`5GM=dX=1eDFlupo+kqrKW|GA&)ni z2pzV3WK#Fpv7GF6Y~?~xSH=y}#?l0tpuSLESF))+;--veAW+EDV;`a~Qx?h8!m1 zbSJXFGHJ1rUPHy^h>M%O;$|D}4dLa4nmi-F*JURz8N}AbxV%Zk@phj1_$G9Hez1q_ zA)AjXB|Br-D@XLab0e|zDLu~$0TT$?cyI7l=9~2;6@Uw^wavWz%fU^hxN39ZV)OL; zmnWO0RPu7HQwf*Y9bpYEBRHVQkw5b0%{02|{7z45ZO_ZAue8apn@7whR~{%y9;l0Z z=JG`Jpflegr=Qav9IsigrkidmAAlH3$ekFbKj%+V1)&n%GDxL)iWnnC{WYgUzZ+ zBg)|$19urj(r|?rS#F54&%$@(f%6JJc@k;82f(&RJLLnXPfIuQ%KN1`F0GxFS8r@=SkW+1p-eBhfI zpDIo@qHSdIuMZt${LW=$b5|YMz~f#pwo28gqOD&3*|Cobl!_oSl2~E0*s{_0WWPlm zSDv}ivKrJE=PJW(t&1CuvKBpLC&L}43c(DVme9q;VBxhJ<5Acg8x~6oe3hX~M%lsIR)M6X2b? zQJ61BBd0GsdUXJ}jN`haKV6GEAjK8AP?*ttq)ad0({Tugk<9Aq7L5T){ha2{NiG=uMZa=8{OcdMF4a^ z?PTy3%u#(&womevXMqnDd-+r+$+?-trl@y#z%rn;I@s77fhjG`yQncQkp-V$2``co zYj-u{`Yl@1Uw}qsI>A>rj?@!2+VDBSZgE^~5$sbB^Qzgmr!fKN;*A21b5RF>6mQ8U zG*+{*U^Wh@O>~|RpbudkR%D9rFPeI7+9iy*D=2meI5ktJ52sg0Ve4=lEfMn$5XLDM zz9mfN8k^=~%YA{bMw~4*7QI=So2MiOL)|L57S46AJ>{B*7!&IKdHp_g6LM|xC?2k@ zjt^|rVkgQCDzNe?uDJyb@j)w# zj48YUa>=5}F6D!bW8h$B0IvAw(u+N=66;#XO}hC3KZnrE$njUpm%{u_wOaFhq!Oo` zErX|Kx(mv3)!>%Frmdff_mUIZk-JG!W_>dl>XiD<#Ppu3HT$Tvn8pQ_gx-5v(qfd0 z-Ss$V1)=xU2?6Q3g{%NRvq4_s9e%S-t6;Sz6a2OUWkM}wzxL|Eed!7p$KEESNRxgb z>X9*j(d4i9e_B-t^UZy&Flw1R&GO*&7`Ktr!Tpa(5^)P#^1iXx%*}cUrX?>mqY7%` zvo4Q{ewff>!28S50ou|=PKO7^Yza-HR?h<+*@jW#KStltG+%5NrqMJBuoMc#=i;OX zAEPGEm8U&aR#L2qed+=UnIv$QL&Zk4b6~4A2Mrj z3gJ_u6J<`?WOk(f;aN)6*(vXWf?bPMPLyKEAp;Q%YahEetn1FGqygX9x%(}h`U5nG z=e_FyC1Exi^108k@F8&@yH8Xoj&CV)23mf~(D>%Y{wjmzC+~;r57Yck=T=yoe07U) z=gUQeHw5;xK^lZ^9}2n_u0yk3ROHwocz@T;ghGpXvQpbsR$Uq^9Oad2bJtBxgHKPm z!%1}0YFxb|VXQ0r1NE-HgzB)wk*L7_(E*%MouPtMU<57syGUfMbb8aH(@pL*v3V~2 zZ(0iRWNs3xKhQShy{BY=n zLyWwNNlJ0{C}B=P%7;bY&8+82JonUx4RUxWxLsa5vff?CFo;q$G5?~LFA&j@Rc z{`oj=i}_^I#Cbp)rKWxN=Q8M(8GWtWrr=;zTrr1rikLK~A4`ueTZ`>m#pax6FK%V& zmA2Boahdn^Yn>D>3Vpjx!!mt4F6``H+k?~z1xqVilrr=B3MBI^Xr~+G!LFxHoZD#I zrAkn?Rm2dMMLE@>!G^?X#WH_!lyb;cL{RPX*k2M{oJVJio!OvB2Y(GQ@qn|VnF(t5 zw;pN|95BQY5w}DevS7`VFSA_*(~ra z?Of?WfXXpn7Qyu4z1epfyqNr{h(YoqbHMOtb5a92CZg(_lX0}M2!T={sbU6vojkA# zC^+#1acSu?5Gef4wy)$mG{B@;Q`ONOk24aatSJ;5fX0z?-3=X@+b(7Nz5qSDZm-dl z9@QUO_vy(r06i4c0nJC=nk*gkm`if6+J+-@h@l{GHn7e)O<1=Tc`-EBN15;G+zHA z;pVTy&;lxe`DI30yI($VrXG{9D0*VV4FWk_WO5ThW#SmOjDXJ%7AnBY96WL##~Cwe z{vEKs;Eg%~myN|S)KUXCX>Xg3AY5K~>93JWo#)crz;J+oOA0?LbY98j?JmWvwkXpe z1)pzw4>8dgO)gnq{OQM!a4wXV@Ub2H8Q@9%I2r*9n%vi?x6q`ux8#OvPf;eorc5$# z7XpaYk9=!#z4qa^1M7Zu&lJlR#m^HDe*;`E%75?4DMR<9@-MEWexN9h>A`yy{`rQe zL>s>xNz2lFp|5$qH301g;2|N^*Fo|DBeDItj?;^8oKxjTh=xti@v>`CXDg_~&S&X> z%V$uDglg=m(B#MFDMbj_WKOaoe45g?t*@_N0AM|nb4>xHE__;R{P~6XH)b{-?F&cd zozE}>-L&h*MzkFMyRYcv$}+eVE;7v1Al?@-Skz$cMF(?hSfTt%tn&5ZZkuA#{DZX* z4s)u_3a(p$5Rk?o+M-$ZW;0@^n_!o}DEny+~ z66*7vXB}u`Q0ojLW99HEk@7g!J0e(-onH8OOXrylt&Jbyk?;+Qc2KDtSIv_Y*8BJqrmWyvk<-PjVVsTWQT9q;CfR&1*MDqdE} z8R+K4h00)}k|`xv5b!f+yCwp&U;|vjaa>3TxgQL{&g2mOmVnZ}3-Bgnl2nRm190+k zMVA&)NNoGyCr9IR#ZkbZglzb!is24U$qTe8-tx%{F3>Z6u#LfPTfaQiaudZ<)TrVO zc|NSKQ=ug$zY7g)pk$gPH^^0W=cJfACERYuTTG{)|M@ieDD-sY@uWwOcp%R^GBG=5 z#%U$?^7tb9L7l^N+uyR^nS9zAo-6iIB&74l!^67YSsww`?R0-P#_B{oS6Fu)yEmIV z`MYz${-BKTbJ4qL@Ra4`16#jiczNP2!@A#CA7wUgOvYFpkCzUVF?%Sr&Qo@P@fCCI z$gNS#7kyvCj82kWbLJ+WW`;E5n~jcjSwz~aWqzi$^!@O4w+B-NYv!jGHL$Nh12F>k z_4#nyUw1RPCj`j>!pGD|i0$*l`}Z80o=&VF z);cEP>H<=GcGNf5NBc~v(Sz_Us6 zjSTPJ`1jn+Dyvu95@Jb8Q~ujW3$-Rn4Gyn4ruNxB-*3v+jHnDR`bCWI?tz^F-mr?z zbt%r43p#^ZVv^xSLpb<5VH{y4)qDdgWtExLfIO1;)?xNV{3FvH8!{n4)2xp=W#MLm zXE?6#M}5X-r?;X~T2{(wPcPeym#3QYwGQ%|Jsm+Kee)xlJ~hTh5Gxt}B+L%_C)=D{ zs+Im)3}Pz0Jb4W5u(96Thlc(VOU=BWsdplV^Kg$Wh5)#XE)l>vc9_KysW8`-DgFqco~%~o(5>^`__PqKV)m8JUQ zO;*2)JbpH;eu0F}c6Ors<%(ws98rtv+Q{8HT*iAXn-mMr59eARs;?&;)39waMT&~< zA8Se4s~6Y2!wPv-xEw#)=TYEj%=!HD9&14A`6w0H9Qxt$z(Rdi7Km!sycJV|~drnPCw+--?6 zl}1~?iruSwT|Inw*6AHXL?~Z8CR)hj=&bOGy}BC=g5|l1kdDua&kE+KUa4Hub`R4} z+i9myQjhH`EAGN4Zh^duqD#T$>rKe_&kCN3%0*asD%ILHO5y7jbaA<&j#!o&;~u^1 zjJ>NT@^R)`Db-H?)bZ+uwZX|{&GxfvX@n2z2dDZlRlM=PsqXPfZ{yXrn_9z{Q!zU^ z-)rJ1Jsv-666?lupC=FNQ#N)X#!1tz!R?L+%5T{7U#w2Whlh7-w5oA$t3B9IGhKJ6 z#Tw3g-itEyfV?U>748|v_4}=J3T{@)ce|`cZ5{56Chk?|1zHQ&ES%cGGqKNPGeXjr zFECm-1*RD9bj>*EZ2SwJUiq$C$EUapV^o)EB1`YfZEz7g(zsfrC4Bibs??Li3jIED zakO02;d$IVXH|FjvG>du--P-0JuMyIrq}KBv;Y|*9}(cq0v**i)-Q(ukYo`&i4WdX z+@pS}rcpSs(_S2kBvG;LO^mDNfb@};Tlym<2K2-JCAc+~-C7A>8ZSU;+M)7{{pD}w z?b95t#N*N&>OC6m=#3`70$=0gjiyg~CBl2+o9E=bfGNf-%mxiQi=;a@Pct|ZsyJ=P zGM8YJl!ls?*X@g<7DbyiT$LvQA)M_GtnfvBn+vvc@{29X?t^pkK8j6T$6D5zj5Isg zkos7)n!uCsI0;pqwuAct`uc{vUJs2qOCHldae~hfv6heyV67n(ruzP!WB#wm@xM5V zKhP+%BNK#%;5lbsT>ZV&W$=+YdSQ5?1WD~5++t9fg$MN-n8Y`4204_69|{ETEV&Vk zQq}sfHCxWyfK}0MWA*d(ttc=r{2ey;9#Bf1Ad$yz-2 zstU!&onJNfD0#T~#`B0T9utj&OHBf`P0Pg&?L{Ev+dJY%Er^`ywZhF1vDTHOqFgut zrvc|cB3B&~Vc`cTGMAOE&nN<}W=SA{r z=Zjj0S47{rhoyS9TvYNHze*~EM)(H;6xI*WuKLz&#-;)U@*VvQ`y+4pD=U|I#`S0` z(Tb_oM0(+{I=u?YE)ej!z@~Z#M#e9URQsd-_jlLHR8Y);Oxl|tEGAC?@D55Cb}xl# z0Xb0ZL`3awKS;y71n*1;6o70$idI6z-&Qwe0z$?jlhkL{OAwt5b%y{rgRJ`j7|cXn zQu2wP^b?0J9}-H?3fVUIPyCRSu_GI97X5xl=M<-C_$?e4xy!Xx1g^gqfU=A~;k9yQ20 zyK(uK=9yK6ZGvh6tHaDX0N65`j7($_(0!nC;K=b##mU>mwl~NKHT1%~S``@_=Nc$k zshm*|sSey;TiYJpA4v<^azeN75`yGedj-G{{+fOO0#S8#?=v-v<_c2E zan(^w%}cCQ`#k5EeIu5?lwPxq7ujS&qYhVQ=|Si|6<3Rj1;~p!l6cEtg`$E6tn>0k z5P+n(3ct8|CGKKeG}*$fVH+)TR96k})8svRN5dg?@8KU+F!5mqWf! zgIl3b4w$cyw>^@PTDz+zfKUIek{C9P9w>E9IXZh}I{AS`+2wxT6UhyCQcYlGrSK9^#D}z@ zlDu+F?85>HD`mrOBiQr@e-W_(xT?Xo{HgeAD*q9D2X|Q6|2dnTCDqVF z`^3hK*8W^r1vWgwqT+xjI2zZ<@4qRz|Ins(zOUn_o0Qgo`{+-vUbk|(Av5)^v%j|eZiF( z`v9}n4qMN~fyC3oPh*3Q=`cqyatLSrTfgEwN526U!_W=&(ajiYXF@bH`2iA2f7C8r z*8`;a?rt$b(~C`eDbh8rvgYx<+^y>djm&BEHVWs(sQg--bfj$^5#PGP)x&!}G&|kmkzF?X~WJP|BL_1EaQV z^r)tFYw#c4tWAjBI?B*6Cho&4=2NU<7y&!DzO%aB1(nNkTfx2b8@wJ*ti8Lgp?dG! zXpnSiEKc-SnHNEdnlY?g*`Y{!>vT1spBRolkdMXHIdnaAs%e3AT%ZXFmAPUiLT_IU zoU2hzg#KdNWUQ3#I2D3TqaB%~Bw=S1PQ8;`IdS3o)#YFmsL~mz@?9BreR-xAsS((o ztms6`3gFiTl2f1>iS#Efu?+A2qy>L@4ToaYwW3}A$nou>x4^7uU`xaeSu{}9!{X_` zm?{Ww*r_FTbDNg3XjnOxvS;_FI%yL`9@{TBoS)kA8dR>>3q+O}JRL~?1=fCq;%BJU zr+^)qzjG(^ecH;A*)2KKe%woIYIkA!+}@?tnhA5i_E;z>Nk+-xu8LDia*3|o5 zDyvEIj@MDGJv{Ob<$l*J@I}ai4E%2VoH-?cUg@G$mmNzLt4VR*+V)Hm{e?&n$)(3^ zP4}7@l>npBY0!98+vq}eR!n$7?u@G``z@sW3D~j~7B^ElGBgrLtGQTT)9P6LbSUA9 z8?N!aQQGA}^5tuxX14se+C6;bMSY*BUYEeFxh%I_qI--FjKa?A9+Haew{Q|L8=NPI z#k802;%4BMM@lr1n_4_Ge&Sl3M&?`VbVN8iSIpFl>-mWEa|b=2bw5NfrV?$3fWtBZeDNS+WOF~f+jCc0q(>hi}hM0d{+CVQ?ePG zC!1B5tI??MOlm$oCUf(LVnpS_Kqk+)cTs+a$L<#6!dq4V#Wp{*1prI6AO&~cGr3-H z*dfy})j3vcue@?070Y(?Z22Z>hLBDNMHJib#j~k36iB3{3%&H9pk63Q57SFP9bW}R z+F67sqokn;5CS=%yN`T6u+8HAiUNe!LUN9J}6c}H#FYgWnsa!SSTR7bY zC@F6ulpA0;ZI*ESvzEuR$=FX3p_V*VHYRs4oc%)3OsFfIEa_UNjEw+Oe_q4y3vlcqsfPiO?fujc#Z7|asvW$h z+j8D&81hugc_7~%73ICti2d*a+ItTNrvX=7=Fpy8c#7$H$~%$n1}8IO5a%K@sneAC z%uJ+WiFo)ZdNh#yQ5G2HXmZf_lZVq-ii(IW9ok}pJJ~=I_g1AzL$9- z|4|#k#I1jDp#OzD&305^z{AL(vcd!Yb39DPsTm7_U>>_}?C(cg(3UKg1weWjqIb>k KD)!35u>S{JBkS1! literal 0 HcmV?d00001 diff --git a/github/seek_bar_hsl_pure.png b/github/seek_bar_hsl_pure.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c36c729a1c49628330f10cbc2d2e4a53119011 GIT binary patch literal 19871 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 literal 0 HcmV?d00001 diff --git a/github/seek_bar_lab_output.png b/github/seek_bar_lab_output.png new file mode 100644 index 0000000000000000000000000000000000000000..77a0538a549c55e3db29e0a8216b46d250b448ef GIT binary patch literal 10935 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 literal 0 HcmV?d00001 From 07d11c4ef08cfba264aa23b7f99f6efb40155999 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 21:05:57 +0300 Subject: [PATCH 31/34] Add basic LAB readme xml layout snippet --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 58e2f78..bcfcaf7 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,17 @@ hueColorPickerSeekBar.progress = 50 ![](github/seek_bar_lab_output.png) +#### Layout XML Snippet + +Basic LAB components: +```xml + +``` + ### CMYK (cyan, magenta, yellow, key) ![](github/seek_bar_cmyk_pure.png) From 610454f8d46a05da49f516013e7f0caf618a6692 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 21:07:46 +0300 Subject: [PATCH 32/34] Add basic RGB readme xml layout snippet --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index bcfcaf7..ea86fdc 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,17 @@ hueColorPickerSeekBar.progress = 50 ![](github/seek_bar_rgb_pure.png) +#### Layout XML Snippet + +Basic RGB components: +```xml + +``` + ### LAB ![](github/seek_bar_lab_output.png) From 9bf6f97faf6e4485c80cc344849ebbd463b6a89d Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 21:23:58 +0300 Subject: [PATCH 33/34] Bump version to 0.3.0 --- andcolorpicker/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andcolorpicker/build.gradle b/andcolorpicker/build.gradle index 01d0257..ac61e16 100644 --- a/andcolorpicker/build.gradle +++ b/andcolorpicker/build.gradle @@ -47,12 +47,12 @@ dependencies { publish { def groupProjectID = "codes.side" def artifactProjectID = "andcolorpicker" - def publishVersionID = "0.2.0" + def publishVersionID = "0.3.0" userOrg = "side-codes" repoName = "andColorPicker" groupId = groupProjectID artifactId = artifactProjectID publishVersion = publishVersionID - desc = "Handy, flexible and lightning-fast material color picker UI component for Android" + desc = "Handy, flexible and lightning-fast material color picker view components for Android" website = "https://github.com/side-codes/andColorPicker" } From 65b6f4b8df025b8140a6f98e7cb625ba05189bf4 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Sun, 29 Mar 2020 21:24:38 +0300 Subject: [PATCH 34/34] Bump readme version to 0.3.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea86fdc..5583c9a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Gradle dependency: ```gradle -implementation "codes.side:andcolorpicker:0.2.0" +implementation "codes.side:andcolorpicker:0.3.0" ``` ## :art: Picker types