mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Some checks failed
macos / macOS ${{ matrix.image }} ${{ matrix.platform }} 🔨${{ matrix.xcode_version }} 🎯${{ matrix.deployment_target }} (10.15, 13, x86-64, 15.2) (push) Has been cancelled
macos / macOS ${{ matrix.image }} ${{ matrix.platform }} 🔨${{ matrix.xcode_version }} 🎯${{ matrix.deployment_target }} (11.0, 14, ARM64, 15.4) (push) Has been cancelled
209 lines
8.8 KiB
Lua
209 lines
8.8 KiB
Lua
local InfoMessage = require("ui/widget/infomessage")
|
|
local Menu = require("ui/widget/menu")
|
|
local UIManager = require("ui/uimanager")
|
|
local logger = require("logger")
|
|
local _ = require("gettext")
|
|
|
|
local BookInfoManager = require("bookinfomanager")
|
|
|
|
-- This is a kind of "base class" for both MosaicMenu and ListMenu.
|
|
-- It implements the common code shared by these, mostly the non-UI
|
|
-- work : the updating of items and the management of background jobs.
|
|
--
|
|
-- Here the common overridden methods of Menu are defined:
|
|
-- :updateItems(select_number, no_recalculate_dimen)
|
|
-- :onCloseWidget()
|
|
--
|
|
-- MosaicMenu or ListMenu should implement specific UI methods:
|
|
-- :_recalculateDimen()
|
|
-- :_updateItemsBuildUI()
|
|
-- This last method is called in the middle of :updateItems() , and
|
|
-- should fill self.item_group with some specific UI layout. It may add
|
|
-- not found item to self.items_to_update for us to update() them
|
|
-- regularly.
|
|
|
|
-- Do some collectgarbage() every few drawings
|
|
local NB_DRAWINGS_BETWEEN_COLLECTGARBAGE = 5
|
|
local nb_drawings_since_last_collectgarbage = 0
|
|
|
|
-- Simple holder of methods that will replace those
|
|
-- in the real Menu class or instance
|
|
local CoverMenu = {}
|
|
|
|
function CoverMenu:updateItems(select_number, no_recalculate_dimen)
|
|
-- As done in Menu:updateItems()
|
|
local old_dimen = self.dimen and self.dimen:copy()
|
|
-- self.layout must be updated for focusmanager
|
|
self.layout = {}
|
|
self.item_group:clear()
|
|
-- NOTE: Our various _recalculateDimen overloads appear to have a stronger dependency
|
|
-- on the rest of the widget elements being properly laid-out,
|
|
-- so we have to run it *first*, unlike in Menu.
|
|
-- Otherwise, various layout issues arise (e.g., MosaicMenu's page_info is misaligned).
|
|
if not no_recalculate_dimen then
|
|
self:_recalculateDimen()
|
|
end
|
|
self.page_info:resetLayout()
|
|
self.return_button:resetLayout()
|
|
self.content_group:resetLayout()
|
|
|
|
-- Reset the list of items not found in db that will need to
|
|
-- be updated by a scheduled action
|
|
self.items_to_update = {}
|
|
-- Cancel any previous (now obsolete) scheduled update
|
|
if self.items_update_action then
|
|
UIManager:unschedule(self.items_update_action)
|
|
self.items_update_action = nil
|
|
end
|
|
|
|
-- Force garbage collecting before drawing a new page.
|
|
-- It's not really needed from a memory usage point of view, we did
|
|
-- all the free() where necessary, and koreader memory usage seems
|
|
-- stable when file browsing only (15-25 MB).
|
|
-- But I witnessed some freezes after browsing a lot when koreader's main
|
|
-- process was using 100% cpu (and some slow downs while drawing soon before
|
|
-- the freeze, like the full refresh happening before the final drawing of
|
|
-- new text covers), while still having a small memory usage (20/30 Mb)
|
|
-- that I suspect may be some garbage collecting happening at one point
|
|
-- and getting stuck...
|
|
-- With this, garbage collecting may be more deterministic, and it has
|
|
-- no negative impact on user experience.
|
|
-- But don't do it on every drawing, to not have all of them slow
|
|
-- when memory usage is already high
|
|
nb_drawings_since_last_collectgarbage = nb_drawings_since_last_collectgarbage + 1
|
|
if nb_drawings_since_last_collectgarbage >= NB_DRAWINGS_BETWEEN_COLLECTGARBAGE then
|
|
-- (delay it a bit so this pause is less noticeable)
|
|
UIManager:scheduleIn(0.2, function()
|
|
collectgarbage()
|
|
collectgarbage()
|
|
end)
|
|
nb_drawings_since_last_collectgarbage = 0
|
|
end
|
|
|
|
-- Specific UI building implementation (defined in some other module)
|
|
self._has_cover_images = false
|
|
select_number = self:_updateItemsBuildUI() or select_number
|
|
|
|
-- As done in Menu:updateItems()
|
|
self:updatePageInfo(select_number)
|
|
Menu.mergeTitleBarIntoLayout(self)
|
|
|
|
self.show_parent.dithered = self._has_cover_images
|
|
UIManager:setDirty(self.show_parent, function()
|
|
local refresh_dimen =
|
|
old_dimen and old_dimen:combine(self.dimen)
|
|
or self.dimen
|
|
return "ui", refresh_dimen, self.show_parent.dithered
|
|
end)
|
|
|
|
-- As additionally done in FileChooser:updateItems()
|
|
if self.path_items then
|
|
self.path_items[self.path] = (self.page - 1) * self.perpage + (select_number or 1)
|
|
end
|
|
|
|
-- Deal with items not found in db
|
|
if #self.items_to_update > 0 then
|
|
-- Prepare for background info extraction job
|
|
local files_to_index = {} -- table of {filepath, cover_specs}
|
|
for i=1, #self.items_to_update do
|
|
table.insert(files_to_index, {
|
|
filepath = self.items_to_update[i].filepath,
|
|
cover_specs = self.items_to_update[i].cover_specs
|
|
})
|
|
end
|
|
-- Launch it at nextTick, so UIManager can render us smoothly
|
|
UIManager:nextTick(function()
|
|
local launched = BookInfoManager:extractInBackground(files_to_index)
|
|
if not launched then -- fork failed (never experienced that, but let's deal with it)
|
|
-- Cancel scheduled update, as it won't get any result
|
|
if self.items_update_action then
|
|
UIManager:unschedule(self.items_update_action)
|
|
self.items_update_action = nil
|
|
end
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("Start-up of background extraction job failed.\nPlease restart KOReader or your device.")
|
|
})
|
|
end
|
|
end)
|
|
|
|
-- Scheduled update action
|
|
self.items_update_action = function()
|
|
logger.dbg("Scheduled items update:", #self.items_to_update, "waiting")
|
|
local is_still_extracting = BookInfoManager:isExtractingInBackground()
|
|
local i = 1
|
|
while i <= #self.items_to_update do -- process and clean in-place
|
|
local item = self.items_to_update[i]
|
|
item:update()
|
|
if item.bookinfo_found then
|
|
logger.dbg(" found", item.text)
|
|
self.show_parent.dithered = item._has_cover_image
|
|
local refreshfunc = function()
|
|
if item.refresh_dimen then
|
|
-- MosaicMenuItem may exceed its own dimen in its paintTo
|
|
-- with its "description" hint
|
|
return "ui", item.refresh_dimen, self.show_parent.dithered
|
|
else
|
|
return "ui", item[1].dimen, self.show_parent.dithered
|
|
end
|
|
end
|
|
UIManager:setDirty(self.show_parent, refreshfunc)
|
|
table.remove(self.items_to_update, i)
|
|
else
|
|
logger.dbg(" not yet found", item.text)
|
|
i = i + 1
|
|
end
|
|
end
|
|
if #self.items_to_update > 0 then -- re-schedule myself
|
|
if is_still_extracting then -- we have still chances to get new stuff
|
|
logger.dbg("re-scheduling items update:", #self.items_to_update, "still waiting")
|
|
UIManager:scheduleIn(1, self.items_update_action)
|
|
else
|
|
logger.dbg("Not all items found, but background extraction has stopped, not re-scheduling")
|
|
end
|
|
else
|
|
logger.dbg("items update completed")
|
|
end
|
|
end
|
|
UIManager:scheduleIn(1, self.items_update_action)
|
|
end
|
|
end
|
|
|
|
function CoverMenu:onCloseWidget()
|
|
-- Due to close callback in FileManagerHistory:onShowHist, we may be called
|
|
-- multiple times (witnessed that with print(debug.traceback())
|
|
-- So, avoid doing what follows twice
|
|
if self._covermenu_onclose_done then
|
|
return
|
|
end
|
|
self._covermenu_onclose_done = true
|
|
|
|
-- Stop background job if any (so that full cpu is available to reader)
|
|
logger.dbg("CoverMenu:onCloseWidget: terminating jobs if needed")
|
|
BookInfoManager:terminateBackgroundJobs()
|
|
BookInfoManager:closeDbConnection() -- sqlite connection no more needed
|
|
BookInfoManager:cleanUp() -- clean temporary resources
|
|
|
|
-- Cancel any still scheduled update
|
|
if self.items_update_action then
|
|
logger.dbg("CoverMenu:onCloseWidget: unscheduling items_update_action")
|
|
UIManager:unschedule(self.items_update_action)
|
|
self.items_update_action = nil
|
|
end
|
|
|
|
-- Propagate a call to free() to all our sub-widgets, to release memory used by their _bb
|
|
self.item_group:free()
|
|
|
|
-- Force garbage collecting when leaving too
|
|
-- (delay it a bit so this pause is less noticeable)
|
|
UIManager:scheduleIn(0.2, function()
|
|
collectgarbage()
|
|
collectgarbage()
|
|
end)
|
|
nb_drawings_since_last_collectgarbage = 0
|
|
|
|
-- Call the object's original onCloseWidget (i.e., Menu's, as none our our expected subclasses currently implement it)
|
|
Menu.onCloseWidget(self)
|
|
end
|
|
|
|
return CoverMenu
|