CoverBrowser: adjustable mosaic grid (#11232)

reviewable/pr11341/r1
hius07 4 months ago committed by GitHub
parent cd903d66e5
commit ea9ef6781c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,9 +4,11 @@ local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device") local Device = require("device")
local FFIUtil = require("ffi/util") local FFIUtil = require("ffi/util")
local InputContainer = require("ui/widget/container/inputcontainer") local InputContainer = require("ui/widget/container/inputcontainer")
local KeyValuePage = require("ui/widget/keyvaluepage")
local PluginLoader = require("pluginloader") local PluginLoader = require("pluginloader")
local SetDefaults = require("apps/filemanager/filemanagersetdefaults") local SetDefaults = require("apps/filemanager/filemanagersetdefaults")
local Size = require("ui/size") local Size = require("ui/size")
local SpinWidget = require("ui/widget/spinwidget")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local Screen = Device.screen local Screen = Device.screen
local filemanagerutil = require("apps/filemanager/filemanagerutil") local filemanagerutil = require("apps/filemanager/filemanagerutil")
@ -168,49 +170,49 @@ function FileManagerMenu:setUpdateItemTable()
text = _("Classic mode settings"), text = _("Classic mode settings"),
sub_item_table = { 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: help_text = _([[This sets the number of items per page in:
- File browser, history and favorites in 'classic' display mode - File browser, history and favorites in 'classic' display mode
- Search results and folder shortcuts - Search results and folder shortcuts
- File and folder selection - File and folder selection
- Calibre and OPDS browsers/search results]]), - Calibre and OPDS browsers/search results]]),
callback = function() callback = function(touchmenu_instance)
local SpinWidget = require("ui/widget/spinwidget") local current_value = FileChooser.perpage
local Menu = require("ui/widget/menu") local default_value = FileChooser.items_per_page_default
local default_perpage = Menu.items_per_page_default local widget = SpinWidget:new{
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,
title_text = _("Items per page"), title_text = _("Items per page"),
value = current_value,
value_min = 6,
value_max = 30,
default_value = default_value,
keep_shown_on_apply = true, keep_shown_on_apply = true,
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("items_per_page", spin.value) G_reader_settings:saveSetting("items_per_page", spin.value)
self.ui:onRefresh() FileChooser:refreshPath()
end touchmenu_instance:updateItems()
end,
} }
UIManager:show(items) UIManager:show(widget)
end, end,
}, },
{ {
text = _("Item font size"), text_func = function()
callback = function() return T(_("Item font size: %1"), FileChooser.font_size)
local SpinWidget = require("ui/widget/spinwidget") end,
local Menu = require("ui/widget/menu") callback = function(touchmenu_instance)
local curr_perpage = G_reader_settings:readSetting("items_per_page") or Menu.items_per_page_default local current_value = FileChooser.font_size
local default_font_size = Menu.getItemFontSize(curr_perpage) local default_value = FileChooser.getItemFontSize(FileChooser.perpage)
local curr_font_size = G_reader_settings:readSetting("items_font_size") or default_font_size local widget = SpinWidget:new{
local items_font = SpinWidget:new{ title_text = _("Item font size"),
value = curr_font_size, value = current_value,
value_min = 10, value_min = 10,
value_max = 72, value_max = 72,
default_value = default_font_size, default_value = default_value,
keep_shown_on_apply = true, keep_shown_on_apply = true,
title_text = _("Item font size"),
callback = function(spin) 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 -- 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 -- assume that if it is the default font size, he will prefer to have
-- our default font size if he later updates per-page -- our default font size if he later updates per-page
@ -218,10 +220,11 @@ function FileManagerMenu:setUpdateItemTable()
else else
G_reader_settings:saveSetting("items_font_size", spin.value) G_reader_settings:saveSetting("items_font_size", spin.value)
end end
self.ui:onRefresh() FileChooser:refreshPath()
end touchmenu_instance:updateItems()
end,
} }
UIManager:show(items_font) UIManager:show(widget)
end, end,
}, },
{ {
@ -373,26 +376,28 @@ To:
separator = true, 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: help_text = _([[This sets the number of items per page in:
- Book information - Book information
- Dictionary and Wikipedia lookup history - Dictionary and Wikipedia lookup history
- Reading statistics details - Reading statistics details
- A few other plugins]]), - A few other plugins]]),
keep_menu_open = true, keep_menu_open = true,
callback = function() callback = function(touchmenu_instance)
local SpinWidget = require("ui/widget/spinwidget") local default_value = KeyValuePage.getDefaultItemsPerPage()
local KeyValuePage = require("ui/widget/keyvaluepage") local current_value = G_reader_settings:readSetting("keyvalues_per_page") or default_value
local default_perpage = KeyValuePage:getDefaultKeyValuesPerPage() local widget = SpinWidget:new{
local curr_perpage = G_reader_settings:readSetting("keyvalues_per_page") or default_perpage value = current_value,
local items = SpinWidget:new{
value = curr_perpage,
value_min = 10, value_min = 10,
value_max = 24, value_max = 30,
default_value = default_perpage, default_value = default_value,
title_text = _("Info lists items per page"), title_text = _("Info lists items per page"),
callback = function(spin) 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 -- 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 -- assume that if it is the default, he will prefer to stay with our
-- default if he later changes screen DPI -- default if he later changes screen DPI
@ -400,9 +405,10 @@ To:
else else
G_reader_settings:saveSetting("keyvalues_per_page", spin.value) G_reader_settings:saveSetting("keyvalues_per_page", spin.value)
end end
touchmenu_instance:updateItems()
end end
} }
UIManager:show(items) UIManager:show(widget)
end, end,
}, },
}, },

@ -467,14 +467,10 @@ function KeyValuePage:init()
- 2*Size.line.thick - 2*Size.line.thick
-- account for possibly 2 separator lines added -- account for possibly 2 separator lines added
local force_items_per_page self.items_per_page = G_reader_settings:readSetting("keyvalues_per_page") or self.getDefaultItemsPerPage()
if self.single_page then if self.single_page and self.items_per_page < #self.kv_pairs then
force_items_per_page = math.max(#self.kv_pairs, self.items_per_page = #self.kv_pairs
G_reader_settings:readSetting("keyvalues_per_page") or self:getDefaultKeyValuesPerPage())
end 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) self.item_height = math.floor(available_height / self.items_per_page)
-- Put half of the pixels lost by floor'ing between title and content -- Put half of the pixels lost by floor'ing between title and content
local content_height = self.items_per_page * self.item_height local content_height = self.items_per_page * self.item_height
@ -522,7 +518,7 @@ function KeyValuePage:init()
} }
end end
function KeyValuePage:getDefaultKeyValuesPerPage() function KeyValuePage.getDefaultItemsPerPage()
-- Get a default according to Screen DPI (roughly following -- Get a default according to Screen DPI (roughly following
-- the former implementation building logic) -- the former implementation building logic)
local default_item_height = Size.item.height_default * 1.5 -- we were adding 1/2 as margin 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, background = Blitbuffer.COLOR_LIGHT_GRAY,
dimen = Geom:new{ dimen = Geom:new{
w = self.item_width, w = self.item_width,
h = Size.line.thick h = Size.line.thick,
}, },
style = "solid", style = "solid",
}) })
@ -699,7 +695,7 @@ function KeyValuePage:_populateItems()
background = Blitbuffer.COLOR_LIGHT_GRAY, background = Blitbuffer.COLOR_LIGHT_GRAY,
dimen = Geom:new{ dimen = Geom:new{
w = self.item_width, w = self.item_width,
h = Size.line.thick h = Size.line.thick,
}, },
style = "solid", style = "solid",
}) })

@ -1040,9 +1040,9 @@ function Menu:updateItems(select_number)
select_number = 1 select_number = 1
end 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) 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 local multilines_show_more_text = self.multilines_show_more_text
if multilines_show_more_text == nil then if multilines_show_more_text == nil then
multilines_show_more_text = G_reader_settings:isTrue("items_multilines_show_more_text") 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, bold = self.item_table.current == i or self.item_table[i].bold == true,
dim = self.item_table[i].dim, dim = self.item_table[i].dim,
font = "smallinfofont", font = "smallinfofont",
font_size = font_size, font_size = self.font_size,
infont = "infont", infont = "infont",
infont_size = infont_size, infont_size = infont_size,
dimen = self.item_dimen:new(), dimen = self.item_dimen:new(),

@ -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) 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_meta TEXT, -- NULL / 'Y' = has metadata (title, authors...)
has_cover TEXT, -- NULL / 'Y' = has cover image (cover_*) 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) -- 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...) -- 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 keys = res[1]
local values = res[2] local values = res[2]
for i, key in ipairs(keys) do 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 end
end end
@ -506,23 +506,18 @@ function BookInfoManager:extractBookInfo(filepath, cover_specs)
dbrow[k] = v dbrow[k] = v
end end
if cover_specs then if cover_specs then
local spec_sizetag = cover_specs.sizetag
local spec_max_cover_w = cover_specs.max_cover_w local spec_max_cover_w = cover_specs.max_cover_w
local spec_max_cover_h = cover_specs.max_cover_h local spec_max_cover_h = cover_specs.max_cover_h
dbrow.cover_fetched = 'Y' -- we had a try at getting a cover dbrow.cover_fetched = 'Y' -- we had a try at getting a cover
local cover_bb = FileManagerBookInfo:getCoverImage(document) local cover_bb = FileManagerBookInfo:getCoverImage(document)
if cover_bb then if cover_bb then
dbrow.has_cover = 'Y' dbrow.has_cover = 'Y'
dbrow.cover_sizetag = spec_sizetag
-- we should scale down the cover to our max size -- we should scale down the cover to our max size
local cbb_w, cbb_h = cover_bb:getWidth(), cover_bb:getHeight() 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 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 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, cbb_h = BookInfoManager.getCachedCoverSize(cbb_w, cbb_h, spec_max_cover_w, spec_max_cover_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)
cover_bb = RenderImage:scaleBlitBuffer(cover_bb, cbb_w, cbb_h, true) cover_bb = RenderImage:scaleBlitBuffer(cover_bb, cbb_w, cbb_h, true)
end end
dbrow.cover_w = cover_bb.w 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_size = cover_bb.stride * cover_bb.h
local cover_zst_ptr, cover_zst_size = zstd.zstd_compress(cover_bb.data, cover_size) 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 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 ;) -- We're done with the uncompressed bb now, and the compressed one has been managed by SQLite ;)
cover_bb:free() cover_bb:free()
end end
@ -886,10 +882,8 @@ Do you want to prune the cache of removed books?]]
local to_extract = not bookinfo local to_extract = not bookinfo
if bookinfo and cover_specs and not bookinfo.ignore_cover then if bookinfo and cover_specs and not bookinfo.ignore_cover then
if bookinfo.cover_fetched then if bookinfo.cover_fetched then
if bookinfo.has_cover and cover_specs.sizetag ~= bookinfo.cover_sizetag then if bookinfo.has_cover and BookInfoManager.isCachedCoverInvalid(bookinfo, cover_specs) then
if bookinfo.cover_sizetag ~= "M" then -- keep the bigger "M" to_extract = true
to_extract = true
end
end end
else else
to_extract = true to_extract = true
@ -985,6 +979,35 @@ Do you want to prune the cache of removed books?]]
UIManager:show(info) UIManager:show(info)
end 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() BookInfoManager:init()
return BookInfoManager return BookInfoManager

@ -209,7 +209,6 @@ function ListMenuItem:update()
local max_img_w = dimen.h - 2*border_size -- width = height, squared local max_img_w = dimen.h - 2*border_size -- width = height, squared
local max_img_h = dimen.h - 2*border_size local max_img_h = dimen.h - 2*border_size
local cover_specs = { local cover_specs = {
sizetag = self.menu.cover_sizetag,
max_cover_w = max_img_w, max_cover_w = max_img_w,
max_cover_h = max_img_h, max_cover_h = max_img_h,
} }
@ -267,24 +266,26 @@ function ListMenuItem:update()
-- File -- File
local bookinfo = BookInfoManager:getBookInfo(self.filepath, self.do_cover_image) 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 and self.do_cover_image and not bookinfo.ignore_cover then
if bookinfo.cover_fetched then if bookinfo.cover_fetched then
-- trigger recalculation of thumbnail if size changed if bookinfo.has_cover and not self.menu.no_refresh_covers then
if bookinfo.has_cover and bookinfo.cover_sizetag ~= "M" and bookinfo.cover_sizetag ~= cover_specs.sizetag then if BookInfoManager.isCachedCoverInvalid(bookinfo, cover_specs) then
if bookinfo.cover_bb then -- there is a thumbnail, but it's smaller than is needed for new grid dimensions,
bookinfo.cover_bb:free() -- 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 end
bookinfo = nil
end end
-- if not has_cover, book has no cover, no need to try again
else else
-- cover was not fetched previously, do as if not found -- cover was not fetched previously, do as if not found
-- to force a new extraction -- to force a new extraction
bookinfo = nil bookinfo = nil
end 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 end
if bookinfo then -- This book is known if bookinfo then -- This book is known
@ -301,7 +302,7 @@ function ListMenuItem:update()
if bookinfo.has_cover and not bookinfo.ignore_cover then if bookinfo.has_cover and not bookinfo.ignore_cover then
cover_bb_used = true cover_bb_used = true
-- Let ImageWidget do the scaling and give us the final size -- 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{ local wimage = ImageWidget:new{
image = bookinfo.cover_bb, image = bookinfo.cover_bb,
scale_factor = scale_factor, scale_factor = scale_factor,
@ -901,6 +902,7 @@ end
local ListMenu = {} local ListMenu = {}
function ListMenu:_recalculateDimen() function ListMenu:_recalculateDimen()
self.portrait_mode = Screen:getWidth() <= Screen:getHeight()
-- Find out available height from other UI elements made in Menu -- Find out available height from other UI elements made in Menu
self.others_height = 0 self.others_height = 0
if self.title_bar then -- Menu:init() has been done if self.title_bar then -- Menu:init() has been done
@ -927,25 +929,16 @@ function ListMenu:_recalculateDimen()
end end
local available_height = self.inner_dimen.h - self.others_height - Size.line.thin 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 if self.files_per_page == nil then -- first drawing
-- be 'nil' if it was not defined, as we'll find instead the value -- Default perpage is computed from a base of 64px per ListMenuItem,
-- defined in the Menu class (14) because of inheritance.) -- which gives 10 items on kobo glo hd.
local files_per_page = BookInfoManager:getSetting("files_per_page") self.files_per_page = math.floor(available_height / scale_by_size / 64)
if files_per_page then BookInfoManager:saveSetting("files_per_page", self.files_per_page)
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))
end end
self.cover_sizetag = "s" .. self.perpage self.perpage = self.files_per_page
if Screen:getWidth() > Screen:getHeight() then -- landscape mode if not self.portrait_mode then
-- When in landscape mode (only possible with History), adjust -- When in landscape mode, adjust perpage so items get a chance
-- perpage so items get a chance to have about the same height -- to have about the same height as when in portrait mode.
-- as when in portrait mode.
-- This computation is not strictly correct, as "others_height" would -- This computation is not strictly correct, as "others_height" would
-- have a different value in portrait mode. But let's go with that. -- 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 local portrait_available_height = Screen:getWidth() - self.others_height - Size.line.thin

@ -2,6 +2,7 @@ local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer") local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger") local logger = require("logger")
local _ = require("gettext") local _ = require("gettext")
local T = require("ffi/util").template
local BookInfoManager = require("bookinfomanager") local BookInfoManager = require("bookinfomanager")
--[[ --[[
@ -50,7 +51,9 @@ local CoverBrowser = WidgetContainer:extend{
} }
function CoverBrowser:init() 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 if init_done then -- things already patched according to current modes
return return
@ -64,6 +67,7 @@ function CoverBrowser:init()
logger.info("CoverBrowser: setting default display modes") logger.info("CoverBrowser: setting default display modes")
BookInfoManager:saveSetting("filemanager_display_mode", "list_image_meta") BookInfoManager:saveSetting("filemanager_display_mode", "list_image_meta")
BookInfoManager:saveSetting("history_display_mode", "mosaic_image") BookInfoManager:saveSetting("history_display_mode", "mosaic_image")
BookInfoManager:saveSetting("collection_display_mode", "mosaic_image")
end end
G_reader_settings:makeTrue("coverbrowser_initial_default_setup_done") G_reader_settings:makeTrue("coverbrowser_initial_default_setup_done")
end end
@ -72,17 +76,11 @@ function CoverBrowser:init()
self:setupHistoryDisplayMode(BookInfoManager:getSetting("history_display_mode")) self:setupHistoryDisplayMode(BookInfoManager:getSetting("history_display_mode"))
self:setupCollectionDisplayMode(BookInfoManager:getSetting("collection_display_mode")) self:setupCollectionDisplayMode(BookInfoManager:getSetting("collection_display_mode"))
series_mode = BookInfoManager:getSetting("series_mode") series_mode = BookInfoManager:getSetting("series_mode")
init_done = true init_done = true
BookInfoManager:closeDbConnection() -- will be re-opened if needed BookInfoManager:closeDbConnection() -- will be re-opened if needed
end end
function CoverBrowser:addToMainMenu(menu_items) function CoverBrowser:addToMainMenu(menu_items)
-- We add it only to FileManager menu
if self.ui.view then -- Reader
return
end
local modes = { local modes = {
{ _("Classic (filename only)") }, { _("Classic (filename only)") },
{ _("Mosaic with cover images"), "mosaic_image" }, { _("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 = {}, {}, {} local sub_item_table, history_sub_item_table, collection_sub_item_table = {}, {}, {}
for i, v in ipairs(modes) do for i, v in ipairs(modes) do
local text, mode = unpack(v) local text, mode = unpack(v)
table.insert(sub_item_table, { sub_item_table[i] = {
text = text, text = text,
checked_func = function() checked_func = function()
return mode == filemanager_display_mode return mode == filemanager_display_mode
@ -106,8 +104,8 @@ function CoverBrowser:addToMainMenu(menu_items)
self:setupCollectionDisplayMode(mode) self:setupCollectionDisplayMode(mode)
end end
end, end,
}) }
table.insert(history_sub_item_table, { history_sub_item_table[i] = {
text = text, text = text,
checked_func = function() checked_func = function()
return mode == history_display_mode return mode == history_display_mode
@ -115,8 +113,8 @@ function CoverBrowser:addToMainMenu(menu_items)
callback = function() callback = function()
self:setupHistoryDisplayMode(mode) self:setupHistoryDisplayMode(mode)
end, end,
}) }
table.insert(collection_sub_item_table, { collection_sub_item_table[i] = {
text = text, text = text,
checked_func = function() checked_func = function()
return mode == collection_display_mode return mode == collection_display_mode
@ -124,7 +122,7 @@ function CoverBrowser:addToMainMenu(menu_items)
callback = function() callback = function()
self:setupCollectionDisplayMode(mode) self:setupCollectionDisplayMode(mode)
end, end,
}) }
end end
sub_item_table[#modes].separator = true sub_item_table[#modes].separator = true
table.insert(sub_item_table, { 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 -- add Mosaic / Detailed list mode settings to File browser Settings submenu
-- next to Classic mode settings -- next to Classic mode settings
if menu_items.filebrowser_settings == nil then return end 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"), text = _("Mosaic and detailed list settings"),
separator = true, separator = true,
sub_item_table = { sub_item_table = {
{ {
text = _("Items per page"), text_func = function()
help_text = _([[This sets the number of files and folders per page in display modes other than classic.]]), 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 -- Best to not "keep_menu_open = true", to see how this apply on the full view
callback = function() 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") local SpinWidget = require("ui/widget/spinwidget")
-- "files_per_page" should have been saved with an adequate value local widget = SpinWidget:new{
-- the first time Detailed list was shown. Fallback to a start title_text = _("Portrait list mode"),
-- value of 10 if it hasn't. value = files_per_page,
local curr_items = BookInfoManager:getSetting("files_per_page") or 10
local items = SpinWidget:new{
value = curr_items,
value_min = 4, value_min = 4,
value_max = 20, value_max = 20,
default_value = 10, default_value = 10,
keep_shown_on_apply = true, keep_shown_on_apply = true,
title_text = _("Items per page"),
callback = function(spin) callback = function(spin)
BookInfoManager:saveSetting("files_per_page", spin.value) fc.files_per_page = spin.value
self.ui:onRefresh() if fc.display_mode_type == "list" then
end 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, end,
separator = true,
}, },
{ {
text = _("Progress"), text = _("Progress"),
sub_item_table = { sub_item_table = {
{ {
text = _("Show progress"), text = _("Show progress in mosaic mode"),
checked_func = function() return checked_func = function() return BookInfoManager:getSetting("show_progress_in_mosaic") end,
not BookInfoManager:getSetting("hide_page_info")
end,
callback = function() callback = function()
BookInfoManager:toggleSetting("hide_page_info") BookInfoManager:toggleSetting("show_progress_in_mosaic")
self:refreshFileManagerInstance() self:refreshFileManagerInstance()
end, end,
separator = true,
}, },
{ {
text = _("Show progress % in mosaic mode"), text = _("Show progress in detailed list mode"),
checked_func = function() return BookInfoManager:getSetting("show_progress_in_mosaic") end, checked_func = function() return not BookInfoManager:getSetting("hide_page_info") end,
callback = function() callback = function()
BookInfoManager:toggleSetting("show_progress_in_mosaic") BookInfoManager:toggleSetting("hide_page_info")
self:refreshFileManagerInstance() self:refreshFileManagerInstance()
end, end,
}, },
{ {
text = _("Show number of pages read instead of progress %"), 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, checked_func = function() return BookInfoManager:getSetting("show_pages_read_as_progress") end,
callback = function() callback = function()
BookInfoManager:toggleSetting("show_pages_read_as_progress") BookInfoManager:toggleSetting("show_pages_read_as_progress")
@ -221,12 +335,12 @@ function CoverBrowser:addToMainMenu(menu_items)
}, },
{ {
text = _("Show number of pages left to read"), 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, checked_func = function() return BookInfoManager:getSetting("show_pages_left_in_progress") end,
callback = function() callback = function()
BookInfoManager:toggleSetting("show_pages_left_in_progress") BookInfoManager:toggleSetting("show_pages_left_in_progress")
self:refreshFileManagerInstance() self:refreshFileManagerInstance()
end, end,
separator = true,
}, },
}, },
}, },
@ -302,7 +416,6 @@ function CoverBrowser:addToMainMenu(menu_items)
end, end,
}, },
}, },
separator = true
}, },
{ {
text = _("Show file properties"), text = _("Show file properties"),
@ -313,6 +426,7 @@ function CoverBrowser:addToMainMenu(menu_items)
BookInfoManager:toggleSetting("hide_file_info") BookInfoManager:toggleSetting("hide_file_info")
self:refreshFileManagerInstance() self:refreshFileManagerInstance()
end, end,
separator = true,
}, },
{ {
text = _("Book info cache management"), text = _("Book info cache management"),
@ -391,6 +505,19 @@ function CoverBrowser:addToMainMenu(menu_items)
}) })
end 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) function CoverBrowser:refreshFileManagerInstance(cleanup, post_init)
local fm = FileManager.instance local fm = FileManager.instance
if fm then if fm then
@ -433,6 +560,9 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode)
filemanager_display_mode = display_mode filemanager_display_mode = display_mode
logger.dbg("CoverBrowser: setting FileManager display mode to:", display_mode or "classic") 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 if not init_done and not display_mode then
return -- starting in classic mode, nothing to patch return -- starting in classic mode, nothing to patch
end end
@ -459,8 +589,7 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode)
FileChooser.updateCache = CoverMenu.updateCache FileChooser.updateCache = CoverMenu.updateCache
FileChooser.updateItems = CoverMenu.updateItems FileChooser.updateItems = CoverMenu.updateItems
FileChooser.onCloseWidget = CoverMenu.onCloseWidget FileChooser.onCloseWidget = CoverMenu.onCloseWidget
if FileChooser.display_mode_type == "mosaic" then
if display_mode == "mosaic_image" or display_mode == "mosaic_text" then -- mosaic mode
-- Replace some other original methods with those from our MosaicMenu -- Replace some other original methods with those from our MosaicMenu
local MosaicMenu = require("mosaicmenu") local MosaicMenu = require("mosaicmenu")
FileChooser._recalculateDimen = MosaicMenu._recalculateDimen FileChooser._recalculateDimen = MosaicMenu._recalculateDimen
@ -470,14 +599,7 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode)
FileChooser._do_hint_opened = true -- dogear at bottom FileChooser._do_hint_opened = true -- dogear at bottom
-- Don't have "../" centered in empty directories -- Don't have "../" centered in empty directories
FileChooser._do_center_partial_rows = false FileChooser._do_center_partial_rows = false
-- One could override default 3x3 grid here (put that as settings ?) elseif FileChooser.display_mode_type == "list" then
-- 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
-- Replace some other original methods with those from our ListMenu -- Replace some other original methods with those from our ListMenu
local ListMenu = require("listmenu") local ListMenu = require("listmenu")
FileChooser._recalculateDimen = ListMenu._recalculateDimen FileChooser._recalculateDimen = ListMenu._recalculateDimen
@ -488,7 +610,6 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode)
FileChooser._do_hint_opened = true -- dogear at bottom FileChooser._do_hint_opened = true -- dogear at bottom
end end
-- Replace this FileManager method with the one from CoverMenu -- Replace this FileManager method with the one from CoverMenu
-- (but first, make the original method saved here as local available -- (but first, make the original method saved here as local available
-- to CoverMenu) -- to CoverMenu)
@ -505,7 +626,6 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode)
self:refreshFileManagerInstance(false, true) self:refreshFileManagerInstance(false, true)
end) end)
end end
end end
local function _FileManagerHistory_updateItemTable(self) local function _FileManagerHistory_updateItemTable(self)
@ -529,7 +649,8 @@ local function _FileManagerHistory_updateItemTable(self)
hist_menu.onMenuHold_orig = hist_menu.onMenuHold hist_menu.onMenuHold_orig = hist_menu.onMenuHold
hist_menu.onMenuHold = CoverMenu.onHistoryMenuHold 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 -- Replace some other original methods with those from our MosaicMenu
local MosaicMenu = require("mosaicmenu") local MosaicMenu = require("mosaicmenu")
hist_menu._recalculateDimen = MosaicMenu._recalculateDimen 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_cover_images = display_mode ~= "mosaic_text"
hist_menu._do_center_partial_rows = true -- nicer looking when few elements 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 elseif hist_menu.display_mode_type == "list" then
display_mode == "list_image_filename" then -- list modes
-- Replace some other original methods with those from our ListMenu -- Replace some other original methods with those from our ListMenu
local ListMenu = require("listmenu") local ListMenu = require("listmenu")
hist_menu._recalculateDimen = ListMenu._recalculateDimen hist_menu._recalculateDimen = ListMenu._recalculateDimen
@ -608,7 +728,8 @@ local function _FileManagerCollections_updateItemTable(self)
coll_menu.onMenuHold_orig = coll_menu.onMenuHold coll_menu.onMenuHold_orig = coll_menu.onMenuHold
coll_menu.onMenuHold = CoverMenu.onCollectionsMenuHold 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 -- Replace some other original methods with those from our MosaicMenu
local MosaicMenu = require("mosaicmenu") local MosaicMenu = require("mosaicmenu")
coll_menu._recalculateDimen = MosaicMenu._recalculateDimen 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_cover_images = display_mode ~= "mosaic_text"
coll_menu._do_center_partial_rows = true -- nicer looking when few elements 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 elseif coll_menu.display_mode_type == "list" then
display_mode == "list_image_filename" then -- list modes
-- Replace some other original methods with those from our ListMenu -- Replace some other original methods with those from our ListMenu
local ListMenu = require("listmenu") local ListMenu = require("listmenu")
coll_menu._recalculateDimen = ListMenu._recalculateDimen coll_menu._recalculateDimen = ListMenu._recalculateDimen

@ -447,7 +447,6 @@ function MosaicMenuItem:update()
local max_img_w = dimen.w - 2*border_size local max_img_w = dimen.w - 2*border_size
local max_img_h = dimen.h - 2*border_size local max_img_h = dimen.h - 2*border_size
local cover_specs = { local cover_specs = {
sizetag = "M",
max_cover_w = max_img_w, max_cover_w = max_img_w,
max_cover_h = max_img_h, max_cover_h = max_img_h,
} }
@ -538,22 +537,19 @@ function MosaicMenuItem:update()
end end
local bookinfo = BookInfoManager:getBookInfo(self.filepath, self.do_cover_image) 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 and self.do_cover_image and not bookinfo.ignore_cover then
if bookinfo.cover_fetched then if bookinfo.cover_fetched then
if bookinfo.has_cover and bookinfo.cover_sizetag ~= "M" then if bookinfo.has_cover and not self.menu.no_refresh_covers then
-- there is a cover, but it's a small one (made by ListMenuItem), if BookInfoManager.isCachedCoverInvalid(bookinfo, cover_specs) then
-- and it would be ugly if scaled up to MosaicMenuItem size: -- there is a thumbnail, but it's smaller than is needed for new grid dimensions,
-- do as if not found to force a new extraction with our size -- and it would be ugly if scaled up to the required size:
if bookinfo.cover_bb then -- do as if not found to force a new extraction with our size
bookinfo.cover_bb:free() if bookinfo.cover_bb then
bookinfo.cover_bb:free()
end
bookinfo = nil
end 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 end
-- if not has_cover, book has no cover, no need to try again -- if not has_cover, book has no cover, no need to try again
else else
@ -595,7 +591,7 @@ function MosaicMenuItem:update()
if self.do_cover_image and bookinfo.has_cover and not bookinfo.ignore_cover then if self.do_cover_image and bookinfo.has_cover and not bookinfo.ignore_cover then
cover_bb_used = true cover_bb_used = true
-- Let ImageWidget do the scaling and give us a bb that fit -- 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{ local image= ImageWidget:new{
image = bookinfo.cover_bb, image = bookinfo.cover_bb,
scale_factor = scale_factor, scale_factor = scale_factor,
@ -858,14 +854,13 @@ end
local MosaicMenu = {} local MosaicMenu = {}
function MosaicMenu:_recalculateDimen() function MosaicMenu:_recalculateDimen()
local portrait_mode = Screen:getWidth() <= Screen:getHeight() self.portrait_mode = Screen:getWidth() <= Screen:getHeight()
-- 3 x 3 grid by default if not initially provided (4 x 2 in landscape mode) if self.portrait_mode then
if portrait_mode then self.nb_cols = self.nb_cols_portrait
self.nb_cols = self.nb_cols_portrait or 3 self.nb_rows = self.nb_rows_portrait
self.nb_rows = self.nb_rows_portrait or 3
else else
self.nb_cols = self.nb_cols_landscape or 4 self.nb_cols = self.nb_cols_landscape
self.nb_rows = self.nb_rows_landscape or 2 self.nb_rows = self.nb_rows_landscape
end end
self.perpage = self.nb_rows * self.nb_cols self.perpage = self.nb_rows * self.nb_cols
self.page_num = math.ceil(#self.item_table / self.perpage) self.page_num = math.ceil(#self.item_table / self.perpage)

Loading…
Cancel
Save