allow shortcut deletion via callback
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com> Former-commit-id: 0a46ffda9945ad95665634c21c9732e7d9d1497e
This commit is contained in:
parent
2fdcdc7f0c
commit
42144ea577
10 changed files with 458 additions and 267 deletions
|
@ -295,6 +295,7 @@ people, person, humans, human, divers, diver, glitch, error, text, watermark, ba
|
||||||
<!-- Load Tools -->
|
<!-- Load Tools -->
|
||||||
<script src="js/ui/tool/dream.js" type="text/javascript"></script>
|
<script src="js/ui/tool/dream.js" type="text/javascript"></script>
|
||||||
<script src="js/ui/tool/maskbrush.js" type="text/javascript"></script>
|
<script src="js/ui/tool/maskbrush.js" type="text/javascript"></script>
|
||||||
|
<script src="js/ui/tool/select.js" type="text/javascript"></script>
|
||||||
|
|
||||||
<script src="js/ui/toolbar.js" type="text/javascript"></script>
|
<script src="js/ui/toolbar.js" type="text/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
14
js/input.js
14
js/input.js
|
@ -201,6 +201,8 @@ window.onmouseup = (evn) => {
|
||||||
target: evn.target,
|
target: evn.target,
|
||||||
initialTarget: mouse.coords[name].dragging[key].target,
|
initialTarget: mouse.coords[name].dragging[key].target,
|
||||||
buttonId: evn.button,
|
buttonId: evn.button,
|
||||||
|
ix: mouse.coords[name].dragging[key].x,
|
||||||
|
iy: mouse.coords[name].dragging[key].y,
|
||||||
x: mouse.coords[name].pos.x,
|
x: mouse.coords[name].pos.x,
|
||||||
y: mouse.coords[name].pos.y,
|
y: mouse.coords[name].pos.y,
|
||||||
evn,
|
evn,
|
||||||
|
@ -213,6 +215,8 @@ window.onmouseup = (evn) => {
|
||||||
target: evn.target,
|
target: evn.target,
|
||||||
initialTarget: mouse.coords[name].dragging[key].target,
|
initialTarget: mouse.coords[name].dragging[key].target,
|
||||||
buttonId: evn.button,
|
buttonId: evn.button,
|
||||||
|
ix: mouse.coords[name].dragging[key].x,
|
||||||
|
iy: mouse.coords[name].dragging[key].y,
|
||||||
x: mouse.coords[name].pos.x,
|
x: mouse.coords[name].pos.x,
|
||||||
y: mouse.coords[name].pos.y,
|
y: mouse.coords[name].pos.y,
|
||||||
evn,
|
evn,
|
||||||
|
@ -264,6 +268,8 @@ window.onmousemove = (evn) => {
|
||||||
mouse.listen[name][key].ondragstart.emit({
|
mouse.listen[name][key].ondragstart.emit({
|
||||||
target: evn.target,
|
target: evn.target,
|
||||||
buttonId: evn.button,
|
buttonId: evn.button,
|
||||||
|
ix: mouse.coords[name].dragging[key].x,
|
||||||
|
iy: mouse.coords[name].dragging[key].y,
|
||||||
x: mouse.coords[name].pos.x,
|
x: mouse.coords[name].pos.x,
|
||||||
y: mouse.coords[name].pos.y,
|
y: mouse.coords[name].pos.y,
|
||||||
evn,
|
evn,
|
||||||
|
@ -283,6 +289,8 @@ window.onmousemove = (evn) => {
|
||||||
target: evn.target,
|
target: evn.target,
|
||||||
initialTarget: mouse.coords[name].dragging[key].target,
|
initialTarget: mouse.coords[name].dragging[key].target,
|
||||||
button: index,
|
button: index,
|
||||||
|
ix: mouse.coords[name].dragging[key].x,
|
||||||
|
iy: mouse.coords[name].dragging[key].y,
|
||||||
px: mouse.coords[name].prev.x,
|
px: mouse.coords[name].prev.x,
|
||||||
py: mouse.coords[name].prev.y,
|
py: mouse.coords[name].prev.y,
|
||||||
x: mouse.coords[name].pos.x,
|
x: mouse.coords[name].pos.x,
|
||||||
|
@ -297,6 +305,8 @@ window.onmousemove = (evn) => {
|
||||||
target: evn.target,
|
target: evn.target,
|
||||||
initialTarget: mouse.coords[name].dragging[key].target,
|
initialTarget: mouse.coords[name].dragging[key].target,
|
||||||
button: index,
|
button: index,
|
||||||
|
ix: mouse.coords[name].dragging[key].x,
|
||||||
|
iy: mouse.coords[name].dragging[key].y,
|
||||||
px: mouse.coords[name].prev.x,
|
px: mouse.coords[name].prev.x,
|
||||||
py: mouse.coords[name].prev.y,
|
py: mouse.coords[name].prev.y,
|
||||||
x: mouse.coords[name].pos.x,
|
x: mouse.coords[name].pos.x,
|
||||||
|
@ -384,7 +394,9 @@ const keyboard = {
|
||||||
},
|
},
|
||||||
deleteShortcut(id) {
|
deleteShortcut(id) {
|
||||||
this.shortcuts.keys().forEach((key) => {
|
this.shortcuts.keys().forEach((key) => {
|
||||||
this.shortcuts[key] = this.shortcuts[key].filter((v) => v.id !== id);
|
this.shortcuts[key] = this.shortcuts[key].filter(
|
||||||
|
(v) => v.id !== id && v.callback !== id
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,7 @@ function createSlider(name, wrapper, options = {}) {
|
||||||
textEl.value = `${name}: ${value}`;
|
textEl.value = `${name}: ${value}`;
|
||||||
});
|
});
|
||||||
textEl.addEventListener("focus", () => {
|
textEl.addEventListener("focus", () => {
|
||||||
|
overEl.style.pointerEvents = "none";
|
||||||
textEl.value = value;
|
textEl.value = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -125,7 +126,6 @@ function createSlider(name, wrapper, options = {}) {
|
||||||
|
|
||||||
mouse.listen.window.left.onclick.on((evn) => {
|
mouse.listen.window.left.onclick.on((evn) => {
|
||||||
if (evn.target === overEl) {
|
if (evn.target === overEl) {
|
||||||
overEl.style.pointerEvents = "none";
|
|
||||||
textEl.select();
|
textEl.select();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -212,3 +212,179 @@ const dream_img2img_callback = (evn, state) => {
|
||||||
dream(bb.x, bb.y, request, {method: "img2img", stopMarching, bb});
|
dream(bb.x, bb.y, request, {method: "img2img", stopMarching, bb});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers Tools
|
||||||
|
*/
|
||||||
|
const dreamTool = () =>
|
||||||
|
toolbar.registerTool(
|
||||||
|
"res/icons/image-plus.svg",
|
||||||
|
"Dream",
|
||||||
|
(state, opt) => {
|
||||||
|
// Draw new cursor immediately
|
||||||
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||||
|
state.mousemovecb({
|
||||||
|
...mouse.coords.canvas.pos,
|
||||||
|
target: {id: "overlayCanvas"},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start Listeners
|
||||||
|
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
||||||
|
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
||||||
|
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
||||||
|
},
|
||||||
|
(state, opt) => {
|
||||||
|
// Clear Listeners
|
||||||
|
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
||||||
|
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
||||||
|
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
init: (state) => {
|
||||||
|
state.snapToGrid = true;
|
||||||
|
state.overMaskPx = 0;
|
||||||
|
state.mousemovecb = (evn) => _reticle_draw(evn, state.snapToGrid);
|
||||||
|
state.dreamcb = (evn) => {
|
||||||
|
dream_generate_callback(evn, state);
|
||||||
|
};
|
||||||
|
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
||||||
|
},
|
||||||
|
populateContextMenu: (menu, state) => {
|
||||||
|
if (!state.ctxmenu) {
|
||||||
|
state.ctxmenu = {};
|
||||||
|
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||||
|
state,
|
||||||
|
"snapToGrid",
|
||||||
|
"Snap To Grid"
|
||||||
|
).label;
|
||||||
|
state.ctxmenu.overMaskPxLabel = _toolbar_input.slider(
|
||||||
|
state,
|
||||||
|
"overMaskPx",
|
||||||
|
"Overmask px",
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
1
|
||||||
|
).slider;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||||
|
menu.appendChild(document.createElement("br"));
|
||||||
|
menu.appendChild(state.ctxmenu.overMaskPxLabel);
|
||||||
|
},
|
||||||
|
shortcut: "D",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const img2imgTool = () =>
|
||||||
|
toolbar.registerTool(
|
||||||
|
"res/icons/image.svg",
|
||||||
|
"Img2Img",
|
||||||
|
(state, opt) => {
|
||||||
|
// Draw new cursor immediately
|
||||||
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||||
|
state.mousemovecb({
|
||||||
|
...mouse.coords.canvas.pos,
|
||||||
|
target: {id: "overlayCanvas"},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start Listeners
|
||||||
|
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
||||||
|
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
||||||
|
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
||||||
|
},
|
||||||
|
(state, opt) => {
|
||||||
|
// Clear Listeners
|
||||||
|
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
||||||
|
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
||||||
|
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
init: (state) => {
|
||||||
|
state.snapToGrid = true;
|
||||||
|
state.denoisingStrength = 0.7;
|
||||||
|
|
||||||
|
state.borderMaskSize = 64;
|
||||||
|
|
||||||
|
state.mousemovecb = (evn) => {
|
||||||
|
_reticle_draw(evn, state.snapToGrid);
|
||||||
|
const bb = getBoundingBox(
|
||||||
|
evn.x,
|
||||||
|
evn.y,
|
||||||
|
basePixelCount * scaleFactor,
|
||||||
|
basePixelCount * scaleFactor,
|
||||||
|
state.snapToGrid && basePixelCount
|
||||||
|
);
|
||||||
|
|
||||||
|
// For displaying border mask
|
||||||
|
const auxCanvas = document.createElement("canvas");
|
||||||
|
auxCanvas.width = bb.w;
|
||||||
|
auxCanvas.height = bb.h;
|
||||||
|
const auxCtx = auxCanvas.getContext("2d");
|
||||||
|
|
||||||
|
if (state.borderMaskSize > 0) {
|
||||||
|
auxCtx.fillStyle = "#FF6A6A50";
|
||||||
|
auxCtx.fillRect(0, 0, state.borderMaskSize, bb.h);
|
||||||
|
auxCtx.fillRect(0, 0, bb.w, state.borderMaskSize);
|
||||||
|
auxCtx.fillRect(
|
||||||
|
bb.w - state.borderMaskSize,
|
||||||
|
0,
|
||||||
|
state.borderMaskSize,
|
||||||
|
bb.h
|
||||||
|
);
|
||||||
|
auxCtx.fillRect(
|
||||||
|
0,
|
||||||
|
bb.h - state.borderMaskSize,
|
||||||
|
bb.w,
|
||||||
|
state.borderMaskSize
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmp = ovCtx.globalAlpha;
|
||||||
|
ovCtx.globalAlpha = 0.4;
|
||||||
|
ovCtx.drawImage(auxCanvas, bb.x, bb.y);
|
||||||
|
ovCtx.globalAlpha = tmp;
|
||||||
|
};
|
||||||
|
state.dreamcb = (evn) => {
|
||||||
|
dream_img2img_callback(evn, state);
|
||||||
|
};
|
||||||
|
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
||||||
|
},
|
||||||
|
populateContextMenu: (menu, state) => {
|
||||||
|
if (!state.ctxmenu) {
|
||||||
|
state.ctxmenu = {};
|
||||||
|
// Snap To Grid Checkbox
|
||||||
|
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||||
|
state,
|
||||||
|
"snapToGrid",
|
||||||
|
"Snap To Grid"
|
||||||
|
).label;
|
||||||
|
|
||||||
|
// Denoising Strength Slider
|
||||||
|
state.ctxmenu.denoisingStrengthSlider = _toolbar_input.slider(
|
||||||
|
state,
|
||||||
|
"denoisingStrength",
|
||||||
|
"Denoising Strength",
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0.05
|
||||||
|
).slider;
|
||||||
|
|
||||||
|
// Border Mask Size Slider
|
||||||
|
state.ctxmenu.borderMaskSlider = _toolbar_input.slider(
|
||||||
|
state,
|
||||||
|
"borderMaskSize",
|
||||||
|
"Border Mask Size",
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
1
|
||||||
|
).slider;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||||
|
menu.appendChild(document.createElement("br"));
|
||||||
|
menu.appendChild(state.ctxmenu.denoisingStrengthSlider);
|
||||||
|
menu.appendChild(state.ctxmenu.borderMaskSlider);
|
||||||
|
},
|
||||||
|
shortcut: "I",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const mask_brush_draw_callback = (evn, state) => {
|
const _mask_brush_draw_callback = (evn, state) => {
|
||||||
if (evn.initialTarget.id === "overlayCanvas") {
|
if (evn.initialTarget.id === "overlayCanvas") {
|
||||||
maskPaintCtx.globalCompositeOperation = "source-over";
|
maskPaintCtx.globalCompositeOperation = "source-over";
|
||||||
maskPaintCtx.strokeStyle = "#FF6A6A";
|
maskPaintCtx.strokeStyle = "#FF6A6A";
|
||||||
|
@ -12,7 +12,7 @@ const mask_brush_draw_callback = (evn, state) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const mask_brush_erase_callback = (evn, state) => {
|
const _mask_brush_erase_callback = (evn, state) => {
|
||||||
if (evn.initialTarget.id === "overlayCanvas") {
|
if (evn.initialTarget.id === "overlayCanvas") {
|
||||||
maskPaintCtx.globalCompositeOperation = "destination-out";
|
maskPaintCtx.globalCompositeOperation = "destination-out";
|
||||||
maskPaintCtx.strokeStyle = "#FFFFFFFF";
|
maskPaintCtx.strokeStyle = "#FFFFFFFF";
|
||||||
|
@ -25,3 +25,85 @@ const mask_brush_erase_callback = (evn, state) => {
|
||||||
maskPaintCtx.stroke();
|
maskPaintCtx.stroke();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const maskBrushTool = () =>
|
||||||
|
toolbar.registerTool(
|
||||||
|
"res/icons/paintbrush.svg",
|
||||||
|
"Mask Brush",
|
||||||
|
(state, opt) => {
|
||||||
|
// Draw new cursor immediately
|
||||||
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||||
|
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
|
||||||
|
|
||||||
|
// Start Listeners
|
||||||
|
mouse.listen.canvas.onmousemove.on(state.movecb);
|
||||||
|
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
||||||
|
mouse.listen.canvas.left.onpaint.on(state.drawcb);
|
||||||
|
mouse.listen.canvas.right.onpaint.on(state.erasecb);
|
||||||
|
},
|
||||||
|
(state, opt) => {
|
||||||
|
// Clear Listeners
|
||||||
|
mouse.listen.canvas.onmousemove.clear(state.movecb);
|
||||||
|
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
||||||
|
mouse.listen.canvas.left.onpaint.clear(state.drawcb);
|
||||||
|
mouse.listen.canvas.right.onpaint.clear(state.erasecb);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
init: (state) => {
|
||||||
|
state.config = {
|
||||||
|
brushScrollSpeed: 1 / 4,
|
||||||
|
minBrushSize: 10,
|
||||||
|
maxBrushSize: 500,
|
||||||
|
};
|
||||||
|
|
||||||
|
state.brushSize = 64;
|
||||||
|
state.setBrushSize = (size) => {
|
||||||
|
state.brushSize = size;
|
||||||
|
state.ctxmenu.brushSizeRange.value = size;
|
||||||
|
state.ctxmenu.brushSizeText.value = size;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.movecb = (evn) => {
|
||||||
|
if (evn.target.id === "overlayCanvas") {
|
||||||
|
// draw big translucent red blob cursor
|
||||||
|
ovCtx.beginPath();
|
||||||
|
ovCtx.arc(evn.x, evn.y, state.brushSize / 2, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
|
||||||
|
ovCtx.fillStyle = "#FF6A6A50";
|
||||||
|
ovCtx.fill();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state.wheelcb = (evn) => {
|
||||||
|
if (evn.target.id === "overlayCanvas") {
|
||||||
|
state.brushSize = state.setBrushSize(
|
||||||
|
state.brushSize -
|
||||||
|
Math.floor(state.config.brushScrollSpeed * evn.delta)
|
||||||
|
);
|
||||||
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||||
|
state.movecb(evn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state.drawcb = (evn) => _mask_brush_draw_callback(evn, state);
|
||||||
|
state.erasecb = (evn) => _mask_brush_erase_callback(evn, state);
|
||||||
|
},
|
||||||
|
populateContextMenu: (menu, state) => {
|
||||||
|
if (!state.ctxmenu) {
|
||||||
|
state.ctxmenu = {};
|
||||||
|
const brushSizeSlider = _toolbar_input.slider(
|
||||||
|
state,
|
||||||
|
"brushSize",
|
||||||
|
"Brush Size",
|
||||||
|
state.config.minBrushSize,
|
||||||
|
state.config.maxBrushSize,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
state.ctxmenu.brushSizeSlider = brushSizeSlider.slider;
|
||||||
|
state.setBrushSize = brushSizeSlider.setValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.appendChild(state.ctxmenu.brushSizeSlider);
|
||||||
|
},
|
||||||
|
shortcut: "M",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
0
js/ui/tool/populate.js
Normal file
0
js/ui/tool/populate.js
Normal file
153
js/ui/tool/select.js
Normal file
153
js/ui/tool/select.js
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
const selectTransformTool = () =>
|
||||||
|
toolbar.registerTool(
|
||||||
|
"res/icons/box-select.svg",
|
||||||
|
"Select Image",
|
||||||
|
(state, opt) => {
|
||||||
|
// Draw new cursor immediately
|
||||||
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||||
|
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
|
||||||
|
|
||||||
|
mouse.listen.canvas.onmousemove.on(state.movecb);
|
||||||
|
mouse.listen.canvas.left.ondragstart.on(state.dragstartcb);
|
||||||
|
mouse.listen.canvas.left.ondragend.on(state.dragendcb);
|
||||||
|
|
||||||
|
mouse.listen.canvas.right.onclick.on(state.cancelcb);
|
||||||
|
},
|
||||||
|
(state, opt) => {
|
||||||
|
mouse.listen.canvas.onmousemove.clear(state.movecb);
|
||||||
|
mouse.listen.canvas.left.ondragstart.clear(state.dragstartcb);
|
||||||
|
mouse.listen.canvas.left.ondragend.clear(state.dragendcb);
|
||||||
|
|
||||||
|
mouse.listen.canvas.right.onclick.clear(state.cancelcb);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
init: (state) => {
|
||||||
|
state.snapToGrid = true;
|
||||||
|
state.dragging = null;
|
||||||
|
|
||||||
|
const selectionBB = (x1, y1, x2, y2) => {
|
||||||
|
return {
|
||||||
|
x: Math.min(x1, x2),
|
||||||
|
y: Math.min(y1, y2),
|
||||||
|
w: Math.abs(x1 - x2),
|
||||||
|
h: Math.abs(y1 - y2),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
state.movecb = (evn) => {
|
||||||
|
if (evn.target.id === "overlayCanvas") {
|
||||||
|
let x = evn.x;
|
||||||
|
let y = evn.y;
|
||||||
|
if (state.snapToGrid) {
|
||||||
|
x += snap(evn.x, true, 64);
|
||||||
|
y += snap(evn.y, true, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw dragging box
|
||||||
|
if (state.dragging) {
|
||||||
|
ovCtx.setLineDash([2, 2]);
|
||||||
|
ovCtx.lineWidth = 1;
|
||||||
|
ovCtx.strokeStyle = "#FFF";
|
||||||
|
|
||||||
|
const ix = state.dragging.ix;
|
||||||
|
const iy = state.dragging.iy;
|
||||||
|
|
||||||
|
const bb = selectionBB(ix, iy, x, y);
|
||||||
|
|
||||||
|
ovCtx.strokeRect(bb.x, bb.y, bb.w, bb.h);
|
||||||
|
ovCtx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw selection box
|
||||||
|
if (state.selected) {
|
||||||
|
ovCtx.lineWidth = 1;
|
||||||
|
ovCtx.strokeStyle = "#FFF";
|
||||||
|
|
||||||
|
ovCtx.setLineDash([4, 2]);
|
||||||
|
ovCtx.strokeRect(
|
||||||
|
state.selected.x,
|
||||||
|
state.selected.y,
|
||||||
|
state.selected.w,
|
||||||
|
state.selected.h
|
||||||
|
);
|
||||||
|
ovCtx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw cuttent cursor location
|
||||||
|
ovCtx.lineWidth = 3;
|
||||||
|
ovCtx.strokeStyle = "#FFF";
|
||||||
|
|
||||||
|
ovCtx.beginPath();
|
||||||
|
ovCtx.moveTo(x, y + 10);
|
||||||
|
ovCtx.lineTo(x, y - 10);
|
||||||
|
ovCtx.moveTo(x + 10, y);
|
||||||
|
ovCtx.lineTo(x - 10, y);
|
||||||
|
ovCtx.stroke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state.dragstartcb = (evn) => {
|
||||||
|
if (evn.target.id === "overlayCanvas") {
|
||||||
|
let ix = evn.ix;
|
||||||
|
let iy = evn.iy;
|
||||||
|
if (state.snapToGrid) {
|
||||||
|
ix += snap(evn.ix, true, 64);
|
||||||
|
iy += snap(evn.iy, true, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.dragging = {ix, iy};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state.dragendcb = (evn) => {
|
||||||
|
if (evn.target.id === "overlayCanvas" && state.dragging) {
|
||||||
|
let x = evn.x;
|
||||||
|
let y = evn.y;
|
||||||
|
if (state.snapToGrid) {
|
||||||
|
x += snap(evn.x, true, 64);
|
||||||
|
y += snap(evn.y, true, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.selected = selectionBB(
|
||||||
|
state.dragging.ix,
|
||||||
|
state.dragging.iy,
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
);
|
||||||
|
state.dragging = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state.cancelcb = (evn) => {
|
||||||
|
if (evn.target.id === "overlayCanvas") {
|
||||||
|
if (state.dragging) state.dragging = null;
|
||||||
|
else state.selected = null;
|
||||||
|
|
||||||
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
||||||
|
state.movecb(evn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keyboard callbacks
|
||||||
|
state.keydowncb = (evn) => {
|
||||||
|
console.debug(evn);
|
||||||
|
};
|
||||||
|
|
||||||
|
state.keyclickcb = (evn) => {
|
||||||
|
console.debug(evn);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
populateContextMenu: (menu, state) => {
|
||||||
|
if (!state.ctxmenu) {
|
||||||
|
state.ctxmenu = {};
|
||||||
|
// Snap To Grid Checkbox
|
||||||
|
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
||||||
|
state,
|
||||||
|
"snapToGrid",
|
||||||
|
"Snap To Grid"
|
||||||
|
).label;
|
||||||
|
}
|
||||||
|
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
273
js/ui/toolbar.js
273
js/ui/toolbar.js
|
@ -134,15 +134,7 @@ const _toolbar_input = {
|
||||||
return {checkbox, label};
|
return {checkbox, label};
|
||||||
},
|
},
|
||||||
|
|
||||||
slider: (
|
slider: (state, dataKey, text, min = 0, max = 1, step = 0.1) => {
|
||||||
state,
|
|
||||||
dataKey,
|
|
||||||
text,
|
|
||||||
min = 0,
|
|
||||||
max = 1,
|
|
||||||
step = 0.1,
|
|
||||||
defaultValue = 0.3
|
|
||||||
) => {
|
|
||||||
const slider = document.createElement("div");
|
const slider = document.createElement("div");
|
||||||
|
|
||||||
const value = createSlider(text, slider, {
|
const value = createSlider(text, slider, {
|
||||||
|
@ -152,7 +144,7 @@ const _toolbar_input = {
|
||||||
valuecb: (v) => {
|
valuecb: (v) => {
|
||||||
state[dataKey] = v;
|
state[dataKey] = v;
|
||||||
},
|
},
|
||||||
defaultValue,
|
defaultValue: state[dataKey],
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -179,6 +171,7 @@ const _reticle_draw = (evn, snapToGrid = true) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// draw targeting square reticle thingy cursor
|
// draw targeting square reticle thingy cursor
|
||||||
|
ovCtx.lineWidth = 1;
|
||||||
ovCtx.strokeStyle = "#FFF";
|
ovCtx.strokeStyle = "#FFF";
|
||||||
ovCtx.strokeRect(bb.x, bb.y, bb.w, bb.h); //origin is middle of the frame
|
ovCtx.strokeRect(bb.x, bb.y, bb.w, bb.h); //origin is middle of the frame
|
||||||
}
|
}
|
||||||
|
@ -189,179 +182,8 @@ const tools = {};
|
||||||
/**
|
/**
|
||||||
* Dream tool
|
* Dream tool
|
||||||
*/
|
*/
|
||||||
tools.dream = toolbar.registerTool(
|
tools.dream = dreamTool();
|
||||||
"res/icons/image-plus.svg",
|
tools.img2img = img2imgTool();
|
||||||
"Dream",
|
|
||||||
(state, opt) => {
|
|
||||||
// Draw new cursor immediately
|
|
||||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
||||||
state.mousemovecb({
|
|
||||||
...mouse.coords.canvas.pos,
|
|
||||||
target: {id: "overlayCanvas"},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start Listeners
|
|
||||||
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
|
||||||
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
|
||||||
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
|
||||||
},
|
|
||||||
(state, opt) => {
|
|
||||||
// Clear Listeners
|
|
||||||
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
|
||||||
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
|
||||||
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
init: (state) => {
|
|
||||||
state.snapToGrid = true;
|
|
||||||
state.overMaskPx = 0;
|
|
||||||
state.mousemovecb = (evn) => _reticle_draw(evn, state.snapToGrid);
|
|
||||||
state.dreamcb = (evn) => {
|
|
||||||
dream_generate_callback(evn, state);
|
|
||||||
};
|
|
||||||
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
|
||||||
},
|
|
||||||
populateContextMenu: (menu, state) => {
|
|
||||||
if (!state.ctxmenu) {
|
|
||||||
state.ctxmenu = {};
|
|
||||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
|
||||||
state,
|
|
||||||
"snapToGrid",
|
|
||||||
"Snap To Grid"
|
|
||||||
).label;
|
|
||||||
state.ctxmenu.overMaskPxLabel = _toolbar_input.slider(
|
|
||||||
state,
|
|
||||||
"overMaskPx",
|
|
||||||
"Overmask px",
|
|
||||||
0,
|
|
||||||
128,
|
|
||||||
1,
|
|
||||||
64
|
|
||||||
).slider;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
|
||||||
menu.appendChild(document.createElement("br"));
|
|
||||||
menu.appendChild(state.ctxmenu.overMaskPxLabel);
|
|
||||||
},
|
|
||||||
shortcut: "D",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
tools.img2img = toolbar.registerTool(
|
|
||||||
"res/icons/image.svg",
|
|
||||||
"Img2Img",
|
|
||||||
(state, opt) => {
|
|
||||||
// Draw new cursor immediately
|
|
||||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
||||||
state.mousemovecb({
|
|
||||||
...mouse.coords.canvas.pos,
|
|
||||||
target: {id: "overlayCanvas"},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start Listeners
|
|
||||||
mouse.listen.canvas.onmousemove.on(state.mousemovecb);
|
|
||||||
mouse.listen.canvas.left.onclick.on(state.dreamcb);
|
|
||||||
mouse.listen.canvas.right.onclick.on(state.erasecb);
|
|
||||||
},
|
|
||||||
(state, opt) => {
|
|
||||||
// Clear Listeners
|
|
||||||
mouse.listen.canvas.onmousemove.clear(state.mousemovecb);
|
|
||||||
mouse.listen.canvas.left.onclick.clear(state.dreamcb);
|
|
||||||
mouse.listen.canvas.right.onclick.clear(state.erasecb);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
init: (state) => {
|
|
||||||
state.snapToGrid = true;
|
|
||||||
state.denoisingStrength = 0.7;
|
|
||||||
|
|
||||||
state.borderMaskSize = 64;
|
|
||||||
|
|
||||||
state.mousemovecb = (evn) => {
|
|
||||||
_reticle_draw(evn, state.snapToGrid);
|
|
||||||
const bb = getBoundingBox(
|
|
||||||
evn.x,
|
|
||||||
evn.y,
|
|
||||||
basePixelCount * scaleFactor,
|
|
||||||
basePixelCount * scaleFactor,
|
|
||||||
state.snapToGrid && basePixelCount
|
|
||||||
);
|
|
||||||
|
|
||||||
// For displaying border mask
|
|
||||||
const auxCanvas = document.createElement("canvas");
|
|
||||||
auxCanvas.width = bb.w;
|
|
||||||
auxCanvas.height = bb.h;
|
|
||||||
const auxCtx = auxCanvas.getContext("2d");
|
|
||||||
|
|
||||||
if (state.borderMaskSize > 0) {
|
|
||||||
auxCtx.fillStyle = "#FF6A6A50";
|
|
||||||
auxCtx.fillRect(0, 0, state.borderMaskSize, bb.h);
|
|
||||||
auxCtx.fillRect(0, 0, bb.w, state.borderMaskSize);
|
|
||||||
auxCtx.fillRect(
|
|
||||||
bb.w - state.borderMaskSize,
|
|
||||||
0,
|
|
||||||
state.borderMaskSize,
|
|
||||||
bb.h
|
|
||||||
);
|
|
||||||
auxCtx.fillRect(
|
|
||||||
0,
|
|
||||||
bb.h - state.borderMaskSize,
|
|
||||||
bb.w,
|
|
||||||
state.borderMaskSize
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tmp = ovCtx.globalAlpha;
|
|
||||||
ovCtx.globalAlpha = 0.4;
|
|
||||||
ovCtx.drawImage(auxCanvas, bb.x, bb.y);
|
|
||||||
ovCtx.globalAlpha = tmp;
|
|
||||||
};
|
|
||||||
state.dreamcb = (evn) => {
|
|
||||||
dream_img2img_callback(evn, state);
|
|
||||||
};
|
|
||||||
state.erasecb = (evn) => dream_erase_callback(evn, state);
|
|
||||||
},
|
|
||||||
populateContextMenu: (menu, state) => {
|
|
||||||
if (!state.ctxmenu) {
|
|
||||||
state.ctxmenu = {};
|
|
||||||
// Snap To Grid Checkbox
|
|
||||||
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
|
|
||||||
state,
|
|
||||||
"snapToGrid",
|
|
||||||
"Snap To Grid"
|
|
||||||
).label;
|
|
||||||
|
|
||||||
// Denoising Strength Slider
|
|
||||||
state.ctxmenu.denoisingStrengthSlider = _toolbar_input.slider(
|
|
||||||
state,
|
|
||||||
"denoisingStrength",
|
|
||||||
"Denoising Strength",
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0.05,
|
|
||||||
0.7
|
|
||||||
).slider;
|
|
||||||
|
|
||||||
// Border Mask Size Slider
|
|
||||||
state.ctxmenu.borderMaskSlider = _toolbar_input.slider(
|
|
||||||
state,
|
|
||||||
"borderMaskSize",
|
|
||||||
"Border Mask Size",
|
|
||||||
0,
|
|
||||||
128,
|
|
||||||
1,
|
|
||||||
64
|
|
||||||
).slider;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.appendChild(state.ctxmenu.snapToGridLabel);
|
|
||||||
menu.appendChild(document.createElement("br"));
|
|
||||||
menu.appendChild(state.ctxmenu.denoisingStrengthSlider);
|
|
||||||
menu.appendChild(state.ctxmenu.borderMaskSlider);
|
|
||||||
},
|
|
||||||
shortcut: "I",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mask Editing tools
|
* Mask Editing tools
|
||||||
|
@ -371,86 +193,13 @@ toolbar.addSeparator();
|
||||||
/**
|
/**
|
||||||
* Mask Brush tool
|
* Mask Brush tool
|
||||||
*/
|
*/
|
||||||
tools.maskbrush = toolbar.registerTool(
|
tools.maskbrush = maskBrushTool();
|
||||||
"res/icons/paintbrush.svg",
|
|
||||||
"Mask Brush",
|
|
||||||
(state, opt) => {
|
|
||||||
// Draw new cursor immediately
|
|
||||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
||||||
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
|
|
||||||
|
|
||||||
// Start Listeners
|
/**
|
||||||
mouse.listen.canvas.onmousemove.on(state.movecb);
|
* Image Editing tools
|
||||||
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
*/
|
||||||
mouse.listen.canvas.left.onpaint.on(state.drawcb);
|
toolbar.addSeparator();
|
||||||
mouse.listen.canvas.right.onpaint.on(state.erasecb);
|
|
||||||
},
|
|
||||||
(state, opt) => {
|
|
||||||
// Clear Listeners
|
|
||||||
mouse.listen.canvas.onmousemove.clear(state.movecb);
|
|
||||||
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
|
||||||
mouse.listen.canvas.left.onpaint.clear(state.drawcb);
|
|
||||||
mouse.listen.canvas.right.onpaint.clear(state.erasecb);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
init: (state) => {
|
|
||||||
state.config = {
|
|
||||||
brushScrollSpeed: 1 / 4,
|
|
||||||
minBrushSize: 10,
|
|
||||||
maxBrushSize: 500,
|
|
||||||
};
|
|
||||||
|
|
||||||
state.brushSize = 64;
|
tools.selecttransform = selectTransformTool();
|
||||||
state.setBrushSize = (size) => {
|
|
||||||
state.brushSize = size;
|
|
||||||
state.ctxmenu.brushSizeRange.value = size;
|
|
||||||
state.ctxmenu.brushSizeText.value = size;
|
|
||||||
};
|
|
||||||
|
|
||||||
state.movecb = (evn) => {
|
|
||||||
if (evn.target.id === "overlayCanvas") {
|
|
||||||
// draw big translucent red blob cursor
|
|
||||||
ovCtx.beginPath();
|
|
||||||
ovCtx.arc(evn.x, evn.y, state.brushSize / 2, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
|
|
||||||
ovCtx.fillStyle = "#FF6A6A50";
|
|
||||||
ovCtx.fill();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
state.wheelcb = (evn) => {
|
|
||||||
if (evn.target.id === "overlayCanvas") {
|
|
||||||
state.brushSize = state.setBrushSize(
|
|
||||||
state.brushSize -
|
|
||||||
Math.floor(state.config.brushScrollSpeed * evn.delta)
|
|
||||||
);
|
|
||||||
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
||||||
state.movecb(evn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
state.drawcb = (evn) => mask_brush_draw_callback(evn, state);
|
|
||||||
state.erasecb = (evn) => mask_brush_erase_callback(evn, state);
|
|
||||||
},
|
|
||||||
populateContextMenu: (menu, state) => {
|
|
||||||
if (!state.ctxmenu) {
|
|
||||||
state.ctxmenu = {};
|
|
||||||
const brushSizeSlider = _toolbar_input.slider(
|
|
||||||
state,
|
|
||||||
"brushSize",
|
|
||||||
"Brush Size",
|
|
||||||
state.config.minBrushSize,
|
|
||||||
state.config.maxBrushSize,
|
|
||||||
1,
|
|
||||||
64
|
|
||||||
);
|
|
||||||
state.ctxmenu.brushSizeSlider = brushSizeSlider.slider;
|
|
||||||
state.setBrushSize = brushSizeSlider.setValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.appendChild(state.ctxmenu.brushSizeSlider);
|
|
||||||
},
|
|
||||||
shortcut: "M",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
toolbar.tools[0].enable();
|
toolbar.tools[0].enable();
|
||||||
|
|
|
@ -64,7 +64,10 @@ function snap(i, scaled = true, gridSize = 64) {
|
||||||
scaleOffset = gridSize / 2;
|
scaleOffset = gridSize / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var snapOffset = (i % gridSize) - scaleOffset;
|
const modulus = i % gridSize;
|
||||||
|
var snapOffset = modulus - scaleOffset;
|
||||||
|
if (modulus > gridSize / 2) snapOffset = modulus - gridSize;
|
||||||
|
|
||||||
if (snapOffset == 0) {
|
if (snapOffset == 0) {
|
||||||
return snapOffset;
|
return snapOffset;
|
||||||
}
|
}
|
||||||
|
|
15
res/icons/box-select.svg
Normal file
15
res/icons/box-select.svg
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M5 3a2 2 0 0 0-2 2"></path>
|
||||||
|
<path d="M19 3a2 2 0 0 1 2 2"></path>
|
||||||
|
<path d="M21 19a2 2 0 0 1-2 2"></path>
|
||||||
|
<path d="M5 21a2 2 0 0 1-2-2"></path>
|
||||||
|
<path d="M9 3h1"></path>
|
||||||
|
<path d="M9 21h1"></path>
|
||||||
|
<path d="M14 3h1"></path>
|
||||||
|
<path d="M14 21h1"></path>
|
||||||
|
<path d="M3 9v1"></path>
|
||||||
|
<path d="M21 9v1"></path>
|
||||||
|
<path d="M3 14v1"></path>
|
||||||
|
<path d="M21 14v1"></path>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 573 B |
Loading…
Reference in a new issue