From 8c6ed7fe11f46682f5032b5fe7b99b6e8be8b0df Mon Sep 17 00:00:00 2001 From: Alex Heller Date: Mon, 5 Jun 2023 00:57:30 +0200 Subject: [PATCH] Added support for differentiation between "default unsaved" and "changed" settings. --- javascript/ui.js | 46 ++++++++++++++++++++++++++++------------------ modules/shared.py | 8 +++++++- style.css | 12 ++++++++++++ 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/javascript/ui.js b/javascript/ui.js index a1be9aa06..4995ad558 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -191,12 +191,16 @@ function recalculate_prompts_img2img(){ } opts = {} -opts_metadata = {changed_tab_items: {}, is_default: {}} +opts_metadata = {} +opts_tabs = {} function updateOpts(json_string){ let settings_data = JSON.parse(json_string) opts = settings_data.values - Object.keys(opts).forEach(function(key){ - opts_metadata.is_default[key] = settings_data.stored_keys.indexOf(key) != -1 + opts_metadata = settings_data.metadata + Object.entries(opts_metadata).forEach(([opt, meta]) => { + opts_tabs[meta.tab_name] ||= {} + let unsaved = (opts_tabs[meta.tab_name].unsaved_keys ||= new Set()) + if (!meta.is_stored) unsaved.add(opt) }) } @@ -283,11 +287,12 @@ onUiLoaded(function(){ tab_elements.forEach(function(elem, index){ // Add a modification indicator to the toplevel tab button + let tab_name = elem.id.replace("settings_", "") let new_indicator = document.createElement('button') - new_indicator.id = "modification_indicator_"+elem.id + new_indicator.id = "modification_indicator_"+tab_name new_indicator.className = "modification-indicator" new_indicator.disabled = true - new_indicator.onclick = () => onSettingsModificationIndicatorClicked(elem.id) + new_indicator.onclick = () => onSettingsModificationIndicatorClicked(tab_name) tab_nav_element.insertBefore(new_indicator, tab_nav_buttons[index]) // Add the tab content to the wrapper @@ -308,24 +313,29 @@ function markIfModified(setting_name, value) { elem.title = `Click to revert to previous value: ${previous_value_json}` } - let unsaved_value = !opts_metadata.is_default[setting_name] - if (unsaved_value) { + is_unsaved = !opts_metadata[setting_name].is_stored + if (is_unsaved) { elem.title = 'Unsaved value, apply settings to save.'; } - // TODO properly propagate unsaved value message to tab indicator / color marker differntly? - let is_dirty = unsaved_value || changed_value - elem.disabled = !is_dirty + elem.disabled = !(is_unsaved || changed_value) + elem.classList.toggle('changed', changed_value) + elem.classList.toggle('unsaved', is_unsaved) - let tab_element = elem.closest('[id^="settings_"]') - if (tab_element == null) return; - let tab_name = tab_element.id - let dirty_tab_items = (opts_metadata.changed_tab_items[tab_name] ||= new Set()) - changed_value ? dirty_tab_items.add(setting_name) : dirty_tab_items.delete(setting_name) + let tab_name = opts_metadata[setting_name].tab_name + let changed_items = (opts_tabs[tab_name].changed ||= new Set()) + changed_value ? changed_items.add(setting_name) : changed_items.delete(setting_name) + let unsaved = opts_tabs[tab_name].unsaved_keys // Set the indicator on the tab nav element let tab_nav_indicator = gradioApp().getElementById('modification_indicator_'+tab_name) - tab_nav_indicator.disabled = dirty_tab_items.size == 0 - tab_nav_indicator.title = `Click to reset ${dirty_tab_items.size} unapplied change${dirty_tab_items.size > 1 ? 's': ''} in this tab.` + tab_nav_indicator.disabled = (changed_items.size == 0) && (unsaved.size == 0) + tab_nav_indicator.title = ''; + tab_nav_indicator.classList.toggle('changed', changed_items.size > 0) + tab_nav_indicator.classList.toggle('unsaved', unsaved.size > 0) + if (changed_items.size > 0) + tab_nav_indicator.title += `Click to reset ${changed_items.size} unapplied change${changed_items.size > 1 ? 's': ''} in this tab.\n` + if (unsaved.size > 0) + tab_nav_indicator.title += `Apply settings to save ${unsaved.size} unsaved value${unsaved.size > 1 ? 's':''}.`; } function onSettingComponentChanged(setting_name, value) { @@ -334,7 +344,7 @@ function onSettingComponentChanged(setting_name, value) { } function onSettingsModificationIndicatorClicked(tab_name) { - opts_metadata.changed_tab_items[tab_name].forEach(function(setting_name){ + opts_tabs[tab_name].changed.forEach(function(setting_name){ // Click each setting's modification indicator gradioApp().getElementById("modification_indicator_"+setting_name).click() }) diff --git a/modules/shared.py b/modules/shared.py index a58a974b3..1fa69a66c 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -544,7 +544,13 @@ class Options: def dumpjson(self): d = {k: self.data.get(k, self.data_labels.get(k).default) for k in self.data_labels.keys()} - return json.dumps({"values": d, "stored_keys": list(self.data.keys())}) + metadata = { + k: { + "is_stored": k in self.data, + "tab_name": v.section[0] + } for k, v in self.data_labels.items() + } + return json.dumps({"values": d, "metadata": metadata}) def add_option(self, key, info): self.data_labels[key] = info diff --git a/style.css b/style.css index bfd2c229b..0c6098fe9 100644 --- a/style.css +++ b/style.css @@ -280,6 +280,18 @@ div#extras_scale_to_tab div.form{ visibility: hidden; } +#settings .modification-indicator.unsaved { + background: var(--color-accent-soft); +} + +#settings .modification-indicator.changed { + background: var(--color-accent); +} + +#settings .modification-indicator.changed.unsaved { + background-image: linear-gradient(var(--color-accent) 25%, var(--color-accent-soft) 75%); +} + #settings_result{ height: 1.4em; margin: 0 1.2em;