diff --git a/ui/index.html b/ui/index.html index 2a99155f..d845c985 100644 --- a/ui/index.html +++ b/ui/index.html @@ -72,19 +72,23 @@ -
- - diff --git a/ui/media/css/image-editor.css b/ui/media/css/image-editor.css index 06f93977..b0f61684 100644 --- a/ui/media/css/image-editor.css +++ b/ui/media/css/image-editor.css @@ -85,8 +85,13 @@ opacity: 0; } +.image_editor_tool .editor-options-container { + flex-wrap: wrap; +} + .image_editor_tool .editor-options-container > * { padding: 2px; + flex: 50%; } .editor-controls-center { @@ -108,16 +113,6 @@ top: 0; } -.editor-controls-center .cursor-icon { - position: absolute; - left: 0px; - right: 0px; - pointer-events: none; - opacity: 0; - width: 0px; - text-shadow: 0 0 5px black; -} - .editor-controls-right { padding: 32px; display: flex; @@ -200,6 +195,10 @@ #init_image_preview_container .button { display: flex; } -#image-editor .button { + +.image-editor-popup .button { display: flex; +} +.image-editor-popup h4 { + text-align: left; } \ No newline at end of file diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 93b334c4..09e2fb94 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -44,9 +44,6 @@ code { margin-top: 5px; display: block; } -.image_preview_container { - margin-top: 6px; -} .image_clear_btn { position: absolute; transform: translate(30%, -30%); @@ -459,31 +456,35 @@ img { display: none; } -#init_image_preview_container:not(.has-image) #inpaintingEditor, -#init_image_preview_container:not(.has-image) #samplerSelection, +#init_image_preview_container { + display: flex; + margin-top: 6px; + margin-bottom: 8px; +} + #init_image_preview_container:not(.has-image) #init_image_wrapper, -#init_image_preview_container:not(.has-image) #init_image_button_inpaint { +#init_image_preview_container:not(.has-image) #inpaint_button_container { display: none; } -#init_image_preview_container:not(.has-image) { - grid-template-columns: 50% 50%; + +#init_image_buttons { + display: flex; + gap: 8px; } -#init_image_preview_container { - padding: 4px; - display: grid; - grid-template-columns: max-content auto; - grid-template-rows: 33% 33% 33%; - gap: 8px +#init_image_preview_container.has-image #init_image_buttons { + flex-direction: column; + padding-left: 8px; } -#init_image_preview_container > div:not(:first-child) { +#init_image_buttons .button { position: relative; - min-height: 32px; + height: 32px; + width: 150px; } -#init_image_preview_container > div:not(:first-child) > input { +#init_image_buttons .button > input { position: absolute; left: 0; top: 0; @@ -492,6 +493,12 @@ img { opacity: 0; } +#inpaint_button_container { + display: flex; + align-items: center; + gap: 8px; +} + #init_image_wrapper { grid-row: span 3; position: relative; @@ -614,20 +621,27 @@ input[type="file"] { button, input::file-selector-button, .button { - padding: 2px 4px; - border-radius: 4px; + padding: 6px; + border-radius: var(--input-border-radius); background: var(--button-color); color: var(--button-text-color); border: var(--button-border); + height: 24px; align-items: center; justify-content: center; cursor: pointer; + box-shadow: 2px 2px 1px 1px #00000088; } .button i { margin-right: 8px; } +button:hover, +.button:hover { + background: var(--background-color4) +} + input::file-selector-button { padding: 0px 4px; height: 19px; @@ -1029,11 +1043,6 @@ i.active { float: right; font-weight: bold; } -button:hover, -.button:hover { - transition-duration: 0.1s; - background: hsl(var(--accent-hue), 100%, calc(var(--accent-lightness) + 6%)); -} button:active { transition-duration: 0.1s; diff --git a/ui/media/css/themes.css b/ui/media/css/themes.css index 4528be8f..3cb02ab3 100644 --- a/ui/media/css/themes.css +++ b/ui/media/css/themes.css @@ -19,7 +19,7 @@ --input-border-color: var(--background-color4); --button-text-color: var(--input-text-color); - --button-color: var(--accent-color); + --button-color: var(--input-background-color); --button-border: none; /* other */ @@ -42,7 +42,6 @@ --background-color4: #cccccc; --text-color: black; - --button-text-color: white; --input-text-color: black; --input-background-color: #f8f9fa; diff --git a/ui/media/images/fa-eraser.png b/ui/media/images/fa-eraser.png new file mode 100644 index 00000000..3176c9ec Binary files /dev/null and b/ui/media/images/fa-eraser.png differ diff --git a/ui/media/images/fa-eye-dropper.png b/ui/media/images/fa-eye-dropper.png new file mode 100644 index 00000000..bbad9a12 Binary files /dev/null and b/ui/media/images/fa-eye-dropper.png differ diff --git a/ui/media/images/fa-pencil.png b/ui/media/images/fa-pencil.png new file mode 100644 index 00000000..1a6de9da Binary files /dev/null and b/ui/media/images/fa-pencil.png differ diff --git a/ui/media/js/dnd.js b/ui/media/js/dnd.js index fae7c5ae..53b55ac7 100644 --- a/ui/media/js/dnd.js +++ b/ui/media/js/dnd.js @@ -128,10 +128,12 @@ const TASK_MAPPING = { }, mask: { name: 'Mask', setUI: (mask) => { - inpaintingEditor.setImg(mask) + setTimeout(() => { // add a delay to insure this happens AFTER the main image loads (which reloads the inpainter) + imageInpainter.setImg(mask) + }, 250) maskSetting.checked = Boolean(mask) }, - readUI: () => (maskSetting.checked ? inpaintingEditor.getImg() : undefined), + readUI: () => (maskSetting.checked ? imageInpainter.getImg() : undefined), parse: (val) => val }, @@ -290,18 +292,11 @@ function restoreTaskToUI(task, fieldsToSkip) { // Show the source picture if present initImagePreview.src = (task.reqBody.init_image == undefined ? '' : task.reqBody.init_image) if (IMAGE_REGEX.test(initImagePreview.src)) { - Boolean(task.reqBody.mask) ? inpaintingEditor.setImg(task.reqBody.mask) : inpaintingEditor.resetBackground() - initImagePreviewContainer.style.display = 'block' - inpaintingEditorContainer.style.display = 'none' - promptStrengthContainer.style.display = 'table-row' - //samplerSelectionContainer.style.display = 'none' - // maskSetting.checked = false - inpaintingEditorContainer.style.display = maskSetting.checked ? 'block' : 'none' - } else { - initImagePreviewContainer.style.display = 'none' - // inpaintingEditorContainer.style.display = 'none' - promptStrengthContainer.style.display = 'none' - // maskSetting.style.display = 'none' + if (Boolean(task.reqBody.mask)) { + setTimeout(() => { // add a delay to insure this happens AFTER the main image loads (which reloads the inpainter) + imageInpainter.setImg(task.reqBody.mask) + }, 250) + } } } function readUI() { diff --git a/ui/media/js/image-editor.js b/ui/media/js/image-editor.js index d39a3ae4..3b54e755 100644 --- a/ui/media/js/image-editor.js +++ b/ui/media/js/image-editor.js @@ -3,18 +3,11 @@ var editorControlsLeft = document.getElementById("image-editor-controls-left") const IMAGE_EDITOR_MAX_SIZE = 800 const IMAGE_EDITOR_BUTTONS = [ - { - name: "Clear", - icon: "fa-solid fa-xmark", - handler: editor => { - editor.clear() - } - }, { name: "Cancel", icon: "fa-regular fa-circle-xmark", handler: editor => { - editor.close() + editor.hide() } }, { @@ -26,16 +19,93 @@ const IMAGE_EDITOR_BUTTONS = [ } ] +const defaultToolBegin = (editor, ctx, x, y, is_overlay = false) => { + ctx.beginPath() + ctx.moveTo(x, y) +} +const defaultToolMove = (editor, ctx, x, y, is_overlay = false) => { + ctx.lineTo(x, y) + if (is_overlay) { + ctx.clearRect(0, 0, editor.width, editor.height) + ctx.stroke() + } +} +const defaultToolEnd = (editor, ctx, x, y, is_overlay = false) => { + ctx.stroke() + if (is_overlay) { + ctx.clearRect(0, 0, editor.width, editor.height) + } +} + const IMAGE_EDITOR_TOOLS = [ { id: "draw", name: "Draw", - icon: "fa-solid fa-pencil" + icon: "fa-solid fa-pencil", + cursor: "url(/media/images/fa-pencil.png) 0 24, pointer", + begin: defaultToolBegin, + move: defaultToolMove, + end: defaultToolEnd }, { id: "erase", name: "Erase", - icon: "fa-solid fa-eraser" + icon: "fa-solid fa-eraser", + cursor: "url(/media/images/fa-eraser.png) 0 18, pointer", + begin: defaultToolBegin, + move: (editor, ctx, x, y, is_overlay = false) => { + ctx.lineTo(x, y) + if (is_overlay) { + ctx.clearRect(0, 0, editor.width, editor.height) + ctx.globalCompositeOperation = "source-over" + ctx.globalAlpha = 1 + ctx.filter = "none" + ctx.drawImage(editor.canvas_current, 0, 0) + editor.setBrush(editor.layers.overlay) + ctx.stroke() + editor.canvas_current.style.opacity = 0 + } + }, + end: (editor, ctx, x, y, is_overlay = false) => { + ctx.stroke() + if (is_overlay) { + ctx.clearRect(0, 0, editor.width, editor.height) + editor.canvas_current.style.opacity = "" + } + }, + setBrush: (editor, layer) => { + layer.ctx.globalCompositeOperation = "destination-out" + } + }, + { + id: "colorpicker", + name: "Color Picker", + icon: "fa-solid fa-eye-dropper", + cursor: "url(/media/images/fa-eye-dropper.png) 0 24, pointer", + begin: (editor, ctx, x, y, is_overlay = false) => { + var img_rgb = editor.layers.background.ctx.getImageData(x, y, 1, 1).data + var drawn_rgb = editor.ctx_current.getImageData(x, y, 1, 1).data + var drawn_opacity = drawn_rgb[3] / 255 + editor.custom_color_input.value = rgbToHex({ + r: (drawn_rgb[0] * drawn_opacity) + (img_rgb[0] * (1 - drawn_opacity)), + g: (drawn_rgb[1] * drawn_opacity) + (img_rgb[1] * (1 - drawn_opacity)), + b: (drawn_rgb[2] * drawn_opacity) + (img_rgb[2] * (1 - drawn_opacity)), + }) + editor.custom_color_input.dispatchEvent(new Event("change")) + }, + move: (editor, ctx, x, y, is_overlay = false) => {}, + end: (editor, ctx, x, y, is_overlay = false) => {} + } +] + +const IMAGE_EDITOR_ACTIONS = [ + { + id: "clear", + name: "Clear", + icon: "fa-solid fa-xmark", + handler: (editor) => { + editor.ctx_current.clearRect(0, 0, editor.width, editor.height) + } } ] @@ -105,7 +175,7 @@ var IMAGE_EDITOR_SECTIONS = [ name: "opacity", title: "Opacity", default: 0, - options: [ 0, 0.25, 0.5, 0.75, 1 ], + options: [ 0, 0.2, 0.4, 0.6, 0.8 ], initElement: (element, option) => { element.style.background = `repeating-conic-gradient(rgba(0, 0, 0, ${option}) 0% 25%, rgba(255, 255, 255, ${option}) 0% 50%) 50% / 10px 10px` } @@ -130,17 +200,118 @@ var IMAGE_EDITOR_SECTIONS = [ } ] +class EditorHistory { + constructor(editor) { + this.editor = editor + this.events = [] // stack of all events (actions/edits) + this.current_edit = null + this.rewind_index = 0 // how many events back into the history we've rewound to. (current state is just after event at index 'length - this.rewind_index - 1') + } + push(event) { + // probably add something here eventually to save state every x events + if (this.rewind_index != 0) { + this.events = this.events.slice(0, 0 - this.rewind_index) + this.rewind_index = 0 + } + var snapshot_frequency = 20 // (every x edits, take a snapshot of the current drawing state, for faster rewinding) + if (this.events.length > 0 && this.events.length % snapshot_frequency == 0) { + event.snapshot = this.editor.layers.drawing.ctx.getImageData(0, 0, this.editor.width, this.editor.height) + } + this.events.push(event) + } + pushAction(action) { + this.push({ + type: "action", + id: action + }); + } + editBegin(x, y) { + this.current_edit = { + type: "edit", + id: this.editor.getOptionValue("tool"), + options: Object.assign({}, this.editor.options), + points: [ { x: x, y: y } ] + } + } + editMove(x, y) { + if (this.current_edit) { + this.current_edit.points.push({ x: x, y: y }) + } + } + editEnd(x, y) { + if (this.current_edit) { + this.push(this.current_edit) + this.current_edit = null + } + } + clear() { + this.events = [] + } + undo() { + this.rewindTo(this.rewind_index + 1) + } + redo() { + this.rewindTo(this.rewind_index - 1) + } + rewindTo(new_rewind_index) { + if (new_rewind_index < 0 || new_rewind_index > this.events.length) { + return; // do nothing if target index is out of bounds + } + + var ctx = this.editor.layers.drawing.ctx + ctx.clearRect(0, 0, this.editor.width, this.editor.height) + + var target_index = this.events.length - 1 - new_rewind_index + var snapshot_index = target_index + while (snapshot_index > -1) { + if (this.events[snapshot_index].snapshot) { + break + } + snapshot_index-- + } + + if (snapshot_index != -1) { + ctx.putImageData(this.events[snapshot_index].snapshot, 0, 0); + } + + for (var i = (snapshot_index + 1); i <= target_index; i++) { + var event = this.events[i] + if (event.type == "action") { + var action = IMAGE_EDITOR_ACTIONS.find(a => a.id == event.id) + action.handler(this.editor) + } + else if (event.type == "edit") { + var tool = IMAGE_EDITOR_TOOLS.find(t => t.id == event.id) + this.editor.setBrush(this.editor.layers.drawing, event.options) + + var first_point = event.points[0] + tool.begin(this.editor, ctx, first_point.x, first_point.y) + for (var point_i = 1; point_i < event.points.length; point_i++) { + tool.move(this.editor, ctx, event.points[point_i].x, event.points[point_i].y) + } + var last_point = event.points[event.points.length - 1] + tool.end(this.editor, ctx, last_point.x, last_point.y) + } + } + + // re-set brush to current settings + this.editor.setBrush(this.editor.layers.drawing) + + this.rewind_index = new_rewind_index + } +} + class ImageEditor { constructor(popup, inpainter = false) { this.inpainter = inpainter this.popup = popup + this.history = new EditorHistory(this) if (inpainter) { this.popup.classList.add("inpainter") } this.drawing = false - this.dropper_active = false + this.temp_previous_tool = null // used for the ctrl-colorpicker functionality this.container = popup.querySelector(".editor-controls-center > div") - this.cursor_icon = document.createElement("i") this.layers = {} var layer_names = [ "background", @@ -158,11 +329,6 @@ class ImageEditor { } }) - this.setSize(512, 512) - - this.cursor_icon.classList.add("cursor-icon") - this.container.appendChild(this.cursor_icon) - // add mouse handlers this.container.addEventListener("mousedown", this.mouseHandler.bind(this)) this.container.addEventListener("mouseup", this.mouseHandler.bind(this)) @@ -174,16 +340,6 @@ class ImageEditor { this.container.addEventListener("touchmove", this.mouseHandler.bind(this)) this.container.addEventListener("touchcancel", this.mouseHandler.bind(this)) this.container.addEventListener("touchend", this.mouseHandler.bind(this)) - // setup forwarding for keypresses so the eyedropper works accordingly - var mouseHandlerHelper = this.mouseHandler.bind(this) - this.container.addEventListener("mouseenter",function() { - document.addEventListener("keyup", mouseHandlerHelper) - document.addEventListener("keydown", mouseHandlerHelper) - }) - this.container.addEventListener("mouseout",function() { - document.removeEventListener("keyup", mouseHandlerHelper) - document.removeEventListener("keydown", mouseHandlerHelper) - }) // initialize editor controls this.options = {} @@ -239,7 +395,36 @@ class ImageEditor { buttonContainer.appendChild(element) element.addEventListener("click", event => button.handler(this)) }) + var actionsContainer = document.createElement("div") + var actionsTitle = document.createElement("h4") + actionsTitle.textContent = "Actions" + actionsContainer.appendChild(actionsTitle); + IMAGE_EDITOR_ACTIONS.forEach(action => { + var element = document.createElement("div") + var icon = document.createElement("i") + element.className = "image-editor-button button" + icon.className = action.icon + element.appendChild(icon) + element.append(action.name) + actionsContainer.appendChild(element) + element.addEventListener("click", event => this.runAction(action.id)) + }) + this.popup.querySelector(".editor-controls-right").appendChild(actionsContainer) this.popup.querySelector(".editor-controls-right").appendChild(buttonContainer) + + this.keyHandlerBound = this.keyHandler.bind(this) + + this.setSize(512, 512) + } + show() { + this.popup.classList.add("active") + document.addEventListener("keydown", this.keyHandlerBound) + document.addEventListener("keyup", this.keyHandlerBound) + } + hide() { + this.popup.classList.remove("active") + document.removeEventListener("keydown", this.keyHandlerBound) + document.removeEventListener("keyup", this.keyHandlerBound) } setSize(width, height) { if (width == this.width && height == this.height) { @@ -272,13 +457,15 @@ class ImageEditor { this.saveImage() // We've reset the size of the image so inpainting is different } this.setBrush() + this.history.clear() } - setCursorIcon(icon_class = null) { - if (icon_class == null) { - var tool = this.getOptionValue("tool") - icon_class = IMAGE_EDITOR_TOOLS.find(t => t.id == tool).icon - } - this.cursor_icon.className = `cursor-icon ${icon_class}` + get tool() { + var tool_id = this.getOptionValue("tool") + return IMAGE_EDITOR_TOOLS.find(t => t.id == tool_id); + } + loadTool() { + this.drawing = false + this.container.style.cursor = this.tool.cursor; } setImage(url, width, height) { this.setSize(width, height) @@ -297,6 +484,7 @@ class ImageEditor { this.layers.background.ctx.rect(0, 0, this.width, this.height) this.layers.background.ctx.fill() } + this.history.clear() } saveImage() { if (!this.inpainter) { @@ -312,31 +500,47 @@ class ImageEditor { .some(channel => channel !== 0) maskSetting.checked = !is_blank } - this.close() + this.hide() } getImg() { // a drop-in replacement of the drawingboard version return this.layers.drawing.canvas.toDataURL() } - close() { - this.popup.classList.remove("active") + setImg(dataUrl) { // a drop-in replacement of the drawingboard version + var image = new Image() + image.onload = () => { + var ctx = this.layers.drawing.ctx; + ctx.clearRect(0, 0, this.width, this.height) + ctx.globalCompositeOperation = "source-over" + ctx.globalAlpha = 1 + ctx.filter = "none" + ctx.drawImage(image, 0, 0, this.width, this.height) + this.setBrush(this.layers.drawing) + } + image.src = dataUrl } - clear() { - this.ctx_current.clearRect(0, 0, this.width, this.height) + runAction(action_id) { + var action = IMAGE_EDITOR_ACTIONS.find(a => a.id == action_id) + this.history.pushAction(action_id) + action.handler(this) } - get eraser_active() { - return this.getOptionValue("tool") == "erase" - } - setBrush(layer = null) { + setBrush(layer = null, options = null) { + if (options == null) { + options = this.options + } if (layer) { layer.ctx.lineCap = "round" layer.ctx.lineJoin = "round" - layer.ctx.lineWidth = this.getOptionValue("brush_size") - layer.ctx.fillStyle = this.getOptionValue("color") - layer.ctx.strokeStyle = this.getOptionValue("color") - var sharpness = parseInt(this.getOptionValue("sharpness") * this.getOptionValue("brush_size")) + layer.ctx.lineWidth = options.brush_size + layer.ctx.fillStyle = options.color + layer.ctx.strokeStyle = options.color + var sharpness = parseInt(options.sharpness * options.brush_size) layer.ctx.filter = sharpness == 0 ? `none` : `blur(${sharpness}px)` - layer.ctx.globalAlpha = (1 - this.getOptionValue("opacity")) - layer.ctx.globalCompositeOperation = this.eraser_active ? "destination-out" : "source-over" + layer.ctx.globalAlpha = (1 - options.opacity) + layer.ctx.globalCompositeOperation = "source-over" + var tool = IMAGE_EDITOR_TOOLS.find(t => t.id == options.tool) + if (tool && tool.setBrush) { + tool.setBrush(editor, layer) + } } else { Object.values([ "drawing", "overlay" ]).map(name => this.layers[name]).forEach(l => { @@ -353,6 +557,39 @@ class ImageEditor { get canvas_current() { return this.layers.drawing.canvas } + keyHandler(event) { // handles keybinds like ctrl+z, ctrl+y + if (!this.popup.classList.contains("active")) { + document.removeEventListener("keydown", this.keyHandlerBound) + document.removeEventListener("keyup", this.keyHandlerBound) + return // this catches if something else closes the window but doesnt properly unbind the key handler + } + + // keybindings + if (event.type == "keydown") { + if ((event.key == "z" || event.key == "Z") && event.ctrlKey) { + if (!event.shiftKey) { + this.history.undo() + } + else { + this.history.redo() + } + } + if (event.key == "y" && event.ctrlKey) { + this.history.redo() + } + } + + // dropper ctrl holding handler stuff + var dropper_active = this.temp_previous_tool != null; + if (dropper_active && !event.ctrlKey) { + this.selectOption("tool", IMAGE_EDITOR_TOOLS.findIndex(t => t.id == this.temp_previous_tool)) + this.temp_previous_tool = null + } + else if (!dropper_active && event.ctrlKey) { + this.temp_previous_tool = this.getOptionValue("tool") + this.selectOption("tool", IMAGE_EDITOR_TOOLS.findIndex(t => t.id == "colorpicker")) + } + } mouseHandler(event) { var bbox = this.layers.overlay.canvas.getBoundingClientRect() var x = (event.clientX || 0) - bbox.left @@ -375,78 +612,26 @@ class ImageEditor { event.preventDefault() // do drawing-related stuff if (type == "mousedown" || (type == "mouseenter" && event.buttons == 1)) { - if (this.dropper_active) { - var img_rgb = this.layers.background.ctx.getImageData(x, y, 1, 1).data - var drw_rgb = this.ctx_current.getImageData(x, y, 1, 1).data - var drw_opacity = drw_rgb[3] / 255 - var test = rgbToHex({ - r: (drw_rgb[0] * drw_opacity) + (img_rgb[0] * (1 - drw_opacity)), - g: (drw_rgb[1] * drw_opacity) + (img_rgb[1] * (1 - drw_opacity)), - b: (drw_rgb[2] * drw_opacity) + (img_rgb[2] * (1 - drw_opacity)), - }) - this.custom_color_input.value = test - this.custom_color_input.dispatchEvent(new Event("change")) - } - else { - this.drawing = true - this.ctx_overlay.beginPath() - this.ctx_overlay.moveTo(x, y) - this.ctx_current.beginPath() - this.ctx_current.moveTo(x, y) - } + this.drawing = true + this.tool.begin(this, this.ctx_current, x, y) + this.tool.begin(this, this.ctx_overlay, x, y, true) + this.history.editBegin(x, y) } if (type == "mouseup" || type == "mousemove") { if (this.drawing) { if (x > 0 && y > 0) { - this.ctx_current.lineTo(x, y) - this.ctx_overlay.lineTo(x, y) - - // This isnt super efficient, but its the only way ive found to have clean updating for the drawing - this.ctx_overlay.clearRect(0, 0, this.width, this.height) - if (this.eraser_active) { - this.ctx_overlay.globalCompositeOperation = "source-over" - this.ctx_overlay.globalAlpha = 1 - this.ctx_overlay.filter = "none" - this.ctx_overlay.drawImage(this.canvas_current, 0, 0) - this.setBrush(this.layers.overlay) - this.canvas_current.style.opacity = 0 - } - - this.ctx_overlay.stroke() + this.tool.move(this, this.ctx_current, x, y) + this.tool.move(this, this.ctx_overlay, x, y, true) + this.history.editMove(x, y) } } } if (type == "mouseup" || type == "mouseout") { if (this.drawing) { this.drawing = false - this.ctx_current.stroke() - this.ctx_overlay.clearRect(0, 0, this.width, this.height) - - if (this.eraser_active) { - this.canvas_current.style.opacity = "" - } - } - } - - // cursor-icon stuff - if (type == "mousemove") { - this.cursor_icon.style.left = `${x + 10}px` - this.cursor_icon.style.top = `${y + 20}px` - } - if (type == "mouseenter") { - this.cursor_icon.style.opacity = 1 - } - if (type == "mouseout") { - this.cursor_icon.style.opacity = 0 - } - if ([ "mouseenter", "mousemove", "keydown", "keyup" ].includes(type)) { - if (this.dropper_active && !event.ctrlKey) { - this.dropper_active = false - this.setCursorIcon() - } - else if (!this.dropper_active && event.ctrlKey) { - this.dropper_active = true - this.setCursorIcon("fa-solid fa-eye-dropper") + this.tool.end(this, this.ctx_current, x, y) + this.tool.end(this, this.ctx_overlay, x, y, true) + this.history.editEnd(x, y) } } } @@ -465,7 +650,7 @@ class ImageEditor { // change the editor this.setBrush() if (section.name == "tool") { - this.setCursorIcon() + this.loadTool() } } } @@ -485,8 +670,8 @@ imageEditor.setImage(null, 512, 512) imageInpainter.setImage(null, 512, 512) document.getElementById("init_image_button_draw").addEventListener("click", () => { - document.getElementById("image-editor").classList.toggle("active") + imageEditor.show() }) document.getElementById("init_image_button_inpaint").addEventListener("click", () => { - document.getElementById("image-inpainter").classList.toggle("active") + imageInpainter.show() }) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 07bd8995..07f49825 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -1254,7 +1254,7 @@ function checkRandomSeed() { randomSeedField.addEventListener('input', checkRandomSeed) checkRandomSeed() -function showInitImagePreview() { +function loadImg2ImgFromFile() { if (initImageSelector.files.length === 0) { return } @@ -1270,27 +1270,32 @@ function showInitImagePreview() { reader.readAsDataURL(file) } } -initImageSelector.addEventListener('change', showInitImagePreview) -showInitImagePreview() +initImageSelector.addEventListener('change', loadImg2ImgFromFile) +loadImg2ImgFromFile() -initImagePreview.addEventListener('load', function() { +function img2imgLoad() { promptStrengthContainer.style.display = 'table-row' + samplerSelectionContainer.style.display = "none" initImagePreviewContainer.classList.add("has-image") initImageSizeBox.textContent = initImagePreview.naturalWidth + " x " + initImagePreview.naturalHeight imageEditor.setImage(this.src, initImagePreview.naturalWidth, initImagePreview.naturalHeight) imageInpainter.setImage(this.src, parseInt(widthField.value), parseInt(heightField.value)) -}) +} -initImageClearBtn.addEventListener('click', function() { +function img2imgUnload() { initImageSelector.value = null initImagePreview.src = '' maskSetting.checked = false - promptStrengthContainer.style.display = 'none' + promptStrengthContainer.style.display = "none" + samplerSelectionContainer.style.display = "" initImagePreviewContainer.classList.remove("has-image") imageEditor.setImage(null, parseInt(widthField.value), parseInt(heightField.value)) -}) + +} +initImagePreview.addEventListener('load', img2imgLoad) +initImageClearBtn.addEventListener('click', img2imgUnload) maskSetting.addEventListener('click', function() { onDimensionChange()