2017-06-17 03:44:29 +00:00
--[[
ReaderUI is an abstraction for a reader interface .
It works using data gathered from a document interface .
] ] --
2020-01-04 00:18:51 +00:00
local BD = require ( " ui/bidi " )
2014-08-24 07:16:01 +00:00
local ConfirmBox = require ( " ui/widget/confirmbox " )
2014-10-30 18:42:18 +00:00
local Device = require ( " device " )
2020-07-12 18:47:49 +00:00
local DeviceListener = require ( " device/devicelistener " )
2021-05-04 21:13:24 +00:00
local DocCache = require ( " document/doccache " )
2017-06-17 03:44:29 +00:00
local DocSettings = require ( " docsettings " )
local DocumentRegistry = require ( " document/documentregistry " )
2013-10-18 20:38:07 +00:00
local Event = require ( " ui/event " )
2017-07-01 10:11:44 +00:00
local FileManagerBookInfo = require ( " apps/filemanager/filemanagerbookinfo " )
2019-11-05 23:17:28 +00:00
local FileManagerCollection = require ( " apps/filemanager/filemanagercollection " )
2017-06-17 03:44:29 +00:00
local FileManagerHistory = require ( " apps/filemanager/filemanagerhistory " )
2019-03-03 11:43:09 +00:00
local FileManagerFileSearcher = require ( " apps/filemanager/filemanagerfilesearcher " )
2019-03-08 20:02:25 +00:00
local FileManagerShortcuts = require ( " apps/filemanager/filemanagershortcuts " )
2017-06-17 03:44:29 +00:00
local InfoMessage = require ( " ui/widget/infomessage " )
local InputContainer = require ( " ui/widget/container/inputcontainer " )
local InputDialog = require ( " ui/widget/inputdialog " )
2021-10-23 10:13:09 +00:00
local LanguageSupport = require ( " languagesupport " )
2024-05-20 06:37:40 +00:00
local NetworkListener = require ( " ui/network/networklistener " )
2023-04-29 11:53:10 +00:00
local Notification = require ( " ui/widget/notification " )
2017-06-17 03:44:29 +00:00
local PluginLoader = require ( " pluginloader " )
local ReaderActivityIndicator = require ( " apps/reader/modules/readeractivityindicator " )
2024-05-03 06:08:57 +00:00
local ReaderAnnotation = require ( " apps/reader/modules/readerannotation " )
2018-03-31 19:19:31 +00:00
local ReaderBack = require ( " apps/reader/modules/readerback " )
2014-04-02 20:59:17 +00:00
local ReaderBookmark = require ( " apps/reader/modules/readerbookmark " )
local ReaderConfig = require ( " apps/reader/modules/readerconfig " )
local ReaderCoptListener = require ( " apps/reader/modules/readercoptlistener " )
2017-06-17 03:44:29 +00:00
local ReaderCropping = require ( " apps/reader/modules/readercropping " )
2018-08-11 20:47:33 +00:00
local ReaderDeviceStatus = require ( " apps/reader/modules/readerdevicestatus " )
2017-06-17 03:44:29 +00:00
local ReaderDictionary = require ( " apps/reader/modules/readerdictionary " )
local ReaderFont = require ( " apps/reader/modules/readerfont " )
local ReaderGoto = require ( " apps/reader/modules/readergoto " )
2023-10-08 15:51:56 +00:00
local ReaderHandMade = require ( " apps/reader/modules/readerhandmade " )
2014-04-02 20:59:17 +00:00
local ReaderHinting = require ( " apps/reader/modules/readerhinting " )
local ReaderHighlight = require ( " apps/reader/modules/readerhighlight " )
2021-05-19 20:57:54 +00:00
local ReaderScrolling = require ( " apps/reader/modules/readerscrolling " )
2017-06-17 03:44:29 +00:00
local ReaderKoptListener = require ( " apps/reader/modules/readerkoptlistener " )
2014-04-02 20:59:17 +00:00
local ReaderLink = require ( " apps/reader/modules/readerlink " )
2017-06-17 03:44:29 +00:00
local ReaderMenu = require ( " apps/reader/modules/readermenu " )
2020-03-26 13:04:59 +00:00
local ReaderPageMap = require ( " apps/reader/modules/readerpagemap " )
2017-06-17 03:44:29 +00:00
local ReaderPanning = require ( " apps/reader/modules/readerpanning " )
2024-08-08 02:53:04 +00:00
--local ReaderRotation = require("apps/reader/modules/readerrotation")
2017-06-17 03:44:29 +00:00
local ReaderPaging = require ( " apps/reader/modules/readerpaging " )
local ReaderRolling = require ( " apps/reader/modules/readerrolling " )
local ReaderSearch = require ( " apps/reader/modules/readersearch " )
2016-02-02 17:38:14 +00:00
local ReaderStatus = require ( " apps/reader/modules/readerstatus " )
2018-05-12 21:24:43 +00:00
local ReaderStyleTweak = require ( " apps/reader/modules/readerstyletweak " )
2022-01-04 20:58:56 +00:00
local ReaderThumbnail = require ( " apps/reader/modules/readerthumbnail " )
2017-06-17 03:44:29 +00:00
local ReaderToc = require ( " apps/reader/modules/readertoc " )
local ReaderTypeset = require ( " apps/reader/modules/readertypeset " )
2020-04-21 19:30:16 +00:00
local ReaderTypography = require ( " apps/reader/modules/readertypography " )
2021-06-01 10:06:55 +00:00
local ReaderUserHyph = require ( " apps/reader/modules/readeruserhyph " )
2017-06-17 03:44:29 +00:00
local ReaderView = require ( " apps/reader/modules/readerview " )
local ReaderWikipedia = require ( " apps/reader/modules/readerwikipedia " )
local ReaderZooming = require ( " apps/reader/modules/readerzooming " )
local Screenshoter = require ( " ui/widget/screenshoter " )
2019-05-01 00:09:01 +00:00
local SettingsMigration = require ( " ui/data/settings_migration " )
2017-06-17 03:44:29 +00:00
local UIManager = require ( " ui/uimanager " )
2020-08-25 13:45:05 +00:00
local ffiUtil = require ( " ffi/util " )
2024-05-19 11:19:38 +00:00
local filemanagerutil = require ( " apps/filemanager/filemanagerutil " )
2017-06-17 03:44:29 +00:00
local lfs = require ( " libs/libkoreader-lfs " )
local logger = require ( " logger " )
2022-05-05 19:00:22 +00:00
local time = require ( " ui/time " )
2018-03-21 19:10:35 +00:00
local util = require ( " util " )
2017-06-17 03:44:29 +00:00
local _ = require ( " gettext " )
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
local Input = Device.input
local Screen = Device.screen
2020-09-15 18:39:32 +00:00
local T = ffiUtil.template
2012-05-18 22:50:26 +00:00
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local ReaderUI = InputContainer : extend {
2016-08-12 06:05:18 +00:00
name = " ReaderUI " ,
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
active_widgets = nil , -- array
2012-05-18 22:50:26 +00:00
2014-03-13 13:52:43 +00:00
-- if we have a parent container, it must be referenced for now
dialog = nil ,
2012-05-18 22:50:26 +00:00
2014-03-13 13:52:43 +00:00
-- the document interface
document = nil ,
2012-06-11 16:35:13 +00:00
2014-03-13 13:52:43 +00:00
-- password for document unlock
password = nil ,
2013-01-13 02:22:33 +00:00
2014-03-13 13:52:43 +00:00
postInitCallback = nil ,
2024-05-03 06:08:57 +00:00
postReaderReadyCallback = nil ,
2012-05-18 22:50:26 +00:00
}
2016-02-04 18:24:39 +00:00
function ReaderUI : registerModule ( name , ui_module , always_active )
2021-10-25 06:41:20 +00:00
if name then
self [ name ] = ui_module
ui_module.name = " reader " .. name
end
2020-11-08 21:43:37 +00:00
table.insert ( self , ui_module )
if always_active then
-- to get events even when hidden
table.insert ( self.active_widgets , ui_module )
end
2014-10-09 09:41:23 +00:00
end
function ReaderUI : registerPostInitCallback ( callback )
table.insert ( self.postInitCallback , callback )
end
2024-05-03 06:08:57 +00:00
function ReaderUI : registerPostReaderReadyCallback ( callback )
table.insert ( self.postReaderReadyCallback , callback )
2017-03-24 19:02:17 +00:00
end
2012-05-18 22:50:26 +00:00
function ReaderUI : init ( )
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
self.active_widgets = { }
2017-03-23 18:40:42 +00:00
-- cap screen refresh on pan to 2 refreshes per second
2018-10-10 19:34:47 +00:00
local pan_rate = Screen.low_pan_rate and 2.0 or 30.0
2017-03-23 18:40:42 +00:00
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
Input : inhibitInput ( true ) -- Inhibit any past and upcoming input events.
2021-12-07 18:55:55 +00:00
Device : setIgnoreInput ( true ) -- Avoid ANRs on Android with unprocessed events.
2014-03-13 13:52:43 +00:00
self.postInitCallback = { }
2024-05-03 06:08:57 +00:00
self.postReaderReadyCallback = { }
2014-03-13 13:52:43 +00:00
-- if we are not the top level dialog ourselves, it must be given in the table
if not self.dialog then
self.dialog = self
end
2012-06-26 09:05:09 +00:00
2018-03-14 21:16:38 +00:00
self.doc_settings = DocSettings : open ( self.document . file )
2019-05-01 00:09:01 +00:00
-- Handle local settings migration
SettingsMigration : migrateSettings ( self.doc_settings )
2018-03-14 21:16:38 +00:00
2022-11-01 22:22:07 +00:00
self : registerKeyEvents ( )
2013-01-01 02:22:45 +00:00
2014-03-13 13:52:43 +00:00
-- a view container (so it must be child #1!)
2016-12-04 23:42:22 +00:00
-- all paintable widgets need to be a child of reader view
2014-10-09 09:41:23 +00:00
self : registerModule ( " view " , ReaderView : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
dimen = self.dimen ,
ui = self ,
document = self.document ,
2014-10-09 09:41:23 +00:00
} )
-- goto link controller
self : registerModule ( " link " , ReaderLink : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
-- text highlight
2014-10-09 09:41:23 +00:00
self : registerModule ( " highlight " , ReaderHighlight : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
-- menu widget should be registered after link widget and highlight widget
-- so that taps on link and highlight areas won't popup reader menu
2014-10-09 09:41:23 +00:00
-- reader menu controller
self : registerModule ( " menu " , ReaderMenu : new {
view = self.view ,
ui = self
} )
2024-08-08 02:53:04 +00:00
-- (legacy, and defunct) rotation controller
--- @fixme: Tripping this would break rendering, c.f., `Document:renderPage`
--[[
2014-10-09 09:41:23 +00:00
self : registerModule ( " rotation " , ReaderRotation : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
2024-08-08 02:53:04 +00:00
--]]
2023-10-08 15:51:56 +00:00
-- Handmade/custom ToC and hidden flows
self : registerModule ( " handmade " , ReaderHandMade : new {
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
2014-03-13 13:52:43 +00:00
-- Table of content controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " toc " , ReaderToc : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2014-03-13 13:52:43 +00:00
-- bookmark controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " bookmark " , ReaderBookmark : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
2024-05-03 06:08:57 +00:00
self : registerModule ( " annotation " , ReaderAnnotation : new {
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
2014-03-13 13:52:43 +00:00
-- reader goto controller
2014-10-09 09:41:23 +00:00
-- "goto" being a dirty keyword in Lua?
self : registerModule ( " gotopage " , ReaderGoto : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
2021-10-23 10:13:09 +00:00
self : registerModule ( " languagesupport " , LanguageSupport : new {
ui = self ,
document = self.document ,
} )
2014-03-13 13:52:43 +00:00
-- dictionary
2014-10-09 09:41:23 +00:00
self : registerModule ( " dictionary " , ReaderDictionary : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
2014-08-20 06:41:45 +00:00
-- wikipedia
2014-10-09 09:41:23 +00:00
self : registerModule ( " wikipedia " , ReaderWikipedia : new {
2014-08-20 06:41:45 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-08-20 06:41:45 +00:00
ui = self ,
document = self.document ,
} )
2014-03-13 13:52:43 +00:00
-- screenshot controller
2016-01-03 08:47:01 +00:00
self : registerModule ( " screenshot " , Screenshoter : new {
prefix = ' Reader ' ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} , true )
2018-08-11 20:47:33 +00:00
-- device status controller
2021-06-29 12:07:29 +00:00
self : registerModule ( " devicestatus " , ReaderDeviceStatus : new {
2018-08-11 20:47:33 +00:00
ui = self ,
} )
2018-03-21 19:10:35 +00:00
-- configurable controller
2014-03-13 13:52:43 +00:00
if self.document . info.configurable then
-- config panel controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " config " , ReaderConfig : new {
2014-03-13 13:52:43 +00:00
configurable = self.document . configurable ,
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2018-10-26 15:27:43 +00:00
ui = self ,
document = self.document ,
2014-03-13 13:52:43 +00:00
} )
2014-10-09 09:41:23 +00:00
if self.document . info.has_pages then
-- kopt option controller
self : registerModule ( " koptlistener " , ReaderKoptListener : new {
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
else
2014-03-13 13:52:43 +00:00
-- cre option controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " crelistener " , ReaderCoptListener : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
end
2020-12-18 17:26:58 +00:00
-- activity indicator for when some settings take time to take effect (Kindle under KPV)
if not ReaderActivityIndicator : isStub ( ) then
self : registerModule ( " activityindicator " , ReaderActivityIndicator : new {
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
end
2014-03-13 13:52:43 +00:00
end
-- for page specific controller
if self.document . info.has_pages then
-- cropping controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " cropping " , ReaderCropping : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
-- paging controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " paging " , ReaderPaging : new {
2017-03-23 18:40:42 +00:00
pan_rate = pan_rate ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- zooming controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " zooming " , ReaderZooming : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2019-08-30 11:47:51 +00:00
document = self.document ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2014-03-13 13:52:43 +00:00
-- panning controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " panning " , ReaderPanning : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- hinting controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " hinting " , ReaderHinting : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
zoom = self.zooming ,
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self ,
document = self.document ,
} )
else
2018-11-20 20:07:59 +00:00
-- load crengine default settings (from cr3.ini, some of these
-- will be overriden by our settings by some reader modules below)
if self.document . setupDefaultView then
self.document : setupDefaultView ( )
end
2014-08-28 05:22:31 +00:00
-- make sure we render document first before calling any callback
2014-10-09 09:41:23 +00:00
self : registerPostInitCallback ( function ( )
2022-05-05 19:00:22 +00:00
local start_time = time.now ( )
2018-01-03 08:43:49 +00:00
if not self.document : loadDocument ( ) then
self : dealWithLoadDocumentFailure ( )
end
2022-05-05 19:00:22 +00:00
logger.dbg ( string.format ( " loading took %.3f seconds " , time.to_s ( time.since ( start_time ) ) ) )
2016-01-16 18:33:16 +00:00
2016-02-07 22:03:53 +00:00
-- used to read additional settings after the document has been
-- loaded (but not rendered yet)
2016-01-16 18:33:16 +00:00
self : handleEvent ( Event : new ( " PreRenderDocument " , self.doc_settings ) )
2022-05-05 19:00:22 +00:00
start_time = time.now ( )
2014-08-28 05:22:31 +00:00
self.document : render ( )
2022-05-05 19:00:22 +00:00
logger.dbg ( string.format ( " rendering took %.3f seconds " , time.to_s ( time.since ( start_time ) ) ) )
2020-08-25 13:45:05 +00:00
-- Uncomment to output the built DOM (for debugging)
-- logger.dbg(self.document:getHTMLFromXPointer(".0", 0x6830))
2014-03-13 13:52:43 +00:00
end )
2018-05-12 21:24:43 +00:00
-- styletweak controller (must be before typeset controller)
self : registerModule ( " styletweak " , ReaderStyleTweak : new {
dialog = self.dialog ,
view = self.view ,
ui = self
} )
2014-03-13 13:52:43 +00:00
-- typeset controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " typeset " , ReaderTypeset : new {
2023-09-04 21:12:08 +00:00
configurable = self.document . configurable ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
-- font menu
2014-10-09 09:41:23 +00:00
self : registerModule ( " font " , ReaderFont : new {
2023-09-08 05:40:47 +00:00
configurable = self.document . configurable ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2021-06-01 10:06:55 +00:00
-- user hyphenation (must be registered before typography)
self : registerModule ( " userhyph " , ReaderUserHyph : new {
dialog = self.dialog ,
view = self.view ,
ui = self
} )
2020-04-21 19:30:16 +00:00
-- typography menu (replaces previous hyphenation menu / ReaderHyphenation)
self : registerModule ( " typography " , ReaderTypography : new {
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
2014-10-09 09:41:23 +00:00
} )
2014-03-13 13:52:43 +00:00
-- rolling controller
2014-10-09 09:41:23 +00:00
self : registerModule ( " rolling " , ReaderRolling : new {
2023-09-08 05:40:47 +00:00
configurable = self.document . configurable ,
2017-03-23 18:40:42 +00:00
pan_rate = pan_rate ,
2014-03-13 13:52:43 +00:00
dialog = self.dialog ,
2014-10-09 09:41:23 +00:00
view = self.view ,
2014-03-13 13:52:43 +00:00
ui = self
} )
2020-03-26 13:04:59 +00:00
-- pagemap controller
self : registerModule ( " pagemap " , ReaderPageMap : new {
dialog = self.dialog ,
view = self.view ,
ui = self
} )
2014-03-13 13:52:43 +00:00
end
2021-03-06 21:44:18 +00:00
self.disable_double_tap = G_reader_settings : nilOrTrue ( " disable_double_tap " )
2021-05-19 20:57:54 +00:00
-- scrolling (scroll settings + inertial scrolling)
self : registerModule ( " scrolling " , ReaderScrolling : new {
pan_rate = pan_rate ,
dialog = self.dialog ,
ui = self ,
view = self.view ,
} )
2018-03-31 19:19:31 +00:00
-- back location stack
self : registerModule ( " back " , ReaderBack : new {
ui = self ,
view = self.view ,
} )
2014-11-05 04:28:11 +00:00
-- fulltext search
self : registerModule ( " search " , ReaderSearch : new {
dialog = self.dialog ,
view = self.view ,
ui = self
} )
2016-03-07 05:52:53 +00:00
-- book status
2016-02-02 17:38:14 +00:00
self : registerModule ( " status " , ReaderStatus : new {
ui = self ,
document = self.document ,
} )
2022-01-04 20:58:56 +00:00
-- thumbnails service (book map, page browser)
self : registerModule ( " thumbnail " , ReaderThumbnail : new {
ui = self ,
document = self.document ,
} )
2019-03-03 11:43:09 +00:00
-- file searcher
self : registerModule ( " filesearcher " , FileManagerFileSearcher : new {
dialog = self.dialog ,
ui = self ,
} )
2019-03-08 20:02:25 +00:00
-- folder shortcuts
self : registerModule ( " folder_shortcuts " , FileManagerShortcuts : new {
dialog = self.dialog ,
ui = self ,
} )
2016-03-07 05:52:53 +00:00
-- history view
self : registerModule ( " history " , FileManagerHistory : new {
dialog = self.dialog ,
ui = self ,
} )
2019-11-05 23:17:28 +00:00
-- collections/favorites view
self : registerModule ( " collections " , FileManagerCollection : new {
dialog = self.dialog ,
ui = self ,
} )
2017-07-01 10:11:44 +00:00
-- book info
self : registerModule ( " bookinfo " , FileManagerBookInfo : new {
dialog = self.dialog ,
document = self.document ,
ui = self ,
} )
2020-07-12 18:47:49 +00:00
-- event listener to change device settings
self : registerModule ( " devicelistener " , DeviceListener : new {
document = self.document ,
view = self.view ,
ui = self ,
} )
2024-05-20 06:37:40 +00:00
self : registerModule ( " networklistener " , NetworkListener : new {
document = self.document ,
view = self.view ,
ui = self ,
} )
2014-04-23 13:57:48 +00:00
-- koreader plugins
2017-04-21 06:37:04 +00:00
for _ , plugin_module in ipairs ( PluginLoader : loadPlugins ( ) ) do
local ok , plugin_or_err = PluginLoader : createPluginInstance (
plugin_module ,
{
dialog = self.dialog ,
view = self.view ,
ui = self ,
document = self.document ,
} )
if ok then
self : registerModule ( plugin_module.name , plugin_or_err )
2021-09-18 16:00:20 +00:00
logger.dbg ( " RD loaded plugin " , plugin_module.name ,
2017-04-21 06:37:04 +00:00
" at " , plugin_module.path )
end
2014-04-23 13:57:48 +00:00
end
2019-10-08 15:14:38 +00:00
-- Allow others to change settings based on external factors
-- Must be called after plugins are loaded & before setting are read.
self : handleEvent ( Event : new ( " DocSettingsLoad " , self.doc_settings , self.document ) )
2014-03-13 13:52:43 +00:00
-- we only read settings after all the widgets are initialized
self : handleEvent ( Event : new ( " ReadSettings " , self.doc_settings ) )
2013-01-13 02:22:33 +00:00
2014-03-13 13:52:43 +00:00
for _ , v in ipairs ( self.postInitCallback ) do
v ( )
end
2017-03-24 19:02:17 +00:00
self.postInitCallback = nil
2016-02-12 14:55:02 +00:00
2023-10-15 04:47:09 +00:00
-- Now that document is loaded, store book metadata in settings.
2023-09-01 05:07:29 +00:00
local props = self.document : getProps ( )
self.doc_settings : saveSetting ( " doc_props " , props )
2023-08-30 04:53:59 +00:00
-- And have an extended and customized copy in memory for quick access.
2023-09-01 05:07:29 +00:00
self.doc_props = FileManagerBookInfo.extendProps ( props , self.document . file )
2017-07-01 09:41:27 +00:00
2024-02-07 08:35:52 +00:00
local md5 = self.doc_settings : readSetting ( " partial_md5_checksum " )
if md5 == nil then
md5 = util.partialMD5 ( self.document . file )
self.doc_settings : saveSetting ( " partial_md5_checksum " , md5 )
end
2023-10-15 04:47:09 +00:00
local summary = self.doc_settings : readSetting ( " summary " , { } )
if summary.status == nil then
2023-01-07 06:09:16 +00:00
summary.status = " reading "
summary.modified = os.date ( " %Y-%m-%d " , os.time ( ) )
2023-10-15 04:47:09 +00:00
end
2024-02-07 08:35:52 +00:00
if summary.status ~= " complete " or not G_reader_settings : isTrue ( " history_freeze_finished_books " ) then
require ( " readhistory " ) : addItem ( self.document . file ) -- (will update "lastfile")
2023-01-07 06:09:16 +00:00
end
2016-06-03 08:31:46 +00:00
-- After initialisation notify that document is loaded and rendered
2016-02-12 14:55:02 +00:00
-- CREngine only reports correct page count after rendering is done
-- Need the same event for PDF document
self : handleEvent ( Event : new ( " ReaderReady " , self.doc_settings ) )
2017-03-24 19:02:17 +00:00
2024-05-03 06:08:57 +00:00
for _ , v in ipairs ( self.postReaderReadyCallback ) do
2017-03-24 19:02:17 +00:00
v ( )
end
2024-05-03 06:08:57 +00:00
self.postReaderReadyCallback = nil
2020-01-24 19:05:21 +00:00
2021-12-07 18:55:55 +00:00
Device : setIgnoreInput ( false ) -- Allow processing of events (on Android).
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
Input : inhibitInputUntil ( 0.2 )
2021-12-07 18:55:55 +00:00
2020-01-24 19:05:21 +00:00
-- print("Ordered registered gestures:")
-- for _, tzone in ipairs(self._ordered_touch_zones) do
-- print(" "..tzone.def.id)
-- end
2021-05-13 11:05:05 +00:00
if ReaderUI.instance == nil then
logger.dbg ( " Spinning up new ReaderUI instance " , tostring ( self ) )
else
2021-05-18 18:54:54 +00:00
-- Should never happen, given what we did in (do)showReader...
2021-05-13 11:05:05 +00:00
logger.err ( " ReaderUI instance mismatch! Opened " , tostring ( self ) , " while we still have an existing instance: " , tostring ( ReaderUI.instance ) , debug.traceback ( ) )
end
ReaderUI.instance = self
2012-05-18 22:50:26 +00:00
end
2022-11-01 22:22:07 +00:00
function ReaderUI : registerKeyEvents ( )
2022-10-31 23:17:25 +00:00
if Device : hasKeys ( ) then
self.key_events . Home = { { " Home " } }
self.key_events . Reload = { { " F5 " } }
2024-05-26 19:25:54 +00:00
if Device : hasDPad ( ) and Device : useDPadAsActionKeys ( ) then
self.key_events . KeyContentSelection = { { { " Up " , " Down " } } , event = " StartHighlightIndicator " }
end
if Device : hasScreenKB ( ) or Device : hasSymKey ( ) then
2024-05-20 19:20:50 +00:00
if Device : hasKeyboard ( ) then
self.key_events . KeyToggleWifi = { { " Shift " , " Home " } , event = " ToggleWifi " }
self.key_events . OpenLastDoc = { { " Shift " , " Back " } }
else -- Currently exclusively targets Kindle 4.
self.key_events . KeyToggleWifi = { { " ScreenKB " , " Home " } , event = " ToggleWifi " }
self.key_events . OpenLastDoc = { { " ScreenKB " , " Back " } }
end
end
2022-10-31 23:17:25 +00:00
end
end
ReaderUI.onPhysicalKeyboardConnected = ReaderUI.registerKeyEvents
2020-05-19 16:21:58 +00:00
function ReaderUI : setLastDirForFileBrowser ( dir )
2020-06-08 19:54:24 +00:00
if dir and # dir > 1 and dir : sub ( - 1 ) == " / " then
dir = dir : sub ( 1 , - 2 )
end
2020-05-19 16:21:58 +00:00
self.last_dir_for_file_browser = dir
end
function ReaderUI : getLastDirFile ( to_file_browser )
if to_file_browser and self.last_dir_for_file_browser then
local dir = self.last_dir_for_file_browser
self.last_dir_for_file_browser = nil
return dir
end
2017-06-17 03:44:29 +00:00
local QuickStart = require ( " ui/quickstart " )
2017-10-13 16:43:02 +00:00
local last_dir
2017-06-17 03:44:29 +00:00
local last_file = G_reader_settings : readSetting ( " lastfile " )
-- ignore quickstart guide as last_file so we can go back to home dir
if last_file and last_file ~= QuickStart.quickstart_filename then
2017-10-13 16:43:02 +00:00
last_dir = last_file : match ( " (.*)/ " )
2017-06-17 03:44:29 +00:00
end
2019-03-03 11:43:09 +00:00
return last_dir , last_file
end
2024-06-06 08:44:03 +00:00
function ReaderUI : showFileManager ( file , selected_files )
2019-03-03 11:43:09 +00:00
local FileManager = require ( " apps/filemanager/filemanager " )
2019-07-20 15:36:41 +00:00
local last_dir , last_file
if file then
2022-03-12 10:24:07 +00:00
last_dir = util.splitFilePathName ( file )
last_file = file
2019-07-20 15:36:41 +00:00
else
2020-05-19 16:21:58 +00:00
last_dir , last_file = self : getLastDirFile ( true )
2019-07-20 15:36:41 +00:00
end
2017-06-17 03:44:29 +00:00
if FileManager.instance then
2017-10-13 16:43:02 +00:00
FileManager.instance : reinit ( last_dir , last_file )
2017-06-17 03:44:29 +00:00
else
2024-06-06 08:44:03 +00:00
FileManager : showFiles ( last_dir , last_file , selected_files )
2017-06-17 03:44:29 +00:00
end
end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
function ReaderUI : onShowingReader ( )
-- Allows us to optimize out a few useless refreshes in various CloseWidgets handlers...
self.tearing_down = true
self.dithered = nil
2021-05-20 18:42:04 +00:00
-- Don't enforce a "full" refresh, leave that decision to the next widget we'll *show*.
self : onClose ( false )
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
end
-- Same as above, except we don't close it yet. Useful for plugins that need to close custom Menus before calling showReader.
function ReaderUI : onSetupShowReader ( )
self.tearing_down = true
self.dithered = nil
end
--- @note: Will sanely close existing FileManager/ReaderUI instance for you!
2021-05-18 18:54:54 +00:00
--- This is the *only* safe way to instantiate a new ReaderUI instance!
--- (i.e., don't look at the testsuite, which resorts to all kinds of nasty hacks).
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
function ReaderUI : showReader ( file , provider , seamless )
2016-12-29 08:10:38 +00:00
logger.dbg ( " show reader ui " )
2018-02-02 20:21:52 +00:00
2024-05-03 06:08:57 +00:00
file = ffiUtil.realpath ( file )
2014-09-03 04:09:25 +00:00
if lfs.attributes ( file , " mode " ) ~= " file " then
UIManager : show ( InfoMessage : new {
2024-05-19 11:19:38 +00:00
text = T ( _ ( " File '%1' does not exist. " ) , BD.filepath ( filemanagerutil.abbreviate ( file ) ) )
2014-09-03 04:09:25 +00:00
} )
return
end
2018-02-02 20:21:52 +00:00
2019-10-23 21:15:30 +00:00
if not DocumentRegistry : hasProvider ( file ) and provider == nil then
2019-07-20 15:36:41 +00:00
UIManager : show ( InfoMessage : new {
2024-05-19 11:19:38 +00:00
text = T ( _ ( " File '%1' is not supported. " ) , BD.filepath ( filemanagerutil.abbreviate ( file ) ) )
2019-07-20 15:36:41 +00:00
} )
self : showFileManager ( file )
return
end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
-- We can now signal the existing ReaderUI/FileManager instances that it's time to go bye-bye...
UIManager : broadcastEvent ( Event : new ( " ShowingReader " ) )
2018-02-02 20:21:52 +00:00
provider = provider or DocumentRegistry : getProvider ( file )
if provider.provider then
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
self : showReaderCoroutine ( file , provider , seamless )
2018-02-02 20:21:52 +00:00
end
end
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
function ReaderUI : showReaderCoroutine ( file , provider , seamless )
2014-09-03 04:09:25 +00:00
UIManager : show ( InfoMessage : new {
2024-05-19 11:19:38 +00:00
text = T ( _ ( " Opening file '%1'. " ) , BD.filepath ( filemanagerutil.abbreviate ( file ) ) ) ,
2016-04-23 15:09:31 +00:00
timeout = 0.0 ,
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
invisible = seamless ,
2014-09-03 04:09:25 +00:00
} )
2016-04-24 04:09:50 +00:00
-- doShowReader might block for a long time, so force repaint here
UIManager : forceRePaint ( )
2016-03-05 20:37:45 +00:00
UIManager : nextTick ( function ( )
2016-12-29 08:10:38 +00:00
logger.dbg ( " creating coroutine for showing reader " )
2015-10-06 13:18:07 +00:00
local co = coroutine.create ( function ( )
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
self : doShowReader ( file , provider , seamless )
2015-10-06 13:18:07 +00:00
end )
2015-12-23 08:01:43 +00:00
local ok , err = coroutine.resume ( co )
2016-01-03 08:56:55 +00:00
if err ~= nil or ok == false then
2016-03-18 04:52:57 +00:00
io.stderr : write ( ' [!] doShowReader coroutine crashed: \n ' )
io.stderr : write ( debug.traceback ( co , err , 1 ) )
2023-02-22 19:32:13 +00:00
-- Restore input if we crashed before ReaderUI has restored it
Device : setIgnoreInput ( false )
Input : inhibitInputUntil ( 0.2 )
2019-04-16 15:54:02 +00:00
UIManager : show ( InfoMessage : new {
text = _ ( " No reader engine for this file or invalid file. " )
} )
2024-06-30 11:29:22 +00:00
self : showFileManager ( file )
2015-12-23 08:01:43 +00:00
end
2015-10-06 13:18:07 +00:00
end )
2014-09-03 04:09:25 +00:00
end
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
function ReaderUI : doShowReader ( file , provider , seamless )
2023-02-22 19:32:13 +00:00
if seamless then
UIManager : avoidFlashOnNextRepaint ( )
end
2016-12-29 08:10:38 +00:00
logger.info ( " opening file " , file )
2021-05-10 22:49:35 +00:00
-- Only keep a single instance running
if ReaderUI.instance then
logger.warn ( " ReaderUI instance mismatch! Tried to spin up a new instance, while we still have an existing one: " , tostring ( ReaderUI.instance ) )
ReaderUI.instance : onClose ( )
2014-10-28 10:09:22 +00:00
end
2018-02-02 20:21:52 +00:00
local document = DocumentRegistry : openDocument ( file , provider )
2014-09-03 04:09:25 +00:00
if not document then
UIManager : show ( InfoMessage : new {
2017-06-17 03:44:29 +00:00
text = _ ( " No reader engine for this file or invalid file. " )
2014-09-03 04:09:25 +00:00
} )
2024-06-30 11:29:22 +00:00
self : showFileManager ( file )
2014-09-03 04:09:25 +00:00
return
end
2015-10-06 13:18:07 +00:00
if document.is_locked then
2016-12-29 08:10:38 +00:00
logger.info ( " document is locked " )
2015-10-06 13:18:07 +00:00
self._coroutine = coroutine.running ( ) or self._coroutine
self : unlockDocumentWithPassword ( document )
if coroutine.running ( ) then
local unlock_success = coroutine.yield ( )
if not unlock_success then
2024-06-30 11:29:22 +00:00
self : showFileManager ( file )
2015-10-06 13:18:07 +00:00
return
end
end
end
2014-09-03 04:09:25 +00:00
local reader = ReaderUI : new {
dimen = Screen : getSize ( ) ,
2018-03-17 22:02:32 +00:00
covers_fullscreen = true , -- hint for UIManager:_repaint()
2014-09-03 04:09:25 +00:00
document = document ,
}
2018-03-21 19:10:35 +00:00
2023-08-30 04:53:59 +00:00
Screen : setWindowTitle ( reader.doc_props . display_title )
Device : notifyBookState ( reader.doc_props . display_title , document )
2018-03-21 19:10:35 +00:00
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
-- This is mostly for the few callers that bypass the coroutine shenanigans and call doShowReader directly,
-- instead of showReader...
-- Otherwise, showReader will have taken care of that *before* instantiating a new RD,
-- in order to ensure a sane ordering of plugins teardown -> instantiation.
2018-05-20 14:35:53 +00:00
local FileManager = require ( " apps/filemanager/filemanager " )
if FileManager.instance then
FileManager.instance : onClose ( )
end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
UIManager : show ( reader , seamless and " ui " or " full " )
2014-09-03 04:09:25 +00:00
end
2015-10-06 13:18:07 +00:00
function ReaderUI : unlockDocumentWithPassword ( document , try_again )
2016-12-29 08:10:38 +00:00
logger.dbg ( " show input password dialog " )
2015-10-06 13:18:07 +00:00
self.password_dialog = InputDialog : new {
title = try_again and _ ( " Password is incorrect, try again? " )
or _ ( " Input document password " ) ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2015-10-06 13:18:07 +00:00
callback = function ( )
self : closeDialog ( )
coroutine.resume ( self._coroutine )
end ,
} ,
{
text = _ ( " OK " ) ,
callback = function ( )
local success = self : onVerifyPassword ( document )
self : closeDialog ( )
if success then
coroutine.resume ( self._coroutine , success )
else
self : unlockDocumentWithPassword ( document , true )
end
end ,
} ,
} ,
} ,
text_type = " password " ,
}
UIManager : show ( self.password_dialog )
2018-03-30 10:46:36 +00:00
self.password_dialog : onShowKeyboard ( )
2015-10-06 13:18:07 +00:00
end
function ReaderUI : onVerifyPassword ( document )
local password = self.password_dialog : getInputText ( )
return document : unlock ( password )
end
function ReaderUI : closeDialog ( )
self.password_dialog : onClose ( )
UIManager : close ( self.password_dialog )
end
2016-12-04 06:57:57 +00:00
function ReaderUI : onScreenResize ( dimen )
2014-03-13 13:52:43 +00:00
self.dimen = dimen
2016-12-04 06:57:57 +00:00
self : updateTouchZonesOnScreenResize ( dimen )
2012-06-12 16:14:23 +00:00
end
2013-12-27 15:18:16 +00:00
function ReaderUI : saveSettings ( )
2014-03-13 13:52:43 +00:00
self : handleEvent ( Event : new ( " SaveSettings " ) )
self.doc_settings : flush ( )
2014-07-03 12:43:17 +00:00
G_reader_settings : flush ( )
2013-12-27 15:18:16 +00:00
end
2023-04-29 11:53:10 +00:00
function ReaderUI : onFlushSettings ( show_notification )
2014-06-08 09:56:52 +00:00
self : saveSettings ( )
2023-04-29 11:53:10 +00:00
if show_notification then
-- Invoked from dispatcher to explicitely flush settings
Notification : notify ( _ ( " Book metadata saved. " ) )
end
2014-06-08 09:56:52 +00:00
end
2014-08-24 07:16:01 +00:00
function ReaderUI : closeDocument ( )
self.document : close ( )
self.document = nil
end
2015-03-09 12:20:32 +00:00
function ReaderUI : notifyCloseDocument ( )
self : handleEvent ( Event : new ( " CloseDocument " ) )
2014-08-24 07:16:01 +00:00
if self.document : isEdited ( ) then
2016-07-14 17:38:00 +00:00
local setting = G_reader_settings : readSetting ( " save_document " )
if setting == " always " then
self : closeDocument ( )
elseif setting == " disable " then
self.document : discardChange ( )
self : closeDocument ( )
else
UIManager : show ( ConfirmBox : new {
2023-07-03 14:43:13 +00:00
text = _ ( " Write highlights into this PDF? " ) ,
2023-02-19 19:49:09 +00:00
ok_text = _ ( " Write " ) ,
dismissable = false ,
2016-07-14 17:38:00 +00:00
ok_callback = function ( )
self : closeDocument ( )
end ,
cancel_callback = function ( )
self.document : discardChange ( )
self : closeDocument ( )
end ,
} )
end
2014-08-24 07:16:01 +00:00
else
self : closeDocument ( )
end
end
2019-11-04 15:44:13 +00:00
function ReaderUI : onClose ( full_refresh )
2016-12-29 08:10:38 +00:00
logger.dbg ( " closing reader " )
2021-04-29 17:30:18 +00:00
PluginLoader : finalize ( )
2020-09-01 15:52:16 +00:00
Device : notifyBookState ( nil , nil )
2019-11-04 15:44:13 +00:00
if full_refresh == nil then
full_refresh = true
end
2017-08-25 16:55:44 +00:00
-- if self.dialog is us, we'll have our onFlushSettings() called
-- by UIManager:close() below, so avoid double save
if self.dialog ~= self then
self : saveSettings ( )
end
2014-03-13 13:52:43 +00:00
if self.document ~= nil then
2023-02-23 17:23:30 +00:00
require ( " readhistory " ) : updateLastBookTime ( self.tearing_down )
2022-09-14 01:49:50 +00:00
-- Serialize the most recently displayed page for later launch
DocCache : serialize ( self.document . file )
2016-12-29 08:10:38 +00:00
logger.dbg ( " closing document " )
2015-03-09 12:20:32 +00:00
self : notifyCloseDocument ( )
2014-03-13 13:52:43 +00:00
end
2019-11-04 15:44:13 +00:00
UIManager : close ( self.dialog , full_refresh and " full " )
2021-05-10 22:49:35 +00:00
end
function ReaderUI : onCloseWidget ( )
if ReaderUI.instance == self then
logger.dbg ( " Tearing down ReaderUI " , tostring ( self ) )
else
logger.warn ( " ReaderUI instance mismatch! Closed " , tostring ( self ) , " while the active one is supposed to be " , tostring ( ReaderUI.instance ) )
2016-02-17 06:36:40 +00:00
end
2021-05-10 22:49:35 +00:00
ReaderUI.instance = nil
self._coroutine = nil
2012-05-18 22:50:26 +00:00
end
2018-01-03 08:43:49 +00:00
function ReaderUI : dealWithLoadDocumentFailure ( )
-- Sadly, we had to delay loadDocument() to about now, so we only
-- know now this document is not valid or recognized.
-- We can't do much more than crash properly here (still better than
-- going on and segfaulting when calling other methods on unitiliazed
-- _document)
-- As we are in a coroutine, we can pause and show an InfoMessage before exiting
local _coroutine = coroutine.running ( )
if coroutine then
logger.warn ( " crengine failed recognizing or parsing this file: unsupported or invalid document " )
UIManager : show ( InfoMessage : new {
text = _ ( " Failed recognizing or parsing this file: unsupported or invalid document. \n KOReader will exit now. " ) ,
dismiss_callback = function ( )
coroutine.resume ( _coroutine , false )
end ,
} )
2023-07-16 17:18:10 +00:00
-- Restore input, so can catch the InfoMessage dismiss and exit
Device : setIgnoreInput ( false )
Input : inhibitInputUntil ( 0.2 )
2018-01-03 08:43:49 +00:00
coroutine.yield ( ) -- pause till InfoMessage is dismissed
end
-- We have to error and exit the coroutine anyway to avoid any segfault
error ( " crengine failed recognizing or parsing this file: unsupported or invalid document " )
end
2018-03-14 21:16:38 +00:00
function ReaderUI : onHome ( )
2024-06-30 11:29:22 +00:00
local file = self.document . file
2018-04-09 07:22:16 +00:00
self : onClose ( )
2024-06-30 11:29:22 +00:00
self : showFileManager ( file )
2018-04-09 07:22:16 +00:00
return true
2018-03-14 21:16:38 +00:00
end
2022-09-11 17:49:10 +00:00
function ReaderUI : onReload ( )
self : reloadDocument ( )
end
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
function ReaderUI : reloadDocument ( after_close_callback , seamless )
2018-05-20 23:19:36 +00:00
local file = self.document . file
local provider = getmetatable ( self.document ) . __index
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
-- Mimic onShowingReader's refresh optimizations
self.tearing_down = true
self.dithered = nil
2018-05-20 23:19:36 +00:00
self : handleEvent ( Event : new ( " CloseReaderMenu " ) )
self : handleEvent ( Event : new ( " CloseConfigMenu " ) )
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
self : handleEvent ( Event : new ( " PreserveCurrentSession " ) ) -- don't reset statistics' start_current_period
2018-07-11 16:05:30 +00:00
self.highlight : onClose ( ) -- close highlight dialog if any
2019-11-04 15:44:13 +00:00
self : onClose ( false )
2018-05-20 23:19:36 +00:00
if after_close_callback then
-- allow caller to do stuff between close an re-open
after_close_callback ( file , provider )
end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
ReaderRolling: quicker partial rerenderings with EPUBs
Only available with EPUBs containing 2 or more fragments,
and a file size large enough to ensure a cache file is used.
The idea is simply, on any rendering setting change, to
skip the rerendering of the full book and to defer any
rerendering to the moment we draw a DocFragment, and
render only it.
So, on a setting change, only the fragment containing the
current page will be rerendered, and the new fragments we
may cross while turning pages.
When having done so, KOReader is in a degraded state (the
full page count is incorrect, the ToC is invalid...).
So, a full rerendering is needed, and one will happen
in the background, and when the user is idle, we reload
seamlessly and quickly from the cache file it has made.
ReaderFlipping will show some icons in the top left
corner to let it know at which steps in this procress
we are.
2023-02-16 22:30:58 +00:00
self : showReader ( file , provider , seamless )
2018-05-20 23:19:36 +00:00
end
2024-08-04 18:02:45 +00:00
function ReaderUI : switchDocument ( new_file , seamless )
2019-02-22 15:29:19 +00:00
if not new_file then return end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
-- Mimic onShowingReader's refresh optimizations
self.tearing_down = true
self.dithered = nil
2018-07-07 11:03:33 +00:00
self : handleEvent ( Event : new ( " CloseReaderMenu " ) )
self : handleEvent ( Event : new ( " CloseConfigMenu " ) )
2018-07-11 16:05:30 +00:00
self.highlight : onClose ( ) -- close highlight dialog if any
2019-11-04 15:44:13 +00:00
self : onClose ( false )
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
2021-05-01 16:53:04 +00:00
2024-08-04 18:02:45 +00:00
self : showReader ( new_file , nil , seamless )
2018-07-07 11:03:33 +00:00
end
2020-07-12 18:47:49 +00:00
function ReaderUI : onOpenLastDoc ( )
self : switchDocument ( self.menu : getPreviousFile ( ) )
end
2020-03-16 15:52:09 +00:00
function ReaderUI : getCurrentPage ( )
2023-07-03 14:43:13 +00:00
return self.paging and self.paging . current_page or self.document : getCurrentPage ( )
2020-03-16 15:52:09 +00:00
end
2013-10-18 20:38:07 +00:00
return ReaderUI