From 6b2a82caf6811836827d714437df2d6c5dc37014 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Tue, 1 Sep 2026 22:25:22 -0400 Subject: [PATCH 1/2] Replace the unread IDE code style with .editorconfig and .gitattributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit info/Project.xml was an IntelliJ code style scheme (version 173, so IntelliJ 2017.3) that nothing has ever read: the IDE looks for .idea/codeStyles/Project.xml, no such directory exists here, and no build file, workflow or doc referenced it. It was also actively wrong — it declared 2-space Kotlin indentation while the codebase is 4-space throughout, so importing it and reformatting would have rewritten every file. Most of its bulk was Android XML layout attribute-arrangement rules, and the Compose Multiplatform rewrite left exactly two XML files, both manifests. Its one live setting, KOTLIN_OFFICIAL, is already set by kotlin.code.style in gradle.properties. .editorconfig takes its place because IntelliJ, Android Studio, VS Code and ktlint all read it, and it can state the trailing-comma preference rather than leaving it to whoever formats next. .gitattributes pins every text file to LF in the repository, with .bat checked out as CRLF for cmd.exe. It also marks jars, images and fonts binary so no normalisation pass is ever run across them — treating a compressed file as text rewrites bytes inside it and corrupts it silently. kotlin-js-store/wasm/yarn.lock was the only tracked text file still holding CRLF, written that way by yarn on Windows, and is converted here. --- .editorconfig | 28 +++++++ .gitattributes | 32 ++++++++ info/Project.xml | 146 --------------------------------- kotlin-js-store/wasm/yarn.lock | 16 ++-- 4 files changed, 68 insertions(+), 154 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes delete mode 100644 info/Project.xml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..2bfe0010 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,28 @@ +# Read by IntelliJ, Android Studio, VS Code and ktlint alike, which is what makes this +# the file that actually governs formatting. +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{kt,kts}] +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL +# Trailing commas keep the diff for a new parameter to a single added line, instead of +# also touching the line before it. +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true + +[*.{yml,yaml,json}] +indent_size = 2 + +[*.md] +# Two trailing spaces are a hard line break in Markdown, so they must survive. +trim_trailing_whitespace = false + +[*.bat] +end_of_line = crlf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..e5c9e0c9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,32 @@ +# Every text file is stored with LF in the repository and checked out with LF, whatever +# operating system the contributor is on. Without this, a Windows checkout can commit CRLF +# and the next Linux contributor sees a whole-file diff that changes nothing. +* text=auto eol=lf + +# Windows batch files are the exception: cmd.exe expects CRLF, so these are checked out +# with CRLF while still being stored as LF. +*.bat text eol=crlf +*.cmd text eol=crlf + +# Marked binary so no line-ending normalisation is ever attempted on them. This matters: +# a CRLF pass that treats a jar or a PNG as text rewrites bytes inside the compressed +# stream and silently corrupts the file. +*.jar binary +*.klib binary +*.zip binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.webp binary +*.ico binary +*.ttf binary +*.otf binary +*.woff binary +*.woff2 binary +*.keystore binary +*.jks binary + +# Generated, and noisy in review. +kotlin-js-store/** linguist-generated=true +colorpicker/api/** linguist-generated=true diff --git a/info/Project.xml b/info/Project.xml deleted file mode 100644 index 5dc28e5c..00000000 --- a/info/Project.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - - - - - -
- - - - xmlns:android - - ^$ - - - -
-
- - - - xmlns:.* - - ^$ - - - BY_NAME - -
-
- - - - .*:id - - http://schemas.android.com/apk/res/android - - - -
-
- - - - .*:name - - http://schemas.android.com/apk/res/android - - - -
-
- - - - name - - ^$ - - - -
-
- - - - style - - ^$ - - - -
-
- - - - .* - - ^$ - - - BY_NAME - -
-
- - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - -
-
- - - - .* - - .* - - - BY_NAME - -
-
-
-
- - -
\ No newline at end of file diff --git a/kotlin-js-store/wasm/yarn.lock b/kotlin-js-store/wasm/yarn.lock index 62b6bd5f..5f4567d6 100644 --- a/kotlin-js-store/wasm/yarn.lock +++ b/kotlin-js-store/wasm/yarn.lock @@ -1,8 +1,8 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@js-joda/core@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" - integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== From ad11a9335ebc1b9b096e19935ea8e8fa9cb55744 Mon Sep 17 00:00:00 2001 From: Illia Achour Date: Tue, 1 Sep 2026 22:25:30 -0400 Subject: [PATCH 2/2] Apply a formatting pass and add trailing commas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A whole-project reformat under the Kotlin official style, plus trailing commas on every multiline parameter and argument list. Both are mechanical: no signature, expression or behaviour changes, which is why it lands in one commit separate from the .editorconfig that now states the rules. Trailing commas are deliberately absent from `if`, `while`, `for` and `catch` parentheses, where Kotlin rejects them — those are not parameter lists. --- .../side/colorpicker/conversion/ArgbInt.kt | 6 +- .../colorpicker/conversion/HexConversions.kt | 1 + .../colorpicker/conversion/HslConversions.kt | 29 ++++-- .../colorpicker/conversion/RgbConversions.kt | 4 +- .../codes/side/colorpicker/model/CmykColor.kt | 27 ++++-- .../codes/side/colorpicker/model/HslColor.kt | 26 ++++-- .../codes/side/colorpicker/model/LabColor.kt | 12 ++- .../codes/side/colorpicker/model/RgbColor.kt | 15 +++- .../state/SaveableColorPickerState.kt | 2 +- .../codes/side/colorpicker/ui/CmykSliders.kt | 88 ++++++++++++++++--- .../side/colorpicker/ui/GradientTrack.kt | 6 +- .../side/colorpicker/ui/HslColorPicker.kt | 14 ++- .../codes/side/colorpicker/ui/HslSliders.kt | 41 +++++++-- .../side/colorpicker/ui/LabColorPicker.kt | 7 +- .../codes/side/colorpicker/ui/RgbSliders.kt | 21 ++++- .../colorpicker/conversion/ArgbIntTest.kt | 18 +++- .../conversion/ChainConversionsTest.kt | 42 +++++++-- .../conversion/CmykConversionsTest.kt | 12 ++- .../conversion/ComposeColorExtTest.kt | 10 ++- .../conversion/HexConversionsTest.kt | 5 +- .../conversion/HslConversionsTest.kt | 32 +++++-- .../conversion/LabConversionsTest.kt | 13 ++- .../side/colorpicker/model/CmykColorTest.kt | 3 +- .../side/colorpicker/model/HslColorTest.kt | 5 +- .../side/colorpicker/model/RgbColorTest.kt | 3 +- .../colorpicker/state/ColorPickerStateTest.kt | 71 +++++++++++---- .../colorpicker/ui/ComponentRenderingTest.kt | 18 +++- .../side/colorpicker/sample/SampleApp.kt | 8 +- .../screenshot/ColorPickerPreviews.kt | 16 ++-- 29 files changed, 438 insertions(+), 117 deletions(-) diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/ArgbInt.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/ArgbInt.kt index 9f7fe9ce..6b816d6f 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/ArgbInt.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/ArgbInt.kt @@ -28,9 +28,9 @@ public fun Int.toRgbColor(): RgbColor = RgbColor( internal fun argb(alpha: Int, red: Int, green: Int, blue: Int): Int = ((alpha.coerceIn(0, 255) and 0xFF) shl 24) or - ((red.coerceIn(0, 255) and 0xFF) shl 16) or - ((green.coerceIn(0, 255) and 0xFF) shl 8) or - (blue.coerceIn(0, 255) and 0xFF) + ((red.coerceIn(0, 255) and 0xFF) shl 16) or + ((green.coerceIn(0, 255) and 0xFF) shl 8) or + (blue.coerceIn(0, 255) and 0xFF) internal fun setAlphaComponent(color: Int, alpha: Int): Int = (color and 0x00FFFFFF) or ((alpha.coerceIn(0, 255) and 0xFF) shl 24) diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HexConversions.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HexConversions.kt index 99f1bce5..2e344065 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HexConversions.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HexConversions.kt @@ -56,6 +56,7 @@ public fun String.toRgbColorOrNull(): RgbColor? { append(char) } } + 6 -> "FF$hex" 8 -> hex else -> return null diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HslConversions.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HslConversions.kt index c2171bbf..dfd5328d 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HslConversions.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/HslConversions.kt @@ -21,12 +21,29 @@ public fun HslColor.toRgb(): RgbColor { val b: Float when (hueSegment) { - 0 -> { r = c + m; g = x + m; b = m } - 1 -> { r = x + m; g = c + m; b = m } - 2 -> { r = m; g = c + m; b = x + m } - 3 -> { r = m; g = x + m; b = c + m } - 4 -> { r = x + m; g = m; b = c + m } - else -> { r = c + m; g = m; b = x + m } + 0 -> { + r = c + m; g = x + m; b = m + } + + 1 -> { + r = x + m; g = c + m; b = m + } + + 2 -> { + r = m; g = c + m; b = x + m + } + + 3 -> { + r = m; g = x + m; b = c + m + } + + 4 -> { + r = x + m; g = m; b = c + m + } + + else -> { + r = c + m; g = m; b = x + m + } } return RgbColor( diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/RgbConversions.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/RgbConversions.kt index 5d23c1ca..1ee25c90 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/RgbConversions.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/conversion/RgbConversions.kt @@ -12,7 +12,7 @@ import codes.side.colorpicker.model.RgbColor internal fun RgbColor.contrastColor(): RgbColor { // WCAG relative luminance on linearized sRGB channels. val luminance = 0.2126 * linearize(red.toDouble()) + - 0.7152 * linearize(green.toDouble()) + - 0.0722 * linearize(blue.toDouble()) + 0.7152 * linearize(green.toDouble()) + + 0.0722 * linearize(blue.toDouble()) return if (luminance > 0.179) RgbColor.Black else RgbColor.White } diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/CmykColor.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/CmykColor.kt index b5a4159a..8fdf1710 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/CmykColor.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/CmykColor.kt @@ -35,22 +35,29 @@ public class CmykColor( // "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero. /** Cyan channel in `0..1`. */ public val cyan: Float = cyan + 0f + /** Magenta channel in `0..1`. */ public val magenta: Float = magenta + 0f + /** Yellow channel in `0..1`. */ public val yellow: Float = yellow + 0f + /** Key (black) channel in `0..1`. */ public val key: Float = key + 0f override val alpha: Float = alpha + 0f /** [cyan] scaled to `0..100` percent and rounded to the nearest integer. */ public val intCyan: Int get() = (cyan * 100f).roundToInt() + /** [magenta] scaled to `0..100` percent and rounded to the nearest integer. */ public val intMagenta: Int get() = (magenta * 100f).roundToInt() + /** [yellow] scaled to `0..100` percent and rounded to the nearest integer. */ public val intYellow: Int get() = (yellow * 100f).roundToInt() + /** [key] scaled to `0..100` percent and rounded to the nearest integer. */ public val intKey: Int get() = (key * 100f).roundToInt() + /** [alpha] scaled to `0..255` and rounded to the nearest integer. */ public val intAlpha: Int get() = (alpha * 255f).roundToInt() @@ -61,16 +68,17 @@ public class CmykColor( yellow: Float = this.yellow, key: Float = this.key, alpha: Float = this.alpha, - ): CmykColor = CmykColor(cyan = cyan, magenta = magenta, yellow = yellow, key = key, alpha = alpha) + ): CmykColor = + CmykColor(cyan = cyan, magenta = magenta, yellow = yellow, key = key, alpha = alpha) override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is CmykColor) return false return cyan == other.cyan && - magenta == other.magenta && - yellow == other.yellow && - key == other.key && - alpha == other.alpha + magenta == other.magenta && + yellow == other.yellow && + key == other.key && + alpha == other.alpha } override fun hashCode(): Int { @@ -88,6 +96,7 @@ public class CmykColor( public companion object { /** Opaque black. */ public val Black: CmykColor = CmykColor(0f, 0f, 0f, 1f) + /** Opaque white. */ public val White: CmykColor = CmykColor(0f, 0f, 0f, 0f) @@ -96,7 +105,13 @@ public class CmykColor( * [key] in `0..100` percent, [alpha] in `0..255`. Unlike the constructor, * out-of-range values are clamped instead of throwing. */ - public fun fromInt(cyan: Int, magenta: Int, yellow: Int, key: Int, alpha: Int = 255): CmykColor = CmykColor( + public fun fromInt( + cyan: Int, + magenta: Int, + yellow: Int, + key: Int, + alpha: Int = 255, + ): CmykColor = CmykColor( cyan = (cyan / 100f).coerceIn(0f, 1f), magenta = (magenta / 100f).coerceIn(0f, 1f), yellow = (yellow / 100f).coerceIn(0f, 1f), diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/HslColor.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/HslColor.kt index 6cf54395..88f157e1 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/HslColor.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/HslColor.kt @@ -35,18 +35,23 @@ public class HslColor( // "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero. /** Hue in degrees, in `0..360` (exclusive) after normalization. */ public val hue: Float = if (hue == 360f) 0f else hue + 0f + /** Saturation in `0..1`. */ public val saturation: Float = saturation + 0f + /** Lightness in `0..1`. */ public val lightness: Float = lightness + 0f override val alpha: Float = alpha + 0f /** [hue] in degrees, rounded to the nearest integer. */ public val intHue: Int get() = hue.roundToInt() + /** [saturation] scaled to `0..100` percent and rounded to the nearest integer. */ public val intSaturation: Int get() = (saturation * 100f).roundToInt() + /** [lightness] scaled to `0..100` percent and rounded to the nearest integer. */ public val intLightness: Int get() = (lightness * 100f).roundToInt() + /** [alpha] scaled to `0..255` and rounded to the nearest integer. */ public val intAlpha: Int get() = (alpha * 255f).roundToInt() @@ -62,9 +67,9 @@ public class HslColor( if (this === other) return true if (other !is HslColor) return false return hue == other.hue && - saturation == other.saturation && - lightness == other.lightness && - alpha == other.alpha + saturation == other.saturation && + lightness == other.lightness && + alpha == other.alpha } override fun hashCode(): Int { @@ -81,8 +86,10 @@ public class HslColor( public companion object { /** Opaque black. */ public val Black: HslColor = HslColor(hue = 0f, saturation = 0f, lightness = 0f) + /** Opaque white. */ public val White: HslColor = HslColor(hue = 0f, saturation = 0f, lightness = 1f) + /** Opaque pure red. */ public val Red: HslColor = HslColor(hue = 0f, saturation = 1f, lightness = 0.5f) @@ -91,11 +98,12 @@ public class HslColor( * [saturation] and [lightness] in `0..100` percent, [alpha] in `0..255`. * Unlike the constructor, out-of-range values are clamped instead of throwing. */ - public fun fromInt(hue: Int, saturation: Int, lightness: Int, alpha: Int = 255): HslColor = HslColor( - hue = hue.toFloat().coerceIn(0f, 360f), - saturation = (saturation / 100f).coerceIn(0f, 1f), - lightness = (lightness / 100f).coerceIn(0f, 1f), - alpha = (alpha / 255f).coerceIn(0f, 1f), - ) + public fun fromInt(hue: Int, saturation: Int, lightness: Int, alpha: Int = 255): HslColor = + HslColor( + hue = hue.toFloat().coerceIn(0f, 360f), + saturation = (saturation / 100f).coerceIn(0f, 1f), + lightness = (lightness / 100f).coerceIn(0f, 1f), + alpha = (alpha / 255f).coerceIn(0f, 1f), + ) } } diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/LabColor.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/LabColor.kt index 3e485457..3dffa919 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/LabColor.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/LabColor.kt @@ -33,18 +33,23 @@ public class LabColor( // "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero. /** Lightness (L*) in `0..100`. */ public val l: Float = l + 0f + /** Green-red axis (a*) in `-128..127`; negative is green, positive is red. */ public val a: Float = a + 0f + /** Blue-yellow axis (b*) in `-128..127`; negative is blue, positive is yellow. */ public val b: Float = b + 0f override val alpha: Float = alpha + 0f /** [l] rounded to the nearest integer. */ public val intL: Int get() = l.roundToInt() + /** [a] rounded to the nearest integer. */ public val intA: Int get() = a.roundToInt() + /** [b] rounded to the nearest integer. */ public val intB: Int get() = b.roundToInt() + /** [alpha] scaled to `0..255` and rounded to the nearest integer. */ public val intAlpha: Int get() = (alpha * 255f).roundToInt() @@ -60,9 +65,9 @@ public class LabColor( if (this === other) return true if (other !is LabColor) return false return l == other.l && - a == other.a && - b == other.b && - alpha == other.alpha + a == other.a && + b == other.b && + alpha == other.alpha } override fun hashCode(): Int { @@ -79,6 +84,7 @@ public class LabColor( public companion object { /** Opaque black. */ public val Black: LabColor = LabColor(l = 0f, a = 0f, b = 0f) + /** Opaque white. */ public val White: LabColor = LabColor(l = 100f, a = 0f, b = 0f) diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/RgbColor.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/RgbColor.kt index ead39951..a0337291 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/RgbColor.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/model/RgbColor.kt @@ -33,18 +33,23 @@ public class RgbColor( // "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero. /** Red channel in `0..1`. */ public val red: Float = red + 0f + /** Green channel in `0..1`. */ public val green: Float = green + 0f + /** Blue channel in `0..1`. */ public val blue: Float = blue + 0f override val alpha: Float = alpha + 0f /** [red] scaled to `0..255` and rounded to the nearest integer. */ public val intRed: Int get() = (red * 255f).roundToInt() + /** [green] scaled to `0..255` and rounded to the nearest integer. */ public val intGreen: Int get() = (green * 255f).roundToInt() + /** [blue] scaled to `0..255` and rounded to the nearest integer. */ public val intBlue: Int get() = (blue * 255f).roundToInt() + /** [alpha] scaled to `0..255` and rounded to the nearest integer. */ public val intAlpha: Int get() = (alpha * 255f).roundToInt() @@ -60,9 +65,9 @@ public class RgbColor( if (this === other) return true if (other !is RgbColor) return false return red == other.red && - green == other.green && - blue == other.blue && - alpha == other.alpha + green == other.green && + blue == other.blue && + alpha == other.alpha } override fun hashCode(): Int { @@ -79,12 +84,16 @@ public class RgbColor( public companion object { /** Opaque black. */ public val Black: RgbColor = RgbColor(0f, 0f, 0f) + /** Opaque white. */ public val White: RgbColor = RgbColor(1f, 1f, 1f) + /** Opaque pure red. */ public val Red: RgbColor = RgbColor(1f, 0f, 0f) + /** Opaque pure green. */ public val Green: RgbColor = RgbColor(0f, 1f, 0f) + /** Opaque pure blue. */ public val Blue: RgbColor = RgbColor(0f, 0f, 1f) diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/state/SaveableColorPickerState.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/state/SaveableColorPickerState.kt index e75457f1..d52c1da8 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/state/SaveableColorPickerState.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/state/SaveableColorPickerState.kt @@ -100,7 +100,7 @@ internal val ColorPickerStateSaver = Saver( } } color?.let { ColorPickerState(it) } - } + }, ) /** diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/CmykSliders.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/CmykSliders.kt index c6fc60dd..6b86ece7 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/CmykSliders.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/CmykSliders.kt @@ -49,14 +49,30 @@ public fun CyanSlider( when (coloringMode) { ColoringMode.Independent -> persistentListOf(Color.White, PureCyan) ColoringMode.Contextual -> persistentListOf( - CmykColor(cyan = 0f, magenta = cmyk.magenta, yellow = cmyk.yellow, key = cmyk.key).toComposeColor(), - CmykColor(cyan = 1f, magenta = cmyk.magenta, yellow = cmyk.yellow, key = cmyk.key).toComposeColor(), + CmykColor( + cyan = 0f, + magenta = cmyk.magenta, + yellow = cmyk.yellow, + key = cmyk.key, + ).toComposeColor(), + CmykColor( + cyan = 1f, + magenta = cmyk.magenta, + yellow = cmyk.yellow, + key = cmyk.key, + ).toComposeColor(), ) } } val thumbColor = remember(cmyk, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> CmykColor(cyan = cmyk.cyan, magenta = 0f, yellow = 0f, key = 0f).toComposeColor() + ColoringMode.Independent -> CmykColor( + cyan = cmyk.cyan, + magenta = 0f, + yellow = 0f, + key = 0f, + ).toComposeColor() + ColoringMode.Contextual -> cmyk.toComposeColor() } } @@ -114,14 +130,30 @@ public fun MagentaSlider( when (coloringMode) { ColoringMode.Independent -> persistentListOf(Color.White, PureMagenta) ColoringMode.Contextual -> persistentListOf( - CmykColor(cyan = cmyk.cyan, magenta = 0f, yellow = cmyk.yellow, key = cmyk.key).toComposeColor(), - CmykColor(cyan = cmyk.cyan, magenta = 1f, yellow = cmyk.yellow, key = cmyk.key).toComposeColor(), + CmykColor( + cyan = cmyk.cyan, + magenta = 0f, + yellow = cmyk.yellow, + key = cmyk.key, + ).toComposeColor(), + CmykColor( + cyan = cmyk.cyan, + magenta = 1f, + yellow = cmyk.yellow, + key = cmyk.key, + ).toComposeColor(), ) } } val thumbColor = remember(cmyk, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> CmykColor(cyan = 0f, magenta = cmyk.magenta, yellow = 0f, key = 0f).toComposeColor() + ColoringMode.Independent -> CmykColor( + cyan = 0f, + magenta = cmyk.magenta, + yellow = 0f, + key = 0f, + ).toComposeColor() + ColoringMode.Contextual -> cmyk.toComposeColor() } } @@ -179,14 +211,30 @@ public fun YellowSlider( when (coloringMode) { ColoringMode.Independent -> persistentListOf(Color.White, PureYellow) ColoringMode.Contextual -> persistentListOf( - CmykColor(cyan = cmyk.cyan, magenta = cmyk.magenta, yellow = 0f, key = cmyk.key).toComposeColor(), - CmykColor(cyan = cmyk.cyan, magenta = cmyk.magenta, yellow = 1f, key = cmyk.key).toComposeColor(), + CmykColor( + cyan = cmyk.cyan, + magenta = cmyk.magenta, + yellow = 0f, + key = cmyk.key, + ).toComposeColor(), + CmykColor( + cyan = cmyk.cyan, + magenta = cmyk.magenta, + yellow = 1f, + key = cmyk.key, + ).toComposeColor(), ) } } val thumbColor = remember(cmyk, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> CmykColor(cyan = 0f, magenta = 0f, yellow = cmyk.yellow, key = 0f).toComposeColor() + ColoringMode.Independent -> CmykColor( + cyan = 0f, + magenta = 0f, + yellow = cmyk.yellow, + key = 0f, + ).toComposeColor() + ColoringMode.Contextual -> cmyk.toComposeColor() } } @@ -244,14 +292,30 @@ public fun KeySlider( when (coloringMode) { ColoringMode.Independent -> persistentListOf(Color.White, Color.Black) ColoringMode.Contextual -> persistentListOf( - CmykColor(cyan = cmyk.cyan, magenta = cmyk.magenta, yellow = cmyk.yellow, key = 0f).toComposeColor(), - CmykColor(cyan = cmyk.cyan, magenta = cmyk.magenta, yellow = cmyk.yellow, key = 1f).toComposeColor(), + CmykColor( + cyan = cmyk.cyan, + magenta = cmyk.magenta, + yellow = cmyk.yellow, + key = 0f, + ).toComposeColor(), + CmykColor( + cyan = cmyk.cyan, + magenta = cmyk.magenta, + yellow = cmyk.yellow, + key = 1f, + ).toComposeColor(), ) } } val thumbColor = remember(cmyk, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> CmykColor(cyan = 0f, magenta = 0f, yellow = 0f, key = cmyk.key).toComposeColor() + ColoringMode.Independent -> CmykColor( + cyan = 0f, + magenta = 0f, + yellow = 0f, + key = cmyk.key, + ).toComposeColor() + ColoringMode.Contextual -> cmyk.toComposeColor() } } diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/GradientTrack.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/GradientTrack.kt index 62ced18a..6c3d3ffb 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/GradientTrack.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/GradientTrack.kt @@ -22,8 +22,8 @@ import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.drawscope.clipPath import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import codes.side.colorpicker.theme.ColorPickerDefaults import kotlinx.collections.immutable.ImmutableList @@ -125,7 +125,7 @@ internal fun GradientTrack( bottomLeft = CornerRadius.Zero, topRight = CornerRadius(insideCornerSize), bottomRight = CornerRadius(insideCornerSize), - ) + ), ) } @@ -140,7 +140,7 @@ internal fun GradientTrack( bottomLeft = CornerRadius(insideCornerSize), topRight = CornerRadius.Zero, bottomRight = CornerRadius.Zero, - ) + ), ) } diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslColorPicker.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslColorPicker.kt index 0de82971..9a0e2831 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslColorPicker.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslColorPicker.kt @@ -39,8 +39,18 @@ public fun HslColorPicker( verticalArrangement = Arrangement.spacedBy(12.dp), ) { HueSlider(state = state, coloringMode = coloringMode, colors = colors, shapes = shapes) - SaturationSlider(state = state, coloringMode = coloringMode, colors = colors, shapes = shapes) - LightnessSlider(state = state, coloringMode = coloringMode, colors = colors, shapes = shapes) + SaturationSlider( + state = state, + coloringMode = coloringMode, + colors = colors, + shapes = shapes, + ) + LightnessSlider( + state = state, + coloringMode = coloringMode, + colors = colors, + shapes = shapes, + ) if (showAlpha) { AlphaSlider(state = state, colors = colors, shapes = shapes) } diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslSliders.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslSliders.kt index 72702b5f..4ac51168 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslSliders.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/HslSliders.kt @@ -73,7 +73,12 @@ public fun HueSlider( } val thumbColor = remember(hsl, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> HslColor(hue = hsl.hue, saturation = 1f, lightness = 0.5f).toComposeColor() + ColoringMode.Independent -> HslColor( + hue = hsl.hue, + saturation = 1f, + lightness = 0.5f, + ).toComposeColor() + ColoringMode.Contextual -> hsl.toComposeColor() } } @@ -133,15 +138,29 @@ public fun SaturationSlider( Color.Gray, HslColor(hue = hsl.hue, saturation = 1f, lightness = 0.5f).toComposeColor(), ) + ColoringMode.Contextual -> persistentListOf( - HslColor(hue = hsl.hue, saturation = 0f, lightness = hsl.lightness).toComposeColor(), - HslColor(hue = hsl.hue, saturation = 1f, lightness = hsl.lightness).toComposeColor(), + HslColor( + hue = hsl.hue, + saturation = 0f, + lightness = hsl.lightness, + ).toComposeColor(), + HslColor( + hue = hsl.hue, + saturation = 1f, + lightness = hsl.lightness, + ).toComposeColor(), ) } } val thumbColor = remember(hsl, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> HslColor(hue = hsl.hue, saturation = hsl.saturation, lightness = 0.5f).toComposeColor() + ColoringMode.Independent -> HslColor( + hue = hsl.hue, + saturation = hsl.saturation, + lightness = 0.5f, + ).toComposeColor() + ColoringMode.Contextual -> hsl.toComposeColor() } } @@ -202,16 +221,26 @@ public fun LightnessSlider( HslColor(hue = hsl.hue, saturation = 1f, lightness = 0.5f).toComposeColor(), Color.White, ) + ColoringMode.Contextual -> persistentListOf( Color.Black, - HslColor(hue = hsl.hue, saturation = hsl.saturation, lightness = 0.5f).toComposeColor(), + HslColor( + hue = hsl.hue, + saturation = hsl.saturation, + lightness = 0.5f, + ).toComposeColor(), Color.White, ) } } val thumbColor = remember(hsl, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> HslColor(hue = hsl.hue, saturation = 1f, lightness = hsl.lightness).toComposeColor() + ColoringMode.Independent -> HslColor( + hue = hsl.hue, + saturation = 1f, + lightness = hsl.lightness, + ).toComposeColor() + ColoringMode.Contextual -> hsl.toComposeColor() } } diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/LabColorPicker.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/LabColorPicker.kt index 63bd3e71..81611a84 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/LabColorPicker.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/LabColorPicker.kt @@ -33,7 +33,12 @@ public fun LabColorPicker( modifier = modifier, verticalArrangement = Arrangement.spacedBy(12.dp), ) { - LightnessLabSlider(state = state, coloringMode = coloringMode, colors = colors, shapes = shapes) + LightnessLabSlider( + state = state, + coloringMode = coloringMode, + colors = colors, + shapes = shapes, + ) LabASlider(state = state, coloringMode = coloringMode, colors = colors, shapes = shapes) LabBSlider(state = state, coloringMode = coloringMode, colors = colors, shapes = shapes) if (showAlpha) { diff --git a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/RgbSliders.kt b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/RgbSliders.kt index 81d3bf4b..6e77ea60 100644 --- a/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/RgbSliders.kt +++ b/colorpicker/src/commonMain/kotlin/codes/side/colorpicker/ui/RgbSliders.kt @@ -52,7 +52,12 @@ public fun RedSlider( } val thumbColor = remember(rgb, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> RgbColor(red = rgb.red, green = 0f, blue = 0f).toComposeColor() + ColoringMode.Independent -> RgbColor( + red = rgb.red, + green = 0f, + blue = 0f, + ).toComposeColor() + ColoringMode.Contextual -> rgb.toComposeColor() } } @@ -117,7 +122,12 @@ public fun GreenSlider( } val thumbColor = remember(rgb, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> RgbColor(red = 0f, green = rgb.green, blue = 0f).toComposeColor() + ColoringMode.Independent -> RgbColor( + red = 0f, + green = rgb.green, + blue = 0f, + ).toComposeColor() + ColoringMode.Contextual -> rgb.toComposeColor() } } @@ -182,7 +192,12 @@ public fun BlueSlider( } val thumbColor = remember(rgb, coloringMode) { when (coloringMode) { - ColoringMode.Independent -> RgbColor(red = 0f, green = 0f, blue = rgb.blue).toComposeColor() + ColoringMode.Independent -> RgbColor( + red = 0f, + green = 0f, + blue = rgb.blue, + ).toComposeColor() + ColoringMode.Contextual -> rgb.toComposeColor() } } diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ArgbIntTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ArgbIntTest.kt index 44b9b114..93d481fd 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ArgbIntTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ArgbIntTest.kt @@ -10,9 +10,21 @@ class ArgbIntTest { private fun assertIntRgbEqual(expected: RgbColor, actual: RgbColor) { assertEquals(expected.intRed, actual.intRed, "red: ${expected.intRed} vs ${actual.intRed}") - assertEquals(expected.intGreen, actual.intGreen, "green: ${expected.intGreen} vs ${actual.intGreen}") - assertEquals(expected.intBlue, actual.intBlue, "blue: ${expected.intBlue} vs ${actual.intBlue}") - assertEquals(expected.intAlpha, actual.intAlpha, "alpha: ${expected.intAlpha} vs ${actual.intAlpha}") + assertEquals( + expected.intGreen, + actual.intGreen, + "green: ${expected.intGreen} vs ${actual.intGreen}", + ) + assertEquals( + expected.intBlue, + actual.intBlue, + "blue: ${expected.intBlue} vs ${actual.intBlue}", + ) + assertEquals( + expected.intAlpha, + actual.intAlpha, + "alpha: ${expected.intAlpha} vs ${actual.intAlpha}", + ) } // ---- Pack & unpack ---- diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ChainConversionsTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ChainConversionsTest.kt index 965bbd4a..86962cbf 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ChainConversionsTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ChainConversionsTest.kt @@ -14,7 +14,12 @@ class ChainConversionsTest { private val eps = 2e-5f private val labEps = 0.5f // LAB uses transcendental functions, needs wider tolerance - private fun assertNear(expected: Float, actual: Float, tolerance: Float = eps, msg: String = "") { + private fun assertNear( + expected: Float, + actual: Float, + tolerance: Float = eps, + msg: String = "", + ) { assertTrue( abs(expected - actual) <= tolerance, "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}" @@ -111,7 +116,12 @@ class ChainConversionsTest { val original = HslColor(hue = 210.5f, saturation = 0.47f, lightness = 0.63f) val roundTripped = original.toRgb().toHsl() assertNear(original.hue, roundTripped.hue, tolerance = eps, msg = "hue") - assertNear(original.saturation, roundTripped.saturation, tolerance = eps, msg = "saturation") + assertNear( + original.saturation, + roundTripped.saturation, + tolerance = eps, + msg = "saturation", + ) assertNear(original.lightness, roundTripped.lightness, tolerance = eps, msg = "lightness") } @@ -120,7 +130,12 @@ class ChainConversionsTest { val original = HslColor(hue = 33.7f, saturation = 0.22f, lightness = 0.77f) val roundTripped = original.toRgb().toHsl() assertNear(original.hue, roundTripped.hue, tolerance = eps, msg = "hue") - assertNear(original.saturation, roundTripped.saturation, tolerance = eps, msg = "saturation") + assertNear( + original.saturation, + roundTripped.saturation, + tolerance = eps, + msg = "saturation", + ) assertNear(original.lightness, roundTripped.lightness, tolerance = eps, msg = "lightness") } @@ -129,7 +144,12 @@ class ChainConversionsTest { val original = HslColor(hue = 275.3f, saturation = 0.85f, lightness = 0.31f) val roundTripped = original.toRgb().toHsl() assertNear(original.hue, roundTripped.hue, tolerance = eps, msg = "hue") - assertNear(original.saturation, roundTripped.saturation, tolerance = eps, msg = "saturation") + assertNear( + original.saturation, + roundTripped.saturation, + tolerance = eps, + msg = "saturation", + ) assertNear(original.lightness, roundTripped.lightness, tolerance = eps, msg = "lightness") } @@ -138,7 +158,12 @@ class ChainConversionsTest { val original = HslColor(hue = 5.5f, saturation = 0.91f, lightness = 0.44f) val roundTripped = original.toRgb().toHsl() assertNear(original.hue, roundTripped.hue, tolerance = eps, msg = "hue") - assertNear(original.saturation, roundTripped.saturation, tolerance = eps, msg = "saturation") + assertNear( + original.saturation, + roundTripped.saturation, + tolerance = eps, + msg = "saturation", + ) assertNear(original.lightness, roundTripped.lightness, tolerance = eps, msg = "lightness") } @@ -147,7 +172,12 @@ class ChainConversionsTest { val original = HslColor(hue = 185.2f, saturation = 0.63f, lightness = 0.52f) val roundTripped = original.toRgb().toHsl() assertNear(original.hue, roundTripped.hue, tolerance = eps, msg = "hue") - assertNear(original.saturation, roundTripped.saturation, tolerance = eps, msg = "saturation") + assertNear( + original.saturation, + roundTripped.saturation, + tolerance = eps, + msg = "saturation", + ) assertNear(original.lightness, roundTripped.lightness, tolerance = eps, msg = "lightness") } diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/CmykConversionsTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/CmykConversionsTest.kt index 24b57959..2d94b055 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/CmykConversionsTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/CmykConversionsTest.kt @@ -11,8 +11,16 @@ class CmykConversionsTest { private val eps = 1e-5f - private fun assertNear(expected: Float, actual: Float, tolerance: Float = eps, msg: String = "") { - assertTrue(abs(expected - actual) <= tolerance, "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}") + private fun assertNear( + expected: Float, + actual: Float, + tolerance: Float = eps, + msg: String = "", + ) { + assertTrue( + abs(expected - actual) <= tolerance, + "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}", + ) } // ---- CMYK -> RGB known colors ---- diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ComposeColorExtTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ComposeColorExtTest.kt index 511dad69..b01ff92d 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ComposeColorExtTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/ComposeColorExtTest.kt @@ -13,7 +13,10 @@ import kotlin.test.assertTrue class ComposeColorExtTest { private fun assertNear(expected: Float, actual: Float, tolerance: Float, msg: String = "") { - assertTrue(abs(expected - actual) <= tolerance, "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}") + assertTrue( + abs(expected - actual) <= tolerance, + "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}", + ) } // ---- sRGB round-trips ---- @@ -55,7 +58,10 @@ class ComposeColorExtTest { val rgb = p3.toRgbColor() // The raw P3 channel values must have gone through a real sRGB conversion assertTrue(abs(rgb.red - 0.5f) > 0.001f, "red should differ from raw P3 value: ${rgb.red}") - assertTrue(rgb.blue > rgb.green, "hue ordering preserved: blue=${rgb.blue} green=${rgb.green}") + assertTrue( + rgb.blue > rgb.green, + "hue ordering preserved: blue=${rgb.blue} green=${rgb.green}", + ) assertEquals(1f, rgb.alpha) } diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HexConversionsTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HexConversionsTest.kt index b27e1c6e..8bde8797 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HexConversionsTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HexConversionsTest.kt @@ -135,7 +135,10 @@ class HexConversionsTest { @Test fun strictParseThrowsWithOffendingString() { val exception = assertFailsWith { "nope".toRgbColor() } - assertTrue(exception.message!!.contains("nope"), "message should contain the input: ${exception.message}") + assertTrue( + exception.message!!.contains("nope"), + "message should contain the input: ${exception.message}", + ) } // ---- Round-trips ---- diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HslConversionsTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HslConversionsTest.kt index 8d8df74c..2c66b43b 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HslConversionsTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/HslConversionsTest.kt @@ -11,8 +11,16 @@ class HslConversionsTest { private val eps = 5e-5f - private fun assertNear(expected: Float, actual: Float, tolerance: Float = eps, msg: String = "") { - assertTrue(abs(expected - actual) <= tolerance, "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}") + private fun assertNear( + expected: Float, + actual: Float, + tolerance: Float = eps, + msg: String = "", + ) { + assertTrue( + abs(expected - actual) <= tolerance, + "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}", + ) } // ---- HSL -> RGB primary colors ---- @@ -207,10 +215,22 @@ class HslConversionsTest { val hsl = HslColor(hue = 180f, saturation = 0.5f, lightness = 0.4f, alpha = 200f / 255f) val restored = hsl.toArgbInt().toHslColor() // ARGB int round-trip quantizes to 8-bit, so allow +-1 int unit tolerance - assertTrue(abs(hsl.intHue - restored.intHue) <= 1, "hue: ${hsl.intHue} vs ${restored.intHue}") - assertTrue(abs(hsl.intSaturation - restored.intSaturation) <= 1, "sat: ${hsl.intSaturation} vs ${restored.intSaturation}") - assertTrue(abs(hsl.intLightness - restored.intLightness) <= 1, "light: ${hsl.intLightness} vs ${restored.intLightness}") - assertTrue(abs(hsl.intAlpha - restored.intAlpha) <= 1, "alpha: ${hsl.intAlpha} vs ${restored.intAlpha}") + assertTrue( + abs(hsl.intHue - restored.intHue) <= 1, + "hue: ${hsl.intHue} vs ${restored.intHue}", + ) + assertTrue( + abs(hsl.intSaturation - restored.intSaturation) <= 1, + "sat: ${hsl.intSaturation} vs ${restored.intSaturation}", + ) + assertTrue( + abs(hsl.intLightness - restored.intLightness) <= 1, + "light: ${hsl.intLightness} vs ${restored.intLightness}", + ) + assertTrue( + abs(hsl.intAlpha - restored.intAlpha) <= 1, + "alpha: ${hsl.intAlpha} vs ${restored.intAlpha}", + ) } @Test diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/LabConversionsTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/LabConversionsTest.kt index e46ac914..4ad3df7c 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/LabConversionsTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/conversion/LabConversionsTest.kt @@ -10,11 +10,20 @@ import kotlin.test.assertTrue class LabConversionsTest { private val eps = 1e-5f + // LAB uses transcendental functions so needs a slightly wider tolerance private val labEps = 0.5f - private fun assertNear(expected: Float, actual: Float, tolerance: Float = eps, msg: String = "") { - assertTrue(abs(expected - actual) <= tolerance, "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}") + private fun assertNear( + expected: Float, + actual: Float, + tolerance: Float = eps, + msg: String = "", + ) { + assertTrue( + abs(expected - actual) <= tolerance, + "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}", + ) } // ---- LAB -> RGB known colors ---- diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/CmykColorTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/CmykColorTest.kt index 3899b034..6c9c516a 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/CmykColorTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/CmykColorTest.kt @@ -72,7 +72,8 @@ class CmykColorTest { @Test fun intAccessorsMidValues() { - val c = CmykColor(cyan = 0.5f, magenta = 0.25f, yellow = 0.75f, key = 0.1f, alpha = 128f / 255f) + val c = + CmykColor(cyan = 0.5f, magenta = 0.25f, yellow = 0.75f, key = 0.1f, alpha = 128f / 255f) assertEquals(50, c.intCyan) assertEquals(25, c.intMagenta) assertEquals(75, c.intYellow) diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/HslColorTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/HslColorTest.kt index 245e27b1..dc3772a0 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/HslColorTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/HslColorTest.kt @@ -147,7 +147,10 @@ class HslColorTest { @Test fun intHueRoundsCorrectly() { val hsl = HslColor(hue = 210.5f) - assertEquals(211, hsl.intHue) // roundToInt rounds 210.5 -> 211 (banker's toward even on .5, but 210.5 -> 211 is nearest) + assertEquals( + 211, + hsl.intHue, + ) // roundToInt rounds 210.5 -> 211 (banker's toward even on .5, but 210.5 -> 211 is nearest) } // ---- Companion constants ---- diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/RgbColorTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/RgbColorTest.kt index 34a778ee..4801b080 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/RgbColorTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/model/RgbColorTest.kt @@ -53,7 +53,8 @@ class RgbColorTest { @Test fun intAccessors() { - val rgb = RgbColor(red = 128f / 255f, green = 64f / 255f, blue = 32f / 255f, alpha = 200f / 255f) + val rgb = + RgbColor(red = 128f / 255f, green = 64f / 255f, blue = 32f / 255f, alpha = 200f / 255f) assertEquals(128, rgb.intRed) assertEquals(64, rgb.intGreen) assertEquals(32, rgb.intBlue) diff --git a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/state/ColorPickerStateTest.kt b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/state/ColorPickerStateTest.kt index d3c445cd..54a27325 100644 --- a/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/state/ColorPickerStateTest.kt +++ b/colorpicker/src/commonTest/kotlin/codes/side/colorpicker/state/ColorPickerStateTest.kt @@ -7,6 +7,12 @@ import codes.side.colorpicker.model.CmykColor import codes.side.colorpicker.model.HslColor import codes.side.colorpicker.model.LabColor import codes.side.colorpicker.model.RgbColor +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest import kotlin.math.abs import kotlin.test.Test import kotlin.test.assertEquals @@ -16,18 +22,17 @@ import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertSame import kotlin.test.assertTrue -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.take -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest class ColorPickerStateTest { private val eps = 1e-5f - private fun assertNear(expected: Float, actual: Float, tolerance: Float = eps, msg: String = "") { + private fun assertNear( + expected: Float, + actual: Float, + tolerance: Float = eps, + msg: String = "", + ) { assertTrue( abs(expected - actual) <= tolerance, "$msg expected=$expected actual=$actual diff=${abs(expected - actual)}" @@ -278,7 +283,8 @@ class ColorPickerStateTest { @Test fun updateHuePreservesSaturationAndLightness() { - val state = createState(HslColor(hue = 0f, saturation = 0.8f, lightness = 0.6f, alpha = 0.784f)) + val state = + createState(HslColor(hue = 0f, saturation = 0.8f, lightness = 0.6f, alpha = 0.784f)) state.updateHue(120f) assertEquals(0.8f, state.hslColor.saturation) assertEquals(0.6f, state.hslColor.lightness) @@ -296,7 +302,8 @@ class ColorPickerStateTest { @Test fun updateSaturationPreservesHueAndLightness() { - val state = createState(HslColor(hue = 200f, saturation = 0.5f, lightness = 0.6f, alpha = 0.9f)) + val state = + createState(HslColor(hue = 200f, saturation = 0.5f, lightness = 0.6f, alpha = 0.9f)) state.updateSaturation(0.3f) assertEquals(200f, state.hslColor.hue) assertEquals(0.6f, state.hslColor.lightness) @@ -366,7 +373,8 @@ class ColorPickerStateTest { @Test fun updateFromCmykReadsBackTheExactInstance() { val state = createState() - val cmyk = CmykColor(cyan = 0.1f, magenta = 1f / 3f, yellow = 0.7f, key = 0.9f, alpha = 0.3f) + val cmyk = + CmykColor(cyan = 0.1f, magenta = 1f / 3f, yellow = 0.7f, key = 0.9f, alpha = 0.3f) state.updateFromCmyk(cmyk) assertSame(cmyk, state.cmykColor) assertSame(cmyk, state.pickerColor) @@ -543,7 +551,14 @@ class ColorPickerStateTest { @Test fun saverRoundTripHsl() { - val original = ColorPickerState(HslColor(hue = 210f, saturation = 0.4f, lightness = 0.6f, alpha = 0.5f)) + val original = ColorPickerState( + HslColor( + hue = 210f, + saturation = 0.4f, + lightness = 0.6f, + alpha = 0.5f, + ), + ) val restored = assertNotNull(ColorPickerStateSaver.restore(saveToArray(original))) assertIs(restored.pickerColor) assertEquals(original.pickerColor, restored.pickerColor) @@ -551,7 +566,8 @@ class ColorPickerStateTest { @Test fun saverRoundTripRgb() { - val original = ColorPickerState(RgbColor(red = 0.1f, green = 0.2f, blue = 0.3f, alpha = 0.4f)) + val original = + ColorPickerState(RgbColor(red = 0.1f, green = 0.2f, blue = 0.3f, alpha = 0.4f)) val restored = assertNotNull(ColorPickerStateSaver.restore(saveToArray(original))) assertIs(restored.pickerColor) assertEquals(original.pickerColor, restored.pickerColor) @@ -559,7 +575,15 @@ class ColorPickerStateTest { @Test fun saverRoundTripCmyk() { - val original = ColorPickerState(CmykColor(cyan = 0.1f, magenta = 0.2f, yellow = 0.3f, key = 0.4f, alpha = 0.5f)) + val original = ColorPickerState( + CmykColor( + cyan = 0.1f, + magenta = 0.2f, + yellow = 0.3f, + key = 0.4f, + alpha = 0.5f, + ), + ) val restored = assertNotNull(ColorPickerStateSaver.restore(saveToArray(original))) assertIs(restored.pickerColor) assertEquals(original.pickerColor, restored.pickerColor) @@ -574,13 +598,18 @@ class ColorPickerStateTest { } private fun assertBitIdentical(expected: Float, actual: Float, msg: String) { - assertEquals(expected.toRawBits(), actual.toRawBits(), "$msg expected=$expected actual=$actual") + assertEquals( + expected.toRawBits(), + actual.toRawBits(), + "$msg expected=$expected actual=$actual", + ) } @Test fun saverRoundTripPreservesExactBitsHsl() { val hsl = HslColor(hue = 123.456f, saturation = 0.1f, lightness = 1f / 3f, alpha = 0.7f) - val restored = assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(hsl)))) + val restored = + assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(hsl)))) val color = assertIs(restored.pickerColor) assertBitIdentical(hsl.hue, color.hue, "hue") assertBitIdentical(hsl.saturation, color.saturation, "saturation") @@ -591,7 +620,8 @@ class ColorPickerStateTest { @Test fun saverRoundTripPreservesExactBitsRgb() { val rgb = RgbColor(red = 0.1f, green = 1f / 3f, blue = 0.7f, alpha = 0.9f) - val restored = assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(rgb)))) + val restored = + assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(rgb)))) val color = assertIs(restored.pickerColor) assertBitIdentical(rgb.red, color.red, "red") assertBitIdentical(rgb.green, color.green, "green") @@ -601,8 +631,10 @@ class ColorPickerStateTest { @Test fun saverRoundTripPreservesExactBitsCmyk() { - val cmyk = CmykColor(cyan = 0.1f, magenta = 1f / 3f, yellow = 0.7f, key = 0.9f, alpha = 0.3f) - val restored = assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(cmyk)))) + val cmyk = + CmykColor(cyan = 0.1f, magenta = 1f / 3f, yellow = 0.7f, key = 0.9f, alpha = 0.3f) + val restored = + assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(cmyk)))) val color = assertIs(restored.pickerColor) assertBitIdentical(cmyk.cyan, color.cyan, "cyan") assertBitIdentical(cmyk.magenta, color.magenta, "magenta") @@ -614,7 +646,8 @@ class ColorPickerStateTest { @Test fun saverRoundTripPreservesExactBitsLab() { val lab = LabColor(l = 33.333f, a = -12.7f, b = 64.1f, alpha = 0.6f) - val restored = assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(lab)))) + val restored = + assertNotNull(ColorPickerStateSaver.restore(saveToArray(ColorPickerState(lab)))) val color = assertIs(restored.pickerColor) assertBitIdentical(lab.l, color.l, "l") assertBitIdentical(lab.a, color.a, "a") diff --git a/colorpicker/src/jvmTest/kotlin/codes/side/colorpicker/ui/ComponentRenderingTest.kt b/colorpicker/src/jvmTest/kotlin/codes/side/colorpicker/ui/ComponentRenderingTest.kt index aa5aa434..12e6316e 100644 --- a/colorpicker/src/jvmTest/kotlin/codes/side/colorpicker/ui/ComponentRenderingTest.kt +++ b/colorpicker/src/jvmTest/kotlin/codes/side/colorpicker/ui/ComponentRenderingTest.kt @@ -52,7 +52,7 @@ class ComponentRenderingTest { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) { AlphaSlider( state = ColorPickerState( - HslColor(hue = 0f, saturation = 1f, lightness = 0.5f, alpha = 0f) + HslColor(hue = 0f, saturation = 1f, lightness = 0.5f, alpha = 0f), ), modifier = Modifier.size(width = 300.dp, height = 80.dp).testTag("alpha"), ) @@ -78,7 +78,13 @@ class ComponentRenderingTest { setContent { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { HueSlider( - state = ColorPickerState(HslColor(hue = 180f, saturation = 1f, lightness = 0.5f)), + state = ColorPickerState( + HslColor( + hue = 180f, + saturation = 1f, + lightness = 0.5f, + ), + ), coloringMode = ColoringMode.Contextual, modifier = Modifier.size(width = 300.dp, height = 60.dp).testTag("hue"), ) @@ -105,7 +111,13 @@ class ComponentRenderingTest { setContent { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) { HueSlider( - state = ColorPickerState(HslColor(hue = 180f, saturation = 1f, lightness = 0.5f)), + state = ColorPickerState( + HslColor( + hue = 180f, + saturation = 1f, + lightness = 0.5f, + ), + ), modifier = Modifier.size(width = 300.dp, height = 60.dp).testTag("hue"), thumb = { source -> received = source diff --git a/sample/shared/src/commonMain/kotlin/codes/side/colorpicker/sample/SampleApp.kt b/sample/shared/src/commonMain/kotlin/codes/side/colorpicker/sample/SampleApp.kt index 15ec4f5e..6d29e059 100644 --- a/sample/shared/src/commonMain/kotlin/codes/side/colorpicker/sample/SampleApp.kt +++ b/sample/shared/src/commonMain/kotlin/codes/side/colorpicker/sample/SampleApp.kt @@ -1,10 +1,10 @@ package codes.side.colorpicker.sample +import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background -import androidx.compose.foundation.interaction.collectIsPressedAsState -import androidx.compose.foundation.interaction.collectIsDraggedAsState import androidx.compose.foundation.interaction.InteractionSource -import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.interaction.collectIsDraggedAsState +import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -266,7 +266,7 @@ private fun SquareThumb(color: Color, interaction: InteractionSource) { .shadow(elevation, shape) .background(ring, shape) .padding(5.dp) - .background(fill, RoundedCornerShape(11.dp)) + .background(fill, RoundedCornerShape(11.dp)), ) } diff --git a/screenshot-tests/src/screenshotTest/kotlin/codes/side/colorpicker/screenshot/ColorPickerPreviews.kt b/screenshot-tests/src/screenshotTest/kotlin/codes/side/colorpicker/screenshot/ColorPickerPreviews.kt index 90838501..5be57ae8 100644 --- a/screenshot-tests/src/screenshotTest/kotlin/codes/side/colorpicker/screenshot/ColorPickerPreviews.kt +++ b/screenshot-tests/src/screenshotTest/kotlin/codes/side/colorpicker/screenshot/ColorPickerPreviews.kt @@ -1,10 +1,10 @@ package codes.side.colorpicker.screenshot +import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background -import androidx.compose.foundation.interaction.collectIsPressedAsState -import androidx.compose.foundation.interaction.collectIsDraggedAsState import androidx.compose.foundation.interaction.InteractionSource -import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.interaction.collectIsDraggedAsState +import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -64,7 +64,9 @@ private fun Frame(content: @Composable () -> Unit) { MaterialTheme(colorScheme = darkColorScheme()) { Surface(modifier = Modifier.fillMaxSize()) { Column( - modifier = Modifier.fillMaxSize().padding(16.dp), + modifier = Modifier + .fillMaxSize() + .padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically), ) { content() } } @@ -152,7 +154,7 @@ private fun SquareThumb(color: Color, interaction: InteractionSource) { .shadow(elevation, shape) .background(ring, shape) .padding(5.dp) - .background(fill, RoundedCornerShape(11.dp)) + .background(fill, RoundedCornerShape(11.dp)), ) } @@ -182,6 +184,8 @@ fun CustomThumbPreview() = Frame { fun SwatchPreview() = Frame { ColorSwatch( color = Seed.copy(alpha = 0.55f).toComposeColor(), - modifier = Modifier.fillMaxWidth().height(80.dp), + modifier = Modifier + .fillMaxWidth() + .height(80.dp), ) }