Skip to content

Commit

Permalink
[desktop] handle three digit hex color codes
Browse files Browse the repository at this point in the history
  • Loading branch information
johnbotris committed Jul 21, 2021
1 parent d7b7940 commit 53481e7
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 49 deletions.
6 changes: 6 additions & 0 deletions src/gui/animation/Animations.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ function buildTransformString(values: TransformValues, percent: number, easing:
}


/**
* We use the alpha channel instead of using opacity for fading colors. Opacity changes are slow on mobile devices as they
* effect the whole tree of the dom element with changing opacity.
*
* See http://stackoverflow.com/a/14677373 for a more detailed explanation.
*/
export function alpha(type: AlphaEnum, colorHex: string, begin: number, end: number): DomMutation {
let color = hexToRgb(colorHex)

Expand Down
67 changes: 23 additions & 44 deletions src/gui/base/Color.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,42 @@
//@flow

import {assertMainOrNodeBoot} from "../../api/common/Env"
import {ProgrammingError} from "../../api/common/error/ProgrammingError"
import {assert} from "../../api/common/utils/Utils"

assertMainOrNodeBoot()

// 3 or 6 digit hex color codes
export const VALID_HEX_CODE_FORMAT: RegExp = new RegExp("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")

export function isColorLight(c: string): boolean {
if (c[0] !== "#" || c.length !== 7) {
throw new ProgrammingError("Invalid color format: " + c)
}
const rgb = parseInt(c.slice(1), 16); // convert rrggbb to decimal
const r = (rgb >> 16) & 0xff // extract red
const g = (rgb >> 8) & 0xff // extract green
const b = (rgb >> 0) & 0xff // extract blue

const {r, g, b} = hexToRgb(c)

// Counting the perceptive luminance
// human eye favors green color...
const a = 1 - (0.299 * r + 0.587 * g + 0.114 * b) / 255
return a < 0.5
}

/**
* We use the alpha channel instead of using opacity for fading colors. Opacity changes are slow on mobile devices as they
* effect the whole tree of the dom element with changing opacity.
*
* See http://stackoverflow.com/a/14677373 for a more detailed explanation.
*/
export function hexToRgb(hexColor: string): {r: number, g: number, b: number} {
if (!hexColor.startsWith("#")) {
throw new Error("Illegal color definition: " + hexColor)
}
const withoutHash = hexColor.substring(1)
let components
if (withoutHash.length === 6) {
components = {
r: withoutHash.slice(0, 2),
g: withoutHash.slice(2, 4),
b: withoutHash.slice(4, 6),
}
} else if (withoutHash.length === 3) {
components = {
r: withoutHash[0] + withoutHash[0],
g: withoutHash[1] + withoutHash[1],
b: withoutHash[2] + withoutHash[2],
}
} else {
throw new Error("Illegal color definition: " + hexColor)
}
const rgb = {
r: parseInt(components.r, 16),
g: parseInt(components.g, 16),
b: parseInt(components.b, 16),
}
if (isNaN(rgb.r) || isNaN(rgb.g) || isNaN(rgb.b)) {
throw new Error("Illegal color definition: " + hexColor)
export function hexToRgb(colorCode: string): {r: number, g: number, b: number} {

assert(VALID_HEX_CODE_FORMAT.test(colorCode), "Invalid color code: " + colorCode)

let hexWithoutHash = colorCode.slice(1)
if (hexWithoutHash.length === 3) {
hexWithoutHash = Array.from(hexWithoutHash).reduce((acc, cur) => `${acc}${cur}${cur}`, "") // convert from 3 to 6 digits by duplicating each digit
}
return rgb

const rgb = parseInt(hexWithoutHash, 16); // convert rrggbb to decimal

const r = (rgb >> 16) & 0xff // extract red
const g = (rgb >> 8) & 0xff // extract green
const b = (rgb >> 0) & 0xff // extract blue


return {r, g, b}
}

export function rgbToHex(color: {r: number, g: number, b: number}): string {
return "#" + ((1 << 24) + (color.r << 16) + (color.g << 8) + color.b).toString(16).slice(1);
}
}
8 changes: 3 additions & 5 deletions src/settings/EditCustomColorsDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ import type {TextFieldAttrs} from "../gui/base/TextFieldN"
import {TextFieldN} from "../gui/base/TextFieldN"
import stream from "mithril/stream/stream.js"
import {downcast} from "../api/common/utils/Utils"
import {VALID_HEX_CODE_FORMAT} from "../gui/base/Color"

assertMainOrNode()

// 3 or 6 digit hex color codes
const COLOR_FORMAT = new RegExp("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")

export function show(themeToEdit: Theme, onThemeChanged: (Theme) => mixed) {
const colorFieldsAttrs = Object.keys(themeController.getDefaultTheme())
.filter(name => name !== "logo" && name !== "themeId")
Expand Down Expand Up @@ -75,7 +73,7 @@ export function show(themeToEdit: Theme, onThemeChanged: (Theme) => mixed) {
for (let i = 0; i < colorFieldsAttrs.length; i++) {
let colorValue = colorFieldsAttrs[i].value().trim()
if (colorValue) {
if (COLOR_FORMAT.test(colorValue)) {
if (VALID_HEX_CODE_FORMAT.test(colorValue)) {
newTheme[(colorFieldsAttrs[i]: any).label()] = colorValue
} else {
Dialog.error("correctValues_msg")
Expand Down Expand Up @@ -104,7 +102,7 @@ export function show(themeToEdit: Theme, onThemeChanged: (Theme) => mixed) {

function getValidColorValue(colorValue: string): ?string {
const trimmedColorValue = colorValue.trim()
if (trimmedColorValue && COLOR_FORMAT.test(trimmedColorValue)) {
if (trimmedColorValue && VALID_HEX_CODE_FORMAT.test(trimmedColorValue)) {
return trimmedColorValue
} else {
return null
Expand Down
13 changes: 13 additions & 0 deletions test/client/gui/ColorTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,18 @@ o.spec("color", function () {
o("blue is light", function () {
o(isColorLight("#3A9AFF")).equals(true)
})

o("three digit white is light", function () {
o(isColorLight("#FFF")).equals(true)

})

o("three digit black is dark", function () {
o(isColorLight("#000")).equals(false)
})

o("three digit cyan is light", function () {
o(isColorLight("#0FF")).equals(true)
})
})
})

0 comments on commit 53481e7

Please sign in to comment.