Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to move toprow to settings - alternative design #12691

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion extensions-builtin/mobile/javascript/mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ function reportWindowSize() {

for (var tab of ["txt2img", "img2img"]) {
var button = gradioApp().getElementById(tab + '_generate_box');
var target = gradioApp().getElementById(currentlyMobile ? tab + '_results' : tab + '_actions_column');
var target = null;
if (currentlyMobile) {
target = gradioApp().getElementById(tab + '_results');
} else {
target = gradioApp().getElementById(tab + '_actions_column') || gradioApp().getElementById(tab + '_generate_row');
}

target.insertBefore(button, target.firstElementChild);
}
}
Expand Down
2 changes: 2 additions & 0 deletions modules/shared_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@
"localization": OptionInfo("None", "Localization", gr.Dropdown, lambda: {"choices": ["None"] + list(localization.localizations.keys())}, refresh=lambda: localization.list_localizations(cmd_opts.localizations_dir)).needs_reload_ui(),
"gradio_theme": OptionInfo("Default", "Gradio theme", ui_components.DropdownEditable, lambda: {"choices": ["Default"] + shared_gradio_themes.gradio_hf_hub_themes}).info("you can also manually enter any of themes from the <a href='https://huggingface.co/spaces/gradio/theme-gallery'>gallery</a>.").needs_reload_ui(),
"gradio_themes_cache": OptionInfo(True, "Cache gradio themes locally").info("disable to update the selected Gradio theme"),
"move_toprow_to_settings_column": OptionInfo(False, "Move top row (prompt, generate button) to settings column").needs_reload_ui(),
"sticky_generate_button": OptionInfo(False, "Make generate button stick during scroll").info("only applies when the setting above is ticked").needs_reload_ui(),
"gallery_height": OptionInfo("", "Gallery height", gr.Textbox).info("an be any valid CSS value").needs_reload_ui(),
"return_grid": OptionInfo(True, "Show grid in results for web"),
"do_not_show_images": OptionInfo(False, "Do not show any images in results for web"),
Expand Down
136 changes: 88 additions & 48 deletions modules/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys
from functools import reduce
import warnings
from contextlib import nullcontext

import gradio as gr
import gradio.utils
Expand Down Expand Up @@ -169,69 +170,58 @@ def update_token_counter(text, steps):
return f"<span class='gr-box gr-text-input'>{token_count}/{max_length}</span>"


class ToprowPrompt:
"""Creates prompt components for Toprow"""
def __init__(self, id_part):
self.prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, lines=3, placeholder="Prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"])
self.negative_prompt = gr.Textbox(label="Negative prompt", elem_id=f"{id_part}_neg_prompt", show_label=False, lines=3, placeholder="Negative prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"])


class Toprow:
"""Creates a top row UI with prompts, generate button, styles, extra little buttons for things, and enables some functionality related to their operation"""

def __init__(self, is_img2img):
def __init__(self, is_img2img: bool, toprow_prompt: ToprowPrompt, in_settings_column=False):
id_part = "img2img" if is_img2img else "txt2img"
self.id_part = id_part
self.in_settings_column = in_settings_column
self.prompt = toprow_prompt.prompt
self.negative_prompt = toprow_prompt.negative_prompt
self.flex_revert = ["flex-basis-revert"] if in_settings_column else []

if in_settings_column and opts.sticky_generate_button:
with gr.Row(elem_id=f"{id_part}_generate_row", elem_classes=["generate-sticky"] + self.flex_revert):
self._create_generate_box()

with gr.Row(elem_id=f"{id_part}_toprow", variant="compact"):
if in_settings_column and not opts.sticky_generate_button:
with gr.Row(elem_id=f"{id_part}_generate_row", elem_classes=self.flex_revert):
self._create_generate_box()

with gr.Column(elem_id=f"{id_part}_prompt_container", scale=6):
with gr.Row():
with gr.Column(scale=80):
with gr.Row():
self.prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, lines=3, placeholder="Prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"])
self.prompt_img = gr.File(label="", elem_id=f"{id_part}_prompt_image", file_count="single", type="binary", visible=False)
with gr.Column() if self.in_settings_column else nullcontext():
with gr.Row():
toprow_prompt.prompt.render()
self.prompt_img = gr.File(label="", elem_id=f"{id_part}_prompt_image", file_count="single", type="binary", visible=False)

with gr.Row():
with gr.Column(scale=80):
with gr.Row():
self.negative_prompt = gr.Textbox(label="Negative prompt", elem_id=f"{id_part}_neg_prompt", show_label=False, lines=3, placeholder="Negative prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"])
with gr.Row():
self.negative_prompt.render()

self.button_interrogate = None
self.button_deepbooru = None
if is_img2img:
with gr.Column(scale=1, elem_classes="interrogate-col"):
with gr.Column(scale=1, elem_classes="interrogate-col") if not in_settings_column else gr.Row():
self.button_interrogate = gr.Button('Interrogate\nCLIP', elem_id="interrogate")
self.button_deepbooru = gr.Button('Interrogate\nDeepBooru', elem_id="deepbooru")

with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"):
with gr.Row(elem_id=f"{id_part}_generate_box", elem_classes="generate-box"):
self.interrupt = gr.Button('Interrupt', elem_id=f"{id_part}_interrupt", elem_classes="generate-box-interrupt")
self.skip = gr.Button('Skip', elem_id=f"{id_part}_skip", elem_classes="generate-box-skip")
self.submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary')

self.skip.click(
fn=lambda: shared.state.skip(),
inputs=[],
outputs=[],
)

self.interrupt.click(
fn=lambda: shared.state.interrupt(),
inputs=[],
outputs=[],
)

with gr.Row(elem_id=f"{id_part}_tools"):
self.paste = ToolButton(value=paste_symbol, elem_id="paste")
self.clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt")
self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress", visible=False)

self.token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"])
self.token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
self.negative_token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{id_part}_negative_token_counter", elem_classes=["token-counter"])
self.negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button")

self.clear_prompt_button.click(
fn=lambda *x: x,
_js="confirm_clear_prompt",
inputs=[self.prompt, self.negative_prompt],
outputs=[self.prompt, self.negative_prompt],
)
if in_settings_column:
with gr.Row(elem_classes=self.flex_revert):
self._create_styles(extra_buttons=self._create_tools)

self.ui_styles = ui_prompt_styles.UiPromptStyles(id_part, self.prompt, self.negative_prompt)
if not in_settings_column:
with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"):
self._create_generate_box()
self._create_tools()
self._create_styles()

self.prompt_img.change(
fn=modules.images.image_data,
Expand All @@ -240,6 +230,46 @@ def __init__(self, is_img2img):
show_progress=False,
)

def _create_generate_box(self):
with gr.Row(elem_id=f"{self.id_part}_generate_box", elem_classes=["generate-box"] + self.flex_revert):
self.interrupt = gr.Button('Interrupt', elem_id=f"{self.id_part}_interrupt", elem_classes="generate-box-interrupt")
self.skip = gr.Button('Skip', elem_id=f"{self.id_part}_skip", elem_classes="generate-box-skip")
self.submit = gr.Button('Generate', elem_id=f"{self.id_part}_generate", variant='primary')

self.skip.click(
fn=lambda: shared.state.skip(),
inputs=[],
outputs=[],
)

self.interrupt.click(
fn=lambda: shared.state.interrupt(),
inputs=[],
outputs=[],
)

def _create_tools(self):
with gr.Row(elem_id=f"{self.id_part}_tools", elem_classes=self.flex_revert) if not self.in_settings_column else nullcontext():
self.paste = ToolButton(value=paste_symbol, elem_id="paste")
self.clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{self.id_part}_clear_prompt")
self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{self.id_part}_restore_progress", visible=False)

self.token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{self.id_part}_token_counter", elem_classes=["token-counter"])
self.token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_token_button")
self.negative_token_counter = gr.HTML(value="<span>0/75</span>", elem_id=f"{self.id_part}_negative_token_counter", elem_classes=["token-counter"])
self.negative_token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_negative_token_button")

self.clear_prompt_button.click(
fn=lambda *x: x,
_js="confirm_clear_prompt",
inputs=[self.prompt, self.negative_prompt],
outputs=[self.prompt, self.negative_prompt],
)

def _create_styles(self, extra_buttons=None):
with gr.Row(elem_classes=self.flex_revert) if self.in_settings_column else nullcontext():
self.ui_styles = ui_prompt_styles.UiPromptStyles(self.id_part, self.prompt, self.negative_prompt, extra_buttons=extra_buttons)


def setup_progressbar(*args, **kwargs):
pass
Expand Down Expand Up @@ -325,8 +355,10 @@ def create_ui():
scripts.scripts_current = scripts.scripts_txt2img
scripts.scripts_txt2img.initialize_scripts(is_img2img=False)

txt2img_prompt = ToprowPrompt("txt2img")
with gr.Blocks(analytics_enabled=False) as txt2img_interface:
toprow = Toprow(is_img2img=False)
if not opts.move_toprow_to_settings_column:
toprow = Toprow(is_img2img=False, toprow_prompt=txt2img_prompt)

dummy_component = gr.Label(visible=False)

Expand All @@ -335,6 +367,9 @@ def create_ui():

with gr.Tab("Generation", id="txt2img_generation") as txt2img_generation_tab, gr.Row(equal_height=False):
with gr.Column(variant='compact', elem_id="txt2img_settings"):
if opts.move_toprow_to_settings_column:
toprow = Toprow(is_img2img=False, toprow_prompt=txt2img_prompt, in_settings_column=True)

scripts.scripts_txt2img.prepare_ui()

for category in ordered_ui_categories():
Expand Down Expand Up @@ -543,14 +578,19 @@ def create_ui():
scripts.scripts_current = scripts.scripts_img2img
scripts.scripts_img2img.initialize_scripts(is_img2img=True)

img2img_prompt = ToprowPrompt("img2img")
with gr.Blocks(analytics_enabled=False) as img2img_interface:
toprow = Toprow(is_img2img=True)
if not opts.move_toprow_to_settings_column:
toprow = Toprow(is_img2img=True, toprow_prompt=img2img_prompt)

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.Column(variant='compact', elem_id="img2img_settings"):
if opts.move_toprow_to_settings_column:
toprow = Toprow(is_img2img=True, toprow_prompt=img2img_prompt, in_settings_column=True)

copy_image_buttons = []
copy_image_destinations = {}

Expand Down
4 changes: 3 additions & 1 deletion modules/ui_prompt_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ def refresh_styles():


class UiPromptStyles:
def __init__(self, tabname, main_ui_prompt, main_ui_negative_prompt):
def __init__(self, tabname, main_ui_prompt, main_ui_negative_prompt, extra_buttons=None):
self.tabname = tabname

with gr.Row(elem_id=f"{tabname}_styles_row"):
self.dropdown = gr.Dropdown(label="Styles", show_label=False, elem_id=f"{tabname}_styles", choices=list(shared.prompt_styles.styles), value=[], multiselect=True, tooltip="Styles")
edit_button = ui_components.ToolButton(value=styles_edit_symbol, elem_id=f"{tabname}_styles_edit_button", tooltip="Edit styles")
if extra_buttons is not None:
extra_buttons()

with gr.Box(elem_id=f"{tabname}_styles_dialog", elem_classes="popup-dialog") as styles_dialog:
with gr.Row():
Expand Down
10 changes: 10 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ div.gradio-container, .block.gradio-textbox, div.gradio-group, div.gradio-dropdo
overflow: visible !important;
}

.flex-basis-revert {
flex-basis: revert !important;
}


/* general styled components */

Expand Down Expand Up @@ -322,6 +326,12 @@ div.block.gradio-accordion {
right: 0;
border-radius: 0 0.5rem 0.5rem 0;
}
.generate-sticky{
position: sticky;
z-index: 500;
top: 0.5em;
margin-bottom: 1em;
}

#img2img_scale_resolution_preview.block{
display: flex;
Expand Down