Skip to content

Commit

Permalink
Add color picker
Browse files Browse the repository at this point in the history
  • Loading branch information
kognise committed Oct 20, 2022
1 parent b6bebe6 commit f6af679
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 59 deletions.
42 changes: 37 additions & 5 deletions codemirror/editors.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class OpenButtonWidget extends WidgetType {
other.editorType === this.editorType &&
other.text === this.text;
}
ignoreEvent() { return false; }
ignoreEvent() { return true; }

toDOM() {
const container = document.createElement("span");
Expand Down Expand Up @@ -92,6 +92,30 @@ export class OpenButtonWidget extends WidgetType {
}
}

export class SwatchWidget extends WidgetType {
constructor(rgba) {
super();
this.rgba = rgba;
}

eq(other) {
return other.rgba.every((value, index) => value === this.rgba[index]);
}
ignoreEvent() { return false; }

toDOM() {
const container = document.createElement("span");
container.classList.add("cm-swatch");
container.style.backgroundColor = `rgba(${this.rgba.join(", ")})`;
return container;
}

updateDOM(container) {
container.style.backgroundColor = `rgba(${this.rgba.join(", ")})`;
return true;
}
}

function makeValue(state) {
const widgets = [];
const foldRanges = [];
Expand All @@ -104,11 +128,19 @@ function makeValue(state) {
if (!tag) continue;
if (tag.nameFrom === tag.nameTo) continue;

const decoration = Decoration.replace({
widgets.push(Decoration.replace({
widget: new OpenButtonWidget(label, icon, editorType, tag.text)
});
widgets.push(decoration.range(tag.nameFrom, tag.nameTo));
if (tag.textFrom !== tag.textTo) foldRanges.push({ from: tag.textFrom, to: tag.textTo });
}).range(tag.nameFrom, tag.nameTo));

if (label === "color") {
const color = global_state.palette.find(([key]) => key === tag.text);
if (color) {
widgets.push(Decoration.widget({ widget: new SwatchWidget(color[1]), side: 1 }).range(tag.textFrom));
}
} else if (tag.textFrom !== tag.textTo) {
foldRanges.push({ from: tag.textFrom, to: tag.textTo });
}

break;
}
}
Expand Down
59 changes: 59 additions & 0 deletions color-picker/color-picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { render, html } from "uhtml";
import { dispatch } from "../dispatch.js";
import { global_state } from "../global_state.js";
import { style } from "./style.js";
import { RGBA_to_hex, transparentBg } from "../palette.js";

export function createColorPicker(target) {
const state = { color: '' };

const r = () => render(target, html`
<style>${style}</style>
<div class="color-picker-outer">
<h3>color picker:</h3>
<div class="color-picker-inner">
${global_state.palette.map(drawColorButton)}
</div>
</div>
`);

const drawColorButton = (color) => {
let style = `background-color: ${RGBA_to_hex(color[1])};`;
color[1][3] === 0 ? style += `background-image: url("${transparentBg}");` : ``

return html`
<div
class=${state.color === color[0] ? "active" : ""}
style=${style}
@click=${() => {
state.color = color[0];
dispatch("SET_EDITOR_TEXT", { text: state.color, range: global_state.editRange });
r();
}}>
</div>
`
};

r();
return {
loadInitValue({ text }) {
if (global_state.palette.find(([key]) => key === text)) state.color = text;
r();
}
};
}

class ColorPicker extends HTMLElement {
constructor() {
super();
}

connectedCallback() {
const shadow = this.attachShadow({ mode: "open" });
const methods = createColorPicker(shadow);
for (const i in methods) this[i] = methods[i];
}
}

customElements.define("color-picker", ColorPicker);
37 changes: 37 additions & 0 deletions color-picker/style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export const style = `
.color-picker-outer {
background: var(--bg-floating);
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 16px;
}
h3 {
color: #ffffff;
text-align: center;
margin: 0;
}
.color-picker-inner {
width: 380px;
height: 300px;
background: transparent;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
padding: 10px;
}
.color-picker-inner div {
border: 2px solid transparent;
border-radius: 4px;
cursor: pointer;
}
.color-picker-inner div.active {
border: 2px solid var(--switch-fg-high);
}
`;
1 change: 1 addition & 0 deletions engine/baseEngine.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ export function baseEngine() {
setPushables,
map: _makeTag(text => text),
bitmap: _makeTag(text => text),
color: _makeTag(text => text),
tune: _makeTag(text => text),
getFirst: (type) => state.sprites.find(t => t.type === type), // **
getAll: (type) => type ? state.sprites.filter(t => t.type === type) : state.sprites, // **
Expand Down
9 changes: 9 additions & 0 deletions index.css
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,15 @@ body {
display: none;
}

.cm-swatch {
display: inline-block;
width: 0.8em;
height: 0.8em;
line-height: 1;
margin-right: 4px;
border: 0.5px solid black;
}

.cm-boolean-toggle, .cm-open-button {
line-height: 1;
vertical-align: middle;
Expand Down
1 change: 0 additions & 1 deletion map-editor/style.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

export const style = `
:root { box-sizing: border-box; }
* { box-sizing: inherit; }
Expand Down
37 changes: 22 additions & 15 deletions palette.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
// export const palette = [
// ["0", [0, 0, 0, 255]],
// ["1", [157, 157, 157, 255]],
// ["L", [91, 101, 112, 255]],
// ["2", [255, 255, 255, 255]],
// ["3", [190, 38, 51, 255]],
// ["4", [163, 206, 39, 255]],
// ["5", [0, 87, 132, 255]],
// ["6", [247, 226, 107, 255]],
// ["7", [49, 162, 242, 255]],
// ["8", [224, 111, 139, 255]],
// [".", [0, 0, 0, 0]]
// ].map(([k, v]) => [k, v.map(Math.round)]);

export const palette = [
// Grey
["0", [0, 0, 0, 255]],
Expand Down Expand Up @@ -44,4 +30,25 @@ export const palette = [

// Transparent
[".", [0, 0, 0, 0]]
].map(([k, v]) => [k, v.map(Math.round)]);
].map(([k, v]) => [k, v.map(Math.round)]);

export const transparentBg = `data:image/svg+xml,%0A%3Csvg width='23' height='23' viewBox='0 0 8 8' fill='none' xmlns='http:https://www.w3.org/2000/svg'%3E%3Crect width='8' height='8' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0H4V4H0V0ZM4 4H8V8H4V4Z' fill='%23DCEFFC'/%3E%3C/svg%3E%0A`;

export const hexToRGBA = (hex) => {
let [r, g, b, a = 255] = hex.match(/\w\w/g).map((x) => parseInt(x, 16));
return [r, g, b, a];
};

export function RGBA_to_hex([r, g, b, a]) {
r = r.toString(16);
g = g.toString(16);
b = b.toString(16);
a = a.toString(16);

if (r.length == 1) r = "0" + r;
if (g.length == 1) g = "0" + g;
if (b.length == 1) b = "0" + b;
if (a.length == 1) a = "0" + a;

return "#" + r + g + b + a;
}
50 changes: 13 additions & 37 deletions pixel-editor/pixel-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,7 @@ import { dispatch } from "../dispatch.js";
import { bitmapTextToImageData } from "../engine/bitmap.js";
import { global_state } from "../global_state.js";
import { style } from "./style.js";

const hexToRGBA = (hex) => {
let [r, g, b, a = 255] = hex.match(/\w\w/g).map((x) => parseInt(x, 16));
return [r, g, b, a];
};

function RGBA_to_hex([r, g, b, a]) {
r = r.toString(16);
g = g.toString(16);
b = b.toString(16);
a = a.toString(16);

if (r.length == 1) r = "0" + r;
if (g.length == 1) g = "0" + g;
if (b.length == 1) b = "0" + b;
if (a.length == 1) a = "0" + a;

return "#" + r + g + b + a;
}
import { hexToRGBA, RGBA_to_hex, transparentBg } from "../palette.js";

// Horrible way of doing this but it works.
//
Expand Down Expand Up @@ -274,29 +256,23 @@ export function createPixelEditor(target) {
</button>
</div>
${drawColorsButtons(state)}
<div class="colors">
${state.palette.map(drawColorButton)}
</div>
</div>
</div>
`;

const drawColorsButtons = (state) => {
const drawColor = (color) => {
const isTransparent = color[1][3] === 0;

let style = `background-color: ${RGBA_to_hex(color[1])};`;
isTransparent ? style += ` background-image: url("data:image/svg+xml,%0A%3Csvg width='23' height='23' viewBox='0 0 8 8' fill='none' xmlns='http:https://www.w3.org/2000/svg'%3E%3Crect width='8' height='8' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0 0H4V4H0V0ZM4 4H8V8H4V4Z' fill='%23DCEFFC'/%3E%3C/svg%3E%0A");` : ``

return html`
<div
class=${RGBA_to_hex(sharedState.color) === RGBA_to_hex(color[1]) ? "active" : ""}
style=${style}
@click=${() => { sharedState.color = color[1]; r(); }}>
</div>
`
}
const drawColorButton = (color) => {
let style = `background-color: ${RGBA_to_hex(color[1])};`;
color[1][3] === 0 ? style += `background-image: url("${transparentBg}");` : ``

return html`
<div class="colors">${state.palette.map(drawColor)}</div>
<div
class=${RGBA_to_hex(sharedState.color) === RGBA_to_hex(color[1]) ? "active" : ""}
style=${style}
@click=${() => { sharedState.color = color[1]; r(); }}>
</div>
`
};

Expand Down Expand Up @@ -756,7 +732,7 @@ export function createPixelEditor(target) {

return {
loadInitValue({ text }) {
const rows = text.trim().split("/n");
const rows = text.trim().split("\n");

while (rows.length < 16) rows.push("................");

Expand Down
2 changes: 1 addition & 1 deletion pixel-editor/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const style = `
.colors div {
height: 40px;
width: 77px;
/*border: 2px solid var(--switch-fg-low);*/
border: 2px solid transparent;
border-radius: 4px;
cursor: pointer;
}
Expand Down
2 changes: 2 additions & 0 deletions view.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { docs } from "./views/docs.js";
import "./pixel-editor/pixel-editor.js";
import "./sequencer/sequencer.js";
import "./map-editor/map-editor.js";
import "./color-picker/color-picker.js";
import "./views/bitmap-preview.js";
import { mute } from "./engine/playTune.js";

Expand Down Expand Up @@ -55,6 +56,7 @@ export const view = (state) => html`
"bitmap": html`<pixel-editor id="asset-editor"></pixel-editor>`,
"sequencer": html`<sequencer-editor id="asset-editor"></sequencer-editor>`,
"map": html`<map-editor id="asset-editor"></map-editor>`,
"palette": html`<color-picker id="asset-editor"></color-picker>`,
[undefined]: ""
}[state.editor]
}
Expand Down

0 comments on commit f6af679

Please sign in to comment.