diff --git a/javascript/gallery.js b/javascript/gallery.js index bfa42c357..7aca1a047 100644 --- a/javascript/gallery.js +++ b/javascript/gallery.js @@ -5,8 +5,6 @@ let currentImage = null; let currentGalleryFolder = null; let pruneImagesTimer; let outstanding = 0; -let lastSort = 0; -let lastSortName = 'None'; let gallerySelection = { files: [], index: -1 }; let maintenanceController = new AbortController(); const folderStylesheet = new CSSStyleSheet(); @@ -25,6 +23,20 @@ const el = { const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl', 'gif', 'mp4', 'mkv', 'avi', 'mjpeg', 'mpg', 'avr']; +const gallerySorter = { + nameA: { name: 'Name Ascending', func: (a, b) => a.name.localeCompare(b.name) }, + nameD: { name: 'Name Descending', func: (b, a) => a.name.localeCompare(b.name) }, + sizeA: { name: 'Size Ascending', func: (a, b) => a.size - b.size }, + sizeD: { name: 'Size Descending', func: (b, a) => a.size - b.size }, + resA: { name: 'Resolution Ascending', func: (a, b) => a.width * a.height - b.width * b.height }, + resD: { name: 'Resolution Descending', func: (b, a) => a.width * a.height - b.width * b.height }, + modA: { name: 'Modified Ascending', func: (a, b) => a.mtime - b.mtime }, + modD: { name: 'Modified Descending', func: (b, a) => a.mtime - b.mtime }, + none: { name: 'None', func: null }, +}; + +let sortMode = gallerySorter.none; + async function getHash(str) { let hex = ''; const strBuf = new TextEncoder().encode(str); @@ -683,7 +695,7 @@ const gallerySendImage = (_images) => [currentImage]; // invoked by gradio butto */ function updateStatusWithSort(...messages) { if (!el.status) return; - messages.unshift(['Sort', lastSortName]); + messages.unshift(['Sort', sortMode.name]); const fragment = document.createDocumentFragment(); for (let i = 0; i < messages.length; i++) { const div = document.createElement('div'); @@ -858,11 +870,14 @@ const findDuplicates = (arr, key) => { }); }; -async function gallerySort(btn) { +async function gallerySort(key) { + if (!Object.hasOwn(gallerySorter, key)) { + error(`Gallery: "${key}" is not a valid gallery sorting key`); + return; + } const t0 = performance.now(); const arr = Array.from(el.files.children).filter((node) => node.name); // filter out separators if (arr.length === 0) return; // no files to sort - if (btn) lastSort = btn.charCodeAt(0); const fragment = document.createDocumentFragment(); // Helper to get directory path from a file node @@ -885,60 +900,17 @@ async function gallerySort(btn) { folderGroups.get(dir).push(file); } - // Sort function based on current sort mode - let sortFn; - switch (lastSort) { - case 61789: // name asc - lastSortName = 'Name Ascending'; - sortFn = (a, b) => a.name.localeCompare(b.name); - break; - case 61790: // name dsc - lastSortName = 'Name Descending'; - sortFn = (a, b) => b.name.localeCompare(a.name); - break; - case 61792: // size asc - lastSortName = 'Size Ascending'; - sortFn = (a, b) => a.size - b.size; - break; - case 61793: // size dsc - lastSortName = 'Size Descending'; - sortFn = (a, b) => b.size - a.size; - break; - case 61794: // resolution asc - lastSortName = 'Resolution Ascending'; - sortFn = (a, b) => a.width * a.height - b.width * b.height; - break; - case 61795: // resolution dsc - lastSortName = 'Resolution Descending'; - sortFn = (a, b) => b.width * b.height - a.width * a.height; - break; - case 61662: - lastSortName = 'Modified Ascending'; - sortFn = (a, b) => a.mtime - b.mtime; - break; - case 61661: - lastSortName = 'Modified Descending'; - sortFn = (a, b) => b.mtime - a.mtime; - break; - default: - lastSortName = 'None'; - sortFn = null; - break; - } + sortMode = gallerySorter[key]; // Sort root files - if (sortFn) { - rootFiles.sort(sortFn); - } + rootFiles.sort(sortMode.func); rootFiles.forEach((node) => fragment.appendChild(node)); // Sort folder names alphabetically, then sort files within each folder const sortedFolderNames = Array.from(folderGroups.keys()).sort((a, b) => a.localeCompare(b)); for (const folderName of sortedFolderNames) { const files = folderGroups.get(folderName); - if (sortFn) { - files.sort(sortFn); - } + files.sort(sortMode.func); files.forEach((node) => fragment.appendChild(node)); } @@ -963,7 +935,7 @@ async function gallerySort(btn) { } const t1 = performance.now(); - log(`gallerySort: char=${lastSort} len=${arr.length} time=${Math.floor(t1 - t0)} sort=${lastSortName}`); + log(`gallerySort: sort=${sortMode.name} len=${arr.length} time=${Math.floor(t1 - t0)}`); updateStatusWithSort(['Images', arr.length.toLocaleString()], `${iconStopwatch} ${Math.floor(t1 - t0).toLocaleString()}ms`); refreshGallerySelection(); } diff --git a/modules/ui_gallery.py b/modules/ui_gallery.py index 6e49182f1..9791598b3 100644 --- a/modules/ui_gallery.py +++ b/modules/ui_gallery.py @@ -59,19 +59,17 @@ def create_ui(): with gr.Blocks() as tab: with gr.Row(elem_id='tab-gallery-sort-buttons'): sort_buttons = [] - sort_buttons.append(ToolButton(value=ui_symbols.sort_alpha_asc, elem_classes=['gallery-sort'])) - sort_buttons.append(ToolButton(value=ui_symbols.sort_alpha_dsc, elem_classes=['gallery-sort'])) - sort_buttons.append(ToolButton(value=ui_symbols.sort_size_asc, elem_classes=['gallery-sort'])) - sort_buttons.append(ToolButton(value=ui_symbols.sort_size_dsc, elem_classes=['gallery-sort'])) - sort_buttons.append(ToolButton(value=ui_symbols.sort_num_asc, elem_classes=['gallery-sort'])) - sort_buttons.append(ToolButton(value=ui_symbols.sort_num_dsc, elem_classes=['gallery-sort'])) - sort_buttons.append(ToolButton(value=ui_symbols.sort_time_asc, elem_classes=['gallery-sort'])) - sort_buttons.append(ToolButton(value=ui_symbols.sort_time_dsc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_nameA := ToolButton(value=ui_symbols.sort_alpha_asc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_nameD := ToolButton(value=ui_symbols.sort_alpha_dsc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_sizeA := ToolButton(value=ui_symbols.sort_size_asc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_sizeD := ToolButton(value=ui_symbols.sort_size_dsc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_resA := ToolButton(value=ui_symbols.sort_num_asc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_resD := ToolButton(value=ui_symbols.sort_num_dsc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_modA := ToolButton(value=ui_symbols.sort_time_asc, elem_classes=['gallery-sort'])) + sort_buttons.append(sort_modD := ToolButton(value=ui_symbols.sort_time_dsc, elem_classes=['gallery-sort'])) gr.Textbox(show_label=False, placeholder='Search', elem_id='tab-gallery-search') gr.HTML('', elem_id='tab-gallery-status') gr.HTML('', elem_id='tab-gallery-progress') - for btn in sort_buttons: - btn.click(fn=None, _js='gallerySort', inputs=[btn], outputs=[]) with gr.Row(): with gr.Column(): gr.HTML('', elem_id='tab-gallery-folders') @@ -82,4 +80,14 @@ def create_ui(): gallery_video = gr.Video(None, elem_id='tab-gallery-video', show_label=False, visible=False) gallery_images, gen_info, html_info, _html_info_formatted, html_log = ui_common.create_output_panel("gallery") btn_gallery_image.click(fn=read_media, _js='gallerySendImage', inputs=[html_info], outputs=[gallery_images, gallery_video, html_info, gen_info, html_log]) + + sort_nameA.click(fn=None, _js='() => gallerySort("nameA")') + sort_nameD.click(fn=None, _js='() => gallerySort("nameD")') + sort_sizeA.click(fn=None, _js='() => gallerySort("sizeA")') + sort_sizeD.click(fn=None, _js='() => gallerySort("sizeD")') + sort_resA.click(fn=None, _js='() => gallerySort("resA")') + sort_resD.click(fn=None, _js='() => gallerySort("resD")') + sort_modA.click(fn=None, _js='() => gallerySort("modA")') + sort_modD.click(fn=None, _js='() => gallerySort("modD")') + return [(tab, 'Gallery', 'tab-gallery')]