mirror of
https://github.com/thomasasfk/sd-webui-aspect-ratio-helper.git
synced 2026-04-28 18:22:04 +00:00
Lots of quality of life changes (#41)
- Support different tabs on img2img now - Fix step counter sometimes not being multiple of 8 - Align text to center on selects - Lots of quality of life code changes
This commit is contained in:
@@ -1,165 +1,274 @@
|
||||
// https://github.com/Gerschel/stable-diffusion-webui/blob/742d86eed4d07eef7db65b3d943f85bdbafc26e4/javascript/ComponentControllers.js#L176
|
||||
class ContainerController {
|
||||
constructor(element) {
|
||||
this.element = element;
|
||||
this.num = this.element.querySelector('input[type=number]');
|
||||
this.range = this.element.querySelector('input[type=range]');
|
||||
}
|
||||
// constants
|
||||
const _OFF = "Off"; // Don't use the functionality at all
|
||||
const _LOCK = '🔓'; // Aspect ratio is "locked"
|
||||
const _IMAGE = '🖼️'; // Aspect ratio is "locked" to that of the image
|
||||
|
||||
getVal() {
|
||||
return this.num.value;
|
||||
}
|
||||
const _MAXIMUM_DIMENSION = 2048;
|
||||
const _MINIMUM_DIMENSION = 64;
|
||||
|
||||
disable() {
|
||||
this.num.setAttribute('disabled', true);
|
||||
this.range.setAttribute('disabled', true);
|
||||
}
|
||||
|
||||
enable() {
|
||||
this.num.removeAttribute('disabled');
|
||||
this.range.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
updateVal(text) {
|
||||
this.num.value = text;
|
||||
this.range.value = text;
|
||||
}
|
||||
|
||||
updateMin(text) {
|
||||
this.num.min = text;
|
||||
this.range.min = text;
|
||||
}
|
||||
|
||||
eventHandler() {
|
||||
this.element.dispatchEvent(
|
||||
new Event("input")
|
||||
);
|
||||
this.num.dispatchEvent(
|
||||
new Event("input")
|
||||
);
|
||||
this.range.dispatchEvent(
|
||||
new Event("input")
|
||||
);
|
||||
}
|
||||
|
||||
setVal(text) {
|
||||
this.updateVal(
|
||||
ContainerController.roundToClosestMultipleOf8(text)
|
||||
);
|
||||
this.eventHandler();
|
||||
}
|
||||
|
||||
static roundToClosestMultipleOf8(num) {
|
||||
return Math.round(Number(num) / 8) * 8;
|
||||
}
|
||||
const _TAB_STRING_TO_IMAGE_ID_MAP = {
|
||||
'img2img': 'img2img_image',
|
||||
'sketch': 'img2img_sketch',
|
||||
'inpaint': 'img2maskimg',
|
||||
'inpaint sketch': 'inpaint_sketch',
|
||||
'inpaint upload': 'img_inpaint_base',
|
||||
}
|
||||
|
||||
function _reverseAspectRatio(ar) {
|
||||
if (_NON_CONFIGURABLE.includes(ar)) return;
|
||||
const [width, height] = ar.split(":");
|
||||
const _IMAGE_INPUT_CONTAINER_IDS = Array.from(Object.values(_TAB_STRING_TO_IMAGE_ID_MAP));
|
||||
|
||||
const getSelectedImage2ImageTab = () => {
|
||||
return document.querySelector('#img2img_settings > div > div > button.selected').textContent.toLowerCase().trim();
|
||||
}
|
||||
|
||||
const getCurrentImage = () => {
|
||||
const currentTab = getSelectedImage2ImageTab();
|
||||
const currentTabImageId = _TAB_STRING_TO_IMAGE_ID_MAP[currentTab];
|
||||
return document.getElementById(currentTabImageId).querySelector('img');
|
||||
}
|
||||
|
||||
// utility functions
|
||||
const roundToClosestMultiple = (num, multiple) => {
|
||||
const rounded = Math.round(Number(num) / multiple) * multiple;
|
||||
return rounded;
|
||||
}
|
||||
|
||||
|
||||
const aspectRatioFromStr = (ar) => {
|
||||
if (!ar.includes(':')) return;
|
||||
return ar.split(':').map(x => Number(x));
|
||||
}
|
||||
|
||||
const reverseAspectRatio = (ar) => {
|
||||
if (!ar.includes(':')) return;
|
||||
const [width, height] = ar.split(':');
|
||||
return `${height}:${width}`;
|
||||
}
|
||||
|
||||
const clampToBoundaries = (width, height) => {
|
||||
const aspectRatio = width / height;
|
||||
width = Math.max(Math.min(width, _MAXIMUM_DIMENSION), _MINIMUM_DIMENSION);
|
||||
height = Math.max(Math.min(height, _MAXIMUM_DIMENSION), _MINIMUM_DIMENSION);
|
||||
if (width / height > aspectRatio) {
|
||||
height = Math.round(width / aspectRatio);
|
||||
} else if (width / height < aspectRatio) {
|
||||
width = Math.round(height * aspectRatio);
|
||||
}
|
||||
|
||||
const _OFF = "Off";
|
||||
const _LOCK = '🔓';
|
||||
const _IMAGE = '🖼️';
|
||||
if (width > _MAXIMUM_DIMENSION) {
|
||||
width = _MAXIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
const _NON_CONFIGURABLE = [_OFF, _LOCK, _IMAGE]
|
||||
if (height < _MINIMUM_DIMENSION) {
|
||||
height = _MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
const _MAXIMUM = 2048;
|
||||
const _MINIMUM = 64;
|
||||
return [width, height];
|
||||
}
|
||||
|
||||
class AspectRatioController {
|
||||
constructor(widthContainer, heightContainer, aspectRatio) {
|
||||
this.widthContainer = new ContainerController(widthContainer);
|
||||
this.heightContainer = new ContainerController(heightContainer);
|
||||
this.dimensions = {
|
||||
widthInput: this.widthContainer.num,
|
||||
widthRange: this.widthContainer.range,
|
||||
heightInput: this.heightContainer.num,
|
||||
heightRange: this.heightContainer.range,
|
||||
const getOptions = () => {
|
||||
return window.opts.arh_javascript_aspect_ratio.split(',').map(o => o.trim());
|
||||
}
|
||||
|
||||
const reverseAllOptions = () => {
|
||||
const allAspectRatioOptions = Array.from(gradioApp().querySelectorAll('.ar-option'));
|
||||
allAspectRatioOptions.forEach(el => {
|
||||
const reversed = reverseAspectRatio(el.value);
|
||||
if (reversed) {
|
||||
el.value = reversed;
|
||||
el.textContent = reversed;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class SelectController {
|
||||
constructor(page, defaultOptions, controller) {
|
||||
this.page = page;
|
||||
this.options = [...new Set([...defaultOptions, ...getOptions()])];
|
||||
this.switchButton = gradioApp().getElementById(page + '_res_switch_btn');
|
||||
|
||||
const wrapperDiv = document.createElement('div');
|
||||
wrapperDiv.setAttribute("id", `${this.page}_size_toolbox`);
|
||||
wrapperDiv.setAttribute("class", "flex flex-col relative col gap-4");
|
||||
wrapperDiv.setAttribute("style", "min-width: min(320px, 100%); flex-grow: 0");
|
||||
wrapperDiv.innerHTML = `
|
||||
<div id="${this.page}_ratio" class="gr-block gr-box relative w-full border-solid border border-gray-200 gr-padded">
|
||||
<select id="${this.page}_select_aspect_ratio" class="gr-box gr-input w-full disabled:cursor-not-allowed">
|
||||
${this.options.map(r => {
|
||||
return '<option class="ar-option">' + r + '</option>'
|
||||
}).join('\n')}
|
||||
</select>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const parent = this.switchButton.parentNode;
|
||||
parent.removeChild(this.switchButton);
|
||||
wrapperDiv.appendChild(this.switchButton);
|
||||
parent.insertBefore(wrapperDiv, parent.lastChild.previousElementSibling);
|
||||
|
||||
const originalBGC = this.switchButton.style.backgroundColor;
|
||||
this.getSelectElement().onchange = () => {
|
||||
const picked = this.getCurrentOption();
|
||||
if (_IMAGE === picked) {
|
||||
this.switchButton.disabled = true;
|
||||
this.switchButton.style.backgroundColor = 'black';
|
||||
} else {
|
||||
this.switchButton.removeAttribute('disabled')
|
||||
this.switchButton.style.backgroundColor = originalBGC;
|
||||
}
|
||||
|
||||
controller.setAspectRatio(picked);
|
||||
};
|
||||
|
||||
Object.values(this.dimensions).forEach(dimension => {
|
||||
dimension.addEventListener('change', (e) => {
|
||||
this.switchButton.onclick = () => {
|
||||
reverseAllOptions();
|
||||
const picked = this.getCurrentOption();
|
||||
if (_LOCK === picked) {
|
||||
controller.setAspectRatio(`${controller.heightRatio}:${controller.widthRatio}`);
|
||||
} else {
|
||||
controller.setAspectRatio(picked);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getSelectElement() {
|
||||
return gradioApp().getElementById(`${this.page}_select_aspect_ratio`);
|
||||
}
|
||||
|
||||
getCurrentOption() {
|
||||
const selectElement = this.getSelectElement();
|
||||
const options = Array.from(selectElement);
|
||||
return options[selectElement.selectedIndex].value;
|
||||
}
|
||||
}
|
||||
|
||||
class SliderController {
|
||||
constructor(element) {
|
||||
this.element = element
|
||||
this.numberInput = this.element.querySelector('input[type=number]');
|
||||
this.rangeInput = this.element.querySelector('input[type=range]');
|
||||
this.inputs = [this.numberInput, this.rangeInput];
|
||||
}
|
||||
|
||||
getVal() {
|
||||
return Number(this.numberInput.value);
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.inputs.forEach(input => {
|
||||
input.disabled = true
|
||||
})
|
||||
}
|
||||
|
||||
enable() {
|
||||
this.inputs.forEach(input => {
|
||||
input.disabled = false
|
||||
})
|
||||
}
|
||||
|
||||
updateVal(value) {
|
||||
this.inputs.forEach(input => {
|
||||
input.value = Number(value)
|
||||
})
|
||||
}
|
||||
|
||||
updateMin(value) {
|
||||
this.inputs.forEach(input => {
|
||||
input.min = roundToClosestMultiple(Number(value), 8);
|
||||
})
|
||||
}
|
||||
|
||||
triggerEvent(event) {
|
||||
this.numberInput.dispatchEvent(event)
|
||||
}
|
||||
|
||||
setVal(value) {
|
||||
value = Number(value)
|
||||
const newValue = roundToClosestMultiple(value, 8)
|
||||
this.updateVal(newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AspectRatioController {
|
||||
constructor(page, widthContainer, heightContainer, defaultOptions) {
|
||||
this.widthContainer = new SliderController(widthContainer);
|
||||
this.heightContainer = new SliderController(heightContainer);
|
||||
this.inputs = [...this.widthContainer.inputs, ...this.heightContainer.inputs];
|
||||
this.inputs.forEach(input => {
|
||||
input.addEventListener('change', (e) => {
|
||||
e.preventDefault()
|
||||
this._syncValues(dimension);
|
||||
this.maintainAspectRatio(input);
|
||||
});
|
||||
})
|
||||
|
||||
this.setAspectRatio(aspectRatio);
|
||||
this.selectController = new SelectController(page, defaultOptions, this);
|
||||
this.setAspectRatio(_OFF);
|
||||
}
|
||||
|
||||
updateInputStates() {
|
||||
if (this.isLandscapeOrSquare()) {
|
||||
this.heightContainer.disable();
|
||||
this.widthContainer.enable();
|
||||
const minByAr = Math.round(_MINIMUM_DIMENSION * this.widthRatio / this.heightRatio);
|
||||
const minimum = Math.max(minByAr, _MINIMUM_DIMENSION);
|
||||
this.widthContainer.updateMin(minimum);
|
||||
this.heightContainer.updateMin(_MINIMUM_DIMENSION);
|
||||
} else {
|
||||
this.widthContainer.disable();
|
||||
this.heightContainer.enable();
|
||||
const minByAr = Math.round(_MINIMUM_DIMENSION * this.heightRatio / this.widthRatio)
|
||||
const minimum = Math.max(minByAr, _MINIMUM_DIMENSION);
|
||||
this.heightContainer.updateMin(minimum);
|
||||
this.widthContainer.updateMin(_MINIMUM_DIMENSION);
|
||||
}
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.widthContainer.enable();
|
||||
this.heightContainer.enable();
|
||||
this.widthContainer.updateMin(_MINIMUM_DIMENSION);
|
||||
this.heightContainer.updateMin(_MINIMUM_DIMENSION);
|
||||
}
|
||||
|
||||
isLandscapeOrSquare() {
|
||||
return this.widthRatio >= this.heightRatio;
|
||||
}
|
||||
|
||||
setAspectRatio(aspectRatio) {
|
||||
this.aspectRatio = aspectRatio;
|
||||
|
||||
let wR, hR;
|
||||
if (aspectRatio === _OFF) {
|
||||
this.widthContainer.enable();
|
||||
this.heightContainer.enable();
|
||||
this.widthContainer.updateMin(_MINIMUM);
|
||||
this.heightContainer.updateMin(_MINIMUM);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aspectRatio === _IMAGE) {
|
||||
const img = gradioApp().querySelector('#img2img_image').querySelector('img');
|
||||
if (img) {
|
||||
aspectRatio = `${img.naturalWidth}:${img.naturalHeight}`;
|
||||
} else {
|
||||
aspectRatio = `1:1`
|
||||
}
|
||||
}
|
||||
|
||||
const lockedSetting = [
|
||||
this.widthContainer.getVal(),
|
||||
this.heightContainer.getVal(),
|
||||
];
|
||||
|
||||
const [widthRatio, heightRatio] = this._clampToBoundaries(
|
||||
...(
|
||||
[_LOCK, _IMAGE].includes(aspectRatio)
|
||||
? lockedSetting
|
||||
: aspectRatio.split(':')
|
||||
).map(Number)
|
||||
)
|
||||
|
||||
this.widthRatio = widthRatio;
|
||||
this.heightRatio = heightRatio;
|
||||
if (widthRatio >= heightRatio) {
|
||||
this.heightContainer.disable();
|
||||
this.widthContainer.enable();
|
||||
const minimum = Math.max(
|
||||
Math.round(_MINIMUM * widthRatio / heightRatio), _MINIMUM
|
||||
);
|
||||
this.widthContainer.updateMin(minimum);
|
||||
this.heightContainer.updateMin(_MINIMUM);
|
||||
return this.disable();
|
||||
} else if (aspectRatio === _IMAGE) {
|
||||
const img = getCurrentImage();
|
||||
wR = img && img.naturalWidth || 1;
|
||||
hR = img && img.naturalHeight || 1;
|
||||
} else if (aspectRatio === _LOCK) {
|
||||
wR = this.widthContainer.getVal();
|
||||
hR = this.heightContainer.getVal();
|
||||
} else {
|
||||
this.widthContainer.disable();
|
||||
this.heightContainer.enable();
|
||||
const minimum = Math.max(
|
||||
Math.round(_MINIMUM * heightRatio / widthRatio), _MINIMUM
|
||||
);
|
||||
this.heightContainer.updateMin(minimum);
|
||||
this.widthContainer.updateMin(_MINIMUM);
|
||||
[wR, hR] = aspectRatioFromStr(aspectRatio);
|
||||
}
|
||||
|
||||
this._syncValues();
|
||||
[wR, hR] = clampToBoundaries(wR, hR);
|
||||
|
||||
this.widthRatio = wR;
|
||||
this.heightRatio = hR;
|
||||
this.updateInputStates();
|
||||
this.maintainAspectRatio();
|
||||
}
|
||||
|
||||
_syncValues(changedElement) {
|
||||
maintainAspectRatio(changedElement) {
|
||||
if (this.aspectRatio === _OFF) return;
|
||||
if (!changedElement) {
|
||||
changedElement = {
|
||||
value: Math.max(
|
||||
...Object.values(this.dimensions).map(x => x.value)
|
||||
)
|
||||
}
|
||||
const allValues = Object.values(this.inputs).map(x => Number(x.value));
|
||||
changedElement = {value: Math.max(...allValues)};
|
||||
}
|
||||
|
||||
const aspectRatio = this.widthRatio / this.heightRatio;
|
||||
let w, h;
|
||||
if (this.widthRatio >= this.heightRatio) {
|
||||
if (this.isLandscapeOrSquare()) {
|
||||
w = Math.round(changedElement.value);
|
||||
h = Math.round(changedElement.value / aspectRatio);
|
||||
} else {
|
||||
@@ -167,153 +276,92 @@ class AspectRatioController {
|
||||
w = Math.round(changedElement.value * aspectRatio);
|
||||
}
|
||||
|
||||
const [width, height] = this._clampToBoundaries(w, h)
|
||||
const [width, height] = clampToBoundaries(w, h)
|
||||
|
||||
const inputEvent = new Event("input", { bubbles: true});
|
||||
this.widthContainer.setVal(width);
|
||||
this.widthContainer.triggerEvent(inputEvent);
|
||||
this.heightContainer.setVal(height);
|
||||
this.heightContainer.triggerEvent(inputEvent);
|
||||
}
|
||||
|
||||
|
||||
_clampToBoundaries(width, height) {
|
||||
const aspectRatio = width / height;
|
||||
const MAX_DIMENSION = _MAXIMUM;
|
||||
const MIN_DIMENSION = _MINIMUM;
|
||||
if (width > MAX_DIMENSION) {
|
||||
width = MAX_DIMENSION;
|
||||
height = Math.round(width / aspectRatio);
|
||||
}
|
||||
if (height > MAX_DIMENSION) {
|
||||
height = MAX_DIMENSION;
|
||||
width = Math.round(height * aspectRatio);
|
||||
}
|
||||
if (width < MIN_DIMENSION) {
|
||||
width = MIN_DIMENSION;
|
||||
height = Math.round(width / aspectRatio);
|
||||
}
|
||||
if (height < MIN_DIMENSION) {
|
||||
height = MIN_DIMENSION;
|
||||
width = Math.round(height * aspectRatio);
|
||||
}
|
||||
if (width < MIN_DIMENSION) {
|
||||
width = MIN_DIMENSION;
|
||||
} else if (width > MAX_DIMENSION) {
|
||||
width = MAX_DIMENSION;
|
||||
}
|
||||
if (height < MIN_DIMENSION) {
|
||||
height = MIN_DIMENSION;
|
||||
} else if (height > MAX_DIMENSION) {
|
||||
height = MAX_DIMENSION;
|
||||
}
|
||||
|
||||
return [width, height]
|
||||
}
|
||||
|
||||
static observeStartup(page, key, defaultOption, defaultOptions) {
|
||||
static observeStartup(key, page, defaultOptions, postSetup = (_) => {}) {
|
||||
let observer = new MutationObserver(() => {
|
||||
const widthContainer = gradioApp().querySelector(`#${page}_width`);
|
||||
const heightContainer = gradioApp().querySelector(`#${page}_height`);
|
||||
if (widthContainer && heightContainer) {
|
||||
|
||||
// wait for width and height containers to exist.
|
||||
if (widthContainer && heightContainer) {
|
||||
observer.disconnect();
|
||||
if (!window.opts.arh_javascript_aspect_ratio_show) return;
|
||||
|
||||
const switchBtn = gradioApp().getElementById(page + '_res_switch_btn');
|
||||
if (!switchBtn) return;
|
||||
|
||||
const wrapperDiv = document.createElement('div');
|
||||
wrapperDiv.setAttribute("id", `${page}_size_toolbox`);
|
||||
wrapperDiv.setAttribute("class", "flex flex-col relative col gap-4");
|
||||
wrapperDiv.setAttribute("style", "min-width: min(320px, 100%); flex-grow: 0");
|
||||
|
||||
const allOptions = [
|
||||
...defaultOptions,
|
||||
...window.opts.arh_javascript_aspect_ratio.split(','),
|
||||
].map(o => o.trim());
|
||||
|
||||
wrapperDiv.innerHTML = `
|
||||
<div id="${page}_ratio" class="gr-block gr-box relative w-full border-solid border border-gray-200 gr-padded">
|
||||
<select id="${page}_select_aspect_ratio" class="gr-box gr-input w-full disabled:cursor-not-allowed">
|
||||
${
|
||||
[...new Set(allOptions)].map(r => {
|
||||
return '<option class="ar-option">' + r + '</option>'
|
||||
}).join('\n')
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const parent = switchBtn.parentNode;
|
||||
parent.removeChild(switchBtn);
|
||||
wrapperDiv.appendChild(switchBtn);
|
||||
parent.insertBefore(wrapperDiv, parent.lastChild.previousElementSibling);
|
||||
const controller = new AspectRatioController(widthContainer, heightContainer, defaultOption);
|
||||
|
||||
if (page === 'img2img') {
|
||||
const img2imgImageContainer = gradioApp().querySelector('#img2img_image');
|
||||
const scaleToImg2ImgImage = (e) => {
|
||||
const options = Array.from(aspectRatioSelect);
|
||||
const picked = options[aspectRatioSelect.selectedIndex].value;
|
||||
if (picked !== _IMAGE) return;
|
||||
|
||||
const files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
|
||||
const img = new Image();
|
||||
img.src = URL.createObjectURL(files[0]);
|
||||
|
||||
img.onload = () => {
|
||||
controller.setAspectRatio(`${img.naturalWidth}:${img.naturalHeight}`)
|
||||
};
|
||||
}
|
||||
|
||||
const img2imgImageInputContainer = img2imgImageContainer.querySelector('input')
|
||||
img2imgImageInputContainer.parentElement.addEventListener('drop', scaleToImg2ImgImage)
|
||||
img2imgImageInputContainer.addEventListener('input', scaleToImg2ImgImage)
|
||||
if (!window.opts.arh_javascript_aspect_ratio_show) {
|
||||
return;
|
||||
}
|
||||
|
||||
const aspectRatioSelect = gradioApp().getElementById(`${page}_select_aspect_ratio`);
|
||||
const originalBGC = switchBtn.style.backgroundColor;
|
||||
aspectRatioSelect.onchange = () => {
|
||||
const options = Array.from(aspectRatioSelect);
|
||||
const picked = options[aspectRatioSelect.selectedIndex].value;
|
||||
|
||||
if (_IMAGE === picked) {
|
||||
switchBtn.setAttribute('disabled', true)
|
||||
switchBtn.style.backgroundColor = 'black';
|
||||
} else if (switchBtn.getAttribute('disabled')) {
|
||||
switchBtn.removeAttribute('disabled')
|
||||
switchBtn.style.backgroundColor = originalBGC;
|
||||
} else {
|
||||
switchBtn.style.backgroundColor = originalBGC;
|
||||
}
|
||||
controller.setAspectRatio(picked);
|
||||
};
|
||||
|
||||
switchBtn.onclick = () => {
|
||||
Array.from(gradioApp().querySelectorAll('.ar-option')).forEach(el => {
|
||||
const reversed = _reverseAspectRatio(el.value);
|
||||
if (reversed) {
|
||||
el.value = reversed;
|
||||
el.textContent = reversed;
|
||||
}
|
||||
});
|
||||
const options = Array.from(aspectRatioSelect);
|
||||
let picked = options[aspectRatioSelect.selectedIndex].value;
|
||||
if (_LOCK === picked) {
|
||||
picked = `${controller.heightRatio}:${controller.widthRatio}`
|
||||
}
|
||||
controller.setAspectRatio(picked);
|
||||
};
|
||||
const controller = new AspectRatioController(
|
||||
page,
|
||||
widthContainer,
|
||||
heightContainer,
|
||||
defaultOptions,
|
||||
);
|
||||
|
||||
postSetup(controller);
|
||||
window[key] = controller;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(gradioApp(), {childList: true, subtree: true});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const addImg2ImgTabSwitchClickListeners = (controller) => {
|
||||
const img2imgTabButtons = Array.from(document.querySelectorAll('#img2img_settings > div > div > button:not(.selected):not(.hasTabSwitchListener)'));
|
||||
img2imgTabButtons.forEach(button => {
|
||||
button.addEventListener('click', (_) => {
|
||||
// set aspect ratio is RECALLED to change to the image specific to the newly selected tab.
|
||||
if (controller.selectController.getCurrentOption() === _IMAGE) {
|
||||
controller.setAspectRatio(_IMAGE);
|
||||
}
|
||||
|
||||
addImg2ImgTabSwitchClickListeners(controller);
|
||||
});
|
||||
|
||||
button.classList.add('hasTabSwitchListener');
|
||||
});
|
||||
}
|
||||
|
||||
const postImageControllerSetupFunction = (controller) => {
|
||||
const scaleToImg2ImgImage = (e) => {
|
||||
const picked = controller.selectController.getCurrentOption();
|
||||
if (picked !== _IMAGE) return;
|
||||
const files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
|
||||
const img = new Image();
|
||||
img.src = URL.createObjectURL(files[0]);
|
||||
img.onload = () => {
|
||||
controller.setAspectRatio(`${img.naturalWidth}:${img.naturalHeight}`)
|
||||
};
|
||||
}
|
||||
|
||||
_IMAGE_INPUT_CONTAINER_IDS.forEach(imageContainerId => {
|
||||
const imageContainer = document.getElementById(imageContainerId);
|
||||
const inputElement = imageContainer.querySelector('input');
|
||||
inputElement.parentElement.addEventListener('drop', scaleToImg2ImgImage);
|
||||
inputElement.addEventListener('input', scaleToImg2ImgImage);
|
||||
})
|
||||
|
||||
addImg2ImgTabSwitchClickListeners(controller);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
window.__txt2imgAspectRatioController = AspectRatioController.observeStartup(
|
||||
"txt2img", "__txt2imgAspectRatioController", _OFF, [_OFF, _LOCK]
|
||||
AspectRatioController.observeStartup(
|
||||
"__txt2imgAspectRatioController",
|
||||
"txt2img",
|
||||
[_OFF, _LOCK]
|
||||
);
|
||||
window.__img2imgAspectRatioController = AspectRatioController.observeStartup(
|
||||
"img2img", "__img2imgAspectRatioController", _OFF, [_OFF, _IMAGE, _LOCK]
|
||||
AspectRatioController.observeStartup(
|
||||
"__img2imgAspectRatioController",
|
||||
"img2img",
|
||||
[_OFF, _IMAGE, _LOCK],
|
||||
postImageControllerSetupFunction
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user