diff --git a/.eslintrc.js b/.eslintrc.js index e3b4fb769..4777c276e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -90,6 +90,8 @@ module.exports = { // localStorage.js localSet: "readonly", localGet: "readonly", - localRemove: "readonly" + localRemove: "readonly", + // resizeHandle.js + setupResizeHandle: "writable" } }; diff --git a/extensions-builtin/resize-handle/javascript/resize-handle.js b/extensions-builtin/resize-handle/javascript/resize-handle.js deleted file mode 100644 index a07a01d2f..000000000 --- a/extensions-builtin/resize-handle/javascript/resize-handle.js +++ /dev/null @@ -1,106 +0,0 @@ -onUiLoaded(async() => { - const GRADIO_MIN_WIDTH = 320; - const GRID_TEMPLATE_COLUMNS = '1fr 16px 1fr'; - const PAD = 16; - const DEBOUNCE_TIME = 100; - - const R = { - tracking: false, - parent: null, - parentWidth: null, - leftCol: null, - leftColStartWidth: null, - screenX: null, - }; - - let resizeTimer; - - const leftCols = [ - gradioApp().querySelector('#txt2img_settings'), - gradioApp().querySelector('#img2img_settings'), - ]; - - function setLeftColGridTemplate(el, width) { - el.style.gridTemplateColumns = `${width}px 16px 1fr`; - } - - function displayResizeHandle(parent) { - if (window.innerWidth < GRADIO_MIN_WIDTH * 2 + PAD * 4) { - parent.style.display = 'flex'; - if (R.handle != null) { - R.handle.style.opacity = '0'; - } - return false; - } else { - parent.style.display = 'grid'; - if (R.handle != null) { - R.handle.style.opacity = '100'; - } - return true; - } - } - - function setup() { - for (const leftCol of leftCols) { - const parent = leftCol.parentElement; - const rightCol = parent.lastElementChild; - - if (!displayResizeHandle(parent)) { - return; - } - - parent.style.display = 'grid'; - parent.style.gap = '0'; - parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS; - - const resizeHandle = document.createElement('div'); - resizeHandle.classList.add('resize-handle'); - parent.insertBefore(resizeHandle, rightCol); - - resizeHandle.addEventListener('mousedown', (evt) => { - R.tracking = true; - R.parent = parent; - R.parentWidth = parent.offsetWidth; - R.handle = resizeHandle; - R.leftCol = leftCol; - R.leftColStartWidth = leftCol.offsetWidth; - R.screenX = evt.screenX; - }); - } - } - - window.addEventListener('mousemove', (evt) => { - if (R.tracking) { - const delta = R.screenX - evt.screenX; - const leftColWidth = Math.max(Math.min(R.leftColStartWidth - delta, R.parent.offsetWidth - GRADIO_MIN_WIDTH - PAD), GRADIO_MIN_WIDTH); - setLeftColGridTemplate(R.parent, leftColWidth); - } - }); - - window.addEventListener('mouseup', () => R.tracking = false); - - window.addEventListener('resize', () => { - clearTimeout(resizeTimer); - - resizeTimer = setTimeout(() => { - for (const leftCol of leftCols) { - const parent = leftCol.parentElement; - - if (displayResizeHandle(parent) && parent.style.gridTemplateColumns != GRID_TEMPLATE_COLUMNS) { - const oldParentWidth = R.parentWidth; - const newParentWidth = parent.offsetWidth; - const widthL = parseInt(parent.style.gridTemplateColumns.split(' ')[0]); - - const ratio = newParentWidth / oldParentWidth; - - const newWidthL = Math.max(Math.floor(ratio * widthL), GRADIO_MIN_WIDTH); - setLeftColGridTemplate(parent, newWidthL); - - R.parentWidth = newParentWidth; - } - } - }, DEBOUNCE_TIME); - }); - - setup(); -}); diff --git a/extensions-builtin/resize-handle/style.css b/extensions-builtin/resize-handle/style.css deleted file mode 100644 index 0e18267ab..000000000 --- a/extensions-builtin/resize-handle/style.css +++ /dev/null @@ -1,10 +0,0 @@ -.resize-handle{ - cursor: col-resize; - grid-column: 2 / 3; - min-width: 8px !important; - max-width: 8px !important; - height: 100%; - border-left: 1px dashed var(--border-color-primary); - user-select: none; - margin-left: 8px; -} \ No newline at end of file diff --git a/javascript/resizeHandle.js b/javascript/resizeHandle.js new file mode 100644 index 000000000..71023f9fe --- /dev/null +++ b/javascript/resizeHandle.js @@ -0,0 +1,109 @@ +(function() { + const GRADIO_MIN_WIDTH = 320; + const GRID_TEMPLATE_COLUMNS = '1fr 16px 1fr'; + const PAD = 16; + const DEBOUNCE_TIME = 100; + + const R = { + tracking: false, + parent: null, + parentWidth: null, + leftCol: null, + leftColStartWidth: null, + screenX: null, + }; + + let resizeTimer; + let parents = []; + + function setLeftColGridTemplate(el, width) { + el.style.gridTemplateColumns = `${width}px 16px 1fr`; + } + + function displayResizeHandle(parent) { + if (window.innerWidth < GRADIO_MIN_WIDTH * 2 + PAD * 4) { + parent.style.display = 'flex'; + if (R.handle != null) { + R.handle.style.opacity = '0'; + } + return false; + } else { + parent.style.display = 'grid'; + if (R.handle != null) { + R.handle.style.opacity = '100'; + } + return true; + } + } + + function afterResize(parent) { + if (displayResizeHandle(parent) && parent.style.gridTemplateColumns != GRID_TEMPLATE_COLUMNS) { + const oldParentWidth = R.parentWidth; + const newParentWidth = parent.offsetWidth; + const widthL = parseInt(parent.style.gridTemplateColumns.split(' ')[0]); + + const ratio = newParentWidth / oldParentWidth; + + const newWidthL = Math.max(Math.floor(ratio * widthL), GRADIO_MIN_WIDTH); + setLeftColGridTemplate(parent, newWidthL); + + R.parentWidth = newParentWidth; + } + } + + function setup(parent) { + const leftCol = parent.firstElementChild; + const rightCol = parent.lastElementChild; + + parents.push(parent); + + parent.style.display = 'grid'; + parent.style.gap = '0'; + parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS; + + const resizeHandle = document.createElement('div'); + resizeHandle.classList.add('resize-handle'); + parent.insertBefore(resizeHandle, rightCol); + + resizeHandle.addEventListener('mousedown', (evt) => { + R.tracking = true; + R.parent = parent; + R.parentWidth = parent.offsetWidth; + R.handle = resizeHandle; + R.leftCol = leftCol; + R.leftColStartWidth = leftCol.offsetWidth; + R.screenX = evt.screenX; + }); + + afterResize(parent); + } + + window.addEventListener('mousemove', (evt) => { + if (R.tracking) { + const delta = R.screenX - evt.screenX; + const leftColWidth = Math.max(Math.min(R.leftColStartWidth - delta, R.parent.offsetWidth - GRADIO_MIN_WIDTH - PAD), GRADIO_MIN_WIDTH); + setLeftColGridTemplate(R.parent, leftColWidth); + } + }); + + window.addEventListener('mouseup', () => R.tracking = false); + + + window.addEventListener('resize', () => { + clearTimeout(resizeTimer); + + resizeTimer = setTimeout(function() { + for (const parent of parents) { + afterResize(parent); + } + }, DEBOUNCE_TIME); + }); + + setupResizeHandle = setup; +})(); + +onUiLoaded(function() { + for (var elem of gradioApp().querySelectorAll('.resize-handle-row')) { + setupResizeHandle(elem); + } +}); diff --git a/modules/ui.py b/modules/ui.py index 01f77849d..2b6a13cbb 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -13,7 +13,7 @@ from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_grad from modules import gradio_extensons # noqa: F401 from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, extra_networks, ui_common, ui_postprocessing, progress, ui_loadsave, shared_items, ui_settings, timer, sysinfo, ui_checkpoint_merger, ui_prompt_styles, scripts, sd_samplers, processing, ui_extra_networks -from modules.ui_components import FormRow, FormGroup, ToolButton, FormHTML, InputAccordion +from modules.ui_components import FormRow, FormGroup, ToolButton, FormHTML, InputAccordion, ResizeHandleRow from modules.paths import script_path from modules.ui_common import create_refresh_button from modules.ui_gradio_extensions import reload_javascript @@ -333,7 +333,7 @@ def create_ui(): extra_tabs = gr.Tabs(elem_id="txt2img_extra_tabs") extra_tabs.__enter__() - with gr.Tab("Generation", id="txt2img_generation") as txt2img_generation_tab, gr.Row(equal_height=False): + with gr.Tab("Generation", id="txt2img_generation") as txt2img_generation_tab, ResizeHandleRow(equal_height=False): with gr.Column(variant='compact', elem_id="txt2img_settings"): scripts.scripts_txt2img.prepare_ui() @@ -549,7 +549,7 @@ def create_ui(): extra_tabs = gr.Tabs(elem_id="img2img_extra_tabs") extra_tabs.__enter__() - with gr.Tab("Generation", id="img2img_generation") as img2img_generation_tab, FormRow(equal_height=False): + with gr.Tab("Generation", id="img2img_generation") as img2img_generation_tab, ResizeHandleRow(equal_height=False): with gr.Column(variant='compact', elem_id="img2img_settings"): copy_image_buttons = [] copy_image_destinations = {} diff --git a/modules/ui_components.py b/modules/ui_components.py index d08b2b997..55979f626 100644 --- a/modules/ui_components.py +++ b/modules/ui_components.py @@ -20,6 +20,18 @@ class ToolButton(FormComponent, gr.Button): return "button" +class ResizeHandleRow(gr.Row): + """Same as gr.Row but fits inside gradio forms""" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.elem_classes.append("resize-handle-row") + + def get_block_name(self): + return "row" + + class FormRow(FormComponent, gr.Row): """Same as gr.Row but fits inside gradio forms""" diff --git a/style.css b/style.css index 46125864c..4166a3df1 100644 --- a/style.css +++ b/style.css @@ -1055,3 +1055,15 @@ div.accordions > div.input-accordion.input-accordion-open{ position: sticky; top: 0.5em; } + + +.resize-handle{ + cursor: col-resize; + grid-column: 2 / 3; + min-width: 8px !important; + max-width: 8px !important; + height: 100%; + border-left: 1px dashed var(--border-color-primary); + user-select: none; + margin-left: 8px; +}