Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
S
stable-diffusion-webui
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
stable-diffusion-webui
Commits
b20f28ee
Unverified
Commit
b20f28ee
authored
Feb 19, 2023
by
AUTOMATIC1111
Committed by
GitHub
Feb 19, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #7601 from Gerschel/aspect_ratio_sliders
Aspect ratio sliders
parents
9c4eaac6
742d86ee
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
517 additions
and
2 deletions
+517
-2
ComponentControllers.js
javascript/ComponentControllers.js
+259
-0
aspectRatioSliders.js
javascript/aspectRatioSliders.js
+181
-0
shared.py
modules/shared.py
+21
-0
ui.py
modules/ui.py
+10
-2
style.css
style.css
+46
-0
No files found.
javascript/ComponentControllers.js
0 → 100644
View file @
b20f28ee
/* 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
<fieldset>
<div> css makes translucent
<span>
serves as label for component
</span>
<div data-testid='checkbox-group'> container for checkboxes
<label>
<input type=checkbox>
<span>checkbox words</span>
</label>
...
</div>
</fieldset>
*/
}
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"
));
}
}
javascript/aspectRatioSliders.js
0 → 100644
View file @
b20f28ee
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"
);
});
modules/shared.py
View file @
b20f28ee
...
...
@@ -139,6 +139,24 @@ ui_reorder_categories = [
"scripts"
,
]
aspect_ratio_defaults
=
[
"🔓"
,
"1:1"
,
"3:2"
,
"4:3"
,
"5:4"
,
"16:9"
,
"9:16"
,
"1.85:1"
,
"2.35:1"
,
"2.39:1"
,
"2.40:1"
,
"21:9"
,
"1.375:1"
,
"1.66:1"
,
"1.75:1"
]
cmd_opts
.
disable_extension_access
=
(
cmd_opts
.
share
or
cmd_opts
.
listen
or
cmd_opts
.
server_name
)
and
not
cmd_opts
.
enable_insecure_extension_access
devices
.
device
,
devices
.
device_interrogate
,
devices
.
device_gfpgan
,
devices
.
device_esrgan
,
devices
.
device_codeformer
=
\
...
...
@@ -459,6 +477,9 @@ options_templates.update(options_section(('ui', "User interface"), {
"keyedit_precision_extra"
:
OptionInfo
(
0.05
,
"Ctrl+up/down precision when editing <extra networks:0.9>"
,
gr
.
Slider
,
{
"minimum"
:
0.01
,
"maximum"
:
0.2
,
"step"
:
0.001
}),
"quicksettings"
:
OptionInfo
(
"sd_model_checkpoint"
,
"Quicksettings list"
),
"ui_reorder"
:
OptionInfo
(
", "
.
join
(
ui_reorder_categories
),
"txt2img/img2img UI item order"
),
"aspect_ratios_rounding"
:
OptionInfo
(
True
,
"Round aspect ratios for more flexibility?"
,
gr
.
Checkbox
),
"aspect_ratios_rounding_method"
:
OptionInfo
(
"Ceiling"
,
"Aspect ratios rounding method"
,
gr
.
Radio
,{
"choices"
:
[
"Round"
,
"Ceiling"
,
"Floor"
]}),
"aspect_ratios"
:
OptionInfo
(
", "
.
join
(
aspect_ratio_defaults
),
"txt2img/img2img aspect ratios"
),
"ui_extra_networks_tab_reorder"
:
OptionInfo
(
""
,
"Extra networks tab order"
),
"localization"
:
OptionInfo
(
"None"
,
"Localization (requires restart)"
,
gr
.
Dropdown
,
lambda
:
{
"choices"
:
[
"None"
]
+
list
(
localization
.
localizations
.
keys
())},
refresh
=
lambda
:
localization
.
list_localizations
(
cmd_opts
.
localizations_dir
)),
}))
...
...
modules/ui.py
View file @
b20f28ee
...
...
@@ -424,6 +424,10 @@ def ordered_ui_categories():
yield
category
def
aspect_ratio_list
():
return
[
ratio
.
strip
()
for
ratio
in
shared
.
opts
.
aspect_ratios
.
split
(
","
)]
def
get_value_for_setting
(
key
):
value
=
getattr
(
opts
,
key
)
...
...
@@ -479,7 +483,9 @@ def create_ui():
width
=
gr
.
Slider
(
minimum
=
64
,
maximum
=
2048
,
step
=
8
,
label
=
"Width"
,
value
=
512
,
elem_id
=
"txt2img_width"
)
height
=
gr
.
Slider
(
minimum
=
64
,
maximum
=
2048
,
step
=
8
,
label
=
"Height"
,
value
=
512
,
elem_id
=
"txt2img_height"
)
res_switch_btn
=
ToolButton
(
value
=
switch_values_symbol
,
elem_id
=
"txt2img_res_switch_btn"
)
with
gr
.
Column
(
elem_id
=
"txt2img_size_toolbox"
,
scale
=
0
):
aspect_ratio_dropdown
=
gr
.
Dropdown
(
value
=
"🔓"
,
choices
=
aspect_ratio_list
(),
interactive
=
True
,
type
=
"value"
,
elem_id
=
"txt2img_ratio"
,
show_label
=
False
,
label
=
"Aspect Ratio"
)
res_switch_btn
=
ToolButton
(
value
=
switch_values_symbol
,
elem_id
=
"txt2img_res_switch_btn"
)
if
opts
.
dimensions_and_batch_together
:
with
gr
.
Column
(
elem_id
=
"txt2img_column_batch"
):
batch_count
=
gr
.
Slider
(
minimum
=
1
,
step
=
1
,
label
=
'Batch count'
,
value
=
1
,
elem_id
=
"txt2img_batch_count"
)
...
...
@@ -757,7 +763,9 @@ def create_ui():
width
=
gr
.
Slider
(
minimum
=
64
,
maximum
=
2048
,
step
=
8
,
label
=
"Width"
,
value
=
512
,
elem_id
=
"img2img_width"
)
height
=
gr
.
Slider
(
minimum
=
64
,
maximum
=
2048
,
step
=
8
,
label
=
"Height"
,
value
=
512
,
elem_id
=
"img2img_height"
)
res_switch_btn
=
ToolButton
(
value
=
switch_values_symbol
,
elem_id
=
"img2img_res_switch_btn"
)
with
gr
.
Column
(
elem_id
=
"img2img_size_toolbox"
,
scale
=
0
):
aspect_ratio_dropdown
=
gr
.
Dropdown
(
value
=
"🔓"
,
choices
=
aspect_ratio_list
(),
interactive
=
True
,
type
=
"value"
,
elem_id
=
"img2img_ratio"
,
show_label
=
False
,
label
=
"Aspect Ratio"
)
res_switch_btn
=
ToolButton
(
value
=
switch_values_symbol
,
elem_id
=
"img2img_res_switch_btn"
)
if
opts
.
dimensions_and_batch_together
:
with
gr
.
Column
(
elem_id
=
"img2img_column_batch"
):
batch_count
=
gr
.
Slider
(
minimum
=
1
,
step
=
1
,
label
=
'Batch count'
,
value
=
1
,
elem_id
=
"img2img_batch_count"
)
...
...
style.css
View file @
b20f28ee
...
...
@@ -747,6 +747,52 @@ footer {
margin-left
:
0em
;
}
#txt2img_size_toolbox
,
#img2img_size_toolbox
{
min-width
:
unset
!important
;
gap
:
0
;
}
#txt2img_ratio
,
#img2img_ratio
{
padding
:
0px
;
min-width
:
unset
;
max-width
:
fit-content
;
}
#txt2img_ratio
select
,
#img2img_ratio
select
{
-o-appearance
:
none
;
-ms-appearance
:
none
;
-webkit-appearance
:
none
;
-moz-appearance
:
none
;
appearance
:
none
;
background-image
:
unset
;
padding-right
:
unset
;
min-width
:
40px
;
max-width
:
40px
;
min-height
:
40px
;
max-height
:
40px
;
line-height
:
40px
;
padding
:
0
;
text-align
:
center
;
}
.rounding-badge
{
display
:
inline-block
;
border-radius
:
0px
;
/*background-color: #ccc;*/
cursor
:
pointer
;
position
:
absolute
;
top
:
-10px
;
right
:
-10px
;
width
:
20px
;
height
:
20px
;
padding
:
1px
;
line-height
:
16px
;
font-size
:
14px
;
}
.rounding-badge.active
{
background-color
:
#007bff
;
border-radius
:
50%
;
}
.inactive
{
opacity
:
0.5
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment