From 585afda4beb385f15729ed9f22948951dda63e6d Mon Sep 17 00:00:00 2001 From: hius07 <62179190+hius07@users.noreply.github.com> Date: Thu, 6 Jun 2024 11:44:03 +0300 Subject: [PATCH] File searcher: group operations (#11980) --- frontend/apps/filemanager/filemanager.lua | 67 ++++---- .../filemanager/filemanagerfilesearcher.lua | 146 +++++++++++++++--- frontend/apps/reader/readerui.lua | 4 +- 3 files changed, 161 insertions(+), 56 deletions(-) diff --git a/frontend/apps/filemanager/filemanager.lua b/frontend/apps/filemanager/filemanager.lua index e5ee173e8..dc710016c 100644 --- a/frontend/apps/filemanager/filemanager.lua +++ b/frontend/apps/filemanager/filemanager.lua @@ -125,6 +125,7 @@ end function FileManager:setupLayout() self.show_parent = self.show_parent or self self.title_bar = TitleBar:new{ + show_parent = self.show_parent, fullscreen = "true", align = "center", title = self.title, @@ -137,7 +138,7 @@ function FileManager:setupLayout() left_icon_size_ratio = 1, left_icon_tap_callback = function() self:goHome() end, left_icon_hold_callback = function() self:onShowFolderMenu() end, - right_icon = "plus", + right_icon = self.selected_files and "check" or "plus", right_icon_size_ratio = 1, right_icon_tap_callback = function() self:onShowPlusMenu() end, right_icon_hold_callback = false, -- propagate long-press to dispatcher @@ -166,37 +167,31 @@ function FileManager:setupLayout() local file_manager = self - function file_chooser:onPathChanged(path) -- luacheck: ignore + function file_chooser:onPathChanged(path) file_manager:updateTitleBarPath(path) return true end - function file_chooser:onFileSelect(item) -- luacheck: ignore - local file = item.path - if file_manager.select_mode then - if file_manager.selected_files[file] then - file_manager.selected_files[file] = nil - item.dim = nil - else - file_manager.selected_files[file] = true - item.dim = true - end + function file_chooser:onFileSelect(item) + if file_manager.selected_files then -- toggle selection + item.dim = not item.dim and true or nil + file_manager.selected_files[item.path] = item.dim self:updateItems() else - file_manager:openFile(file) + file_manager:openFile(item.path) end return true end function file_chooser:onFileHold(item) - if file_manager.select_mode then + if file_manager.selected_files then file_manager:tapPlus() else self:showFileDialog(item) end end - function file_chooser:showFileDialog(item) -- luacheck: ignore + function file_chooser:showFileDialog(item) local file = item.path local is_file = item.is_file local is_not_parent_folder = not item.is_go_up @@ -234,7 +229,7 @@ function FileManager:setupLayout() text = _("Select"), callback = function() UIManager:close(self.file_dialog) - file_manager:onToggleSelectMode(true) -- no full screen refresh + file_manager:onToggleSelectMode() if is_file then file_manager.selected_files[file] = true item.dim = true @@ -381,7 +376,7 @@ function FileManager:registerKeyEvents() -- Override the menu.lua way of handling the back key self.file_chooser.key_events.Back = { { Device.input.group.Back } } if Device:hasScreenKB() then - self.key_events.KeyToggleWifi = { { "ScreenKB", "Home" }, event = "ToggleWifi" } + self.key_events.ToggleWifi = { { "ScreenKB", "Home" } } end if not Device:hasFewKeys() then -- Also remove the handler assigned to the "Back" key by menu.lua @@ -495,14 +490,21 @@ function FileManager:onShowPlusMenu() return true end -function FileManager:onToggleSelectMode(no_refresh) +function FileManager:onToggleSelectMode(do_refresh) logger.dbg("toggle select mode") - self.select_mode = not self.select_mode - self.selected_files = self.select_mode and {} or nil - self.title_bar:setRightIcon(self.select_mode and "check" or "plus") - if not no_refresh then - self:onRefresh() + if self.selected_files then + self.selected_files = nil + self.title_bar:setRightIcon("plus") + if do_refresh then + self.file_chooser:refreshPath() + else + self.file_chooser:selectAllFilesInFolder(false) -- undim + end + else + self.selected_files = {} + self.title_bar:setRightIcon("check") end + return true end function FileManager:tapPlus() @@ -514,9 +516,9 @@ function FileManager:tapPlus() end local title, buttons - if self.select_mode then + if self.selected_files then local function toggle_select_mode_callback() - self:onToggleSelectMode() + self:onToggleSelectMode(true) end local select_count = util.tableSize(self.selected_files) local actions_enabled = select_count > 0 @@ -624,7 +626,7 @@ function FileManager:tapPlus() text = _("Select files"), callback = function() UIManager:close(self.file_dialog) - self:onToggleSelectMode(true) -- no full screen refresh + self:onToggleSelectMode() end, }, }, @@ -722,7 +724,7 @@ function FileManager:tapPlus() title = title, title_align = "center", buttons = buttons, - select_mode = self.select_mode, -- for coverbrowser + select_mode = self.selected_files and true or nil, -- for coverbrowser } UIManager:show(self.file_dialog) end @@ -746,9 +748,6 @@ function FileManager:reinit(path, focused_file) -- looks unnecessary (cheap with classic mode, less cheap with -- CoverBrowser plugin's cover image renderings) -- self:onRefresh() - if self.select_mode then - self.title_bar:setRightIcon("check") - end end function FileManager:getCurrentDir() @@ -989,7 +988,7 @@ function FileManager:pasteSelectedFiles(overwrite) icon = "notice-warning", }) else - self:onToggleSelectMode() + self:onToggleSelectMode(true) end end @@ -1117,7 +1116,7 @@ function FileManager:deleteSelectedFiles() icon = "notice-warning", }) else - self:onToggleSelectMode() + self:onToggleSelectMode(true) end end @@ -1208,7 +1207,7 @@ function FileManager:renameFile(file, basename, is_file) end --- @note: This is the *only* safe way to instantiate a new FileManager instance! -function FileManager:showFiles(path, focused_file) +function FileManager:showFiles(path, focused_file, selected_files) -- Warn about and close any pre-existing FM instances first... if FileManager.instance then logger.warn("FileManager instance mismatch! Tried to spin up a new instance, while we still have an existing one:", tostring(FileManager.instance)) @@ -1224,6 +1223,7 @@ function FileManager:showFiles(path, focused_file) covers_fullscreen = true, -- hint for UIManager:_repaint() root_path = path, focused_file = focused_file, + selected_files = selected_files, } UIManager:show(file_manager) end @@ -1348,6 +1348,7 @@ function FileManager:showSelectedFilesList() item_table = selected_files, is_borderless = true, is_popout = false, + title_bar_fm_style = true, truncate_left = true, onMenuSelect = function(_, item) UIManager:close(menu) diff --git a/frontend/apps/filemanager/filemanagerfilesearcher.lua b/frontend/apps/filemanager/filemanagerfilesearcher.lua index 540d92f32..7076f1d14 100644 --- a/frontend/apps/filemanager/filemanagerfilesearcher.lua +++ b/frontend/apps/filemanager/filemanagerfilesearcher.lua @@ -97,14 +97,9 @@ function FileSearcher:onShowFileSearch(search_string) end function FileSearcher:doSearch() - local results local dirs, files = self:getList() -- If we have a FileChooser instance, use it, to be able to make use of its natsort cache - if self.ui.file_chooser then - results = self.ui.file_chooser:genItemTable(dirs, files) - else - results = FileChooser:genItemTable(dirs, files) - end + local results = (self.ui.file_chooser or FileChooser):genItemTable(dirs, files) if #results > 0 then self:showSearchResults(results) else @@ -221,31 +216,51 @@ end function FileSearcher:showSearchResults(results) self.search_menu = Menu:new{ - title = T(_("Search results (%1)"), #results), subtitle = T(_("Query: %1"), self.search_string), - item_table = results, - ui = self.ui, covers_fullscreen = true, -- hint for UIManager:_repaint() is_borderless = true, is_popout = false, title_bar_fm_style = true, + title_bar_left_icon = "appbar.menu", + onLeftButtonTap = function() self:setSelectMode() end, onMenuSelect = self.onMenuSelect, onMenuHold = self.onMenuHold, handle_hold_on_hold_release = true, + ui = self.ui, + _manager = self, } self.search_menu.close_callback = function() + self.selected_files = nil UIManager:close(self.search_menu) if self.ui.file_chooser then self.ui.file_chooser:refreshPath() end end + self:updateMenu(results) UIManager:show(self.search_menu) if self.no_metadata_count ~= 0 then self:showSearchResultsMessage() end end +function FileSearcher:updateMenu(item_table) + item_table = item_table or self.search_menu.item_table + self.search_menu:switchItemTable(T(_("Search results (%1)"), #item_table), item_table, -1) +end + function FileSearcher:onMenuSelect(item) + if self._manager.selected_files then + if item.is_file then + item.dim = not item.dim and true or nil + self._manager.selected_files[item.path] = item.dim + self._manager:updateMenu() + end + else + self._manager:showFileDialog(item) + end +end + +function FileSearcher:showFileDialog(item) local file = item.path local bookinfo, dialog local function close_dialog_callback() @@ -253,7 +268,11 @@ function FileSearcher:onMenuSelect(item) end local function close_dialog_menu_callback() UIManager:close(dialog) - self.close_callback() + self.search_menu.close_callback() + end + local function update_item_callback() + item.mandatory = FileChooser:getMenuItemMandatory(item, FileChooser:getCollate()) + self:updateMenu() end local buttons = {} if item.is_file then @@ -265,7 +284,7 @@ function FileSearcher:onMenuSelect(item) table.insert(buttons, {}) -- separator table.insert(buttons, { filemanagerutil.genResetSettingsButton(file, close_dialog_callback, is_currently_opened), - self.ui.collections:genAddToCollectionButton(file, close_dialog_callback), + self.ui.collections:genAddToCollectionButton(file, close_dialog_callback, update_item_callback), }) end table.insert(buttons, { @@ -275,13 +294,8 @@ function FileSearcher:onMenuSelect(item) callback = function() local function post_delete_callback() UIManager:close(dialog) - for i, menu_item in ipairs(self.item_table) do - if menu_item.path == file then - table.remove(self.item_table, i) - break - end - self:switchItemTable(T(_("Search results (%1)"), #self.item_table), self.item_table) - end + table.remove(self.search_menu.item_table, item.idx) + self:updateMenu() end local FileManager = require("apps/filemanager/filemanager") FileManager:showDeleteFileDialog(file, post_delete_callback) @@ -296,9 +310,9 @@ function FileSearcher:onMenuSelect(item) text = _("Open"), enabled = DocumentRegistry:hasProvider(file, nil, true), -- allow auxiliary providers callback = function() - close_dialog_callback() + close_dialog_menu_callback() local FileManager = require("apps/filemanager/filemanager") - FileManager.openFile(self.ui, file, nil, self.close_callback) + FileManager.openFile(self.ui, file) end, }, }) @@ -319,10 +333,12 @@ function FileSearcher:onMenuSelect(item) end function FileSearcher:onMenuHold(item) + if self._manager.selected_files then return true end if item.is_file then if DocumentRegistry:hasProvider(item.path, nil, true) then + self.close_callback() local FileManager = require("apps/filemanager/filemanager") - FileManager.openFile(self.ui, item.path, nil, self.close_callback) + FileManager.openFile(self.ui, item.path) end else self.close_callback() @@ -337,4 +353,92 @@ function FileSearcher:onMenuHold(item) return true end +function FileSearcher:setSelectMode() + if self.selected_files then + self:showSelectModeDialog() + else + self.selected_files = {} + self.search_menu:setTitleBarLeftIcon("check") + end +end + +function FileSearcher:showSelectModeDialog() + local item_table = self.search_menu.item_table + local select_count = util.tableSize(self.selected_files) + local actions_enabled = select_count > 0 + local title = actions_enabled and T(N_("1 file selected", "%1 files selected", select_count), select_count) + or _("No files selected") + local select_dialog + local buttons = { + { + { + text = _("Deselect all"), + enabled = actions_enabled, + callback = function() + UIManager:close(select_dialog) + for file in pairs (self.selected_files) do + self.selected_files[file] = nil + end + for _, item in ipairs(item_table) do + item.dim = nil + end + self:updateMenu() + end, + }, + { + text = _("Select all"), + callback = function() + UIManager:close(select_dialog) + for _, item in ipairs(item_table) do + if item.is_file then + item.dim = true + self.selected_files[item.path] = true + end + end + self:updateMenu() + end, + }, + }, + { + { + text = _("Exit select mode"), + callback = function() + UIManager:close(select_dialog) + self.selected_files = nil + self.search_menu:setTitleBarLeftIcon("appbar.menu") + if actions_enabled then + for _, item in ipairs(item_table) do + item.dim = nil + end + end + self:updateMenu() + end, + }, + { + text = _("Select in file browser"), + enabled = actions_enabled, + callback = function() + UIManager:close(select_dialog) + local selected_files = self.selected_files + self.search_menu.close_callback() + if self.ui.file_chooser then + self.ui.selected_files = selected_files + self.ui.title_bar:setRightIcon("check") + self.ui.file_chooser:refreshPath() + else -- called from Reader + self.ui:onClose() + self.ui:showFileManager(self.path .. "/", selected_files) + end + end, + }, + }, + } + select_dialog = ButtonDialog:new{ + title = title, + title_align = "center", + buttons = buttons, + } + UIManager:show(select_dialog) +end + return FileSearcher diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index cf13800cc..33c1a03c7 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -563,7 +563,7 @@ function ReaderUI:getLastDirFile(to_file_browser) return last_dir, last_file end -function ReaderUI:showFileManager(file) +function ReaderUI:showFileManager(file, selected_files) local FileManager = require("apps/filemanager/filemanager") local last_dir, last_file @@ -576,7 +576,7 @@ function ReaderUI:showFileManager(file) if FileManager.instance then FileManager.instance:reinit(last_dir, last_file) else - FileManager:showFiles(last_dir, last_file) + FileManager:showFiles(last_dir, last_file, selected_files) end end