From ea9ef6781ce03546077cae29e55912520b199b53 Mon Sep 17 00:00:00 2001 From: hius07 <62179190+hius07@users.noreply.github.com> Date: Fri, 12 Jan 2024 08:13:50 +0200 Subject: [PATCH] CoverBrowser: adjustable mosaic grid (#11232) --- frontend/apps/filemanager/filemanagermenu.lua | 88 +++---- frontend/ui/widget/keyvaluepage.lua | 16 +- frontend/ui/widget/menu.lua | 6 +- .../coverbrowser.koplugin/bookinfomanager.lua | 51 ++-- plugins/coverbrowser.koplugin/listmenu.lua | 51 ++-- plugins/coverbrowser.koplugin/main.lua | 230 +++++++++++++----- plugins/coverbrowser.koplugin/mosaicmenu.lua | 39 ++- 7 files changed, 307 insertions(+), 174 deletions(-) diff --git a/frontend/apps/filemanager/filemanagermenu.lua b/frontend/apps/filemanager/filemanagermenu.lua index a3bdb95e9..1f1000e5c 100644 --- a/frontend/apps/filemanager/filemanagermenu.lua +++ b/frontend/apps/filemanager/filemanagermenu.lua @@ -4,9 +4,11 @@ local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") local FFIUtil = require("ffi/util") local InputContainer = require("ui/widget/container/inputcontainer") +local KeyValuePage = require("ui/widget/keyvaluepage") local PluginLoader = require("pluginloader") local SetDefaults = require("apps/filemanager/filemanagersetdefaults") local Size = require("ui/size") +local SpinWidget = require("ui/widget/spinwidget") local UIManager = require("ui/uimanager") local Screen = Device.screen local filemanagerutil = require("apps/filemanager/filemanagerutil") @@ -168,49 +170,49 @@ function FileManagerMenu:setUpdateItemTable() text = _("Classic mode settings"), sub_item_table = { { - text = _("Items per page"), + text_func = function() + return T(_("Items per page: %1"), FileChooser.perpage) + end, help_text = _([[This sets the number of items per page in: - File browser, history and favorites in 'classic' display mode - Search results and folder shortcuts - File and folder selection - Calibre and OPDS browsers/search results]]), - callback = function() - local SpinWidget = require("ui/widget/spinwidget") - local Menu = require("ui/widget/menu") - local default_perpage = Menu.items_per_page_default - local curr_perpage = G_reader_settings:readSetting("items_per_page") or default_perpage - local items = SpinWidget:new{ - value = curr_perpage, - value_min = 6, - value_max = 24, - default_value = default_perpage, + callback = function(touchmenu_instance) + local current_value = FileChooser.perpage + local default_value = FileChooser.items_per_page_default + local widget = SpinWidget:new{ title_text = _("Items per page"), + value = current_value, + value_min = 6, + value_max = 30, + default_value = default_value, keep_shown_on_apply = true, callback = function(spin) G_reader_settings:saveSetting("items_per_page", spin.value) - self.ui:onRefresh() - end + FileChooser:refreshPath() + touchmenu_instance:updateItems() + end, } - UIManager:show(items) + UIManager:show(widget) end, }, { - text = _("Item font size"), - callback = function() - local SpinWidget = require("ui/widget/spinwidget") - local Menu = require("ui/widget/menu") - local curr_perpage = G_reader_settings:readSetting("items_per_page") or Menu.items_per_page_default - local default_font_size = Menu.getItemFontSize(curr_perpage) - local curr_font_size = G_reader_settings:readSetting("items_font_size") or default_font_size - local items_font = SpinWidget:new{ - value = curr_font_size, + text_func = function() + return T(_("Item font size: %1"), FileChooser.font_size) + end, + callback = function(touchmenu_instance) + local current_value = FileChooser.font_size + local default_value = FileChooser.getItemFontSize(FileChooser.perpage) + local widget = SpinWidget:new{ + title_text = _("Item font size"), + value = current_value, value_min = 10, value_max = 72, - default_value = default_font_size, + default_value = default_value, keep_shown_on_apply = true, - title_text = _("Item font size"), callback = function(spin) - if spin.value == default_font_size then + if spin.value == default_value then -- We can't know if the user has set a size or hit "Use default", but -- assume that if it is the default font size, he will prefer to have -- our default font size if he later updates per-page @@ -218,10 +220,11 @@ function FileManagerMenu:setUpdateItemTable() else G_reader_settings:saveSetting("items_font_size", spin.value) end - self.ui:onRefresh() - end + FileChooser:refreshPath() + touchmenu_instance:updateItems() + end, } - UIManager:show(items_font) + UIManager:show(widget) end, }, { @@ -373,26 +376,28 @@ To: separator = true, }, { - text = _("Info lists items per page"), + text_func = function() + local default_value = KeyValuePage.getDefaultItemsPerPage() + local current_value = G_reader_settings:readSetting("keyvalues_per_page") or default_value + return T(_("Info lists items per page: %1"), current_value) + end, help_text = _([[This sets the number of items per page in: - Book information - Dictionary and Wikipedia lookup history - Reading statistics details - A few other plugins]]), keep_menu_open = true, - callback = function() - local SpinWidget = require("ui/widget/spinwidget") - local KeyValuePage = require("ui/widget/keyvaluepage") - local default_perpage = KeyValuePage:getDefaultKeyValuesPerPage() - local curr_perpage = G_reader_settings:readSetting("keyvalues_per_page") or default_perpage - local items = SpinWidget:new{ - value = curr_perpage, + callback = function(touchmenu_instance) + local default_value = KeyValuePage.getDefaultItemsPerPage() + local current_value = G_reader_settings:readSetting("keyvalues_per_page") or default_value + local widget = SpinWidget:new{ + value = current_value, value_min = 10, - value_max = 24, - default_value = default_perpage, + value_max = 30, + default_value = default_value, title_text = _("Info lists items per page"), callback = function(spin) - if spin.value == default_perpage then + if spin.value == default_value then -- We can't know if the user has set a value or hit "Use default", but -- assume that if it is the default, he will prefer to stay with our -- default if he later changes screen DPI @@ -400,9 +405,10 @@ To: else G_reader_settings:saveSetting("keyvalues_per_page", spin.value) end + touchmenu_instance:updateItems() end } - UIManager:show(items) + UIManager:show(widget) end, }, }, diff --git a/frontend/ui/widget/keyvaluepage.lua b/frontend/ui/widget/keyvaluepage.lua index 2f437a19c..1f78ed08d 100644 --- a/frontend/ui/widget/keyvaluepage.lua +++ b/frontend/ui/widget/keyvaluepage.lua @@ -467,14 +467,10 @@ function KeyValuePage:init() - 2*Size.line.thick -- account for possibly 2 separator lines added - local force_items_per_page - if self.single_page then - force_items_per_page = math.max(#self.kv_pairs, - G_reader_settings:readSetting("keyvalues_per_page") or self:getDefaultKeyValuesPerPage()) + self.items_per_page = G_reader_settings:readSetting("keyvalues_per_page") or self.getDefaultItemsPerPage() + if self.single_page and self.items_per_page < #self.kv_pairs then + self.items_per_page = #self.kv_pairs end - - self.items_per_page = force_items_per_page or - G_reader_settings:readSetting("keyvalues_per_page") or self:getDefaultKeyValuesPerPage() self.item_height = math.floor(available_height / self.items_per_page) -- Put half of the pixels lost by floor'ing between title and content local content_height = self.items_per_page * self.item_height @@ -522,7 +518,7 @@ function KeyValuePage:init() } end -function KeyValuePage:getDefaultKeyValuesPerPage() +function KeyValuePage.getDefaultItemsPerPage() -- Get a default according to Screen DPI (roughly following -- the former implementation building logic) local default_item_height = Size.item.height_default * 1.5 -- we were adding 1/2 as margin @@ -685,7 +681,7 @@ function KeyValuePage:_populateItems() background = Blitbuffer.COLOR_LIGHT_GRAY, dimen = Geom:new{ w = self.item_width, - h = Size.line.thick + h = Size.line.thick, }, style = "solid", }) @@ -699,7 +695,7 @@ function KeyValuePage:_populateItems() background = Blitbuffer.COLOR_LIGHT_GRAY, dimen = Geom:new{ w = self.item_width, - h = Size.line.thick + h = Size.line.thick, }, style = "solid", }) diff --git a/frontend/ui/widget/menu.lua b/frontend/ui/widget/menu.lua index 54c6222d6..c0759b95f 100644 --- a/frontend/ui/widget/menu.lua +++ b/frontend/ui/widget/menu.lua @@ -1040,9 +1040,9 @@ function Menu:updateItems(select_number) select_number = 1 end - local font_size = self.items_font_size or G_reader_settings:readSetting("items_font_size") + self.font_size = self.items_font_size or G_reader_settings:readSetting("items_font_size") or Menu.getItemFontSize(self.perpage) - local infont_size = self.items_mandatory_font_size or (font_size - 4) + local infont_size = self.items_mandatory_font_size or (self.font_size - 4) local multilines_show_more_text = self.multilines_show_more_text if multilines_show_more_text == nil then multilines_show_more_text = G_reader_settings:isTrue("items_multilines_show_more_text") @@ -1075,7 +1075,7 @@ function Menu:updateItems(select_number) bold = self.item_table.current == i or self.item_table[i].bold == true, dim = self.item_table[i].dim, font = "smallinfofont", - font_size = font_size, + font_size = self.font_size, infont = "infont", infont_size = infont_size, dimen = self.item_dimen:new(), diff --git a/plugins/coverbrowser.koplugin/bookinfomanager.lua b/plugins/coverbrowser.koplugin/bookinfomanager.lua index e214508eb..4762c8392 100644 --- a/plugins/coverbrowser.koplugin/bookinfomanager.lua +++ b/plugins/coverbrowser.koplugin/bookinfomanager.lua @@ -39,7 +39,7 @@ local BOOKINFO_DB_SCHEMA = [[ cover_fetched TEXT, -- NULL / 'Y' = we tried to fetch a cover (but we may not have gotten one) has_meta TEXT, -- NULL / 'Y' = has metadata (title, authors...) has_cover TEXT, -- NULL / 'Y' = has cover image (cover_*) - cover_sizetag TEXT, -- 'M' (Medium, MosaicMenuItem) / 's' (small, ListMenuItem) + cover_sizetag TEXT, -- '1072x1448' (example, is the original cover image width and height) -- Other properties that can be set and returned as is (not used here) -- If user doesn't want to see these (wrong metadata, offending cover...) @@ -270,7 +270,7 @@ function BookInfoManager:loadSettings(db_conn) local keys = res[1] local values = res[2] for i, key in ipairs(keys) do - self.settings[key] = values[i] + self.settings[key] = tonumber(values[i]) or values[i] -- TEXT db field end end end @@ -506,23 +506,18 @@ function BookInfoManager:extractBookInfo(filepath, cover_specs) dbrow[k] = v end if cover_specs then - local spec_sizetag = cover_specs.sizetag local spec_max_cover_w = cover_specs.max_cover_w local spec_max_cover_h = cover_specs.max_cover_h - dbrow.cover_fetched = 'Y' -- we had a try at getting a cover local cover_bb = FileManagerBookInfo:getCoverImage(document) if cover_bb then dbrow.has_cover = 'Y' - dbrow.cover_sizetag = spec_sizetag -- we should scale down the cover to our max size local cbb_w, cbb_h = cover_bb:getWidth(), cover_bb:getHeight() - local scale_factor = 1 + dbrow.cover_sizetag = cbb_w .. "x" .. cbb_h -- store original cover size if cbb_w > spec_max_cover_w or cbb_h > spec_max_cover_h then -- scale down if bigger than what we will display - scale_factor = math.min(spec_max_cover_w / cbb_w, spec_max_cover_h / cbb_h) - cbb_w = math.min(math.floor(cbb_w * scale_factor)+1, spec_max_cover_w) - cbb_h = math.min(math.floor(cbb_h * scale_factor)+1, spec_max_cover_h) + cbb_w, cbb_h = BookInfoManager.getCachedCoverSize(cbb_w, cbb_h, spec_max_cover_w, spec_max_cover_h) cover_bb = RenderImage:scaleBlitBuffer(cover_bb, cbb_w, cbb_h, true) end dbrow.cover_w = cover_bb.w @@ -532,7 +527,8 @@ function BookInfoManager:extractBookInfo(filepath, cover_specs) local cover_size = cover_bb.stride * cover_bb.h local cover_zst_ptr, cover_zst_size = zstd.zstd_compress(cover_bb.data, cover_size) dbrow.cover_bb_data = SQ3.blob(cover_zst_ptr, cover_zst_size) -- cast to blob for sqlite - logger.dbg("cover for", filename, "scaled by", scale_factor, "=>", cover_bb.w, "x", cover_bb.h, ", compressed from", tonumber(cover_size), "to", tonumber(cover_zst_size)) + logger.dbg("cover for", filename, "scaled from", dbrow.cover_sizetag, "to", cover_bb.w, "x", cover_bb.h, + ", compressed from", tonumber(cover_size), "to", tonumber(cover_zst_size)) -- We're done with the uncompressed bb now, and the compressed one has been managed by SQLite ;) cover_bb:free() end @@ -886,10 +882,8 @@ Do you want to prune the cache of removed books?]] local to_extract = not bookinfo if bookinfo and cover_specs and not bookinfo.ignore_cover then if bookinfo.cover_fetched then - if bookinfo.has_cover and cover_specs.sizetag ~= bookinfo.cover_sizetag then - if bookinfo.cover_sizetag ~= "M" then -- keep the bigger "M" - to_extract = true - end + if bookinfo.has_cover and BookInfoManager.isCachedCoverInvalid(bookinfo, cover_specs) then + to_extract = true end else to_extract = true @@ -985,6 +979,35 @@ Do you want to prune the cache of removed books?]] UIManager:show(info) end +function BookInfoManager.getCachedCoverSize(img_w, img_h, max_img_w, max_img_h) + local scale_factor + local width = math.floor(max_img_h * img_w / img_h + 0.5) + if max_img_w >= width then + max_img_w = width + scale_factor = max_img_w / img_w + else + max_img_h = math.floor(max_img_w * img_h / img_w + 0.5) + scale_factor = max_img_h / img_h + end + return max_img_w, max_img_h, scale_factor +end + +function BookInfoManager.isCachedCoverInvalid(bookinfo, cover_specs) + local img_w, img_h = bookinfo.cover_sizetag:match("(%d+)x(%d+)") -- original image + if not img_w or not img_h then -- old or bad cover_sizetag + return true + end + img_w, img_h = tonumber(img_w), tonumber(img_h) + local max_img_w = cover_specs.max_cover_w + local max_img_h = cover_specs.max_cover_h + if img_w > max_img_w or img_h > max_img_h then -- original image bigger than placeholder + local new_cover_w, new_cover_h = BookInfoManager.getCachedCoverSize(img_w, img_h, max_img_w, max_img_h) + if new_cover_w > bookinfo.cover_w or new_cover_h > bookinfo.cover_h then -- bigger thumbnail needed + return true + end + end +end + BookInfoManager:init() return BookInfoManager diff --git a/plugins/coverbrowser.koplugin/listmenu.lua b/plugins/coverbrowser.koplugin/listmenu.lua index ee3e1d24d..61617fde9 100644 --- a/plugins/coverbrowser.koplugin/listmenu.lua +++ b/plugins/coverbrowser.koplugin/listmenu.lua @@ -209,7 +209,6 @@ function ListMenuItem:update() local max_img_w = dimen.h - 2*border_size -- width = height, squared local max_img_h = dimen.h - 2*border_size local cover_specs = { - sizetag = self.menu.cover_sizetag, max_cover_w = max_img_w, max_cover_h = max_img_h, } @@ -267,24 +266,26 @@ function ListMenuItem:update() -- File local bookinfo = BookInfoManager:getBookInfo(self.filepath, self.do_cover_image) + if bookinfo and self.do_cover_image and not bookinfo.ignore_cover then if bookinfo.cover_fetched then - -- trigger recalculation of thumbnail if size changed - if bookinfo.has_cover and bookinfo.cover_sizetag ~= "M" and bookinfo.cover_sizetag ~= cover_specs.sizetag then - if bookinfo.cover_bb then - bookinfo.cover_bb:free() + if bookinfo.has_cover and not self.menu.no_refresh_covers then + if BookInfoManager.isCachedCoverInvalid(bookinfo, cover_specs) then + -- there is a thumbnail, but it's smaller than is needed for new grid dimensions, + -- and it would be ugly if scaled up to the required size: + -- do as if not found to force a new extraction with our size + if bookinfo.cover_bb then + bookinfo.cover_bb:free() + end + bookinfo = nil end - bookinfo = nil end + -- if not has_cover, book has no cover, no need to try again else -- cover was not fetched previously, do as if not found -- to force a new extraction bookinfo = nil end - -- If there's already a cover and it's a "M" size (MosaicMenuItem), - -- we'll use it and scale it down (it may slow a bit rendering, - -- but "M" size may be useful in another view (FileBrowser/History), - -- so we don't replace it). end if bookinfo then -- This book is known @@ -301,7 +302,7 @@ function ListMenuItem:update() if bookinfo.has_cover and not bookinfo.ignore_cover then cover_bb_used = true -- Let ImageWidget do the scaling and give us the final size - local scale_factor = math.min(max_img_w / bookinfo.cover_w, max_img_h / bookinfo.cover_h) + local _, _, scale_factor = BookInfoManager.getCachedCoverSize(bookinfo.cover_w, bookinfo.cover_h, max_img_w, max_img_h) local wimage = ImageWidget:new{ image = bookinfo.cover_bb, scale_factor = scale_factor, @@ -901,6 +902,7 @@ end local ListMenu = {} function ListMenu:_recalculateDimen() + self.portrait_mode = Screen:getWidth() <= Screen:getHeight() -- Find out available height from other UI elements made in Menu self.others_height = 0 if self.title_bar then -- Menu:init() has been done @@ -927,25 +929,16 @@ function ListMenu:_recalculateDimen() end local available_height = self.inner_dimen.h - self.others_height - Size.line.thin - -- (Note: we can't assign directly to self.perpage and expect it to - -- be 'nil' if it was not defined, as we'll find instead the value - -- defined in the Menu class (14) because of inheritance.) - local files_per_page = BookInfoManager:getSetting("files_per_page") - if files_per_page then - self.perpage = tonumber(files_per_page) - else - -- perpage used to be static and computed from a base of 64px per ListMenuItem, - -- which gave 10 items both in filemanager and history on kobo glo hd. - -- Now that we can change the nb of items, let's start with a similar default - -- and save it so it's known as the initial value by the menu selection widget. - self.perpage = math.floor(available_height / scale_by_size / 64) - BookInfoManager:saveSetting("files_per_page", tostring(self.perpage)) + if self.files_per_page == nil then -- first drawing + -- Default perpage is computed from a base of 64px per ListMenuItem, + -- which gives 10 items on kobo glo hd. + self.files_per_page = math.floor(available_height / scale_by_size / 64) + BookInfoManager:saveSetting("files_per_page", self.files_per_page) end - self.cover_sizetag = "s" .. self.perpage - if Screen:getWidth() > Screen:getHeight() then -- landscape mode - -- When in landscape mode (only possible with History), adjust - -- perpage so items get a chance to have about the same height - -- as when in portrait mode. + self.perpage = self.files_per_page + if not self.portrait_mode then + -- When in landscape mode, adjust perpage so items get a chance + -- to have about the same height as when in portrait mode. -- This computation is not strictly correct, as "others_height" would -- have a different value in portrait mode. But let's go with that. local portrait_available_height = Screen:getWidth() - self.others_height - Size.line.thin diff --git a/plugins/coverbrowser.koplugin/main.lua b/plugins/coverbrowser.koplugin/main.lua index aa3982b5d..108872842 100644 --- a/plugins/coverbrowser.koplugin/main.lua +++ b/plugins/coverbrowser.koplugin/main.lua @@ -2,6 +2,7 @@ local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local logger = require("logger") local _ = require("gettext") +local T = require("ffi/util").template local BookInfoManager = require("bookinfomanager") --[[ @@ -50,7 +51,9 @@ local CoverBrowser = WidgetContainer:extend{ } function CoverBrowser:init() - self.ui.menu:registerToMainMenu(self) + if self.ui.file_chooser then -- FileManager menu only + self.ui.menu:registerToMainMenu(self) + end if init_done then -- things already patched according to current modes return @@ -64,6 +67,7 @@ function CoverBrowser:init() logger.info("CoverBrowser: setting default display modes") BookInfoManager:saveSetting("filemanager_display_mode", "list_image_meta") BookInfoManager:saveSetting("history_display_mode", "mosaic_image") + BookInfoManager:saveSetting("collection_display_mode", "mosaic_image") end G_reader_settings:makeTrue("coverbrowser_initial_default_setup_done") end @@ -72,17 +76,11 @@ function CoverBrowser:init() self:setupHistoryDisplayMode(BookInfoManager:getSetting("history_display_mode")) self:setupCollectionDisplayMode(BookInfoManager:getSetting("collection_display_mode")) series_mode = BookInfoManager:getSetting("series_mode") - init_done = true BookInfoManager:closeDbConnection() -- will be re-opened if needed end function CoverBrowser:addToMainMenu(menu_items) - -- We add it only to FileManager menu - if self.ui.view then -- Reader - return - end - local modes = { { _("Classic (filename only)") }, { _("Mosaic with cover images"), "mosaic_image" }, @@ -94,7 +92,7 @@ function CoverBrowser:addToMainMenu(menu_items) local sub_item_table, history_sub_item_table, collection_sub_item_table = {}, {}, {} for i, v in ipairs(modes) do local text, mode = unpack(v) - table.insert(sub_item_table, { + sub_item_table[i] = { text = text, checked_func = function() return mode == filemanager_display_mode @@ -106,8 +104,8 @@ function CoverBrowser:addToMainMenu(menu_items) self:setupCollectionDisplayMode(mode) end end, - }) - table.insert(history_sub_item_table, { + } + history_sub_item_table[i] = { text = text, checked_func = function() return mode == history_display_mode @@ -115,8 +113,8 @@ function CoverBrowser:addToMainMenu(menu_items) callback = function() self:setupHistoryDisplayMode(mode) end, - }) - table.insert(collection_sub_item_table, { + } + collection_sub_item_table[i] = { text = text, checked_func = function() return mode == collection_display_mode @@ -124,7 +122,7 @@ function CoverBrowser:addToMainMenu(menu_items) callback = function() self:setupCollectionDisplayMode(mode) end, - }) + } end sub_item_table[#modes].separator = true table.insert(sub_item_table, { @@ -161,58 +159,174 @@ function CoverBrowser:addToMainMenu(menu_items) -- add Mosaic / Detailed list mode settings to File browser Settings submenu -- next to Classic mode settings if menu_items.filebrowser_settings == nil then return end - table.insert (menu_items.filebrowser_settings.sub_item_table, 4, { + table.insert (menu_items.filebrowser_settings.sub_item_table, 5, { text = _("Mosaic and detailed list settings"), separator = true, sub_item_table = { { - text = _("Items per page"), - help_text = _([[This sets the number of files and folders per page in display modes other than classic.]]), + text_func = function() + local fc = self.ui.file_chooser + return T(_("Items per page in portrait mosaic mode: %1 × %2"), fc.nb_cols_portrait, fc.nb_rows_portrait) + end, -- Best to not "keep_menu_open = true", to see how this apply on the full view callback = function() + local fc = self.ui.file_chooser + local nb_cols = fc.nb_cols_portrait + local nb_rows = fc.nb_rows_portrait + local DoubleSpinWidget = require("/ui/widget/doublespinwidget") + local widget = DoubleSpinWidget:new{ + title_text = _("Portrait mosaic mode"), + width_factor = 0.6, + left_text = _("Columns"), + left_value = nb_cols, + left_min = 2, + left_max = 8, + left_default = 3, + left_precision = "%01d", + right_text = _("Rows"), + right_value = nb_rows, + right_min = 2, + right_max = 8, + right_default = 3, + right_precision = "%01d", + keep_shown_on_apply = true, + callback = function(left_value, right_value) + fc.nb_cols_portrait = left_value + fc.nb_rows_portrait = right_value + if fc.display_mode_type == "mosaic" and fc.portrait_mode then + fc.no_refresh_covers = true + fc:updateItems() + end + end, + close_callback = function() + if fc.nb_cols_portrait ~= nb_cols or fc.nb_rows_portrait ~= nb_rows then + BookInfoManager:saveSetting("nb_cols_portrait", fc.nb_cols_portrait) + BookInfoManager:saveSetting("nb_rows_portrait", fc.nb_rows_portrait) + FileChooser.nb_cols_portrait = fc.nb_cols_portrait + FileChooser.nb_rows_portrait = fc.nb_rows_portrait + if fc.display_mode_type == "mosaic" and fc.portrait_mode then + fc.no_refresh_covers = nil + fc:updateItems() + end + end + end, + } + UIManager:show(widget) + end, + }, + { + text_func = function() + local fc = self.ui.file_chooser + return T(_("Items per page in landscape mosaic mode: %1 × %2"), fc.nb_cols_landscape, fc.nb_rows_landscape) + end, + callback = function() + local fc = self.ui.file_chooser + local nb_cols = fc.nb_cols_landscape + local nb_rows = fc.nb_rows_landscape + local DoubleSpinWidget = require("/ui/widget/doublespinwidget") + local widget = DoubleSpinWidget:new{ + title_text = _("Landscape mosaic mode"), + width_factor = 0.6, + left_text = _("Columns"), + left_value = nb_cols, + left_min = 2, + left_max = 8, + left_default = 4, + left_precision = "%01d", + right_text = _("Rows"), + right_value = nb_rows, + right_min = 2, + right_max = 8, + right_default = 2, + right_precision = "%01d", + keep_shown_on_apply = true, + callback = function(left_value, right_value) + fc.nb_cols_landscape = left_value + fc.nb_rows_landscape = right_value + if fc.display_mode_type == "mosaic" and not fc.portrait_mode then + fc.no_refresh_covers = true + fc:updateItems() + end + end, + close_callback = function() + if fc.nb_cols_landscape ~= nb_cols or fc.nb_rows_landscape ~= nb_rows then + BookInfoManager:saveSetting("nb_cols_landscape", fc.nb_cols_landscape) + BookInfoManager:saveSetting("nb_rows_landscape", fc.nb_rows_landscape) + FileChooser.nb_cols_landscape = fc.nb_cols_landscape + FileChooser.nb_rows_landscape = fc.nb_rows_landscape + if fc.display_mode_type == "mosaic" and not fc.portrait_mode then + fc.no_refresh_covers = nil + fc:updateItems() + end + end + end, + } + UIManager:show(widget) + end, + }, + { + text_func = function() + local fc = self.ui.file_chooser + -- default files_per_page should be calculated by ListMenu on the first drawing, + -- use 10 if ListMenu has not been drawn yet + return T(_("Items per page in portrait list mode: %1"), fc.files_per_page or 10) + end, + callback = function() + local fc = self.ui.file_chooser + local files_per_page = fc.files_per_page or 10 local SpinWidget = require("ui/widget/spinwidget") - -- "files_per_page" should have been saved with an adequate value - -- the first time Detailed list was shown. Fallback to a start - -- value of 10 if it hasn't. - local curr_items = BookInfoManager:getSetting("files_per_page") or 10 - local items = SpinWidget:new{ - value = curr_items, + local widget = SpinWidget:new{ + title_text = _("Portrait list mode"), + value = files_per_page, value_min = 4, value_max = 20, default_value = 10, keep_shown_on_apply = true, - title_text = _("Items per page"), callback = function(spin) - BookInfoManager:saveSetting("files_per_page", spin.value) - self.ui:onRefresh() - end + fc.files_per_page = spin.value + if fc.display_mode_type == "list" then + fc.no_refresh_covers = true + fc:updateItems() + end + end, + close_callback = function() + if fc.files_per_page ~= files_per_page then + BookInfoManager:saveSetting("files_per_page", fc.files_per_page) + FileChooser.files_per_page = fc.files_per_page + if fc.display_mode_type == "list" then + fc.no_refresh_covers = nil + fc:updateItems() + end + end + end, } - UIManager:show(items) + UIManager:show(widget) end, + separator = true, }, { text = _("Progress"), sub_item_table = { { - text = _("Show progress"), - checked_func = function() return - not BookInfoManager:getSetting("hide_page_info") - end, + text = _("Show progress in mosaic mode"), + checked_func = function() return BookInfoManager:getSetting("show_progress_in_mosaic") end, callback = function() - BookInfoManager:toggleSetting("hide_page_info") + BookInfoManager:toggleSetting("show_progress_in_mosaic") self:refreshFileManagerInstance() end, + separator = true, }, { - text = _("Show progress % in mosaic mode"), - checked_func = function() return BookInfoManager:getSetting("show_progress_in_mosaic") end, + text = _("Show progress in detailed list mode"), + checked_func = function() return not BookInfoManager:getSetting("hide_page_info") end, callback = function() - BookInfoManager:toggleSetting("show_progress_in_mosaic") + BookInfoManager:toggleSetting("hide_page_info") self:refreshFileManagerInstance() end, }, { text = _("Show number of pages read instead of progress %"), + enabled_func = function() return not BookInfoManager:getSetting("hide_page_info") end, checked_func = function() return BookInfoManager:getSetting("show_pages_read_as_progress") end, callback = function() BookInfoManager:toggleSetting("show_pages_read_as_progress") @@ -221,12 +335,12 @@ function CoverBrowser:addToMainMenu(menu_items) }, { text = _("Show number of pages left to read"), + enabled_func = function() return not BookInfoManager:getSetting("hide_page_info") end, checked_func = function() return BookInfoManager:getSetting("show_pages_left_in_progress") end, callback = function() BookInfoManager:toggleSetting("show_pages_left_in_progress") self:refreshFileManagerInstance() end, - separator = true, }, }, }, @@ -302,7 +416,6 @@ function CoverBrowser:addToMainMenu(menu_items) end, }, }, - separator = true }, { text = _("Show file properties"), @@ -313,6 +426,7 @@ function CoverBrowser:addToMainMenu(menu_items) BookInfoManager:toggleSetting("hide_file_info") self:refreshFileManagerInstance() end, + separator = true, }, { text = _("Book info cache management"), @@ -391,6 +505,19 @@ function CoverBrowser:addToMainMenu(menu_items) }) end +function CoverBrowser.initGrid(menu, display_mode) + if menu == nil then return end + if menu.nb_cols_portrait == nil then + menu.nb_cols_portrait = BookInfoManager:getSetting("nb_cols_portrait") or 3 + menu.nb_rows_portrait = BookInfoManager:getSetting("nb_rows_portrait") or 3 + menu.nb_cols_landscape = BookInfoManager:getSetting("nb_cols_landscape") or 4 + menu.nb_rows_landscape = BookInfoManager:getSetting("nb_rows_landscape") or 2 + -- initial List mode files_per_page will be calculated and saved by ListMenu on the first drawing + menu.files_per_page = BookInfoManager:getSetting("files_per_page") + end + menu.display_mode_type = display_mode and display_mode:gsub("_.*", "") -- "mosaic" or "list" +end + function CoverBrowser:refreshFileManagerInstance(cleanup, post_init) local fm = FileManager.instance if fm then @@ -433,6 +560,9 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode) filemanager_display_mode = display_mode logger.dbg("CoverBrowser: setting FileManager display mode to:", display_mode or "classic") + -- init Mosaic and List grid dimensions (in Classic mode used in the settings menu) + CoverBrowser.initGrid(FileChooser, display_mode) + if not init_done and not display_mode then return -- starting in classic mode, nothing to patch end @@ -459,8 +589,7 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode) FileChooser.updateCache = CoverMenu.updateCache FileChooser.updateItems = CoverMenu.updateItems FileChooser.onCloseWidget = CoverMenu.onCloseWidget - - if display_mode == "mosaic_image" or display_mode == "mosaic_text" then -- mosaic mode + if FileChooser.display_mode_type == "mosaic" then -- Replace some other original methods with those from our MosaicMenu local MosaicMenu = require("mosaicmenu") FileChooser._recalculateDimen = MosaicMenu._recalculateDimen @@ -470,14 +599,7 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode) FileChooser._do_hint_opened = true -- dogear at bottom -- Don't have "../" centered in empty directories FileChooser._do_center_partial_rows = false - -- One could override default 3x3 grid here (put that as settings ?) - -- FileChooser.nb_cols_portrait = 4 - -- FileChooser.nb_rows_portrait = 4 - -- FileChooser.nb_cols_landscape = 6 - -- FileChooser.nb_rows_landscape = 3 - - elseif display_mode == "list_image_meta" or display_mode == "list_only_meta" or - display_mode == "list_image_filename" then -- list modes + elseif FileChooser.display_mode_type == "list" then -- Replace some other original methods with those from our ListMenu local ListMenu = require("listmenu") FileChooser._recalculateDimen = ListMenu._recalculateDimen @@ -488,7 +610,6 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode) FileChooser._do_hint_opened = true -- dogear at bottom end - -- Replace this FileManager method with the one from CoverMenu -- (but first, make the original method saved here as local available -- to CoverMenu) @@ -505,7 +626,6 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode) self:refreshFileManagerInstance(false, true) end) end - end local function _FileManagerHistory_updateItemTable(self) @@ -529,7 +649,8 @@ local function _FileManagerHistory_updateItemTable(self) hist_menu.onMenuHold_orig = hist_menu.onMenuHold hist_menu.onMenuHold = CoverMenu.onHistoryMenuHold - if display_mode == "mosaic_image" or display_mode == "mosaic_text" then -- mosaic mode + CoverBrowser.initGrid(hist_menu, display_mode) + if hist_menu.display_mode_type == "mosaic" then -- Replace some other original methods with those from our MosaicMenu local MosaicMenu = require("mosaicmenu") hist_menu._recalculateDimen = MosaicMenu._recalculateDimen @@ -538,8 +659,7 @@ local function _FileManagerHistory_updateItemTable(self) hist_menu._do_cover_images = display_mode ~= "mosaic_text" hist_menu._do_center_partial_rows = true -- nicer looking when few elements - elseif display_mode == "list_image_meta" or display_mode == "list_only_meta" or - display_mode == "list_image_filename" then -- list modes + elseif hist_menu.display_mode_type == "list" then -- Replace some other original methods with those from our ListMenu local ListMenu = require("listmenu") hist_menu._recalculateDimen = ListMenu._recalculateDimen @@ -608,7 +728,8 @@ local function _FileManagerCollections_updateItemTable(self) coll_menu.onMenuHold_orig = coll_menu.onMenuHold coll_menu.onMenuHold = CoverMenu.onCollectionsMenuHold - if display_mode == "mosaic_image" or display_mode == "mosaic_text" then -- mosaic mode + CoverBrowser.initGrid(coll_menu, display_mode) + if coll_menu.display_mode_type == "mosaic" then -- Replace some other original methods with those from our MosaicMenu local MosaicMenu = require("mosaicmenu") coll_menu._recalculateDimen = MosaicMenu._recalculateDimen @@ -617,8 +738,7 @@ local function _FileManagerCollections_updateItemTable(self) coll_menu._do_cover_images = display_mode ~= "mosaic_text" coll_menu._do_center_partial_rows = true -- nicer looking when few elements - elseif display_mode == "list_image_meta" or display_mode == "list_only_meta" or - display_mode == "list_image_filename" then -- list modes + elseif coll_menu.display_mode_type == "list" then -- Replace some other original methods with those from our ListMenu local ListMenu = require("listmenu") coll_menu._recalculateDimen = ListMenu._recalculateDimen diff --git a/plugins/coverbrowser.koplugin/mosaicmenu.lua b/plugins/coverbrowser.koplugin/mosaicmenu.lua index e0eee33bf..8e8a004db 100644 --- a/plugins/coverbrowser.koplugin/mosaicmenu.lua +++ b/plugins/coverbrowser.koplugin/mosaicmenu.lua @@ -447,7 +447,6 @@ function MosaicMenuItem:update() local max_img_w = dimen.w - 2*border_size local max_img_h = dimen.h - 2*border_size local cover_specs = { - sizetag = "M", max_cover_w = max_img_w, max_cover_h = max_img_h, } @@ -538,22 +537,19 @@ function MosaicMenuItem:update() end local bookinfo = BookInfoManager:getBookInfo(self.filepath, self.do_cover_image) + if bookinfo and self.do_cover_image and not bookinfo.ignore_cover then if bookinfo.cover_fetched then - if bookinfo.has_cover and bookinfo.cover_sizetag ~= "M" then - -- there is a cover, but it's a small one (made by ListMenuItem), - -- and it would be ugly if scaled up to MosaicMenuItem size: - -- do as if not found to force a new extraction with our size - if bookinfo.cover_bb then - bookinfo.cover_bb:free() + if bookinfo.has_cover and not self.menu.no_refresh_covers then + if BookInfoManager.isCachedCoverInvalid(bookinfo, cover_specs) then + -- there is a thumbnail, but it's smaller than is needed for new grid dimensions, + -- and it would be ugly if scaled up to the required size: + -- do as if not found to force a new extraction with our size + if bookinfo.cover_bb then + bookinfo.cover_bb:free() + end + bookinfo = nil end - bookinfo = nil - -- Note: with the current size differences between FileManager - -- and the History windows, we'll get lower max_img_* in History. - -- So, when one get Items first generated by the other, it will - -- have to do some scaling. Hopefully, people most probably - -- browse a lot more files than have them in history, so - -- it's most probably History that will have to do some scaling. end -- if not has_cover, book has no cover, no need to try again else @@ -595,7 +591,7 @@ function MosaicMenuItem:update() if self.do_cover_image and bookinfo.has_cover and not bookinfo.ignore_cover then cover_bb_used = true -- Let ImageWidget do the scaling and give us a bb that fit - local scale_factor = math.min(max_img_w / bookinfo.cover_w, max_img_h / bookinfo.cover_h) + local _, _, scale_factor = BookInfoManager.getCachedCoverSize(bookinfo.cover_w, bookinfo.cover_h, max_img_w, max_img_h) local image= ImageWidget:new{ image = bookinfo.cover_bb, scale_factor = scale_factor, @@ -858,14 +854,13 @@ end local MosaicMenu = {} function MosaicMenu:_recalculateDimen() - local portrait_mode = Screen:getWidth() <= Screen:getHeight() - -- 3 x 3 grid by default if not initially provided (4 x 2 in landscape mode) - if portrait_mode then - self.nb_cols = self.nb_cols_portrait or 3 - self.nb_rows = self.nb_rows_portrait or 3 + self.portrait_mode = Screen:getWidth() <= Screen:getHeight() + if self.portrait_mode then + self.nb_cols = self.nb_cols_portrait + self.nb_rows = self.nb_rows_portrait else - self.nb_cols = self.nb_cols_landscape or 4 - self.nb_rows = self.nb_rows_landscape or 2 + self.nb_cols = self.nb_cols_landscape + self.nb_rows = self.nb_rows_landscape end self.perpage = self.nb_rows * self.nb_cols self.page_num = math.ceil(#self.item_table / self.perpage)