diff --git a/css/icons.css b/css/icons.css
index 214460d..45ab99a 100644
--- a/css/icons.css
+++ b/css/icons.css
@@ -202,3 +202,9 @@
-webkit-mask-image: url("../res/icons/squircle.svg");
mask-image: url("../res/icons/squircle.svg");
}
+
+.ui.inline-icon.icon-file-plus::after,
+.ui.icon > .icon-file-plus {
+ -webkit-mask-image: url("../res/icons/file-plus.svg");
+ mask-image: url("../res/icons/file-plus.svg");
+}
diff --git a/index.html b/index.html
index c5dddae..d4a3d9a 100644
--- a/index.html
+++ b/index.html
@@ -339,7 +339,7 @@
- v20240427.001
+ v20240824.001
-
+
@@ -576,7 +576,7 @@
src="js/ui/tool/generic.js?v=3e678e0"
type="text/javascript">
-
+
diff --git a/js/index.js b/js/index.js
index 7d852aa..97277ff 100644
--- a/js/index.js
+++ b/js/index.js
@@ -1534,7 +1534,7 @@ async function upscaleAndDownload(
body: JSON.stringify(data),
})
.then((response) => response.json())
- .then((data) => {
+ .then(async (data) => {
console.log(data);
var link = document.createElement("a");
link.download =
@@ -1554,6 +1554,7 @@ async function upscaleAndDownload(
console.log("Add upscaled to resource");
const img = new Image();
img.src = link.href;
+ await img.decode();
tools.stamp.state.addResource(guid() + " (upscaled)", img);
}
if (download == true) {
diff --git a/js/lib/commands.js b/js/lib/commands.js
index 0dc492f..3b9a3ca 100644
--- a/js/lib/commands.js
+++ b/js/lib/commands.js
@@ -21,6 +21,14 @@ const commands = makeReadOnly(
_history: [],
/** The types of commands we can run (private) */
_types: {},
+
+ /** @type {Observer<{n: int, cancel: function}>} */
+ get onundo() { return this._onundo; },
+ _onundo: new Observer(),
+
+ /** @type {Observer<{n: int, cancel: function}>} */
+ get onredo() { return this._onredo; },
+ _onredo: new Observer(),
/**
* Undoes the last commands in the history
@@ -28,6 +36,12 @@ const commands = makeReadOnly(
* @param {number} [n] Number of actions to undo
*/
async undo(n = 1) {
+ var cancelled = false;
+ await this._onundo.emit({
+ n:n,
+ cancel: ()=>{cancelled=true;},
+ });
+ if (cancelled) return;
for (var i = 0; i < n && this.current > -1; i++) {
try {
await this._history[this._current--].undo();
@@ -45,6 +59,12 @@ const commands = makeReadOnly(
* @param {number} [n] Number of actions to redo
*/
async redo(n = 1) {
+ let cancelled = false;
+ await this._onredo.emit({
+ n:n,
+ cancel: ()=>{cancelled=true;},
+ });
+ if (cancelled) return;
for (var i = 0; i < n && this.current + 1 < this._history.length; i++) {
try {
await this._history[++this._current].redo();
diff --git a/js/lib/input.js b/js/lib/input.js
index d5f48cf..1585f12 100644
--- a/js/lib/input.js
+++ b/js/lib/input.js
@@ -660,6 +660,9 @@ window.onkeyup = (evn) => {
});
}
+ if (keyboard.keys[evn.code]._hold_to)
+ clearTimeout(keyboard.keys[evn.code]._hold_to);
+
keyboard.keys[evn.code] = {
pressed: false,
held: false,
diff --git a/js/ui/tool/dream.js b/js/ui/tool/dream.js
index 5d23d73..f12ecf7 100644
--- a/js/ui/tool/dream.js
+++ b/js/ui/tool/dream.js
@@ -3339,6 +3339,7 @@ function addControlNetToAlwaysOnScripts(state, initCanvas, maskCanvas) {
//img2img?
state.alwayson_scripts.controlnet.args = [
{
+ enabled: true,
module: extensions.selectedControlNetModule,
model: extensions.selectedControlNetModel,
control_mode: document.getElementById("controlNetMode-select").value,
@@ -3351,10 +3352,11 @@ function addControlNetToAlwaysOnScripts(state, initCanvas, maskCanvas) {
} else {
state.alwayson_scripts.controlnet.args = [
{
+ enabled: true,
module: extensions.selectedControlNetModule,
model: extensions.selectedControlNetModel,
control_mode: document.getElementById("controlNetMode-select").value,
- input_image: initimg, //initCanvas.toDataURL(),
+ image: initimg, //initCanvas.toDataURL(),
mask: maskimg, //maskCanvas.toDataURL(),
processor_res: 64,
resize_mode: document.getElementById("controlNetResize-select").value,
diff --git a/js/ui/tool/select.js b/js/ui/tool/select.js
index f89da91..a1e69e7 100644
--- a/js/ui/tool/select.js
+++ b/js/ui/tool/select.js
@@ -13,6 +13,9 @@ const selectTransformTool = () =>
mouse.listen.world.btn.left.ondragstart.on(state.dragstartcb);
mouse.listen.world.btn.left.ondrag.on(state.dragcb);
mouse.listen.world.btn.left.ondragend.on(state.dragendcb);
+
+ mouse.listen.world.btn.left.ondclick.on(state.dclickcb);
+ mouse.listen.world.btn.right.ondclick.on(state.drclickcb);
// Canvas right mouse handler
mouse.listen.world.btn.right.onclick.on(state.cancelcb);
@@ -23,6 +26,10 @@ const selectTransformTool = () =>
// Layer system handlers
uil.onactive.on(state.uilayeractivecb);
+
+ // Undo
+ commands.onundo.on(state.undocb);
+ commands.onredo.on(state.redocb);
// Registers keyboard shortcuts
keyboard.onShortcut({ctrl: true, key: "KeyA"}, state.ctrlacb);
@@ -34,6 +41,15 @@ const selectTransformTool = () =>
keyboard.onShortcut({ctrl: true, key: "KeyV"}, state.ctrlvcb);
keyboard.onShortcut({ctrl: true, key: "KeyX"}, state.ctrlxcb);
keyboard.onShortcut({key: "Equal"}, state.togglemirror);
+
+ keyboard.onShortcut({key: "Enter"}, state.entercb);
+ keyboard.onShortcut({shift: true, key: "Enter"}, state.sentercb);
+ keyboard.onShortcut({ctrl: true, key: "Enter"}, state.ctentercb);
+ keyboard.onShortcut({ctrl: true, shift: true, key: "Enter"}, state.sctentercb);
+ keyboard.onShortcut({key: "Delete"}, state.delcb);
+ keyboard.onShortcut({shift: true, key: "Delete"}, state.sdelcb);
+
+ keyboard.onShortcut({key: "Escape"}, state.escapecb);
state.ctxmenu.mirrorSelectionCheckbox.disabled = true;
state.selected = null;
@@ -51,20 +67,34 @@ const selectTransformTool = () =>
mouse.listen.world.btn.left.ondragstart.clear(state.dragstartcb);
mouse.listen.world.btn.left.ondrag.clear(state.dragcb);
mouse.listen.world.btn.left.ondragend.clear(state.dragendcb);
+
+ mouse.listen.world.btn.left.ondclick.clear(state.dclickcb);
+ mouse.listen.world.btn.right.ondclick.clear(state.drclickcb);
mouse.listen.world.btn.right.onclick.clear(state.cancelcb);
keyboard.listen.onkeyclick.clear(state.keyclickcb);
keyboard.listen.onkeydown.clear(state.keydowncb);
+
keyboard.deleteShortcut(state.ctrlacb, "KeyA");
keyboard.deleteShortcut(state.ctrlsacb, "KeyA");
keyboard.deleteShortcut(state.ctrlccb, "KeyC");
keyboard.deleteShortcut(state.ctrlvcb, "KeyV");
keyboard.deleteShortcut(state.ctrlxcb, "KeyX");
keyboard.deleteShortcut(state.togglemirror, "Equal");
-
+ keyboard.deleteShortcut(state.entercb,"Enter");
+ keyboard.deleteShortcut(state.sentercb,"Enter");
+ keyboard.deleteShortcut(state.ctentercb,"Enter");
+ keyboard.deleteShortcut(state.sctentercb,"Enter");
+ keyboard.deleteShortcut(state.delcb,"Delete");
+ keyboard.deleteShortcut(state.sdelcb,"Delete");
+ keyboard.deleteShortcut(state.escapecb,"Escape");
+
uil.onactive.clear(state.uilayeractivecb);
+ commands.onundo.clear(state.undocb);
+ commands.onredo.clear(state.redocb);
+
// Clear any selections
state.reset();
@@ -85,6 +115,9 @@ const selectTransformTool = () =>
state.snapToGrid = true;
state.keepAspectRatio = true;
state.block_res_change = true;
+
+ state.toNewLayer = false;
+
state.useClipboard = !!(
navigator.clipboard && navigator.clipboard.write
); // Use it by default if supported
@@ -179,6 +212,19 @@ const selectTransformTool = () =>
);
}
};
+
+ // Undo/Redo Handling, reset state before Undo/Redo
+ state.undocb= (undo)=>{
+ if (state.selected){
+ // Cancel so undo shortcut effectively undoes the current transform, unless requesting multiple steps
+ if (state.selectionTransformed() && undo.n<=1)
+ undo.cancel();
+ state.reset(false);
+ }
+ }
+ state.redocb= (redo)=>{
+ if (state.selected){ state.reset(false); }
+ }
// Mirroring
state.togglemirror = () => {
@@ -251,94 +297,70 @@ const selectTransformTool = () =>
// Handles left mouse clicks
state.clickcb = (evn) => {
- if (
- state.selected &&
- !(
- state.selected.rotation === 0 &&
- state.selected.scale.x === 1 &&
- state.selected.scale.y === 1 &&
- state.selected.position.x === state.original.sx &&
- state.selected.position.y === state.original.sy &&
- !state.mirrorSelection &&
- state.original.layer === uil.layer
- ) &&
- !isCanvasBlank(
- 0,
- 0,
- state.selected.canvas.width,
- state.selected.canvas.height,
- state.selected.canvas
- )
- ) {
- // Put original image back
- state.original.layer.ctx.drawImage(
- state.selected.canvas,
- state.original.x,
- state.original.y
- );
-
- // Erase Original Selection Area
- commands.runCommand(
- "eraseImage",
- "Transform Tool Erase",
- {
- layer: state.original.layer,
- x: state.original.x,
- y: state.original.y,
- w: state.selected.canvas.width,
- h: state.selected.canvas.height,
- },
- {
- extra: {
- log: `Erased original selection area at x: ${state.original.x}, y: ${state.original.y}, width: ${state.selected.canvas.width}, height: ${state.selected.canvas.height} from layer ${state.original.layer.id}`,
- },
- }
- );
-
- // Draw Image
- const {canvas, bb} = cropCanvas(state.originalDisplayLayer.canvas, {
- border: 10,
- });
-
- let commandLog = "";
- const addline = (v, newline = true) => {
- commandLog += v;
- if (newline) commandLog += "\n";
- };
-
- addline(
- `Draw selected area to x: ${bb.x}, y: ${bb.y}, width: ${bb.w}, height: ${bb.h} to layer ${state.original.layer.id}`
- );
- addline(
- ` - translation: (x: ${state.selected.position.x}, y: ${state.selected.position.y})`
- );
- addline(
- ` - rotation : ${
- Math.round(1000 * ((180 * state.selected.rotation) / Math.PI)) /
- 1000
- } degrees`,
- false
- );
-
- commands.runCommand(
- "drawImage",
- "Transform Tool Apply",
- {
- image: canvas,
- ...bb,
- },
- {
- extra: {
- log: commandLog,
- },
- }
- );
-
- state.reset(true);
+ if (state.selectionTransformed()) {
+ state.applyTransform();
} else {
state.reset();
}
};
+
+ // Check if selection has been transformed in any way.
+ state.selectionTransformed = ()=>{
+ return state.selected &&
+ !(
+ state.selected.rotation === 0 &&
+ state.selected.scale.x === 1 &&
+ state.selected.scale.y === 1 &&
+ state.selected.position.x === state.original.sx &&
+ state.selected.position.y === state.original.sy &&
+ !state.mirrorSelection &&
+ state.original.layer === uil.layer
+ );
+ }
+
+ // Handles left mouse double clicks - Select All Ctrl-A
+ // Holding shift key - Ctrl-Shift-A
+ state.dclickcb = (evn) => {
+ // Do nothing if Ctrl Key is held for panning
+ if (state.selected || evn.evn.ctrlKey) return;
+ let shift = evn.evn.shiftKey;
+ // Wait so clickcb doesn't immediately deselect.
+ state.dclickcb_timeout = state.dclickcb_timeout ?? window.setTimeout(async ()=>{
+ state.dclickcb_timeout = null;
+ if (!state.selected && !selection.exists) {
+ if (shift) state.ctrlsacb(evn);
+ else state.ctrlacb(evn);
+ }
+ },300);
+ };
+
+ // Handles right mouse double clicks - Select topmost layer with content under pointer
+ // Holding shift key Selects the next topmost if current layer has visible content under pointer.
+ state.drclickcb = (evn) => {
+ if (state.selected) return;
+ // If shift key is held, and current layer is has visible pixels under pointer
+ // select topmost visible layer beneath the active layer
+ let shift = evn.evn.shiftKey
+ && !uil.active.hidden
+ && !isCanvasBlank(evn.x,evn.y,2,2,uil.active.canvas);
+ let layer = shift ? uil.active : null;
+ for (let l of uil.layers.toReversed()) {
+ if (shift) {
+ if (layer==l) shift = false;
+ }
+ else if (!l.hidden && !isCanvasBlank(evn.x,evn.y,2,2,l.canvas)){
+ layer = l;
+ break;
+ }
+ }
+ if (layer) {
+ uil.active=layer;
+ state.dclickcb_timeout = state.dclickcb_timeout ?? window.setTimeout(async ()=>{
+ state.dclickcb_timeout = null;
+ if (!state.selected && !selection.exists) { state.ctrlacb(evn); }
+ },300);
+ }
+ };
// Handles left mouse drag start events
state.dragstartcb = (evn) => {
@@ -513,33 +535,54 @@ const selectTransformTool = () =>
state.cancelcb = (evn) => {
state.reset();
};
+
+ state.keydowncb = (evn) => { };
+
+ // Keyboard callbacks
+ state.keyclickcb = (evn) => { };
+
+ // Register Delete Shortcut
+ state.delcb = (evn) => { state.applyTransform(true,false,false,false); };
+
+ // Register Escape Shortcut
+ state.escapecb = (evn) => { state.reset(false); };
+
+ // Register Shift-Delete Shortcut - Delete Outside Selection and Apply
+ state.sdelcb = (evn) => { state.applyTransform(false,true,false,false); };
- // Keyboard callbacks (For now, they just handle the "delete" key)
- state.keydowncb = (evn) => {};
+ // Register Enter Shortcut - Apply Transform (Delegates to clickcb)
+ state.entercb = (evn) => { state.clickcb(evn); };
- state.keyclickcb = (evn) => {
- switch (evn.code) {
- case "Delete":
- // Deletes selected area
- state.selected &&
- commands.runCommand(
- "eraseImage",
- "Erase Area",
- state.selected,
- {
- extra: {
- log: `[Placeholder] Delete selected area. TODO it's also broken`,
- },
- }
- );
- state.ctxmenu.mirrorSelectionCheckbox.disabled = true;
- state.selected = null;
- state.redraw();
- }
+ // Register Ctrl-Enter Shortcut - Copy Selection to new layer, restore original
+ state.ctentercb = (evn) => { state.applyTransform(false,false,true,true); };
+
+ // Register Shift-Enter Shortcut - Move Selection to new layer
+ state.sentercb = (evn) => { state.applyTransform(false,false,true,false); };
+
+ // Register Ctrl-Shift-Enter Shortcut - Copy Visible Selection to new layer
+ state.sctentercb = async (evn) => {
+ var selectBB =
+ state.selected.bb != undefined
+ ? state.selected.bb
+ : state.backupBB;
+ const canvas = uil.getVisible(selectBB, {
+ categories: ["image", "user", "select-display"],
+ });
+ await commands.runCommand("addLayer", "Added Layer");
+
+ await commands.runCommand("drawImage", "Transform Tool Apply",
+ {
+ image: canvas,
+ ...selectBB,
+ }
+ );
+ state.reset(false);
};
+
// Register Ctrl-A Shortcut
state.ctrlacb = () => {
+ state.reset(false); // Reset to preserve selected content
try {
const {bb} = cropCanvas(uil.canvas);
select(bb);
@@ -549,6 +592,8 @@ const selectTransformTool = () =>
};
state.ctrlsacb = () => {
+ state.reset(false); // Reset to preserve selected content
+
// Shift Key selects based on all visible layer information
const tl = {x: Infinity, y: Infinity};
const br = {x: -Infinity, y: -Infinity};
@@ -667,6 +712,111 @@ const selectTransformTool = () =>
state.ctrlxcb = (evn) => {
state.ctrlccb(evn, true);
};
+
+ // Apply Transform and Reset State, optionally erase Selection or Clear Original Layer
+ // newLayer defaults to null, overriding the forced variants if explicitly set to false
+ // Only checks if Selection exists and content has been selected
+ // Does not check if content has been transformed, eg for deletion/applying to new layer
+ state.applyTransform = async (eraseSelected = false, clearLayer = false, newLayer = null, keepOriginal = false) => {
+ const isBlank = !state.selected ||
+ isCanvasBlank( 0, 0, state.selected.canvas.width, state.selected.canvas.height, state.selected.canvas);
+
+ // Just reset state if nothing is selected, unless Clearing layer
+ if (!state.selected || !clearLayer && isBlank ){
+ state.reset(false);
+ return;
+ }
+
+ // Put original image back
+ state.original.layer.ctx.drawImage(
+ state.selected.canvas,
+ state.original.x,
+ state.original.y
+ );
+
+ // Erase Entire Layer
+ if (clearLayer) await commands.runCommand(
+ "eraseImage",
+ "Transform Tool Erase",
+ {
+ ...state.original.layer.bb,
+ layer: state.original.layer,
+ },
+ {
+ extra: {
+ log: `Erased layer ${state.original.layer.id}`,
+ },
+ }
+ );
+ // Erase Original Selection Area
+ else if (eraseSelected || !keepOriginal) await commands.runCommand(
+ "eraseImage",
+ "Transform Tool Erase",
+ {
+ layer: state.original.layer,
+ x: state.original.x,
+ y: state.original.y,
+ w: state.selected.canvas.width,
+ h: state.selected.canvas.height,
+ },
+ {
+ extra: {
+ log: `Erased original selection area at x: ${state.original.x}, y: ${state.original.y}, width: ${state.selected.canvas.width}, height: ${state.selected.canvas.height} from layer ${state.original.layer.id}`,
+ },
+ }
+ );
+
+ // Selection erased or was blank, no need to draw anything
+ if (eraseSelected || isBlank){
+ state.reset(true);
+ return;
+ }
+
+ // Draw Image
+ const {canvas, bb} = cropCanvas(state.originalDisplayLayer.canvas, {
+ border: 10,
+ });
+
+ if ( (newLayer ?? state.toNewLayer) && !clearLayer)
+ await commands.runCommand("addLayer", "Added Layer");
+
+ let commandLog = "";
+ const addline = (v, newline = true) => {
+ commandLog += v;
+ if (newline) commandLog += "\n";
+ };
+
+ addline(
+ `Draw selected area to x: ${bb.x}, y: ${bb.y}, width: ${bb.w}, height: ${bb.h} to layer ${state.original.layer.id}`
+ );
+ addline(
+ ` - translation: (x: ${state.selected.position.x}, y: ${state.selected.position.y})`
+ );
+ addline(
+ ` - rotation : ${
+ Math.round(1000 * ((180 * state.selected.rotation) / Math.PI)) /
+ 1000
+ } degrees`,
+ false
+ );
+
+ await commands.runCommand(
+ "drawImage",
+ "Transform Tool Apply",
+ {
+ image: canvas,
+ ...bb,
+ },
+ {
+ extra: {
+ log: commandLog,
+ },
+ }
+ );
+
+ state.reset(true);
+ }
+
},
populateContextMenu: (menu, state) => {
if (!state.ctxmenu) {
@@ -732,6 +882,15 @@ const selectTransformTool = () =>
state.ctxmenu.useClipboardLabel = clipboardCheckbox.checkbox;
if (!(navigator.clipboard && navigator.clipboard.write))
clipboardCheckbox.checkbox.disabled = true; // Disable if not available
+
+ // toNewLayer
+ state.ctxmenu.toNewLayerLabel = _toolbar_input.checkbox(
+ state,
+ "openoutpaint/select-toNewLayer",
+ "toNewLayer",
+ "Always Create New Layer",
+ "icon-file-plus"
+ ).checkbox;
// Selection Peek Opacity
state.ctxmenu.selectionPeekOpacitySlider = _toolbar_input.slider(
@@ -757,7 +916,7 @@ const selectTransformTool = () =>
// Save button
const saveSelectionButton = document.createElement("button");
saveSelectionButton.classList.add("button", "tool");
- saveSelectionButton.textContent = "Save Sel.";
+ saveSelectionButton.innerHTML = "Save Sel."; // nbsp as a quick hack for unwanted text wrapping
saveSelectionButton.title = "Saves Selection";
saveSelectionButton.onclick = () => {
downloadCanvas({
@@ -779,9 +938,25 @@ const selectTransformTool = () =>
tools.stamp.enable();
};
};
-
+
+ const copyNewLayerButton = document.createElement("button");
+ copyNewLayerButton.classList.add("button", "tool");
+ copyNewLayerButton.textContent = "Layer";
+ copyNewLayerButton.title = "Copies selection to a new Layer (Ctrl+Enter)";
+ copyNewLayerButton.onclick = () => { state.applyTransform(false,false,true,true); };
+
+ // Dummy button for saving active selection
+ const ActiveSelectionButton = document.createElement("button");
+ ActiveSelectionButton.classList.add("button", "tool");
+ ActiveSelectionButton.textContent = "📄";
+ ActiveSelectionButton.title = "Commands Applied to the Current Selection";
+ ActiveSelectionButton.disabled = true;
+
+
actionArray.appendChild(saveSelectionButton);
actionArray.appendChild(createResourceButton);
+ actionArray.appendChild(copyNewLayerButton);
+ actionArray.appendChild(ActiveSelectionButton);
// Some useful actions to do with selection
const visibleActionArray = document.createElement("div");
@@ -790,8 +965,8 @@ const selectTransformTool = () =>
// Save Visible button
const saveVisibleSelectionButton = document.createElement("button");
saveVisibleSelectionButton.classList.add("button", "tool");
- saveVisibleSelectionButton.textContent = "Save Vis.";
- saveVisibleSelectionButton.title = "Saves Visible Selection";
+ saveVisibleSelectionButton.innerHTML = "Save Vis."; // nbsp as a quick hack for unwanted text wrapping
+ saveVisibleSelectionButton.title = "Saves Visible Selection And Download";
saveVisibleSelectionButton.onclick = () => {
console.debug(state.selected);
console.debug(state.selected.bb);
@@ -811,7 +986,7 @@ const selectTransformTool = () =>
// Save Visible as Resource Button
const createVisibleResourceButton = document.createElement("button");
createVisibleResourceButton.classList.add("button", "tool");
- createVisibleResourceButton.textContent = "Vis. to Res.";
+ createVisibleResourceButton.textContent = "Resource";
createVisibleResourceButton.title =
"Saves Visible Selection as a Resource";
createVisibleResourceButton.onclick = () => {
@@ -830,8 +1005,52 @@ const selectTransformTool = () =>
};
};
+ // Copy To Layer Buttons
+ const copyVisNewLayerButton = document.createElement("button");
+ copyVisNewLayerButton.classList.add("button", "tool");
+ copyVisNewLayerButton.textContent = "Layer";
+ copyVisNewLayerButton.title = "Copies Visible Selection to a new Layer (Ctrl+Shift+Enter)";
+ copyVisNewLayerButton.onclick = (e) => { state.sctentercb(e); };
+
+ // Dummy button for saving visible Selection
+ const VisibleSelectionButton = document.createElement("button");
+ VisibleSelectionButton.classList.add("button", "tool");
+ VisibleSelectionButton.textContent = "👁";
+ VisibleSelectionButton.title = "Commands Applied to All Visible Content In the Selected Area";
+ VisibleSelectionButton.disabled = true;
+
visibleActionArray.appendChild(saveVisibleSelectionButton);
visibleActionArray.appendChild(createVisibleResourceButton);
+ visibleActionArray.appendChild(copyVisNewLayerButton);
+ visibleActionArray.appendChild(VisibleSelectionButton);
+
+ const actionArrayRow3 = document.createElement("div");
+ actionArrayRow3.classList.add("button-array");
+
+ // Clear Button
+ const applyClearButton = document.createElement("button");
+ applyClearButton.classList.add("button", "tool");
+ applyClearButton.textContent = "Isolate";
+ applyClearButton.title = "Erases everything in the current layer outside the selection (Shift+Delete)";
+ applyClearButton.onclick = () => { state.applyTransform(false,true,false,false); };
+
+ // Erase Button
+ const eraseSelectionButton = document.createElement("button");
+ eraseSelectionButton.classList.add("button", "tool");
+ eraseSelectionButton.textContent = "Erase";
+ eraseSelectionButton.title = "Erases current selection (Delete)";
+ eraseSelectionButton.onclick = () => { state.applyTransform(true,false,false,false); };
+
+ // Apply To New Layer button
+ const applyNewLayerButton = document.createElement("button");
+ applyNewLayerButton.classList.add("button", "tool");
+ applyNewLayerButton.textContent = "Extract";
+ applyNewLayerButton.title = "Moves Selection to a New Layer (Shift+Enter)";
+ applyNewLayerButton.onclick = () => { state.applyTransform(false,false,true,false); };
+
+ actionArrayRow3.appendChild(applyClearButton);
+ actionArrayRow3.appendChild(eraseSelectionButton);
+ actionArrayRow3.appendChild(applyNewLayerButton);
// Disable buttons (if nothing is selected)
state.ctxmenu.disableButtons = () => {
@@ -839,6 +1058,11 @@ const selectTransformTool = () =>
createResourceButton.disabled = true;
saveVisibleSelectionButton.disabled = true;
createVisibleResourceButton.disabled = true;
+ applyNewLayerButton.disabled = true;
+ copyNewLayerButton.disabled = true;
+ applyClearButton.disabled = true;
+ eraseSelectionButton.disabled = true;
+ copyVisNewLayerButton.disabled = true;
};
// Enable buttons (if something is selected)
@@ -847,9 +1071,17 @@ const selectTransformTool = () =>
createResourceButton.disabled = "";
saveVisibleSelectionButton.disabled = "";
createVisibleResourceButton.disabled = "";
+ applyNewLayerButton.disabled = "";
+ copyNewLayerButton.disabled = "";
+ applyClearButton.disabled = "";
+ eraseSelectionButton.disabled = "";
+ copyVisNewLayerButton.disabled = "";
};
+
state.ctxmenu.actionArray = actionArray;
state.ctxmenu.visibleActionArray = visibleActionArray;
+
+ state.ctxmenu.actionArrayRow3 = actionArrayRow3;
// Send Selection to Destination
state.ctxmenu.sendSelected = document.createElement("select");
@@ -872,10 +1104,15 @@ const selectTransformTool = () =>
array.appendChild(state.ctxmenu.keepAspectRatioLabel);
array.appendChild(state.ctxmenu.mirrorSelectionCheckbox);
array.appendChild(state.ctxmenu.useClipboardLabel);
+
+ array.appendChild(state.ctxmenu.toNewLayerLabel);
+
menu.appendChild(array);
menu.appendChild(state.ctxmenu.selectionPeekOpacitySlider);
menu.appendChild(state.ctxmenu.actionArray);
menu.appendChild(state.ctxmenu.visibleActionArray);
+ menu.appendChild(state.ctxmenu.actionArrayRow3);
+
if (global.webui && global.webui.destinations) {
while (state.ctxmenu.sendSelected.lastChild.value !== "None") {
state.ctxmenu.sendSelected.removeChild(
diff --git a/pages/embed.test.html b/pages/embed.test.html
index 420c634..ee7c2e4 100644
--- a/pages/embed.test.html
+++ b/pages/embed.test.html
@@ -8,7 +8,7 @@