CoverBrowser: some optimizations

Speedup initialization (needs to be done only once, and avoid
saving current mode to sqlite each time) and reader-to-filebrowser
switch, by doing a single rendering (instead of 2 or 3 previously).
ListMenu: cache sidecar file parsing results (page/percent
completed) for the browsing session duration.
Fix "No choice available" when on last page and changing
to a display mode with less pages.
pull/3393/head
poire-z 7 years ago committed by Frans de Jonge
parent 591dc2119c
commit c573bdd610

@ -195,120 +195,124 @@ function CoverMenu:updateItems(select_number)
-- to replace it again if it is not ours)
if not self.onFileHold_ours -- never replaced
or self.onFileHold ~= self.onFileHold_ours then -- it is no more ours
-- Store original function, so we can call it
self.onFileHold_orig = self.onFileHold
-- Replace it with ours
-- This causes luacheck warning: "shadowing upvalue argument 'self' on line 34".
-- Ignoring it (as done in filemanager.lua for the same onFileHold)
self.onFileHold = function(self, file) -- luacheck: ignore
-- Call original function: it will create a ButtonDialogTitle
-- and store it as self.file_dialog, and UIManager:show() it.
self.onFileHold_orig(self, file)
local bookinfo = BookInfoManager:getBookInfo(file)
if not bookinfo then
-- If no bookinfo (yet) about this file, let the original dialog be
return true
end
-- We need to do it at nextTick, once FileManager has instantiated
-- its FileChooser completely
UIManager:nextTick(function()
-- Store original function, so we can call it
self.onFileHold_orig = self.onFileHold
-- Replace it with ours
-- This causes luacheck warning: "shadowing upvalue argument 'self' on line 34".
-- Ignoring it (as done in filemanager.lua for the same onFileHold)
self.onFileHold = function(self, file) -- luacheck: ignore
-- Call original function: it will create a ButtonDialogTitle
-- and store it as self.file_dialog, and UIManager:show() it.
self.onFileHold_orig(self, file)
local bookinfo = BookInfoManager:getBookInfo(file)
if not bookinfo then
-- If no bookinfo (yet) about this file, let the original dialog be
return true
end
-- Remember some of this original ButtonDialogTitle properties
local orig_title = self.file_dialog.title
local orig_title_align = self.file_dialog.title_align
local orig_buttons = self.file_dialog.buttons
-- Close original ButtonDialogTitle (it has not yet been painted
-- on screen, so we won't see it)
UIManager:close(self.file_dialog)
-- Replace Book information callback to use directly our bookinfo
orig_buttons[4][2].callback = function()
FileManagerBookInfo:show(file, bookinfo)
-- Remember some of this original ButtonDialogTitle properties
local orig_title = self.file_dialog.title
local orig_title_align = self.file_dialog.title_align
local orig_buttons = self.file_dialog.buttons
-- Close original ButtonDialogTitle (it has not yet been painted
-- on screen, so we won't see it)
UIManager:close(self.file_dialog)
end
-- Add some new buttons to original buttons set
table.insert(orig_buttons, {
{ -- Allow user to view real size cover in ImageViewer
text = _("View full size cover"),
enabled = bookinfo.has_cover and true or false,
callback = function()
local document = DocumentRegistry:openDocument(file)
if document then
local cover_bb = document:getCoverPageImage()
local imgviewer = ImageViewer:new{
image = cover_bb,
with_title_bar = false,
fullscreen = true,
-- Replace Book information callback to use directly our bookinfo
orig_buttons[4][2].callback = function()
FileManagerBookInfo:show(file, bookinfo)
UIManager:close(self.file_dialog)
end
-- Add some new buttons to original buttons set
table.insert(orig_buttons, {
{ -- Allow user to view real size cover in ImageViewer
text = _("View full size cover"),
enabled = bookinfo.has_cover and true or false,
callback = function()
local document = DocumentRegistry:openDocument(file)
if document then
local cover_bb = document:getCoverPageImage()
local imgviewer = ImageViewer:new{
image = cover_bb,
with_title_bar = false,
fullscreen = true,
}
UIManager:show(imgviewer)
UIManager:close(self.file_dialog)
DocumentRegistry:closeDocument(file)
end
end,
},
{ -- Allow user to directly view description in TextViewer
text = bookinfo.description and _("View book description") or _("No book description"),
enabled = bookinfo.description and true or false,
callback = function()
local description = require("util").htmlToPlainTextIfHtml(bookinfo.description)
local textviewer = TextViewer:new{
title = bookinfo.title,
text = description,
}
UIManager:show(imgviewer)
UIManager:show(textviewer)
UIManager:close(self.file_dialog)
DocumentRegistry:closeDocument(file)
end
end,
},
{ -- Allow user to directly view description in TextViewer
text = bookinfo.description and _("View book description") or _("No book description"),
enabled = bookinfo.description and true or false,
callback = function()
local description = require("util").htmlToPlainTextIfHtml(bookinfo.description)
local textviewer = TextViewer:new{
title = bookinfo.title,
text = description,
}
UIManager:show(textviewer)
UIManager:close(self.file_dialog)
end,
},
})
table.insert(orig_buttons, {
{ -- Allow user to ignore some offending cover image
text = bookinfo.ignore_cover and _("Unignore cover") or _("Ignore cover"),
enabled = bookinfo.has_cover and true or false,
callback = function()
BookInfoManager:setBookInfoProperties(file, {
["ignore_cover"] = not bookinfo.ignore_cover and 'Y' or false,
})
UIManager:close(self.file_dialog)
self:updateItems()
end,
},
{ -- Allow user to ignore some bad metadata (filename will be used instead)
text = bookinfo.ignore_meta and _("Unignore metadata") or _("Ignore metadata"),
enabled = bookinfo.has_meta and true or false,
callback = function()
BookInfoManager:setBookInfoProperties(file, {
["ignore_meta"] = not bookinfo.ignore_meta and 'Y' or false,
})
UIManager:close(self.file_dialog)
self:updateItems()
end,
},
})
table.insert(orig_buttons, {
{ -- Allow a new extraction (multiple interruptions, book replaced)...
text = _("Refresh cached book information"),
enabled = bookinfo and true or false,
callback = function()
BookInfoManager:deleteBookInfo(file)
UIManager:close(self.file_dialog)
self:updateItems()
end,
},
})
end,
},
})
table.insert(orig_buttons, {
{ -- Allow user to ignore some offending cover image
text = bookinfo.ignore_cover and _("Unignore cover") or _("Ignore cover"),
enabled = bookinfo.has_cover and true or false,
callback = function()
BookInfoManager:setBookInfoProperties(file, {
["ignore_cover"] = not bookinfo.ignore_cover and 'Y' or false,
})
UIManager:close(self.file_dialog)
self:updateItems()
end,
},
{ -- Allow user to ignore some bad metadata (filename will be used instead)
text = bookinfo.ignore_meta and _("Unignore metadata") or _("Ignore metadata"),
enabled = bookinfo.has_meta and true or false,
callback = function()
BookInfoManager:setBookInfoProperties(file, {
["ignore_meta"] = not bookinfo.ignore_meta and 'Y' or false,
})
UIManager:close(self.file_dialog)
self:updateItems()
end,
},
})
table.insert(orig_buttons, {
{ -- Allow a new extraction (multiple interruptions, book replaced)...
text = _("Refresh cached book information"),
enabled = bookinfo and true or false,
callback = function()
BookInfoManager:deleteBookInfo(file)
UIManager:close(self.file_dialog)
self:updateItems()
end,
},
})
-- Create the new ButtonDialogTitle, and let UIManager show it
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
self.file_dialog = ButtonDialogTitle:new{
title = orig_title,
title_align = orig_title_align,
buttons = orig_buttons,
}
UIManager:show(self.file_dialog)
return true
end
-- Create the new ButtonDialogTitle, and let UIManager show it
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
self.file_dialog = ButtonDialogTitle:new{
title = orig_title,
title_align = orig_title_align,
buttons = orig_buttons,
}
UIManager:show(self.file_dialog)
return true
end
-- Remember our function
self.onFileHold_ours = self.onFileHold
-- Remember our function
self.onFileHold_ours = self.onFileHold
end)
end
end
@ -448,6 +452,9 @@ function CoverMenu:onCloseWidget()
-- Propagate a call to free() to all our sub-widgets, to release memory used by their _bb
self.item_group:free()
-- Clean any short term cache (used by ListMenu to cache some DocSettings info)
self.cover_info_cache = nil
-- Force garbage collecting when leaving too
collectgarbage()
collectgarbage()

@ -304,21 +304,32 @@ function ListMenuItem:update()
fileinfo_str = self.mandatory .. " " .. fileinfo_str
end
-- Current page / pages are available or more accurate in .sdr/metadata.lua
-- We use a cache (cleaned at end of this browsing session) to store
-- page and percent read from sidecar files, to avoid re-parsing them
-- when re-rendering a visited page
if not self.menu.cover_info_cache then
self.menu.cover_info_cache = {}
end
local pages_str = ""
local percent_finished
local pages = bookinfo.pages -- default to those in bookinfo db
if DocSettings:hasSidecarFile(self.filepath) then
self.been_opened = true
local docinfo = DocSettings:open(self.filepath)
-- We can get nb of page in the new 'doc_pages' setting, or from the old 'stats.page'
if docinfo.data.doc_pages then
pages = docinfo.data.doc_pages
elseif docinfo.data.stats and docinfo.data.stats.pages then
if docinfo.data.stats.pages ~= 0 then -- crengine with statistics disabled stores 0
pages = docinfo.data.stats.pages
if self.menu.cover_info_cache[self.filepath] then
pages, percent_finished = unpack(self.menu.cover_info_cache[self.filepath])
else
local docinfo = DocSettings:open(self.filepath)
-- We can get nb of page in the new 'doc_pages' setting, or from the old 'stats.page'
if docinfo.data.doc_pages then
pages = docinfo.data.doc_pages
elseif docinfo.data.stats and docinfo.data.stats.pages then
if docinfo.data.stats.pages ~= 0 then -- crengine with statistics disabled stores 0
pages = docinfo.data.stats.pages
end
end
percent_finished = docinfo.data.percent_finished
self.menu.cover_info_cache[self.filepath] = {pages, percent_finished}
end
percent_finished = docinfo.data.percent_finished
end
if percent_finished then
if pages then
@ -664,9 +675,10 @@ function ListMenu:_recalculateDimen()
-- a self.perpage higher than it should be: Menu:init() will set a wrong self.page.
-- We'll have to update it, if we want FileManager to get back to the original page.
self.page_recalc_needed_next_time = true
-- Also remember original position, which will be changed by Menu/FileChooser
-- to a probably wrong value
-- Also remember original position (and focused_path), which will be changed by
-- Menu/FileChooser to a probably wrong value
self.itemnum_orig = self.path_items[self.path]
self.focused_path_orig = self.focused_path
end
local available_height = self.dimen.h - self.others_height
@ -675,6 +687,8 @@ function ListMenu:_recalculateDimen()
local item_height_min = Screen:scaleBySize(64)
self.perpage = math.floor(available_height / item_height_min)
self.page_num = math.ceil(#self.item_table / self.perpage)
-- fix current page if out of range
if self.page_num > 0 and self.page > self.page_num then self.page = self.page_num end
local height_remaining = available_height - self.perpage * item_height_min
height_remaining = height_remaining - (self.perpage+1) -- N+1 LineWidget separators
@ -686,13 +700,23 @@ function ListMenu:_recalculateDimen()
}
if self.page_recalc_needed then
-- self.page has probably been set to a wrong value,
-- we recalculate it here as done in Menu:init()
-- self.page has probably been set to a wrong value, we recalculate
-- it here as done in Menu:init() or Menu:switchItemTable()
if #self.item_table > 0 then
self.page = math.ceil((self.itemnum_orig or 1) / self.perpage)
end
if self.focused_path_orig then
for num, item in ipairs(self.item_table) do
if item.path == self.focused_path_orig then
self.page = math.floor((num-1) / self.perpage) + 1
break
end
end
end
if self.page_num > 0 and self.page > self.page_num then self.page = self.page_num end
self.page_recalc_needed = nil
self.itemnum_orig = nil
self.focused_path_orig = nil
end
if self.page_recalc_needed_next_time then
self.page_recalc_needed = true

@ -33,30 +33,31 @@ local DISPLAY_MODES = {
list_image_filename = true, -- image with filename (no metadata)
}
-- Store some states as locals, to be permanent across instantiations
local init_done = false
local filemanager_display_mode = false -- not initialized yet
local history_display_mode = false -- not initialized yet
local CoverBrowser = InputContainer:new{}
function CoverBrowser:init()
self.full_featured = true
-- Set to false to provide a fallback option with only a menu for managing a few core settings.
-- (Could be set to false for some platforms to provide a fallback
-- option with only a menu for managing a few core settings)
self.ui.menu:registerToMainMenu(self)
if not self.full_featured then
if not self.full_featured then -- nothing else than menu registration
return
end
self.filemanager_display_mode = BookInfoManager:getSetting("filemanager_display_mode")
self:setupFileManagerDisplayMode()
self.history_display_mode = BookInfoManager:getSetting("history_display_mode")
self:setupHistoryDisplayMode()
if init_done then -- things already patched according to current modes
return
end
-- If KOReader has started directly to FileManager, the FileManager
-- instance is being init()'ed and there is no FileManager.instance yet,
-- but there'll be one at next tick.
UIManager:nextTick(function()
self:refreshFileManagerInstance()
end)
self:setupFileManagerDisplayMode(BookInfoManager:getSetting("filemanager_display_mode"))
self:setupHistoryDisplayMode(BookInfoManager:getSetting("history_display_mode"))
init_done = true
end
function CoverBrowser:addToMainMenu(menu_items)
@ -153,42 +154,42 @@ function CoverBrowser:addToMainMenu(menu_items)
-- so one can see how they look below the menu
{
text = _("Classic (filename only)"),
checked_func = function() return not self.filemanager_display_mode end,
checked_func = function() return not filemanager_display_mode end,
callback = function()
self:setupFileManagerDisplayMode("")
end,
},
{
text = _("Mosaic with cover images"),
checked_func = function() return self.filemanager_display_mode == "mosaic_image" end,
checked_func = function() return filemanager_display_mode == "mosaic_image" end,
callback = function()
self:setupFileManagerDisplayMode("mosaic_image")
end,
},
{
text = _("Mosaic with text covers"),
checked_func = function() return self.filemanager_display_mode == "mosaic_text" end,
checked_func = function() return filemanager_display_mode == "mosaic_text" end,
callback = function()
self:setupFileManagerDisplayMode("mosaic_text")
end,
},
{
text = _("Detailed list with cover images and metadata"),
checked_func = function() return self.filemanager_display_mode == "list_image_meta" end,
checked_func = function() return filemanager_display_mode == "list_image_meta" end,
callback = function()
self:setupFileManagerDisplayMode("list_image_meta")
end,
},
{
text = _("Detailed list with metadata, no images"),
checked_func = function() return self.filemanager_display_mode == "list_only_meta" end,
checked_func = function() return filemanager_display_mode == "list_only_meta" end,
callback = function()
self:setupFileManagerDisplayMode("list_only_meta")
end,
},
{
text = _("Detailed list with cover images and filenames"),
checked_func = function() return self.filemanager_display_mode == "list_image_filename" end,
checked_func = function() return filemanager_display_mode == "list_image_filename" end,
callback = function()
self:setupFileManagerDisplayMode("list_image_filename")
end,
@ -201,42 +202,42 @@ function CoverBrowser:addToMainMenu(menu_items)
sub_item_table = {
{
text = _("Classic (filename only)"),
checked_func = function() return not self.history_display_mode end,
checked_func = function() return not history_display_mode end,
callback = function()
self:setupHistoryDisplayMode("")
end,
},
{
text = _("Mosaic with cover images"),
checked_func = function() return self.history_display_mode == "mosaic_image" end,
checked_func = function() return history_display_mode == "mosaic_image" end,
callback = function()
self:setupHistoryDisplayMode("mosaic_image")
end,
},
{
text = _("Mosaic with text covers"),
checked_func = function() return self.history_display_mode == "mosaic_text" end,
checked_func = function() return history_display_mode == "mosaic_text" end,
callback = function()
self:setupHistoryDisplayMode("mosaic_text")
end,
},
{
text = _("Detailed list with cover images and metadata"),
checked_func = function() return self.history_display_mode == "list_image_meta" end,
checked_func = function() return history_display_mode == "list_image_meta" end,
callback = function()
self:setupHistoryDisplayMode("list_image_meta")
end,
},
{
text = _("Detailed list with metadata, no images"),
checked_func = function() return self.history_display_mode == "list_only_meta" end,
checked_func = function() return history_display_mode == "list_only_meta" end,
callback = function()
self:setupHistoryDisplayMode("list_only_meta")
end,
},
{
text = _("Detailed list with cover images and filenames"),
checked_func = function() return self.history_display_mode == "list_image_filename" end,
checked_func = function() return history_display_mode == "list_image_filename" end,
callback = function()
self:setupHistoryDisplayMode("list_image_filename")
end,
@ -379,7 +380,7 @@ function CoverBrowser:addToMainMenu(menu_items)
end
end
function CoverBrowser:refreshFileManagerInstance(cleanup)
function CoverBrowser:refreshFileManagerInstance(cleanup, post_init)
local FileManager = require("apps/filemanager/filemanager")
local fm = FileManager.instance
if fm then
@ -392,8 +393,16 @@ function CoverBrowser:refreshFileManagerInstance(cleanup)
fc.onFileHold_ours = nil
end
end
if self.filemanager_display_mode then
fc:updateItems()
if filemanager_display_mode then
if post_init then
-- FileBrowser was initialized in classic mode, but we changed
-- display mode: items per page may have changed, and we want
-- to re-position on the focused_file
fc:_recalculateDimen()
fc:changeToPath(fc.path, fc.prev_focused_path)
else
fc:updateItems()
end
else -- classic file_chooser needs this for a full redraw
fc:refreshPath()
end
@ -401,16 +410,23 @@ function CoverBrowser:refreshFileManagerInstance(cleanup)
end
function CoverBrowser:setupFileManagerDisplayMode(display_mode)
if not display_mode then -- if none provided, use current one
display_mode = self.filemanager_display_mode
end
if not DISPLAY_MODES[display_mode] then
display_mode = nil
display_mode = nil -- unknow mode, fallback to classic
end
if init_done and display_mode == filemanager_display_mode then -- no change
return
end
if init_done then -- save new mode in db
BookInfoManager:saveSetting("filemanager_display_mode", display_mode)
end
self.filemanager_display_mode = display_mode
BookInfoManager:saveSetting("filemanager_display_mode", self.filemanager_display_mode)
-- remember current mode in module variable
filemanager_display_mode = display_mode
logger.dbg("CoverBrowser: setting FileManager display mode to:", display_mode or "classic")
if not init_done and not display_mode then
return -- starting in classic mode, nothing to patch
end
if not display_mode then -- classic mode
-- Put back original methods
FileChooser.updateItems = _FileChooser_updateItems_orig
@ -459,7 +475,17 @@ function CoverBrowser:setupFileManagerDisplayMode(display_mode)
FileChooser._do_hint_opened = true -- dogear at bottom
end
self:refreshFileManagerInstance()
if init_done then
self:refreshFileManagerInstance()
else
-- If KOReader has started directly to FileManager, the FileManager
-- instance is being init()'ed and there is no FileManager.instance yet,
-- but there'll be one at next tick.
UIManager:nextTick(function()
self:refreshFileManagerInstance(false, true)
end)
end
end
local function _FileManagerHistory_updateItemTable(self)
@ -512,16 +538,23 @@ local function _FileManagerHistory_updateItemTable(self)
end
function CoverBrowser:setupHistoryDisplayMode(display_mode)
if not display_mode then -- if none provided, use current one
display_mode = self.history_display_mode
end
if not DISPLAY_MODES[display_mode] then
display_mode = nil
display_mode = nil -- unknow mode, fallback to classic
end
self.history_display_mode = display_mode
BookInfoManager:saveSetting("history_display_mode", self.history_display_mode)
if init_done and display_mode == history_display_mode then -- no change
return
end
if init_done then -- save new mode in db
BookInfoManager:saveSetting("history_display_mode", display_mode)
end
-- remember current mode in module variable
history_display_mode = display_mode
logger.dbg("CoverBrowser: setting History display mode to:", display_mode or "classic")
if not init_done and not display_mode then
return -- starting in classic mode, nothing to patch
end
-- We only need to replace one FileManagerHistory method
if not display_mode then -- classic mode
-- Put back original methods

@ -672,6 +672,8 @@ function MosaicMenu:_recalculateDimen()
end
self.perpage = self.nb_rows * self.nb_cols
self.page_num = math.ceil(#self.item_table / self.perpage)
-- fix current page if out of range
if self.page_num > 0 and self.page > self.page_num then self.page = self.page_num end
-- Find out available height from other UI elements made in Menu
self.others_height = 0

Loading…
Cancel
Save