From 0995e879cea8ce871489ea8e393bb0eba6edc09c Mon Sep 17 00:00:00 2001
From: Florian Horn <florianins@gmail.com>
Date: Thu, 27 Oct 2022 16:20:01 +0200
Subject: [PATCH 1/4] added save button and shortcut (s) to Modal View

---
 javascript/imageviewer.js | 29 +++++++++++++++++++++++++++++
 modules/ui.py             |  8 +++++---
 style.css                 | 18 ++++++++++++++++--
 3 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js
index 9e380c65..3aa3b29e 100644
--- a/javascript/imageviewer.js
+++ b/javascript/imageviewer.js
@@ -81,6 +81,25 @@ function modalImageSwitch(offset) {
     }
 }
 
+function saveImage(){
+    const tabTxt2Img = gradioApp().getElementById("tab_txt2img")
+    const tabImg2Img = gradioApp().getElementById("tab_img2img")
+    const saveTxt2Img = "save_txt2img"
+    const saveImg2Img = "save_img2img"
+    if (tabTxt2Img.style["display"] != "none") {
+        gradioApp().getElementById(saveTxt2Img).click()
+    } else if (tabImg2Img.style["display"] != "none") {
+        gradioApp().getElementById(saveImg2Img).click()
+    } else {
+        console.error("missing implementation for saving modal of this type")
+    }
+}
+
+function modalSaveImage(event) {
+    saveImage()
+    event.stopPropagation()
+}
+
 function modalNextImage(event) {
     modalImageSwitch(1)
     event.stopPropagation()
@@ -93,6 +112,9 @@ function modalPrevImage(event) {
 
 function modalKeyHandler(event) {
     switch (event.key) {
+        case "s":
+            saveImage()
+            break;
         case "ArrowLeft":
             modalPrevImage(event)
             break;
@@ -198,6 +220,13 @@ document.addEventListener("DOMContentLoaded", function() {
     modalTileImage.title = "Preview tiling";
     modalControls.appendChild(modalTileImage)
 
+    const modalSave = document.createElement("span")
+    modalSave.className = "modalSave cursor"
+    modalSave.innerHTML = "&#x1F5AB;"
+    modalSave.addEventListener("click", modalSaveImage, true)
+    modalSave.title = "Save Image(s)"
+    modalControls.appendChild(modalSave)
+
     const modalClose = document.createElement('span')
     modalClose.className = 'modalClose cursor';
     modalClose.innerHTML = '&times;'
diff --git a/modules/ui.py b/modules/ui.py
index 0a63e357..1332e265 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -630,7 +630,7 @@ def create_ui(wrap_gradio_gpu_call):
     import modules.img2img
     import modules.txt2img
 
-
+    
     with gr.Blocks(analytics_enabled=False) as txt2img_interface:
         txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, _, txt2img_prompt_style_apply, txt2img_save_style, txt2img_paste, token_counter, token_button = create_toprow(is_img2img=False)
         dummy_component = gr.Label(visible=False)
@@ -683,7 +683,8 @@ def create_ui(wrap_gradio_gpu_call):
 
                 with gr.Column():
                     with gr.Row():
-                        save = gr.Button('Save')
+                        saveButtonId = 'save_txt2img'
+                        save = gr.Button('Save', elem_id=saveButtonId)
                         send_to_img2img = gr.Button('Send to img2img')
                         send_to_inpaint = gr.Button('Send to inpaint')
                         send_to_extras = gr.Button('Send to extras')
@@ -901,7 +902,8 @@ def create_ui(wrap_gradio_gpu_call):
 
                 with gr.Column():
                     with gr.Row():
-                        save = gr.Button('Save')
+                        saveButtonId = 'save_img2img'
+                        save = gr.Button('Save', elem_id=saveButtonId)
                         img2img_send_to_img2img = gr.Button('Send to img2img')
                         img2img_send_to_inpaint = gr.Button('Send to inpaint')
                         img2img_send_to_extras = gr.Button('Send to extras')
diff --git a/style.css b/style.css
index 13a9fb07..fa357a49 100644
--- a/style.css
+++ b/style.css
@@ -314,8 +314,8 @@ input[type="range"]{
 
 .modalControls {
     display: grid;
-    grid-template-columns: 32px auto 1fr 32px;
-    grid-template-areas: "zoom tile space close";
+    grid-template-columns: 32px auto 32px 1fr 32px;
+    grid-template-areas: "zoom tile save space close";
     position: absolute;
     top: 0;
     left: 0;
@@ -333,6 +333,10 @@ input[type="range"]{
     grid-area: zoom;
 }
 
+.modalSave {
+    grid-area: save;
+}
+
 .modalTileImage {
     grid-area: tile;
 }
@@ -346,8 +350,18 @@ input[type="range"]{
   cursor: pointer;
 }
 
+.modalSave {
+    color: white;
+    font-size: 30px;
+    margin-top: 6px;
+    font-weight: bold;
+    cursor: pointer;
+}
+
 .modalClose:hover,
 .modalClose:focus,
+.modalSave:hover,
+.modalSave:focus,
 .modalZoom:hover,
 .modalZoom:focus {
   color: #999;

From 268159cfe3231743c554a1a9bf15d090c758f920 Mon Sep 17 00:00:00 2001
From: Florian Horn <florianins@gmail.com>
Date: Thu, 27 Oct 2022 16:32:10 +0200
Subject: [PATCH 2/4] fixed indentation

---
 modules/ui.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/ui.py b/modules/ui.py
index 1332e265..d49b10b2 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -630,7 +630,7 @@ def create_ui(wrap_gradio_gpu_call):
     import modules.img2img
     import modules.txt2img
 
-    
+
     with gr.Blocks(analytics_enabled=False) as txt2img_interface:
         txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, _, txt2img_prompt_style_apply, txt2img_save_style, txt2img_paste, token_counter, token_button = create_toprow(is_img2img=False)
         dummy_component = gr.Label(visible=False)

From bf25b51c3167426d3a340d2a3facea58813d4914 Mon Sep 17 00:00:00 2001
From: Florian Horn <florianins@gmail.com>
Date: Thu, 27 Oct 2022 16:38:55 +0200
Subject: [PATCH 3/4] fixed position to be in line with the other icons

---
 style.css | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/style.css b/style.css
index fa357a49..7b3ad050 100644
--- a/style.css
+++ b/style.css
@@ -314,7 +314,7 @@ input[type="range"]{
 
 .modalControls {
     display: grid;
-    grid-template-columns: 32px auto 32px 1fr 32px;
+    grid-template-columns: 32px 32px 32px 1fr 32px;
     grid-template-areas: "zoom tile save space close";
     position: absolute;
     top: 0;
@@ -352,8 +352,8 @@ input[type="range"]{
 
 .modalSave {
     color: white;
-    font-size: 30px;
-    margin-top: 6px;
+    font-size: 28px;
+    margin-top: 8px;
     font-weight: bold;
     cursor: pointer;
 }

From 403c5dba86f0faac6746df24d16e4d08aab8c9c5 Mon Sep 17 00:00:00 2001
From: Florian Horn <florianins@gmail.com>
Date: Fri, 28 Oct 2022 00:58:18 +0200
Subject: [PATCH 4/4] hide save btn for other tabs than txt2img and img2img

---
 javascript/imageviewer.js | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js
index 3aa3b29e..67916536 100644
--- a/javascript/imageviewer.js
+++ b/javascript/imageviewer.js
@@ -13,6 +13,15 @@ function showModal(event) {
     }
     lb.style.display = "block";
     lb.focus()
+
+    const tabTxt2Img = gradioApp().getElementById("tab_txt2img")
+    const tabImg2Img = gradioApp().getElementById("tab_img2img")
+    // show the save button in modal only on txt2img or img2img tabs
+    if (tabTxt2Img.style.display != "none" || tabImg2Img.style.display != "none") {
+        gradioApp().getElementById("modal_save").style.display = "inline"
+    } else {
+        gradioApp().getElementById("modal_save").style.display = "none"
+    }
     event.stopPropagation()
 }
 
@@ -86,9 +95,9 @@ function saveImage(){
     const tabImg2Img = gradioApp().getElementById("tab_img2img")
     const saveTxt2Img = "save_txt2img"
     const saveImg2Img = "save_img2img"
-    if (tabTxt2Img.style["display"] != "none") {
+    if (tabTxt2Img.style.display != "none") {
         gradioApp().getElementById(saveTxt2Img).click()
-    } else if (tabImg2Img.style["display"] != "none") {
+    } else if (tabImg2Img.style.display != "none") {
         gradioApp().getElementById(saveImg2Img).click()
     } else {
         console.error("missing implementation for saving modal of this type")
@@ -222,6 +231,7 @@ document.addEventListener("DOMContentLoaded", function() {
 
     const modalSave = document.createElement("span")
     modalSave.className = "modalSave cursor"
+    modalSave.id = "modal_save"
     modalSave.innerHTML = "&#x1F5AB;"
     modalSave.addEventListener("click", modalSaveImage, true)
     modalSave.title = "Save Image(s)"