From fd4ac5187a1ae42be3f131770ea21e2158f75dcd Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 19 Feb 2023 10:55:39 +0300 Subject: Revert "Aspect ratio sliders" --- javascript/ComponentControllers.js | 259 ------------------------------------- javascript/aspectRatioSliders.js | 181 -------------------------- 2 files changed, 440 deletions(-) delete mode 100644 javascript/ComponentControllers.js delete mode 100644 javascript/aspectRatioSliders.js (limited to 'javascript') diff --git a/javascript/ComponentControllers.js b/javascript/ComponentControllers.js deleted file mode 100644 index 2888679b..00000000 --- a/javascript/ComponentControllers.js +++ /dev/null @@ -1,259 +0,0 @@ -/* This is a basic library that allows controlling elements that take some form of user input. - -This was previously written in typescript, where all controllers implemented an interface. Not -all methods were needed in all the controllers, but it was done to keep a common interface, so -your main app can serve as a controller of controllers. - -These controllers were built to work on the shapes of html elements that gradio components use. - -There may be some notes in it that only applied to my use case, but I left them to help others -along. - -You will need the parent element for these to work. -The parent element can be defined as the element (div) that gets the element id when assigning -an element id to a gradio component. - -Example: - gr.TextBox(value="...", elem_id="THISID") - -Basic usage, grab an element that is the parent container for the component. - -Send it in to the class, like a function, don't forget the "new" keyword so it calls the constructor -and sends back a new object. - -Example: - -let txt2imgPrompt = new TextComponentController(gradioApp().querySelector("#txt2img_prompt")) - -Then use the getVal() method to get the value, or use the setVal(myValue) method to set the value. - -Input types that are groups, like Checkbox groups (not individual checkboxes), take in an array of values. - -Checkbox group has to reset all values to False (unchecked), then set the values in your array to true (checked). -If you don't hold a reference to the values (the labels in string format), you can acquire them using the getVal() method. -*/ -class DropdownComponentController { - constructor(element) { - this.element = element; - this.childSelector = this.element.querySelector('select'); - this.children = new Map(); - Array.from(this.childSelector.querySelectorAll('option')).forEach(opt => this.children.set(opt.value, opt)); - } - getVal() { - return this.childSelector.value; - } - updateVal(optionElement) { - optionElement.selected = true; - } - setVal(name) { - this.updateVal(this.children.get(name)); - this.eventHandler(); - } - eventHandler() { - this.childSelector.dispatchEvent(new Event("change")); - } -} -class CheckboxComponentController { - constructor(element) { - this.element = element; - this.child = this.element.querySelector('input'); - } - getVal() { - return this.child.checked; - } - updateVal(checked) { - this.child.checked = checked; - } - setVal(checked) { - this.updateVal(checked); - this.eventHandler(); - } - eventHandler() { - this.child.dispatchEvent(new Event("change")); - } -} -class CheckboxGroupComponentController { - constructor(element) { - this.element = element; - //this.checkBoxes = new Object; - this.children = new Map(); - Array.from(this.element.querySelectorAll('input')).forEach(input => this.children.set(input.nextElementSibling.innerText, input)); - /* element id gets use fieldset, grab all inputs (the bool val) get the userfriendly label, use as key, put bool value in mapping */ - //Array.from(this.component.querySelectorAll("input")).forEach( _input => this.checkBoxes[_input.nextElementSibling.innerText] = _input) - /*Checkboxgroup structure -
-
css makes translucent - - serves as label for component - -
container for checkboxes - - ... -
-
- */ - } - updateVal(label) { - /********* - calls updates using a throttle or else the backend does not get updated properly - * ********/ - setTimeout(() => this.conditionalToggle(true, this.children.get(label)), 2); - } - setVal(labels) { - /* Handles reset and updates all in array to true */ - this.reupdateVals(); - labels.forEach(l => this.updateVal(l)); - } - getVal() { - //return the list of values that are true - return [...this.children].filter(([k, v]) => v.checked).map(arr => arr[0]); - } - reupdateVals() { - /************** - * for reupdating all vals, first set to false - **************/ - this.children.forEach(inputChild => this.conditionalToggle(false, inputChild)); - } - conditionalToggle(desiredVal, inputChild) { - //This method behaves like 'set this value to this' - //Using element.checked = true/false, does not register the change, even if you called change afterwards, - // it only sets what it looks like in our case, because there is no form submit, a person then has to click on it twice. - //Options are to use .click() or dispatch an event - if (desiredVal != inputChild.checked) { - inputChild.dispatchEvent(new Event("change")); //using change event instead of click, in case browser ad-blockers blocks the click method - } - } - eventHandler(checkbox) { - checkbox.dispatchEvent(new Event("change")); - } -} -class RadioComponentController { - constructor(element) { - this.element = element; - this.children = new Map(); - Array.from(this.element.querySelectorAll("input")).forEach(input => this.children.set(input.value, input)); - } - getVal() { - //radio groups have a single element that's checked is true - // as array arr k,v pair element.checked ) -> array of len(1) with [k,v] so either [0] [1].value - return [...this.children].filter(([l, e]) => e.checked)[0][0]; - //return Array.from(this.children).filter( ([label, input]) => input.checked)[0][1].value - } - updateVal(child) { - this.eventHandler(child); - } - setVal(name) { - //radio will trigger all false except the one that get the event change - //to keep the api similar, other methods are still called - this.updateVal(this.children.get(name)); - } - eventHandler(child) { - child.dispatchEvent(new Event("change")); - } -} -class NumberComponentController { - constructor(element) { - this.element = element; - this.childNumField = element.querySelector('input[type=number]'); - } - getVal() { - return this.childNumField.value; - } - updateVal(text) { - this.childNumField.value = text; - } - eventHandler() { - this.element.dispatchEvent(new Event("input")); - } - setVal(text) { - this.updateVal(text); - this.eventHandler(); - } -} -class SliderComponentController { - constructor(element) { - this.element = element; - this.childNumField = this.element.querySelector('input[type=number]'); - this.childRangeField = this.element.querySelector('input[type=range]'); - } - getVal() { - return this.childNumField.value; - } - updateVal(text) { - //both are not needed, either works, both are left in so one is a fallback in case of gradio changes - this.childNumField.value = text; - this.childRangeField.value = text; - } - eventHandler() { - this.element.dispatchEvent(new Event("input")); - this.childNumField.dispatchEvent(new Event("input")); - this.childRangeField.dispatchEvent(new Event("input")); - } - setVal(text) { - this.updateVal(text); - this.eventHandler(); - } -} -class TextComponentController { - constructor(element) { - this.element = element; - this.child = element.querySelector('textarea'); - } - getVal() { - return this.child.value; - } - eventHandler() { - this.element.dispatchEvent(new Event("input")); - this.child.dispatchEvent(new Event("change")); - //Workaround to solve no target with v(o) on eventhandler, define my own target - let ne = new Event("input"); - Object.defineProperty(ne, "target", { value: this.child }); - this.child.dispatchEvent(ne); - } - updateVal(text) { - this.child.value = text; - } - appendValue(text) { - //might add delimiter option - this.child.value += ` ${text}`; - } - setVal(text, append = false) { - if (append) { - this.appendValue(text); - } - else { - this.updateVal(text); - } - this.eventHandler(); - } -} -class JsonComponentController extends TextComponentController { - constructor(element) { - super(element); - } - getVal() { - return JSON.parse(this.child.value); - } -} -class ColorComponentController { - constructor(element) { - this.element = element; - this.child = this.element.querySelector('input[type=color]'); - } - updateVal(text) { - this.child.value = text; - } - getVal() { - return this.child.value; - } - setVal(text) { - this.updateVal(text); - this.eventHandler(); - } - eventHandler() { - this.child.dispatchEvent(new Event("input")); - } -} diff --git a/javascript/aspectRatioSliders.js b/javascript/aspectRatioSliders.js deleted file mode 100644 index 3def5158..00000000 --- a/javascript/aspectRatioSliders.js +++ /dev/null @@ -1,181 +0,0 @@ -class AspectRatioSliderController { - constructor(widthSlider, heightSlider, ratioSource, roundingSource, roundingMethod) { - //References - this.widthSlider = new SliderComponentController(widthSlider); - this.heightSlider = new SliderComponentController(heightSlider); - this.ratioSource = new DropdownComponentController(ratioSource); - this.roundingSource = new CheckboxComponentController(roundingSource); - this.roundingMethod = new RadioComponentController(roundingMethod); - this.roundingIndicatorBadge = document.createElement("div"); - // Badge implementation - this.roundingIndicatorBadge.innerText = "📐"; - this.roundingIndicatorBadge.classList.add("rounding-badge"); - this.ratioSource.element.appendChild(this.roundingIndicatorBadge); - // Check initial value of ratioSource to set badge visbility - let initialRatio = this.ratioSource.getVal(); - if (!initialRatio.includes(":")) { - this.roundingIndicatorBadge.style.display = "none"; - } - //Adjust badge icon if rounding is on - if (this.roundingSource.getVal()) { - //this.roundingIndicatorBadge.classList.add("active"); - this.roundingIndicatorBadge.innerText = "📏"; - } - //Make badge clickable to toggle setting - this.roundingIndicatorBadge.addEventListener("click", () => { - this.roundingSource.setVal(!this.roundingSource.getVal()); - }); - //Make rounding setting toggle badge text and style if setting changes - this.roundingSource.child.addEventListener("change", () => { - if (this.roundingSource.getVal()) { - //this.roundingIndicatorBadge.classList.add("active"); - this.roundingIndicatorBadge.innerText = "📏"; - } - else { - //this.roundingIndicatorBadge.classList.remove("active"); - this.roundingIndicatorBadge.innerText = "📐"; - } - this.adjustStepSize(); - }); - //Other event listeners - this.widthSlider.childRangeField.addEventListener("change", (e) => { e.preventDefault(); this.resize("width"); }); - this.widthSlider.childNumField.addEventListener("change", (e) => { e.preventDefault(); this.resize("width"); }); - this.heightSlider.childRangeField.addEventListener("change", (e) => { e.preventDefault(); this.resize("height"); }); - this.heightSlider.childNumField.addEventListener("change", (e) => { e.preventDefault(); this.resize("height"); }); - this.ratioSource.childSelector.addEventListener("change", (e) => { - e.preventDefault(); - //Check and toggle display of badge conditionally on dropdown selection - if (!this.ratioSource.getVal().includes(":")) { - this.roundingIndicatorBadge.style.display = 'none'; - } - else { - this.roundingIndicatorBadge.style.display = 'block'; - } - this.adjustStepSize(); - }); - } - resize(dimension) { - //For moving slider or number field - let val = this.ratioSource.getVal(); - if (!val.includes(":")) { - return; - } - let [width, height] = val.split(":").map(Number); - let ratio = width / height; - if (dimension == 'width') { - let newHeight = parseInt(this.widthSlider.getVal()) / ratio; - if (this.roundingSource.getVal()) { - switch (this.roundingMethod.getVal()) { - case 'Round': - newHeight = Math.round(newHeight / 8) * 8; - break; - case 'Ceiling': - newHeight = Math.ceil(newHeight / 8) * 8; - break; - case 'Floor': - newHeight = Math.floor(newHeight / 8) * 8; - break; - } - } - this.heightSlider.setVal(newHeight.toString()); - } - else if (dimension == "height") { - let newWidth = parseInt(this.heightSlider.getVal()) * ratio; - if (this.roundingSource.getVal()) { - switch (this.roundingMethod.getVal()) { - case 'Round': - newWidth = Math.round(newWidth / 8) * 8; - break; - case 'Ceiling': - newWidth = Math.ceil(newWidth / 8) * 8; - break; - case 'Floor': - newWidth = Math.floor(newWidth / 8) * 8; - break; - } - } - this.widthSlider.setVal(newWidth.toString()); - } - } - adjustStepSize() { - /* Sets scales/precision/rounding steps;*/ - let val = this.ratioSource.getVal(); - if (!val.includes(":")) { - //If ratio unlocked - this.widthSlider.childRangeField.step = "8"; - this.widthSlider.childRangeField.min = "64"; - this.widthSlider.childNumField.step = "8"; - this.widthSlider.childNumField.min = "64"; - this.heightSlider.childRangeField.step = "8"; - this.heightSlider.childRangeField.min = "64"; - this.heightSlider.childNumField.step = "8"; - this.heightSlider.childNumField.min = "64"; - return; - } - //Format string and calculate step sizes - let [width, height] = val.split(":").map(Number); - let decimalPlaces = (width.toString().split(".")[1] || []).length; - //keep upto 6 decimal points of precision of ratio - //euclidean gcd does not support floats, so we scale it up - decimalPlaces = decimalPlaces > 6 ? 6 : decimalPlaces; - let gcd = this.gcd(width * 10 ** decimalPlaces, height * 10 ** decimalPlaces) / 10 ** decimalPlaces; - let stepSize = 8 * height / gcd; - let stepSizeOther = 8 * width / gcd; - if (this.roundingSource.getVal()) { - //If rounding is on set/keep default stepsizes - this.widthSlider.childRangeField.step = "8"; - this.widthSlider.childRangeField.min = "64"; - this.widthSlider.childNumField.step = "8"; - this.widthSlider.childNumField.min = "64"; - this.heightSlider.childRangeField.step = "8"; - this.heightSlider.childRangeField.min = "64"; - this.heightSlider.childNumField.step = "8"; - this.heightSlider.childNumField.min = "64"; - } - else { - //if rounding is off, set step sizes so they enforce snapping - //min is changed, because it offsets snap positions - this.widthSlider.childRangeField.step = stepSizeOther.toString(); - this.widthSlider.childRangeField.min = stepSizeOther.toString(); - this.widthSlider.childNumField.step = stepSizeOther.toString(); - this.widthSlider.childNumField.min = stepSizeOther.toString(); - this.heightSlider.childRangeField.step = stepSize.toString(); - this.heightSlider.childRangeField.min = stepSize.toString(); - this.heightSlider.childNumField.step = stepSize.toString(); - this.heightSlider.childNumField.min = stepSize.toString(); - } - let currentWidth = parseInt(this.widthSlider.getVal()); - //Rounding treated kinda like pythons divmod - let stepsTaken = Math.round(currentWidth / stepSizeOther); - //this snaps it to closest rule matches (rules being html step points, and ratio) - let newWidth = stepsTaken * stepSizeOther; - this.widthSlider.setVal(newWidth.toString()); - this.heightSlider.setVal(Math.round(newWidth / (width / height)).toString()); - } - gcd(a, b) { - //euclidean gcd - if (b === 0) { - return a; - } - return this.gcd(b, a % b); - } - static observeStartup(widthSliderId, heightSliderId, ratioSourceId, roundingSourceId, roundingMethodId) { - let observer = new MutationObserver(() => { - let widthSlider = document.querySelector("gradio-app").shadowRoot.getElementById(widthSliderId); - let heightSlider = document.querySelector("gradio-app").shadowRoot.getElementById(heightSliderId); - let ratioSource = document.querySelector("gradio-app").shadowRoot.getElementById(ratioSourceId); - let roundingSource = document.querySelector("gradio-app").shadowRoot.getElementById(roundingSourceId); - let roundingMethod = document.querySelector("gradio-app").shadowRoot.getElementById(roundingMethodId); - if (widthSlider && heightSlider && ratioSource && roundingSource && roundingMethod) { - observer.disconnect(); - new AspectRatioSliderController(widthSlider, heightSlider, ratioSource, roundingSource, roundingMethod); - } - }); - observer.observe(gradioApp(), { childList: true, subtree: true }); - } -} -document.addEventListener("DOMContentLoaded", () => { - //Register mutation observer for self start-up; - AspectRatioSliderController.observeStartup("txt2img_width", "txt2img_height", "txt2img_ratio", "setting_aspect_ratios_rounding", "setting_aspect_ratios_rounding_method"); - AspectRatioSliderController.observeStartup("img2img_width", "img2img_height", "img2img_ratio", "setting_aspect_ratios_rounding", "setting_aspect_ratios_rounding_method"); -}); -- cgit v1.2.3