WIP: implement Edit Colors dialog
Fixes https://github.com/1j01/jspaint/issues/114 and makes opening the color dialog work with Eye Gaze Mode dwell clicks and Speech Recognition as well (which can't trigger a "user gesture" as far as the browser's security model is concerned).main
parent
4ef30d7eb0
commit
d62d244777
|
@ -142,7 +142,7 @@ context('tool tests', () => {
|
|||
// it('brush tool', () => {
|
||||
// cy.get(".tool[title='Brush']").click();
|
||||
// // gesture([{x: 50, y: 50}, {x: 100, y: 100}]);
|
||||
// cy.get(":nth-child(21) > input").rightclick();
|
||||
// cy.get(".swatch:nth-child(21)").rightclick();
|
||||
// cy.window().then({timeout: 8000}, async (win)=> {
|
||||
// for (let secondary=0; secondary<=1; secondary++) {
|
||||
// for (let b=0; b<12; b++) {
|
||||
|
@ -195,7 +195,7 @@ context('tool tests', () => {
|
|||
it(`${toolName.toLowerCase()} tool`, () => {
|
||||
cy.get(`.tool[title='${toolName}']`).click();
|
||||
// gesture([{x: 50, y: 50}, {x: 100, y: 100}]);
|
||||
cy.get(":nth-child(22) > input").rightclick();
|
||||
cy.get(".swatch:nth-child(22)").rightclick();
|
||||
cy.window().then({timeout: 60000}, async (win)=> {
|
||||
for (let row=0; row<4; row++) {
|
||||
const secondary = !!(row % 2);
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
</ol>
|
||||
<p><b>Notes</b></p>
|
||||
<ul>
|
||||
<!-- @TODO: maybe emulate the color picker dialog and make this note unnecessary: -->
|
||||
<li>
|
||||
These instructions are very operating system specific and you may get a completely different color picker.
|
||||
On some systems the color picker may be utterly useless, and give you less color options than the default palette.
|
||||
</li>
|
||||
<li>You can also double click on a color in the color box to edit.</li>
|
||||
</ul>
|
||||
</body>
|
||||
|
|
121
src/$ColorBox.js
121
src/$ColorBox.js
|
@ -1,21 +1,23 @@
|
|||
|
||||
|
||||
/** Used by the Colors Box and by the Edit Colors dialog */
|
||||
function $Swatch(color){
|
||||
const $b = $(E("div")).addClass("swatch");
|
||||
const $swatch = $(E("div")).addClass("swatch");
|
||||
const swatch_canvas = make_canvas();
|
||||
$(swatch_canvas).css({pointerEvents: "none"}).appendTo($b);
|
||||
$(swatch_canvas).css({pointerEvents: "none"}).appendTo($swatch);
|
||||
|
||||
$b.update = (set_color_to = color) => {
|
||||
$swatch.update = (set_color_to = color) => {
|
||||
color = set_color_to;
|
||||
if(color instanceof CanvasPattern){
|
||||
$b.addClass("pattern");
|
||||
$swatch.addClass("pattern");
|
||||
$swatch[0].dataset.color = "";
|
||||
}else{
|
||||
$b.removeClass("pattern");
|
||||
$swatch.removeClass("pattern");
|
||||
$swatch[0].dataset.color = color;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
swatch_canvas.width = $b.innerWidth();
|
||||
swatch_canvas.height = $b.innerHeight();
|
||||
swatch_canvas.width = $swatch.innerWidth();
|
||||
swatch_canvas.height = $swatch.innerHeight();
|
||||
// I don't think disable_image_smoothing() is needed here
|
||||
|
||||
if(color){
|
||||
|
@ -25,11 +27,11 @@ function $Swatch(color){
|
|||
});
|
||||
};
|
||||
$G.on("theme-load", () => {
|
||||
$b.update();
|
||||
$swatch.update();
|
||||
});
|
||||
$b.update();
|
||||
$swatch.update();
|
||||
|
||||
return $b;
|
||||
return $swatch;
|
||||
}
|
||||
|
||||
function $ColorBox(vertical){
|
||||
|
@ -57,73 +59,45 @@ function $ColorBox(vertical){
|
|||
$G.triggerHandler("option-changed");
|
||||
});
|
||||
|
||||
// the one color editted by "Edit Colors..."
|
||||
let $last_fg_color_button;
|
||||
|
||||
function set_color(col){
|
||||
if(ctrl){
|
||||
colors.ternary = col;
|
||||
}else if(button === 0){
|
||||
colors.foreground = col;
|
||||
}else if(button === 2){
|
||||
colors.background = col;
|
||||
}
|
||||
$G.trigger("option-changed");
|
||||
}
|
||||
function color_to_hex(col){
|
||||
if(!col.match){ // i.e. CanvasPattern
|
||||
return "#000000";
|
||||
}
|
||||
const rgb_match = col.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
||||
const rgb = rgb_match ? rgb_match.slice(1) : get_rgba_from_color(col).slice(0, 3);
|
||||
function hex(x){
|
||||
return (`0${parseInt(x).toString(16)}`).slice(-2);
|
||||
}
|
||||
return rgb ? (`#${hex(rgb[0])}${hex(rgb[1])}${hex(rgb[2])}`) : col;
|
||||
}
|
||||
|
||||
const make_color_button = (color) => {
|
||||
|
||||
const $b = $Swatch(color).addClass("color-button");
|
||||
$b.appendTo($palette);
|
||||
|
||||
const $i = $(E("input")).attr({type: "color"});
|
||||
$i.appendTo($b);
|
||||
$i.on("change", () => {
|
||||
color = $i.val();
|
||||
$b.update(color);
|
||||
set_color(color);
|
||||
$b.data("set_color", (new_color)=> {
|
||||
$b.update(new_color);
|
||||
});
|
||||
|
||||
$i.css("opacity", 0);
|
||||
$i.prop("enabled", false);
|
||||
|
||||
$i.val(color_to_hex(color));
|
||||
|
||||
const double_click_period_ms = 400;
|
||||
let within_double_click_period = false;
|
||||
let double_click_button = null;
|
||||
let double_click_tid;
|
||||
// @TODO: handle left+right click at same time
|
||||
// can do this with mousedown instead of pointerdown, but may need to improve eye gaze mode click simulation
|
||||
$b.on("pointerdown", e => {
|
||||
// @TODO: how should the ternary color, and selection cropping, work on macOS?
|
||||
// @TODO: allow metaKey for ternary color, and selection cropping, on macOS?
|
||||
ctrl = e.ctrlKey;
|
||||
button = e.button;
|
||||
if(button === 0){
|
||||
$last_fg_color_button = $b;
|
||||
$c.data("$last_fg_color_button", $b);
|
||||
}
|
||||
|
||||
set_color(color);
|
||||
|
||||
$i.val(color_to_hex(color));
|
||||
|
||||
if(e.button === button && $i.prop("enabled")){
|
||||
$i.trigger("click", "synthetic");
|
||||
}
|
||||
|
||||
$i.prop("enabled", true);
|
||||
setTimeout(() => {
|
||||
$i.prop("enabled", false);
|
||||
}, 400);
|
||||
});
|
||||
$i.on("click", (e, synthetic) => {
|
||||
if(!synthetic){
|
||||
e.preventDefault();
|
||||
const color_selection_slot = ctrl ? "ternary" : button === 0 ? "foreground" : button === 2 ? "background" : null;
|
||||
if (color_selection_slot) {
|
||||
if (within_double_click_period && button === double_click_button) {
|
||||
show_edit_colors_window($b, color_selection_slot);
|
||||
} else {
|
||||
colors[color_selection_slot] = color;
|
||||
$G.trigger("option-changed");
|
||||
}
|
||||
|
||||
clearTimeout(double_click_tid);
|
||||
double_click_tid = setTimeout(() => {
|
||||
within_double_click_period = false;
|
||||
double_click_button = null;
|
||||
}, double_click_period_ms);
|
||||
within_double_click_period = true;
|
||||
double_click_button = button;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -150,11 +124,8 @@ function $ColorBox(vertical){
|
|||
}
|
||||
|
||||
// the "last foreground color button" starts out as the first in the palette
|
||||
$last_fg_color_button = $palette.find(".color-button");
|
||||
$c.data("$last_fg_color_button", $palette.find(".color-button:first-child"));
|
||||
};
|
||||
build_palette();
|
||||
$(window).on("theme-change", build_palette);
|
||||
|
||||
let $c;
|
||||
if (vertical) {
|
||||
$c = $Component("Colors", "tall", $cb);
|
||||
|
@ -164,16 +135,8 @@ function $ColorBox(vertical){
|
|||
$c.appendTo($bottom);
|
||||
}
|
||||
|
||||
$c.edit_last_color = () => {
|
||||
// Edit the last color cell that's been selected as the foreground color.
|
||||
create_and_trigger_input({type: "color"}, input => {
|
||||
// window.console && console.log(input, input.value);
|
||||
// @FIXME
|
||||
$last_fg_color_button.trigger({type: "pointerdown", ctrlKey: false, button: 0});
|
||||
$last_fg_color_button.find("input").val(input.value).triggerHandler("change");
|
||||
})
|
||||
.show().css({width: 0, height: 0, padding: 0, border: 0, position: "absolute", pointerEvents: "none", overflow: "hidden"});
|
||||
};
|
||||
build_palette();
|
||||
$(window).on("theme-change", build_palette);
|
||||
|
||||
$c.rebuild_palette = build_palette;
|
||||
|
||||
|
|
24
src/app.js
24
src/app.js
|
@ -26,6 +26,30 @@ let palette = default_palette;
|
|||
let polychrome_palette = palette;
|
||||
let monochrome_palette = make_monochrome_palette();
|
||||
|
||||
// https://github.com/kouzhudong/win2k/blob/ce6323f76d5cd7d136b74427dad8f94ee4c389d2/trunk/private/shell/win16/comdlg/color.c#L38-L43
|
||||
// These are a fallback in case colors are not recieved from some driver.
|
||||
// const default_basic_colors = [
|
||||
// "#8080FF", "#80FFFF", "#80FF80", "#80FF00", "#FFFF80", "#FF8000", "#C080FF", "#FF80FF",
|
||||
// "#0000FF", "#00FFFF", "#00FF80", "#40FF00", "#FFFF00", "#C08000", "#C08080", "#FF00FF",
|
||||
// "#404080", "#4080FF", "#00FF00", "#808000", "#804000", "#FF8080", "#400080", "#8000FF",
|
||||
// "#000080", "#0080FF", "#008000", "#408000", "#FF0000", "#A00000", "#800080", "#FF0080",
|
||||
// "#000040", "#004080", "#004000", "#404000", "#800000", "#400000", "#400040", "#800040",
|
||||
// "#000000", "#008080", "#408080", "#808080", "#808040", "#C0C0C0", "#400040", "#FFFFFF",
|
||||
// ];
|
||||
// Grabbed with Color Cop from the screen with Windows 98 SE running in VMWare
|
||||
const basic_colors = [
|
||||
"#FF8080", "#FFFF80", "#80FF80", "#00FF80", "#80FFFF", "#0080FF", "#FF80C0", "#FF80FF",
|
||||
"#FF0000", "#FFFF00", "#80FF00", "#00FF40", "#00FFFF", "#0080C0", "#8080C0", "#FF00FF",
|
||||
"#804040", "#FF8040", "#00FF00", "#008080", "#004080", "#8080FF", "#800040", "#FF0080",
|
||||
"#800000", "#FF8000", "#008000", "#008040", "#0000FF", "#0000A0", "#800080", "#8000FF",
|
||||
"#400000", "#804000", "#004000", "#004040", "#000080", "#000040", "#400040", "#400080",
|
||||
"#000000", "#808000", "#808040", "#808080", "#408080", "#C0C0C0", "#400040", "#FFFFFF",
|
||||
];
|
||||
let custom_colors = [
|
||||
"#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF",
|
||||
"#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF",
|
||||
];
|
||||
|
||||
// declared like this for Cypress tests
|
||||
window.default_brush_shape = "circle";
|
||||
window.default_brush_size = 4;
|
||||
|
|
191
src/functions.js
191
src/functions.js
|
@ -332,6 +332,197 @@ function show_custom_zoom_window() {
|
|||
$w.center();
|
||||
}
|
||||
|
||||
let $edit_colors_window;
|
||||
// @TODO: add custom colors to the list
|
||||
// @TODO: initially select the first color cell that matches the swatch to edit, if any
|
||||
// @TODO: initialize custom colors list index to matched cell, if matched
|
||||
// @TODO: persist custom colors list
|
||||
// @TODO: more keyboard navigation
|
||||
// @TODO: OK with Enter, after selecting a focused color if applicable
|
||||
// @TODO: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Grid_Role
|
||||
// or https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role
|
||||
// @TODO: question mark button in titlebar that lets you click on parts of UI to ask about them; also context menu "What's this?"
|
||||
// $(()=> { show_edit_colors_window(); $(".expando-button").click(); $edit_colors_window.center(); }); // for development
|
||||
|
||||
function show_edit_colors_window($swatch_to_edit, color_selection_slot_to_edit) {
|
||||
// console.log($swatch_to_edit, $colorbox.data("$last_fg_color_button"));
|
||||
$swatch_to_edit = $swatch_to_edit || $colorbox.data("$last_fg_color_button");
|
||||
color_selection_slot_to_edit = color_selection_slot_to_edit || "foreground";
|
||||
|
||||
if ($edit_colors_window) {
|
||||
$edit_colors_window.close();
|
||||
}
|
||||
const $w = new $FormToolWindow("Edit Colors");
|
||||
$w.addClass("edit-colors-window");
|
||||
$edit_colors_window = $w;
|
||||
|
||||
let hue_degrees = 0;
|
||||
let sat_percent = 50;
|
||||
let lum_percent = 50;
|
||||
|
||||
const make_color_grid = (colors, name)=> {
|
||||
const $color_grid = $(`<div class="color-grid">`).attr({name});
|
||||
for (const color of colors) {
|
||||
const $swatch = $Swatch(color);
|
||||
$swatch.appendTo($color_grid).addClass("inset-deep");
|
||||
$swatch.attr("tabindex", 0);
|
||||
}
|
||||
const num_colors_per_row = 8;
|
||||
const navigate = (relative_index)=> {
|
||||
const $focused = $color_grid.find(".swatch:focus");
|
||||
if (!$focused.length) { return; }
|
||||
const $swatches = $color_grid.find(".swatch");
|
||||
const from_index = $swatches.toArray().indexOf($focused[0]);
|
||||
if (relative_index === -1 && (from_index % num_colors_per_row) === 0) { return; }
|
||||
if (relative_index === +1 && (from_index % num_colors_per_row) === num_colors_per_row - 1) { return; }
|
||||
const to_index = from_index + relative_index;
|
||||
const $to_focus = $($swatches.toArray()[to_index]);
|
||||
// console.log({from_index, to_index, $focused, $to_focus});
|
||||
if (!$to_focus.length) { return; }
|
||||
$to_focus.focus();
|
||||
};
|
||||
const select = ($swatch)=> {
|
||||
$color_grid.find(".swatch").removeClass("selected");
|
||||
$swatch.addClass("selected");
|
||||
const [r, g, b] = get_rgba_from_color($swatch[0].dataset.color);
|
||||
const [h, s, l] = rgb_to_hsl(r, g, b);
|
||||
hue_degrees = h * 360;
|
||||
sat_percent = s * 100;
|
||||
lum_percent = l * 100;
|
||||
draw();
|
||||
};
|
||||
$color_grid.on("keydown", (event)=> {
|
||||
// console.log(event.code);
|
||||
if (event.code === "ArrowRight") { navigate(+1); }
|
||||
if (event.code === "ArrowLeft") { navigate(-1); }
|
||||
if (event.code === "ArrowDown") { navigate(+num_colors_per_row); }
|
||||
if (event.code === "ArrowUp") { navigate(-num_colors_per_row); }
|
||||
if (event.code === "Home") { $color_grid.find(".swatch:first-child").focus(); }
|
||||
if (event.code === "End") { $color_grid.find(".swatch:last-child").focus(); }
|
||||
if (event.code === "Space" || event.code === "Enter") {
|
||||
select($color_grid.find(".swatch:focus"));
|
||||
}
|
||||
});
|
||||
$color_grid.on("pointerdown", (event)=> {
|
||||
const $swatch = $(event.target).closest(".swatch");
|
||||
if ($swatch.length) {
|
||||
select($swatch);
|
||||
}
|
||||
});
|
||||
$color_grid.on("dragstart", (event)=> {
|
||||
event.preventDefault();
|
||||
});
|
||||
return $color_grid;
|
||||
};
|
||||
const $left_right_split = $(`<div class="left-right-split">`).appendTo($w.$main);
|
||||
const $left = $(`<div class="left-side">`).appendTo($left_right_split);
|
||||
const $right = $(`<div class="right-side">`).appendTo($left_right_split).hide();
|
||||
$left.append(`<label for="basic-colors">Basic colors:</label>`);
|
||||
make_color_grid(basic_colors, "basic-colors").appendTo($left);
|
||||
$left.append(`<label for="custom-colors">Custom colors:</label>`);
|
||||
make_color_grid(custom_colors, "custom-colors").appendTo($left);
|
||||
|
||||
const $expando_button = $(`<button class="expando-button">`)
|
||||
.text("Define Custom Colors >>")
|
||||
.appendTo($left)
|
||||
.on("click", ()=> {
|
||||
$right.show();
|
||||
$expando_button.attr("disabled", "disabled");
|
||||
})
|
||||
|
||||
const rainbow_canvas = make_canvas(175, 187);
|
||||
const luminosity_canvas = make_canvas(10, 187);
|
||||
const result_canvas = make_canvas(58, 40);
|
||||
const lum_arrow_canvas = make_canvas(5, 9);
|
||||
|
||||
const draw = ()=> {
|
||||
for (let y = 0; y < rainbow_canvas.height; y += 6) {
|
||||
for (let x = -1; x < rainbow_canvas.width; x += 3) {
|
||||
rainbow_canvas.ctx.fillStyle = `hsl(${x/rainbow_canvas.width*360}deg, ${(1-y/rainbow_canvas.height)*100}%, 50%)`;
|
||||
rainbow_canvas.ctx.fillRect(x, y, 3, 6);
|
||||
}
|
||||
}
|
||||
const x = ~~(hue_degrees/360*rainbow_canvas.width);
|
||||
const y = ~~((1-sat_percent/100)*rainbow_canvas.height);
|
||||
rainbow_canvas.ctx.fillStyle = "black";
|
||||
rainbow_canvas.ctx.fillRect(x-1, y-9, 3, 5);
|
||||
rainbow_canvas.ctx.fillRect(x-1, y+5, 3, 5);
|
||||
rainbow_canvas.ctx.fillRect(x-9, y-1, 5, 3);
|
||||
rainbow_canvas.ctx.fillRect(x+5, y-1, 5, 3);
|
||||
|
||||
for (let y = -2; y < luminosity_canvas.height; y += 6) {
|
||||
luminosity_canvas.ctx.fillStyle = `hsl(${hue_degrees}deg, ${sat_percent}%, ${(1-y/luminosity_canvas.height)*100}%)`;
|
||||
luminosity_canvas.ctx.fillRect(0, y, luminosity_canvas.width, 6);
|
||||
}
|
||||
|
||||
lum_arrow_canvas.ctx.fillStyle = "black";
|
||||
for (let x = 0; x < lum_arrow_canvas.width; x++) {
|
||||
lum_arrow_canvas.ctx.fillRect(x, lum_arrow_canvas.width-x-1, 1, 1+x*2);
|
||||
}
|
||||
lum_arrow_canvas.style.position = "absolute";
|
||||
lum_arrow_canvas.style.right = "7px";
|
||||
lum_arrow_canvas.style.top = `${3 + ~~((1-lum_percent/100)*luminosity_canvas.height)}px`;
|
||||
|
||||
result_canvas.ctx.fillStyle = `hsl(${hue_degrees}deg, ${sat_percent}%, ${lum_percent}%)`;
|
||||
result_canvas.ctx.fillRect(0, 0, result_canvas.width, result_canvas.height);
|
||||
};
|
||||
draw();
|
||||
$(rainbow_canvas).addClass("rainbow-canvas inset-shallow");
|
||||
$(luminosity_canvas).addClass("luminosity-canvas inset-shallow");
|
||||
$(result_canvas).addClass("result-color-canvas inset-shallow");
|
||||
|
||||
const select_hue_sat = (event)=> {
|
||||
hue_degrees = Math.min(1, Math.max(0, event.offsetX/rainbow_canvas.width))*360;
|
||||
sat_percent = Math.min(1, Math.max(0, (1 - event.offsetY/rainbow_canvas.height)))*100;
|
||||
draw();
|
||||
event.preventDefault();
|
||||
};
|
||||
$(rainbow_canvas).on("pointerdown", (event)=> {
|
||||
select_hue_sat(event);
|
||||
|
||||
$(rainbow_canvas).on("pointermove", select_hue_sat);
|
||||
rainbow_canvas.setPointerCapture(event.pointerId);
|
||||
});
|
||||
$G.on("pointerup pointercancel", (event)=> {
|
||||
$(rainbow_canvas).off("pointermove", select_hue_sat);
|
||||
// rainbow_canvas.releasePointerCapture(event.pointerId);
|
||||
});
|
||||
|
||||
const select_lum = (event)=> {
|
||||
lum_percent = Math.min(1, Math.max(0, (1 - event.offsetY/luminosity_canvas.height)))*100;
|
||||
draw();
|
||||
event.preventDefault();
|
||||
};
|
||||
$(luminosity_canvas).on("pointerdown", (event)=> {
|
||||
select_lum(event);
|
||||
|
||||
$(luminosity_canvas).on("pointermove", select_lum);
|
||||
luminosity_canvas.setPointerCapture(event.pointerId);
|
||||
});
|
||||
$G.on("pointerup pointercancel", (event)=> {
|
||||
$(luminosity_canvas).off("pointermove", select_lum);
|
||||
// luminosity_canvas.releasePointerCapture(event.pointerId);
|
||||
});
|
||||
|
||||
$right.append(rainbow_canvas, luminosity_canvas, result_canvas, lum_arrow_canvas);
|
||||
|
||||
$w.$Button("OK", () => {
|
||||
const color = `hsl(${hue_degrees}deg, ${sat_percent}%, ${lum_percent}%)`;
|
||||
// console.log($swatch_to_edit, $swatch_to_edit.data("set_color"));
|
||||
$swatch_to_edit.data("set_color")(color);
|
||||
colors[color_selection_slot_to_edit] = color;
|
||||
$G.triggerHandler("option-changed");
|
||||
$w.close();
|
||||
})[0].focus();
|
||||
$w.$Button("Cancel", () => {
|
||||
$w.close();
|
||||
});
|
||||
|
||||
$left.append($w.$buttons);
|
||||
|
||||
$w.center();
|
||||
}
|
||||
|
||||
function toggle_grid() {
|
||||
show_grid = !show_grid;
|
||||
// $G.trigger("option-changed");
|
||||
|
|
|
@ -221,3 +221,38 @@ function get_icon_for_tools(tools) {
|
|||
})
|
||||
return icon_canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an RGB color value to HSL. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes r, g, and b are contained in the set [0, 255] and
|
||||
* returns h, s, and l in the set [0, 1].
|
||||
*
|
||||
* @param Number r The red color value
|
||||
* @param Number g The green color value
|
||||
* @param Number b The blue color value
|
||||
* @return Array The HSL representation
|
||||
*/
|
||||
function rgb_to_hsl(r, g, b) {
|
||||
r /= 255, g /= 255, b /= 255;
|
||||
|
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
var h, s, l = (max + min) / 2;
|
||||
|
||||
if (max == min) {
|
||||
h = s = 0; // achromatic
|
||||
} else {
|
||||
var d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
return [h, s, l];
|
||||
}
|
||||
|
|
|
@ -612,7 +612,7 @@ window.menus = {
|
|||
"edit last color", "create new color", "choose new color", "create a new color", "pick a new color",
|
||||
],
|
||||
action: ()=> {
|
||||
$colorbox.edit_last_color();
|
||||
show_edit_colors_window();
|
||||
},
|
||||
description: "Creates a new color.",
|
||||
},
|
||||
|
|
|
@ -390,16 +390,99 @@ html, body, .jspaint {
|
|||
height: 15px;
|
||||
border: 0;
|
||||
}
|
||||
.color-button input {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.edit-colors-window .color-grid {
|
||||
width: 222px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 16px);
|
||||
grid-gap: 5px 9px;
|
||||
user-select: none;
|
||||
}
|
||||
.edit-colors-window .swatch {
|
||||
width: 16px;
|
||||
height: 13px;
|
||||
display: flex;
|
||||
}
|
||||
.edit-colors-window .window-content {
|
||||
font-family: Tahoma, sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
.edit-colors-window .swatch {
|
||||
outline: none; /* we'll provide a new focus indicator below */
|
||||
}
|
||||
.edit-colors-window .swatch.selected {
|
||||
outline: 1px solid black;
|
||||
outline-offset: 0px;
|
||||
}
|
||||
.edit-colors-window .swatch:focus::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
outline: 1px dotted black;
|
||||
outline-offset: 5px;
|
||||
}
|
||||
.edit-colors-window .window-content .left-right-split {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.edit-colors-window .window-content .left-side {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
width: 217px;
|
||||
height: 298px;
|
||||
}
|
||||
.edit-colors-window .window-content .right-side {
|
||||
flex-flow: column;
|
||||
width: 218px;
|
||||
position: relative;
|
||||
padding-top: 7px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.edit-colors-window .window-content .button-group {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.edit-colors-window .window-content .button-group button {
|
||||
min-width: 66px;
|
||||
margin: 3px;
|
||||
}
|
||||
.edit-colors-window .window-content .expando-button,
|
||||
.edit-colors-window .window-content .button-group button:first-of-type {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.edit-colors-window .window-content button {
|
||||
height: 23px;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.edit-colors-window .window-content .expando-button {
|
||||
margin-top: 13px;
|
||||
width: 210px;
|
||||
}
|
||||
.edit-colors-window .left-side label {
|
||||
display: block;
|
||||
margin-top: 7px;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.edit-colors-window .left-side label:nth-of-type(2) {
|
||||
margin-top: 18px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
.edit-colors-window .color-grid {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.edit-colors-window .luminosity-canvas {
|
||||
margin-left: 16px;
|
||||
}
|
||||
.edit-colors-window .result-color-canvas {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
|
||||
.canvas-area {
|
||||
flex: 1;
|
||||
|
|
|
@ -209,14 +209,19 @@ body {
|
|||
height: 18px;
|
||||
}
|
||||
.color-button, /* this outer one is only for detection for the hover halo in eye gaze mode */
|
||||
.edit-colors-window .swatch, /* this outer one is only for detection for the hover halo in eye gaze mode */
|
||||
.color-button canvas,
|
||||
.color-selection canvas,
|
||||
.edit-colors-window .swatch canvas,
|
||||
.color-button:after,
|
||||
.color-selection:after {
|
||||
.color-selection:after,
|
||||
.edit-colors-window .swatch:after {
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
}
|
||||
.color-button:after,
|
||||
.color-selection:after {
|
||||
.color-button::after,
|
||||
.color-selection::after,
|
||||
.edit-colors-window .swatch::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
@ -229,6 +234,14 @@ body {
|
|||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.edit-colors-window .swatch {
|
||||
width: 20px;
|
||||
height: 17px;
|
||||
}
|
||||
.edit-colors-window .swatch:focus::after {
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
/* @TODO: padding/margin on the top at least when in the sidebar */
|
||||
.tools {
|
||||
width: 50px;
|
||||
|
|
Loading…
Reference in New Issue