some fixes and comments

Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
(cherry picked from commit 6299b561f196a7c78c66a8d21b2e1b27cecdee77)
This commit is contained in:
Victor Seiji Hariki 2022-11-25 15:22:16 -03:00
parent e19f7af581
commit 72ebe5ba21
No known key found for this signature in database
GPG key ID: F369E3EA50A0DEEE
4 changed files with 103 additions and 26 deletions

View file

@ -44,7 +44,7 @@ const maskBrushTool = () =>
(state, opt) => { (state, opt) => {
// Clear Listeners // Clear Listeners
mouse.listen.canvas.onmousemove.clear(state.movecb); mouse.listen.canvas.onmousemove.clear(state.movecb);
mouse.listen.canvas.onwheel.on(state.wheelcb); mouse.listen.canvas.onwheel.clear(state.wheelcb);
mouse.listen.canvas.left.onpaint.clear(state.drawcb); mouse.listen.canvas.left.onpaint.clear(state.drawcb);
mouse.listen.canvas.right.onpaint.clear(state.erasecb); mouse.listen.canvas.right.onpaint.clear(state.erasecb);
}, },

View file

@ -7,23 +7,28 @@ const selectTransformTool = () =>
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height); ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}}); state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
// Canvas left mouse handlers
mouse.listen.canvas.onmousemove.on(state.movecb); mouse.listen.canvas.onmousemove.on(state.movecb);
mouse.listen.canvas.left.onclick.on(state.clickcb); mouse.listen.canvas.left.onclick.on(state.clickcb);
mouse.listen.canvas.left.ondragstart.on(state.dragstartcb); mouse.listen.canvas.left.ondragstart.on(state.dragstartcb);
mouse.listen.canvas.left.ondragend.on(state.dragendcb); mouse.listen.canvas.left.ondragend.on(state.dragendcb);
// Canvas right mouse handler
mouse.listen.canvas.right.onclick.on(state.cancelcb); mouse.listen.canvas.right.onclick.on(state.cancelcb);
// Keyboard click handlers
keyboard.listen.onkeyclick.on(state.keyclickcb); keyboard.listen.onkeyclick.on(state.keyclickcb);
keyboard.listen.onkeydown.on(state.keydowncb); keyboard.listen.onkeydown.on(state.keydowncb);
// Registers keyboard shortcuts
keyboard.onShortcut({ctrl: true, key: "KeyC"}, state.ctrlccb); keyboard.onShortcut({ctrl: true, key: "KeyC"}, state.ctrlccb);
keyboard.onShortcut({ctrl: true, key: "KeyV"}, state.ctrlvcb); keyboard.onShortcut({ctrl: true, key: "KeyV"}, state.ctrlvcb);
keyboard.onShortcut({ctrl: true, key: "KeyX"}, state.ctrlxcb); keyboard.onShortcut({ctrl: true, key: "KeyX"}, state.ctrlxcb);
keyboard.onShortcut({ctrl: true, key: "KeyS"}, state.ctrlscb);
state.selected = null; state.selected = null;
}, },
(state, opt) => { (state, opt) => {
// Clear all those listeners and shortcuts we set up
mouse.listen.canvas.onmousemove.clear(state.movecb); mouse.listen.canvas.onmousemove.clear(state.movecb);
mouse.listen.canvas.left.onclick.clear(state.clickcb); mouse.listen.canvas.left.onclick.clear(state.clickcb);
mouse.listen.canvas.left.ondragstart.clear(state.dragstartcb); mouse.listen.canvas.left.ondragstart.clear(state.dragstartcb);
@ -36,10 +41,11 @@ const selectTransformTool = () =>
keyboard.deleteShortcut(state.ctrlccb, "KeyC"); keyboard.deleteShortcut(state.ctrlccb, "KeyC");
keyboard.deleteShortcut(state.ctrlvcb, "KeyV"); keyboard.deleteShortcut(state.ctrlvcb, "KeyV");
keyboard.deleteShortcut(state.ctrlxcb, "KeyX"); keyboard.deleteShortcut(state.ctrlxcb, "KeyX");
keyboard.deleteShortcut(state.ctrlscb, "KeyS");
// Clear any selections
state.reset(); state.reset();
// Resets cursor
ovCanvas.style.cursor = "auto"; ovCanvas.style.cursor = "auto";
}, },
{ {
@ -64,6 +70,7 @@ const selectTransformTool = () =>
}); });
state.moving = null; state.moving = null;
// Some things to easy request for a redraw
state.lastMouseTarget = null; state.lastMouseTarget = null;
state.lastMouseMove = null; state.lastMouseMove = null;
@ -72,12 +79,13 @@ const selectTransformTool = () =>
state.movecb(state.lastMouseMove); state.movecb(state.lastMouseMove);
}; };
// Clears selection and make things right
state.reset = () => { state.reset = () => {
if (state.selected) if (state.selected)
imgCtx.drawImage( imgCtx.drawImage(
state.selected.image, state.original.image,
state.selected.original.x, state.original.x,
state.selected.original.y state.original.y
); );
if (state.dragging) state.dragging = null; if (state.dragging) state.dragging = null;
@ -86,6 +94,7 @@ const selectTransformTool = () =>
redraw(); redraw();
}; };
// Selection bounding box object. Has some witchery to deal with handles.
const selectionBB = (x1, y1, x2, y2) => { const selectionBB = (x1, y1, x2, y2) => {
return { return {
original: { original: {
@ -174,6 +183,7 @@ const selectTransformTool = () =>
}; };
}; };
// Mouse move handelr. As always, also renders cursor
state.movecb = (evn) => { state.movecb = (evn) => {
ovCanvas.style.cursor = "auto"; ovCanvas.style.cursor = "auto";
state.lastMouseTarget = evn.target; state.lastMouseTarget = evn.target;
@ -287,8 +297,10 @@ const selectTransformTool = () =>
} }
}; };
// Handles left mouse clicks
state.clickcb = (evn) => { state.clickcb = (evn) => {
if (evn.target.id === "overlayCanvas") { if (evn.target.id === "overlayCanvas") {
// If something is selected, commit changes to the canvas
if (state.selected) { if (state.selected) {
imgCtx.drawImage( imgCtx.drawImage(
state.selected.image, state.selected.image,
@ -312,6 +324,8 @@ const selectTransformTool = () =>
} }
} }
}; };
// Handles left mouse drag events
state.dragstartcb = (evn) => { state.dragstartcb = (evn) => {
if (evn.target.id === "overlayCanvas") { if (evn.target.id === "overlayCanvas") {
let ix = evn.ix; let ix = evn.ix;
@ -321,6 +335,7 @@ const selectTransformTool = () =>
iy += snap(evn.iy, true, 64); iy += snap(evn.iy, true, 64);
} }
// If is selected, check if drag is in handles/body and act accordingly
if (state.selected) { if (state.selected) {
const handles = state.selected.handles(); const handles = state.selected.handles();
@ -329,17 +344,21 @@ const selectTransformTool = () =>
); );
if (activeHandle) { if (activeHandle) {
state.scaling = activeHandle; state.scaling = activeHandle;
return;
} else if (state.selected.contains(ix, iy)) { } else if (state.selected.contains(ix, iy)) {
state.moving = { state.moving = {
offset: {x: ix - state.selected.x, y: iy - state.selected.y}, offset: {x: ix - state.selected.x, y: iy - state.selected.y},
}; };
return;
} }
} else {
state.dragging = {ix, iy};
} }
// If it is not, just create new selection
state.reset();
state.dragging = {ix, iy};
} }
}; };
// Handles left mouse drag end events
state.dragendcb = (evn) => { state.dragendcb = (evn) => {
if (evn.target.id === "overlayCanvas") { if (evn.target.id === "overlayCanvas") {
let x = evn.x; let x = evn.x;
@ -349,11 +368,17 @@ const selectTransformTool = () =>
y += snap(evn.y, true, 64); y += snap(evn.y, true, 64);
} }
// If we are scaling, stop scaling and do some handler magic
if (state.scaling) { if (state.scaling) {
state.selected.updateOriginal(); state.selected.updateOriginal();
state.scaling = null; state.scaling = null;
// If we are moving the selection, just... stop
} else if (state.moving) { } else if (state.moving) {
state.moving = null; state.moving = null;
/**
* If we are dragging, create a cutout selection area and save to an auxiliar image
* We will be rendering the image to the overlay, so it will not be noticeable
*/
} else if (state.dragging) { } else if (state.dragging) {
state.original = selectionBB( state.original = selectionBB(
state.dragging.ix, state.dragging.ix,
@ -404,19 +429,21 @@ const selectTransformTool = () =>
} }
}; };
// Handler for right clicks. Basically resets everything
state.cancelcb = (evn) => { state.cancelcb = (evn) => {
if (evn.target.id === "overlayCanvas") { if (evn.target.id === "overlayCanvas") {
state.reset(); state.reset();
} }
}; };
// Keyboard callbacks // Keyboard callbacks (For now, they just handle the "delete" key)
state.keydowncb = (evn) => {}; state.keydowncb = (evn) => {};
state.keyclickcb = (evn) => { state.keyclickcb = (evn) => {
if (state.lastMouseTarget.id === "overlayCanvas") { if (state.lastMouseTarget.id === "overlayCanvas") {
switch (evn.code) { switch (evn.code) {
case "Delete": case "Delete":
// Deletes selected area
state.selected && state.selected &&
commands.runCommand( commands.runCommand(
"eraseImage", "eraseImage",
@ -430,8 +457,11 @@ const selectTransformTool = () =>
}; };
// Register Ctrl-C/V Shortcut // Register Ctrl-C/V Shortcut
state.ctrlccb = (evn) => {
// Handles copying
state.ctrlccb = (evn, cut = false) => {
if (state.selected && state.lastMouseTarget.id === "overlayCanvas") { if (state.selected && state.lastMouseTarget.id === "overlayCanvas") {
// We create a new canvas to store the data
state.clipboard.copy = document.createElement("canvas"); state.clipboard.copy = document.createElement("canvas");
state.clipboard.copy.width = state.selected.w; state.clipboard.copy.width = state.selected.w;
@ -440,10 +470,29 @@ const selectTransformTool = () =>
const ctx = state.clipboard.copy.getContext("2d"); const ctx = state.clipboard.copy.getContext("2d");
ctx.clearRect(0, 0, state.selected.w, state.selected.h); ctx.clearRect(0, 0, state.selected.w, state.selected.h);
ctx.drawImage(state.selected.image, 0, 0); ctx.drawImage(
state.selected.image,
0,
0,
state.selected.image.width,
state.selected.image.height,
0,
0,
state.selected.w,
state.selected.h
);
// If cutting, we reverse the selection and erase the selection area
if (cut) {
const aux = state.original;
state.reset();
commands.runCommand("eraseImage", "Cut Image", aux);
}
// Because firefox needs manual activation of the feature // Because firefox needs manual activation of the feature
if (state.useClipboard) { if (state.useClipboard) {
// Send to clipboard
state.clipboard.copy.toBlob((blob) => { state.clipboard.copy.toBlob((blob) => {
const item = new ClipboardItem({"image/png": blob}); const item = new ClipboardItem({"image/png": blob});
navigator.clipboard.write([item]).catch((e) => { navigator.clipboard.write([item]).catch((e) => {
@ -454,8 +503,11 @@ const selectTransformTool = () =>
} }
} }
}; };
// Handles pasting
state.ctrlvcb = (evn) => { state.ctrlvcb = (evn) => {
if (state.useClipboard) { if (state.useClipboard) {
// If we use the clipboard, do some proccessing of clipboard data (ugly but kind of minimum required)
navigator.clipboard.read().then((items) => { navigator.clipboard.read().then((items) => {
console.info(items[0]); console.info(items[0]);
for (const item of items) { for (const item of items) {
@ -476,35 +528,41 @@ const selectTransformTool = () =>
} }
}); });
} else if (state.clipboard.copy) { } else if (state.clipboard.copy) {
// Use internal clipboard
const image = document.createElement("img"); const image = document.createElement("img");
image.src = state.clipboard.copy.toDataURL(); image.src = state.clipboard.copy.toDataURL();
// Send to stamp, as clipboard temporary data
tools.stamp.enable({ tools.stamp.enable({
image, image,
back: tools.selecttransform.enable, back: tools.selecttransform.enable,
}); });
} }
}; };
state.ctrlxcb = (evn) => {};
state.ctrlscb = (evn) => { // Cut shortcut. Basically, send to copy handler
evn.evn.preventDefault(); state.ctrlxcb = (evn) => {
state.ctrlccb(evn, true);
}; };
}, },
populateContextMenu: (menu, state) => { populateContextMenu: (menu, state) => {
if (!state.ctxmenu) { if (!state.ctxmenu) {
state.ctxmenu = {}; state.ctxmenu = {};
// Snap To Grid Checkbox // Snap To Grid Checkbox
state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox( state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox(
state, state,
"snapToGrid", "snapToGrid",
"Snap To Grid" "Snap To Grid"
).label; ).label;
// Keep Aspect Ratio // Keep Aspect Ratio
state.ctxmenu.keepAspectRatioLabel = _toolbar_input.checkbox( state.ctxmenu.keepAspectRatioLabel = _toolbar_input.checkbox(
state, state,
"keepAspectRatio", "keepAspectRatio",
"Keep Aspect Ratio" "Keep Aspect Ratio"
).label; ).label;
// Use Clipboard // Use Clipboard
const clipboardCheckbox = _toolbar_input.checkbox( const clipboardCheckbox = _toolbar_input.checkbox(
state, state,
@ -519,6 +577,7 @@ const selectTransformTool = () =>
const actionArray = document.createElement("div"); const actionArray = document.createElement("div");
actionArray.classList.add("button-array"); actionArray.classList.add("button-array");
// Save button
const saveSelectionButton = document.createElement("button"); const saveSelectionButton = document.createElement("button");
saveSelectionButton.classList.add("button", "tool"); saveSelectionButton.classList.add("button", "tool");
saveSelectionButton.textContent = "Save"; saveSelectionButton.textContent = "Save";
@ -530,6 +589,7 @@ const selectTransformTool = () =>
}); });
}; };
// Save as Resource Button
const createResourceButton = document.createElement("button"); const createResourceButton = document.createElement("button");
createResourceButton.classList.add("button", "tool"); createResourceButton.classList.add("button", "tool");
createResourceButton.textContent = "Resource"; createResourceButton.textContent = "Resource";
@ -544,10 +604,13 @@ const selectTransformTool = () =>
actionArray.appendChild(saveSelectionButton); actionArray.appendChild(saveSelectionButton);
actionArray.appendChild(createResourceButton); actionArray.appendChild(createResourceButton);
// Disable buttons (if nothing is selected)
state.ctxmenu.disableButtons = () => { state.ctxmenu.disableButtons = () => {
saveSelectionButton.disabled = true; saveSelectionButton.disabled = true;
createResourceButton.disabled = true; createResourceButton.disabled = true;
}; };
// Disable buttons (if something is selected)
state.ctxmenu.enableButtons = () => { state.ctxmenu.enableButtons = () => {
saveSelectionButton.disabled = ""; saveSelectionButton.disabled = "";
createResourceButton.disabled = ""; createResourceButton.disabled = "";

View file

@ -10,16 +10,15 @@ const stampTool = () =>
// Start Listeners // Start Listeners
mouse.listen.canvas.onmousemove.on(state.movecb); mouse.listen.canvas.onmousemove.on(state.movecb);
mouse.listen.canvas.left.onclick.on(state.drawcb); mouse.listen.canvas.left.onclick.on(state.drawcb);
mouse.listen.canvas.right.onclick.on(state.cancelcb);
// For calls from other tools to paste image // For calls from other tools to paste image
if (opt && opt.image) { if (opt && opt.image) {
const resource = state.addResource( state.addResource(
opt.name || "Clipboard", opt.name || "Clipboard",
opt.image, opt.image,
opt.temporary === undefined ? true : opt.temporary opt.temporary === undefined ? true : opt.temporary
); );
state.selected = resource;
document.getElementById(`resource-${resource.id}`).click();
state.ctxmenu.uploadButton.disabled = true; state.ctxmenu.uploadButton.disabled = true;
state.back = opt.back || null; state.back = opt.back || null;
toolbar.lock(); toolbar.lock();
@ -35,6 +34,7 @@ const stampTool = () =>
// Clear Listeners // Clear Listeners
mouse.listen.canvas.onmousemove.clear(state.movecb); mouse.listen.canvas.onmousemove.clear(state.movecb);
mouse.listen.canvas.left.onclick.clear(state.drawcb); mouse.listen.canvas.left.onclick.clear(state.drawcb);
mouse.listen.canvas.right.onclick.clear(state.cancelcb);
// Deselect // Deselect
state.selected = null; state.selected = null;
@ -49,12 +49,15 @@ const stampTool = () =>
state.selected = null; state.selected = null;
state.back = null; state.back = null;
const selectResource = (resource) => { state.lastMouseMove = {x: 0, y: 0};
state.selectResource = (resource) => {
if (state.ctxmenu.uploadButton.disabled) return; if (state.ctxmenu.uploadButton.disabled) return;
const resourceWrapper = resource.dom.wrapper; const resourceWrapper = resource && resource.dom.wrapper;
const wasSelected = resourceWrapper.classList.contains("selected"); const wasSelected =
resourceWrapper && resourceWrapper.classList.contains("selected");
Array.from(state.ctxmenu.resourceList.children).forEach((child) => { Array.from(state.ctxmenu.resourceList.children).forEach((child) => {
child.classList.remove("selected"); child.classList.remove("selected");
@ -62,7 +65,7 @@ const stampTool = () =>
// Select // Select
if (!wasSelected) { if (!wasSelected) {
resourceWrapper.classList.add("selected"); resourceWrapper && resourceWrapper.classList.add("selected");
state.selected = resource; state.selected = resource;
} }
// If already selected, clear selection // If already selected, clear selection
@ -70,6 +73,9 @@ const stampTool = () =>
resourceWrapper.classList.remove("selected"); resourceWrapper.classList.remove("selected");
state.selected = null; state.selected = null;
} }
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
state.movecb(state.lastMouseMove);
}; };
// Synchronizes resources array with the DOM // Synchronizes resources array with the DOM
@ -90,7 +96,7 @@ const stampTool = () =>
resourceWrapper.classList.add("resource"); resourceWrapper.classList.add("resource");
resourceWrapper.addEventListener("click", () => resourceWrapper.addEventListener("click", () =>
selectResource(resource) state.selectResource(resource)
); );
resourceWrapper.addEventListener("mouseover", () => { resourceWrapper.addEventListener("mouseover", () => {
@ -133,7 +139,7 @@ const stampTool = () =>
syncResources(); syncResources();
// Select this resource // Select this resource
selectResource(resource); state.selectResource(resource);
return resource; return resource;
}; };
@ -155,6 +161,8 @@ const stampTool = () =>
y += snap(evn.y, true, 64); y += snap(evn.y, true, 64);
} }
state.lastMouseMove = evn;
// Draw selected image // Draw selected image
if (state.selected) { if (state.selected) {
ovCtx.drawImage(state.selected.image, x, y); ovCtx.drawImage(state.selected.image, x, y);
@ -196,15 +204,21 @@ const stampTool = () =>
if (state.back) { if (state.back) {
toolbar.unlock(); toolbar.unlock();
state.back({message: "Returning from stamp", pasted: true}); const backfn = state.back;
state.back = null;
backfn({message: "Returning from stamp", pasted: true});
} }
} }
}; };
state.cancelcb = (evn) => { state.cancelcb = (evn) => {
if (evn.target.id === "overlayCanvas") { if (evn.target.id === "overlayCanvas") {
state.selectResource(null);
if (state.back) { if (state.back) {
toolbar.unlock(); toolbar.unlock();
state.back({message: "Returning from stamp", pasted: false}); const backfn = state.back;
state.back = null;
backfn({message: "Returning from stamp", pasted: false});
} }
} }
}; };

View file

@ -143,7 +143,7 @@ function cropCanvas(sourceCanvas) {
return cutCanvas; return cutCanvas;
} }
function downloadCanvas(options) { function downloadCanvas(options = {}) {
defaultOpt(options, { defaultOpt(options, {
cropToContent: true, cropToContent: true,
canvas: imgCanvas, canvas: imgCanvas,