|
|
|
local BD = require("ui/bidi")
|
|
|
|
local Device = require("device")
|
|
|
|
local Event = require("ui/event")
|
|
|
|
local Geom = require("ui/geometry")
|
|
|
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
|
|
|
local Math = require("optmath")
|
|
|
|
local UIManager = require("ui/uimanager")
|
|
|
|
local bit = require("bit")
|
|
|
|
local logger = require("logger")
|
|
|
|
local util = require("util")
|
|
|
|
local time = require("ui/time")
|
|
|
|
local _ = require("gettext")
|
|
|
|
local Input = Device.input
|
|
|
|
local Screen = Device.screen
|
|
|
|
|
|
|
|
local function copyPageState(page_state)
|
|
|
|
return {
|
|
|
|
page = page_state.page,
|
|
|
|
zoom = page_state.zoom,
|
|
|
|
rotation = page_state.rotation,
|
|
|
|
gamma = page_state.gamma,
|
|
|
|
offset = page_state.offset:copy(),
|
|
|
|
visible_area = page_state.visible_area:copy(),
|
|
|
|
page_area = page_state.page_area:copy(),
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
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.
2 years ago
|
|
|
local ReaderPaging = InputContainer:extend{
|
|
|
|
pan_rate = 30, -- default 30 ops, will be adjusted in readerui
|
|
|
|
current_page = 0,
|
|
|
|
number_of_pages = 0,
|
|
|
|
visible_area = nil,
|
|
|
|
page_area = nil,
|
|
|
|
overlap = Screen:scaleBySize(G_defaults:readSetting("DOVERLAPPIXELS")),
|
|
|
|
|
|
|
|
page_flipping_mode = false,
|
|
|
|
bookmark_flipping_mode = false,
|
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.
2 years ago
|
|
|
flip_steps = {0, 1, 2, 5, 10, 20, 50, 100},
|
|
|
|
}
|
|
|
|
|
|
|
|
function ReaderPaging:init()
|
|
|
|
self:registerKeyEvents()
|
|
|
|
self.pan_interval = time.s(1 / self.pan_rate)
|
|
|
|
self.number_of_pages = self.ui.document.info.number_of_pages
|
|
|
|
|
|
|
|
-- delegate gesture listener to readerui, NOP our own
|
|
|
|
self.ges_events = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGesture() end
|
|
|
|
|
|
|
|
function ReaderPaging:registerKeyEvents()
|
|
|
|
if Device:hasFiveWay() then
|
|
|
|
-- this targets all devices (kindles) non-touch with dPads (i.e dx, kk, k4 and others)
|
|
|
|
self.key_events.GotoNextPos = {
|
|
|
|
{ { "RPgFwd", "LPgFwd" } },
|
|
|
|
event = "GotoPosRel",
|
|
|
|
args = 1,
|
|
|
|
}
|
|
|
|
self.key_events.GotoPrevPos = {
|
|
|
|
{ { "RPgBack", "LPgBack" } },
|
|
|
|
event = "GotoPosRel",
|
|
|
|
args = -1,
|
|
|
|
}
|
|
|
|
self.key_events.GotoNextChapter = {
|
|
|
|
{ "Right" },
|
|
|
|
event = "GotoNextChapter",
|
|
|
|
args = 1,
|
|
|
|
}
|
|
|
|
self.key_events.GotoPrevChapter = {
|
|
|
|
{ "Left" },
|
|
|
|
event = "GotoPrevChapter",
|
|
|
|
args = -1,
|
|
|
|
}
|
|
|
|
elseif Device:hasKeys() then
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
self.key_events.GotoNextPage = {
|
|
|
|
{ { "RPgFwd", "LPgFwd", not Device:hasFewKeys() and "Right" } },
|
|
|
|
event = "GotoViewRel",
|
|
|
|
args = 1,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.GotoPrevPage = {
|
|
|
|
{ { "RPgBack", "LPgBack", not Device:hasFewKeys() and "Left" } },
|
|
|
|
event = "GotoViewRel",
|
|
|
|
args = -1,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.GotoNextPos = {
|
|
|
|
{ "Down" },
|
|
|
|
event = "GotoPosRel",
|
|
|
|
args = 1,
|
|
|
|
}
|
|
|
|
self.key_events.GotoPrevPos = {
|
|
|
|
{ "Up" },
|
|
|
|
event = "GotoPosRel",
|
|
|
|
args = -1,
|
|
|
|
}
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
end
|
|
|
|
if Device:hasKeyboard() then
|
|
|
|
self.key_events.GotoFirst = {
|
|
|
|
{ "1" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 0,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto11 = {
|
|
|
|
{ "2" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 11,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto22 = {
|
|
|
|
{ "3" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 22,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto33 = {
|
|
|
|
{ "4" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 33,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto44 = {
|
|
|
|
{ "5" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 44,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto55 = {
|
|
|
|
{ "6" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 55,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto66 = {
|
|
|
|
{ "7" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 66,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto77 = {
|
|
|
|
{ "8" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 77,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.Goto88 = {
|
|
|
|
{ "9" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 88,
|
A few graphics fixes after #4541 (#4554)
* Various FocusManager related tweaks to limit its usage to devices with a DPad, and prevent initial button highlights in Dialogs on devices where it makes no sense (i.e., those without a DPad. And even on DPad devices, I'm not even sure how we'd go about making one of those pop up anyway, because no Touch ;)!).
* One mysterious fix to text-only Buttons so that the flash_ui highlight always works, and always honors `FrameContainer`'s pill shape. (Before that, an unhighlight on a text button with a callback that didn't repaint anything [say, the find first/find last buttons in the Reader's search bar when you're already on the first/last match] would do a square black highlight, and a white pill-shaped unhighlight (leaving the black corners visible)).
The workaround makes *absolutely* no sense to me (as `self[1] -> self.frame`, AFAICT), but it works, and ensures all highlights/unhighlights are pill-shaped, so at least we're not doing maths for rounded corners for nothing ;).
6 years ago
|
|
|
}
|
|
|
|
self.key_events.GotoLast = {
|
|
|
|
{ "0" },
|
|
|
|
event = "GotoPercent",
|
|
|
|
args = 100,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
ReaderPaging.onPhysicalKeyboardConnected = ReaderPaging.registerKeyEvents
|
|
|
|
|
|
|
|
function ReaderPaging:onReaderReady()
|
|
|
|
self:setupTouchZones()
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:setupTouchZones()
|
|
|
|
if not Device:isTouchDevice() then return end
|
|
|
|
|
|
|
|
local forward_zone, backward_zone = self.view:getTapZones()
|
|
|
|
|
|
|
|
self.ui:registerTouchZones({
|
|
|
|
{
|
|
|
|
id = "tap_forward",
|
|
|
|
ges = "tap",
|
|
|
|
screen_zone = forward_zone,
|
|
|
|
handler = function()
|
|
|
|
if G_reader_settings:nilOrFalse("page_turns_disable_tap") then
|
|
|
|
return self:onGotoViewRel(1)
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id = "tap_backward",
|
|
|
|
ges = "tap",
|
|
|
|
screen_zone = backward_zone,
|
|
|
|
handler = function()
|
|
|
|
if G_reader_settings:nilOrFalse("page_turns_disable_tap") then
|
|
|
|
return self:onGotoViewRel(-1)
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id = "paging_swipe",
|
|
|
|
ges = "swipe",
|
|
|
|
screen_zone = {
|
|
|
|
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
|
|
|
|
},
|
|
|
|
handler = function(ges) return self:onSwipe(nil, ges) end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id = "paging_pan",
|
|
|
|
ges = "pan",
|
|
|
|
screen_zone = {
|
|
|
|
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
|
|
|
|
},
|
|
|
|
handler = function(ges) return self:onPan(nil, ges) end,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id = "paging_pan_release",
|
|
|
|
ges = "pan_release",
|
|
|
|
screen_zone = {
|
|
|
|
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
|
|
|
|
},
|
|
|
|
handler = function(ges) return self:onPanRelease(nil, ges) end,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onReadSettings(config)
|
|
|
|
self.page_positions = config:readSetting("page_positions") or {}
|
|
|
|
self:_gotoPage(config:readSetting("last_page") or 1)
|
|
|
|
self.flipping_zoom_mode = config:readSetting("flipping_zoom_mode") or "page"
|
|
|
|
self.flipping_scroll_mode = config:isTrue("flipping_scroll_mode")
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onSaveSettings()
|
|
|
|
--- @todo only save current_page page position
|
|
|
|
self.ui.doc_settings:saveSetting("page_positions", self.page_positions)
|
|
|
|
self.ui.doc_settings:saveSetting("last_page", self:getTopPage())
|
|
|
|
self.ui.doc_settings:saveSetting("percent_finished", self:getLastPercent())
|
|
|
|
self.ui.doc_settings:saveSetting("flipping_zoom_mode", self.flipping_zoom_mode)
|
|
|
|
self.ui.doc_settings:saveSetting("flipping_scroll_mode", self.flipping_scroll_mode)
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:getLastProgress()
|
|
|
|
return self:getTopPage()
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:getLastPercent()
|
|
|
|
if self.current_page > 0 and self.number_of_pages > 0 then
|
|
|
|
return self.current_page/self.number_of_pages
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onColorRenderingUpdate()
|
|
|
|
self.ui.document:updateColorRendering()
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
Set reading position on certain page
|
|
|
|
Page position is a fractional number ranging from 0 to 1, indicating the read
|
|
|
|
percentage on certain page. With the position information on each page whenever
|
|
|
|
users change font size, page margin or line spacing or close and reopen the
|
|
|
|
book, the page view will be roughly the same.
|
|
|
|
--]]
|
|
|
|
function ReaderPaging:setPagePosition(page, pos)
|
|
|
|
logger.dbg("set page position", pos)
|
|
|
|
self.page_positions[page] = pos ~= 0 and pos or nil
|
|
|
|
self.ui:handleEvent(Event:new("PagePositionUpdated"))
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
Get reading position on certain page
|
|
|
|
--]]
|
|
|
|
function ReaderPaging:getPagePosition(page)
|
|
|
|
-- Page number ought to be integer, somehow I notice that with
|
|
|
|
-- fractional page number the reader runs silently well, but the
|
|
|
|
-- number won't fit to retrieve page position.
|
|
|
|
page = math.floor(page)
|
|
|
|
logger.dbg("get page position", self.page_positions[page])
|
|
|
|
return self.page_positions[page] or 0
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onTogglePageFlipping()
|
|
|
|
if self.bookmark_flipping_mode then
|
|
|
|
-- do nothing if we're in bookmark flipping mode
|
|
|
|
return
|
|
|
|
end
|
|
|
|
self.view.flipping_visible = not self.view.flipping_visible
|
|
|
|
self.page_flipping_mode = self.view.flipping_visible
|
|
|
|
self.flipping_page = self.current_page
|
|
|
|
|
|
|
|
if self.page_flipping_mode then
|
|
|
|
self:updateOriginalPage(self.current_page)
|
|
|
|
self:enterFlippingMode()
|
|
|
|
else
|
|
|
|
self:updateOriginalPage(nil)
|
|
|
|
self:exitFlippingMode()
|
|
|
|
end
|
|
|
|
self.ui:handleEvent(Event:new("SetHinting", not self.page_flipping_mode))
|
|
|
|
self.ui:handleEvent(Event:new("ReZoom"))
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onToggleBookmarkFlipping()
|
|
|
|
self.bookmark_flipping_mode = not self.bookmark_flipping_mode
|
|
|
|
|
|
|
|
if self.bookmark_flipping_mode then
|
|
|
|
self.orig_flipping_mode = self.view.flipping_visible
|
|
|
|
self.view.flipping_visible = true
|
|
|
|
self.bm_flipping_orig_page = self.current_page
|
|
|
|
self:enterFlippingMode()
|
|
|
|
else
|
|
|
|
self.view.flipping_visible = self.orig_flipping_mode
|
|
|
|
self:exitFlippingMode()
|
|
|
|
self:_gotoPage(self.bm_flipping_orig_page)
|
|
|
|
end
|
|
|
|
self.ui:handleEvent(Event:new("SetHinting", not self.bookmark_flipping_mode))
|
|
|
|
self.ui:handleEvent(Event:new("ReZoom"))
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:enterFlippingMode()
|
|
|
|
self.orig_reflow_mode = self.view.document.configurable.text_wrap
|
|
|
|
self.orig_scroll_mode = self.view.page_scroll
|
|
|
|
self.orig_zoom_mode = self.view.zoom_mode
|
|
|
|
logger.dbg("store zoom mode", self.orig_zoom_mode)
|
|
|
|
self.view.document.configurable.text_wrap = 0
|
|
|
|
self.view.page_scroll = self.flipping_scroll_mode
|
|
|
|
Input.disable_double_tap = false
|
|
|
|
self.ui:handleEvent(Event:new("EnterFlippingMode", self.flipping_zoom_mode))
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:exitFlippingMode()
|
|
|
|
Input.disable_double_tap = true
|
|
|
|
self.view.document.configurable.text_wrap = self.orig_reflow_mode
|
|
|
|
self.view.page_scroll = self.orig_scroll_mode
|
|
|
|
self.flipping_zoom_mode = self.view.zoom_mode
|
|
|
|
self.flipping_scroll_mode = self.view.page_scroll
|
|
|
|
logger.dbg("restore zoom mode", self.orig_zoom_mode)
|
|
|
|
self.ui:handleEvent(Event:new("ExitFlippingMode", self.orig_zoom_mode))
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:updateOriginalPage(page)
|
|
|
|
self.original_page = page
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:updateFlippingPage(page)
|
|
|
|
self.flipping_page = page
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:pageFlipping(flipping_page, flipping_ges)
|
|
|
|
local whole = self.number_of_pages
|
|
|
|
local steps = #self.flip_steps
|
|
|
|
local stp_proportion = flipping_ges.distance / Screen:getWidth()
|
|
|
|
local abs_proportion = flipping_ges.distance / Screen:getHeight()
|
|
|
|
local direction = BD.flipDirectionIfMirroredUILayout(flipping_ges.direction)
|
|
|
|
if direction == "east" then
|
|
|
|
self:_gotoPage(flipping_page - self.flip_steps[math.ceil(steps*stp_proportion)])
|
|
|
|
elseif direction == "west" then
|
|
|
|
self:_gotoPage(flipping_page + self.flip_steps[math.ceil(steps*stp_proportion)])
|
|
|
|
elseif direction == "south" then
|
|
|
|
self:_gotoPage(flipping_page - math.floor(whole*abs_proportion))
|
|
|
|
elseif direction == "north" then
|
|
|
|
self:_gotoPage(flipping_page + math.floor(whole*abs_proportion))
|
|
|
|
end
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges)
|
|
|
|
local direction = BD.flipDirectionIfMirroredUILayout(flipping_ges.direction)
|
|
|
|
if direction == "east" then
|
|
|
|
self.ui:handleEvent(Event:new("GotoPreviousBookmark", flipping_page))
|
|
|
|
elseif direction == "west" then
|
|
|
|
self.ui:handleEvent(Event:new("GotoNextBookmark", flipping_page))
|
|
|
|
end
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onScrollSettingsUpdated(scroll_method, inertial_scroll_enabled, scroll_activation_delay_ms)
|
|
|
|
self.scroll_method = scroll_method
|
|
|
|
self.scroll_activation_delay = time.ms(scroll_activation_delay_ms)
|
|
|
|
if inertial_scroll_enabled then
|
|
|
|
self.ui.scrolling:setInertialScrollCallbacks(
|
|
|
|
function(distance) -- do_scroll_callback
|
|
|
|
if not self.ui.document then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
UIManager.currently_scrolling = true
|
|
|
|
local top_page, top_position = self:getTopPage(), self:getTopPosition()
|
|
|
|
self:onPanningRel(distance)
|
|
|
|
return not (top_page == self:getTopPage() and top_position == self:getTopPosition())
|
|
|
|
end,
|
|
|
|
function() -- scroll_done_callback
|
|
|
|
UIManager.currently_scrolling = false
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
end
|
|
|
|
)
|
|
|
|
else
|
|
|
|
self.ui.scrolling:setInertialScrollCallbacks(nil, nil)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onSwipe(_, ges)
|
|
|
|
if self._pan_has_scrolled then
|
|
|
|
-- We did some panning but released after a short amount of time,
|
|
|
|
-- so this gesture ended up being a Swipe - and this swipe was
|
|
|
|
-- not handled by the other modules (so, not opening the menus).
|
|
|
|
-- Do as :onPanRelease() and ignore this swipe.
|
|
|
|
self:onPanRelease() -- no arg, so we know there we come from here
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
self._pan_started = false
|
|
|
|
UIManager.currently_scrolling = false
|
|
|
|
self._pan_page_states_to_restore = nil
|
|
|
|
end
|
|
|
|
local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
|
|
|
|
if self.bookmark_flipping_mode then
|
|
|
|
self:bookmarkFlipping(self.current_page, ges)
|
|
|
|
return true
|
|
|
|
elseif self.page_flipping_mode and self.original_page then
|
|
|
|
self:_gotoPage(self.original_page)
|
|
|
|
return true
|
|
|
|
elseif direction == "west" then
|
|
|
|
if G_reader_settings:nilOrFalse("page_turns_disable_swipe") then
|
|
|
|
if self.view.inverse_reading_order then
|
|
|
|
self:onGotoViewRel(-1)
|
|
|
|
else
|
|
|
|
self:onGotoViewRel(1)
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
elseif direction == "east" then
|
|
|
|
if G_reader_settings:nilOrFalse("page_turns_disable_swipe") then
|
|
|
|
if self.view.inverse_reading_order then
|
|
|
|
self:onGotoViewRel(1)
|
|
|
|
else
|
|
|
|
self:onGotoViewRel(-1)
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onPan(_, ges)
|
|
|
|
if self.bookmark_flipping_mode then
|
|
|
|
return true
|
|
|
|
elseif self.page_flipping_mode then
|
|
|
|
if self.view.zoom_mode == "page" then
|
|
|
|
self:pageFlipping(self.flipping_page, ges)
|
|
|
|
else
|
|
|
|
self.view:PanningStart(-ges.relative.x, -ges.relative.y)
|
|
|
|
end
|
|
|
|
elseif ges.direction == "north" or ges.direction == "south" then
|
|
|
|
if ges.mousewheel_direction and not self.view.page_scroll then
|
|
|
|
-- Mouse wheel generates a Pan event: in page mode, move one
|
|
|
|
-- page per event. Scroll mode is handled in the 'else' branch
|
|
|
|
-- and use the wheeled distance.
|
|
|
|
self:onGotoViewRel(-1 * ges.mousewheel_direction)
|
|
|
|
elseif self.view.page_scroll then
|
|
|
|
if not self._pan_started then
|
|
|
|
self._pan_started = true
|
|
|
|
-- Re-init state variables
|
|
|
|
self._pan_has_scrolled = false
|
|
|
|
self._pan_prev_relative_y = 0
|
|
|
|
self._pan_to_scroll_later = 0
|
|
|
|
self._pan_real_last_time = 0
|
|
|
|
if ges.mousewheel_direction then
|
|
|
|
self._pan_activation_time = false
|
|
|
|
else
|
|
|
|
self._pan_activation_time = ges.time + self.scroll_activation_delay
|
|
|
|
end
|
|
|
|
-- We will restore the previous position if this pan
|
|
|
|
-- ends up being a swipe or a multiswipe
|
|
|
|
-- Somehow, accumulating the distances scrolled in a self._pan_dist_to_restore
|
|
|
|
-- so we can scroll these back may not always put us back to the original
|
|
|
|
-- position (possibly because of these page_states?). It's safer
|
|
|
|
-- to remember the original page_states and restore that. We can keep
|
|
|
|
-- a reference to the original table as onPanningRel() will have this
|
|
|
|
-- table replaced.
|
|
|
|
self._pan_page_states_to_restore = self.view.page_states
|
|
|
|
end
|
|
|
|
local scroll_now = false
|
|
|
|
if self._pan_activation_time and ges.time >= self._pan_activation_time then
|
|
|
|
self._pan_activation_time = false -- We can go on, no need to check again
|
|
|
|
end
|
|
|
|
if not self._pan_activation_time and ges.time - self._pan_real_last_time >= self.pan_interval then
|
|
|
|
scroll_now = true
|
|
|
|
self._pan_real_last_time = ges.time
|
|
|
|
end
|
|
|
|
local scroll_dist = 0
|
|
|
|
if self.scroll_method == self.ui.scrolling.SCROLL_METHOD_CLASSIC then
|
|
|
|
-- Scroll by the distance the finger moved since last pan event,
|
|
|
|
-- having the document follows the finger
|
|
|
|
scroll_dist = self._pan_prev_relative_y - ges.relative.y
|
|
|
|
self._pan_prev_relative_y = ges.relative.y
|
|
|
|
if not self._pan_has_scrolled then
|
|
|
|
-- Avoid checking this for each pan, no need once we have scrolled
|
|
|
|
if self.ui.scrolling:cancelInertialScroll() or self.ui.scrolling:cancelledByTouch() then
|
|
|
|
-- If this pan or its initial touch did cancel some inertial scrolling,
|
|
|
|
-- ignore activation delay to allow continuous scrolling
|
|
|
|
self._pan_activation_time = false
|
|
|
|
scroll_now = true
|
|
|
|
self._pan_real_last_time = ges.time
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self.ui.scrolling:accountManualScroll(scroll_dist, ges.time)
|
|
|
|
elseif self.scroll_method == self.ui.scrolling.SCROLL_METHOD_TURBO then
|
|
|
|
-- Legacy scrolling "buggy" behaviour, that can actually be nice
|
|
|
|
-- Scroll by the distance from the initial finger position, this distance
|
|
|
|
-- controlling the speed of the scrolling)
|
|
|
|
if scroll_now then
|
|
|
|
scroll_dist = -ges.relative.y
|
|
|
|
end
|
|
|
|
-- We don't accumulate in _pan_to_scroll_later
|
|
|
|
elseif self.scroll_method == self.ui.scrolling.SCROLL_METHOD_ON_RELEASE then
|
|
|
|
self._pan_to_scroll_later = -ges.relative.y
|
|
|
|
if scroll_now then
|
|
|
|
self._pan_has_scrolled = true -- so we really apply it later
|
|
|
|
end
|
|
|
|
scroll_dist = 0
|
|
|
|
scroll_now = false
|
|
|
|
end
|
|
|
|
if scroll_now then
|
|
|
|
local dist = self._pan_to_scroll_later + scroll_dist
|
|
|
|
self._pan_to_scroll_later = 0
|
|
|
|
if dist ~= 0 then
|
|
|
|
self._pan_has_scrolled = true
|
|
|
|
UIManager.currently_scrolling = true
|
|
|
|
self:onPanningRel(dist)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
self._pan_to_scroll_later = self._pan_to_scroll_later + scroll_dist
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onPanRelease(_, ges)
|
|
|
|
if self.page_flipping_mode then
|
|
|
|
if self.view.zoom_mode == "page" then
|
|
|
|
self:updateFlippingPage(self.current_page)
|
|
|
|
else
|
|
|
|
self.view:PanningStop()
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if self._pan_has_scrolled and self._pan_to_scroll_later ~= 0 then
|
|
|
|
self:onPanningRel(self._pan_to_scroll_later)
|
|
|
|
end
|
|
|
|
self._pan_started = false
|
|
|
|
self._pan_page_states_to_restore = nil
|
|
|
|
UIManager.currently_scrolling = false
|
|
|
|
if self._pan_has_scrolled then
|
|
|
|
self._pan_has_scrolled = false
|
|
|
|
-- Don't do any inertial scrolling if pan events come from
|
|
|
|
-- a mousewheel (which may have itself some inertia)
|
|
|
|
if (ges and ges.from_mousewheel) or not self.ui.scrolling:startInertialScroll() then
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onHandledAsSwipe()
|
|
|
|
if self._pan_started then
|
|
|
|
-- Restore original position as this pan we've started handling
|
|
|
|
-- has ended up being a multiswipe or handled as a swipe to open
|
|
|
|
-- top or bottom menus
|
|
|
|
if self._pan_has_scrolled then
|
|
|
|
self.view.page_states = self._pan_page_states_to_restore
|
|
|
|
self:_gotoPage(self.view.page_states[#self.view.page_states].page, "scrolling")
|
|
|
|
UIManager:setDirty(self.view.dialog, "ui")
|
|
|
|
end
|
|
|
|
self._pan_page_states_to_restore = nil
|
|
|
|
self._pan_started = false
|
|
|
|
self._pan_has_scrolled = false
|
|
|
|
UIManager.currently_scrolling = false
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
function ReaderPaging:onZoomModeUpdate(new_mode)
|
|
|
|
-- we need to remember zoom mode to handle page turn event
|
|
|
|
self.zoom_mode = new_mode
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onPageUpdate(new_page_no, orig_mode)
|
|
|
|
self.current_page = new_page_no
|
|
|
|
if self.view.page_scroll and orig_mode ~= "scrolling" then
|
|
|
|
self.ui:handleEvent(Event:new("InitScrollPageStates", orig_mode))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onViewRecalculate(visible_area, page_area)
|
|
|
|
-- we need to remember areas to handle page turn event
|
|
|
|
self.visible_area = visible_area:copy()
|
|
|
|
self.page_area = page_area
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoPercent(percent)
|
|
|
|
logger.dbg("goto document offset in percent:", percent)
|
|
|
|
local dest = math.floor(self.number_of_pages * percent * (1/100))
|
|
|
|
if dest < 1 then dest = 1 end
|
|
|
|
if dest > self.number_of_pages then
|
|
|
|
dest = self.number_of_pages
|
|
|
|
end
|
|
|
|
self:_gotoPage(dest)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoViewRel(diff)
|
|
|
|
if self.view.page_scroll then
|
|
|
|
self:onScrollPageRel(diff)
|
|
|
|
else
|
|
|
|
self:onGotoPageRel(diff)
|
|
|
|
end
|
|
|
|
self:setPagePosition(self:getTopPage(), self:getTopPosition())
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoPosRel(diff)
|
|
|
|
if self.view.page_scroll then
|
|
|
|
self:onPanningRel(100*diff)
|
|
|
|
else
|
|
|
|
self:onGotoPageRel(diff)
|
|
|
|
end
|
|
|
|
self:setPagePosition(self:getTopPage(), self:getTopPosition())
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onPanningRel(diff)
|
|
|
|
if self.view.page_scroll then
|
|
|
|
self:onScrollPanRel(diff)
|
|
|
|
end
|
|
|
|
self:setPagePosition(self:getTopPage(), self:getTopPosition())
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Used by ReaderBack & ReaderLink.
|
|
|
|
function ReaderPaging:getBookLocation()
|
|
|
|
local ctx = self.view:getViewContext()
|
|
|
|
if ctx then
|
|
|
|
-- We need a copy, as we're getting references to
|
|
|
|
-- objects ReaderPaging/ReaderView may still modify
|
|
|
|
local current_location = {}
|
|
|
|
for i=1, #ctx do
|
|
|
|
current_location[i] = util.tableDeepCopy(ctx[i])
|
|
|
|
end
|
|
|
|
return current_location
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onRestoreBookLocation(saved_location)
|
|
|
|
if self.view.page_scroll then
|
|
|
|
if self.view:restoreViewContext(saved_location) then
|
|
|
|
self:_gotoPage(saved_location[1].page, "scrolling")
|
|
|
|
else
|
|
|
|
-- If context is unusable (not from scroll mode), trigger
|
|
|
|
-- this to go at least to its page and redraw it
|
|
|
|
self.ui:handleEvent(Event:new("PageUpdate", saved_location[1].page))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- gotoPage may emit PageUpdate event, which will trigger recalculate
|
|
|
|
-- in ReaderView and resets the view context. So we need to call
|
|
|
|
-- restoreViewContext after gotoPage.
|
|
|
|
-- But if we're restoring to the same page, it will not emit
|
|
|
|
-- PageUpdate event - so we need to do it for a correct redrawing
|
|
|
|
local send_PageUpdate = saved_location[1].page == self.current_page
|
|
|
|
self:_gotoPage(saved_location[1].page)
|
|
|
|
if not self.view:restoreViewContext(saved_location) then
|
|
|
|
-- If context is unusable (not from page mode), also
|
|
|
|
-- send PageUpdate event to go to its page and redraw it
|
|
|
|
send_PageUpdate = true
|
|
|
|
end
|
|
|
|
if send_PageUpdate then
|
|
|
|
self.ui:handleEvent(Event:new("PageUpdate", saved_location[1].page))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self:setPagePosition(self:getTopPage(), self:getTopPosition())
|
|
|
|
-- In some cases (same page, different offset), doing the above
|
|
|
|
-- might not redraw the screen. Ensure it is.
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
Get read percentage on current page.
|
|
|
|
--]]
|
|
|
|
function ReaderPaging:getTopPosition()
|
|
|
|
if self.view.page_scroll then
|
|
|
|
local state = self.view.page_states[1]
|
|
|
|
return (state.visible_area.y - state.page_area.y)/state.page_area.h
|
|
|
|
else
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
Get page number of the page drawn at the very top part of the screen.
|
|
|
|
--]]
|
|
|
|
function ReaderPaging:getTopPage()
|
|
|
|
if self.view.page_scroll then
|
|
|
|
local state = self.view.page_states[1]
|
|
|
|
return state and state.page or self.current_page
|
|
|
|
else
|
|
|
|
return self.current_page
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onInitScrollPageStates(orig_mode)
|
|
|
|
logger.dbg("init scroll page states", orig_mode)
|
|
|
|
if self.view.page_scroll and self.view.state.page then
|
|
|
|
self.orig_page = self.current_page
|
|
|
|
self.view.page_states = {}
|
|
|
|
local blank_area = Geom:new()
|
|
|
|
blank_area:setSizeTo(self.view.visible_area)
|
|
|
|
while blank_area.h > 0 do
|
|
|
|
local offset = Geom:new()
|
|
|
|
-- caculate position in current page
|
|
|
|
if self.current_page == self.orig_page then
|
|
|
|
local page_area = self.view:getPageArea(
|
|
|
|
self.view.state.page,
|
|
|
|
self.view.state.zoom,
|
|
|
|
self.view.state.rotation)
|
|
|
|
offset.y = page_area.h * self:getPagePosition(self.current_page)
|
|
|
|
end
|
|
|
|
local state = self:getNextPageState(blank_area, offset)
|
|
|
|
table.insert(self.view.page_states, state)
|
|
|
|
if blank_area.h > 0 then
|
|
|
|
blank_area.h = blank_area.h - self.view.page_gap.height
|
|
|
|
end
|
|
|
|
if blank_area.h > 0 then
|
|
|
|
local next_page = self.ui.document:getNextPage(self.current_page)
|
|
|
|
if next_page == 0 then break end -- end of document reached
|
|
|
|
self:_gotoPage(next_page, "scrolling")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self:_gotoPage(self.orig_page, "scrolling")
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onUpdateScrollPageRotation(rotation)
|
|
|
|
for _, state in ipairs(self.view.page_states) do
|
|
|
|
state.rotation = rotation
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onUpdateScrollPageGamma(gamma)
|
|
|
|
for _, state in ipairs(self.view.page_states) do
|
|
|
|
state.gamma = gamma
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:getNextPageState(blank_area, image_offset)
|
|
|
|
local page_area = self.view:getPageArea(
|
|
|
|
self.view.state.page,
|
|
|
|
self.view.state.zoom,
|
|
|
|
self.view.state.rotation)
|
|
|
|
local visible_area = Geom:new{x = 0, y = 0}
|
|
|
|
visible_area.w, visible_area.h = blank_area.w, blank_area.h
|
|
|
|
visible_area.x, visible_area.y = page_area.x, page_area.y
|
|
|
|
visible_area = visible_area:shrinkInside(page_area, image_offset.x, image_offset.y)
|
|
|
|
-- shrink blank area by the height of visible area
|
|
|
|
blank_area.h = blank_area.h - visible_area.h
|
|
|
|
local page_offset = Geom:new{x = self.view.state.offset.x, y = 0}
|
|
|
|
if blank_area.w > page_area.w then
|
|
|
|
page_offset:offsetBy((blank_area.w - page_area.w) / 2, 0)
|
|
|
|
end
|
|
|
|
return {
|
|
|
|
page = self.view.state.page,
|
|
|
|
zoom = self.view.state.zoom,
|
|
|
|
rotation = self.view.state.rotation,
|
|
|
|
gamma = self.view.state.gamma,
|
|
|
|
offset = page_offset,
|
|
|
|
visible_area = visible_area,
|
|
|
|
page_area = page_area,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:getPrevPageState(blank_area, image_offset)
|
|
|
|
local page_area = self.view:getPageArea(
|
|
|
|
self.view.state.page,
|
|
|
|
self.view.state.zoom,
|
|
|
|
self.view.state.rotation)
|
|
|
|
local visible_area = Geom:new{x = 0, y = 0}
|
|
|
|
visible_area.w, visible_area.h = blank_area.w, blank_area.h
|
|
|
|
visible_area.x = page_area.x
|
|
|
|
visible_area.y = page_area.y + page_area.h - visible_area.h
|
|
|
|
visible_area = visible_area:shrinkInside(page_area, image_offset.x, image_offset.y)
|
|
|
|
-- shrink blank area by the height of visible area
|
|
|
|
blank_area.h = blank_area.h - visible_area.h
|
|
|
|
local page_offset = Geom:new{x = self.view.state.offset.x, y = 0}
|
|
|
|
if blank_area.w > page_area.w then
|
|
|
|
page_offset:offsetBy((blank_area.w - page_area.w) / 2, 0)
|
|
|
|
end
|
|
|
|
return {
|
|
|
|
page = self.view.state.page,
|
|
|
|
zoom = self.view.state.zoom,
|
|
|
|
rotation = self.view.state.rotation,
|
|
|
|
gamma = self.view.state.gamma,
|
|
|
|
offset = page_offset,
|
|
|
|
visible_area = visible_area,
|
|
|
|
page_area = page_area,
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:updateTopPageState(state, blank_area, offset)
|
|
|
|
local visible_area = Geom:new{
|
|
|
|
x = state.visible_area.x,
|
|
|
|
y = state.visible_area.y,
|
|
|
|
w = blank_area.w,
|
|
|
|
h = blank_area.h,
|
|
|
|
}
|
|
|
|
if self.ui.document:getNextPage(state.page) == 0 then -- last page
|
|
|
|
visible_area:offsetWithin(state.page_area, offset.x, offset.y)
|
|
|
|
else
|
|
|
|
visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y)
|
|
|
|
end
|
|
|
|
-- shrink blank area by the height of visible area
|
|
|
|
blank_area.h = blank_area.h - visible_area.h
|
|
|
|
state.visible_area = visible_area
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:updateBottomPageState(state, blank_area, offset)
|
|
|
|
local visible_area = Geom:new{
|
|
|
|
x = state.page_area.x,
|
|
|
|
y = state.visible_area.y + state.visible_area.h - blank_area.h,
|
|
|
|
w = blank_area.w,
|
|
|
|
h = blank_area.h,
|
|
|
|
}
|
|
|
|
if self.ui.document:getPrevPage(state.page) == 0 then -- first page
|
|
|
|
visible_area:offsetWithin(state.page_area, offset.x, offset.y)
|
|
|
|
else
|
|
|
|
visible_area = visible_area:shrinkInside(state.page_area, offset.x, offset.y)
|
|
|
|
end
|
|
|
|
-- shrink blank area by the height of visible area
|
|
|
|
blank_area.h = blank_area.h - visible_area.h
|
|
|
|
state.visible_area = visible_area
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:genPageStatesFromTop(top_page_state, blank_area, offset)
|
|
|
|
-- Offset should always be greater than 0
|
|
|
|
-- otherwise if offset is less than 0 the height of blank area will be
|
|
|
|
-- larger than 0 even if page area is much larger than visible area,
|
|
|
|
-- which will trigger the drawing of next page leaving part of current
|
|
|
|
-- page undrawn. This should also be true for generating from bottom.
|
|
|
|
if offset.y < 0 then offset.y = 0 end
|
|
|
|
self:updateTopPageState(top_page_state, blank_area, offset)
|
|
|
|
local page_states = {}
|
|
|
|
if top_page_state.visible_area.h > 0 then
|
|
|
|
-- offset does not scroll pass top_page_state
|
|
|
|
table.insert(page_states, top_page_state)
|
|
|
|
end
|
|
|
|
local state
|
|
|
|
local current_page = top_page_state.page
|
|
|
|
while blank_area.h > 0 do
|
|
|
|
blank_area.h = blank_area.h - self.view.page_gap.height
|
|
|
|
if blank_area.h > 0 then
|
|
|
|
current_page = self.ui.document:getNextPage(current_page)
|
|
|
|
if current_page == 0 then break end -- end of document reached
|
|
|
|
self:_gotoPage(current_page, "scrolling")
|
|
|
|
state = self:getNextPageState(blank_area, Geom:new())
|
|
|
|
table.insert(page_states, state)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return page_states
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:genPageStatesFromBottom(bottom_page_state, blank_area, offset)
|
|
|
|
-- scroll up offset should always be less than 0
|
|
|
|
if offset.y > 0 then offset.y = 0 end
|
|
|
|
-- find out number of pages need to be removed from current view
|
|
|
|
self:updateBottomPageState(bottom_page_state, blank_area, offset)
|
|
|
|
local page_states = {}
|
|
|
|
if bottom_page_state.visible_area.h > 0 then
|
|
|
|
table.insert(page_states, bottom_page_state)
|
|
|
|
end
|
|
|
|
-- fill up current view from bottom to top
|
|
|
|
local state
|
|
|
|
local current_page = bottom_page_state.page
|
|
|
|
while blank_area.h > 0 do
|
|
|
|
blank_area.h = blank_area.h - self.view.page_gap.height
|
|
|
|
if blank_area.h > 0 then
|
|
|
|
current_page = self.ui.document:getPrevPage(current_page)
|
|
|
|
if current_page == 0 then break end -- start of document reached
|
|
|
|
self:_gotoPage(current_page, "scrolling")
|
|
|
|
state = self:getPrevPageState(blank_area, Geom:new())
|
|
|
|
table.insert(page_states, 1, state)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if current_page == 0 then
|
|
|
|
-- We reached the start of document: we may have truncated too much
|
|
|
|
-- of the bottom page while scrolling up.
|
|
|
|
-- Re-generate everything with first page starting at top
|
|
|
|
offset = Geom:new{x = 0, y = 0}
|
|
|
|
blank_area:setSizeTo(self.view.visible_area)
|
|
|
|
local first_page_state = page_states[1]
|
|
|
|
first_page_state.visible_area.y = 0 -- anchor first page at top
|
|
|
|
return self:genPageStatesFromTop(first_page_state, blank_area, offset)
|
|
|
|
end
|
|
|
|
return page_states
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onScrollPanRel(diff)
|
|
|
|
if diff == 0 then return true end
|
|
|
|
logger.dbg("pan relative height:", diff)
|
|
|
|
local offset = Geom:new{x = 0, y = diff}
|
|
|
|
local blank_area = Geom:new()
|
|
|
|
blank_area:setSizeTo(self.view.visible_area)
|
|
|
|
local new_page_states
|
|
|
|
if diff > 0 then
|
|
|
|
-- pan to scroll down
|
|
|
|
local first_page_state = copyPageState(self.view.page_states[1])
|
|
|
|
new_page_states = self:genPageStatesFromTop(
|
|
|
|
first_page_state, blank_area, offset)
|
|
|
|
elseif diff < 0 then
|
|
|
|
local last_page_state = copyPageState(
|
|
|
|
self.view.page_states[#self.view.page_states])
|
|
|
|
new_page_states = self:genPageStatesFromBottom(
|
|
|
|
last_page_state, blank_area, offset)
|
|
|
|
end
|
|
|
|
if #new_page_states == 0 then
|
|
|
|
-- if we are already at the top of first page or bottom of the last
|
|
|
|
-- page, new_page_states will be empty, in this case, nothing to update
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
self.view.page_states = new_page_states
|
|
|
|
-- update current pageno to the very last part in current view
|
|
|
|
self:_gotoPage(self.view.page_states[#self.view.page_states].page,
|
|
|
|
"scrolling")
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onScrollPageRel(page_diff)
|
|
|
|
if page_diff == 0 then return true end
|
|
|
|
if page_diff > 1 or page_diff < -1 then
|
|
|
|
-- More than 1 page, don't bother with how far we've scrolled.
|
|
|
|
self:onGotoRelativePage(Math.round(page_diff))
|
|
|
|
return true
|
|
|
|
elseif page_diff > 0 then
|
|
|
|
-- page down, last page should be moved to top
|
|
|
|
local last_page_state = table.remove(self.view.page_states)
|
|
|
|
local last_visible_area = last_page_state.visible_area
|
|
|
|
if self.ui.document:getNextPage(last_page_state.page) == 0 and
|
|
|
|
last_visible_area.y + last_visible_area.h >= last_page_state.page_area.h then
|
|
|
|
table.insert(self.view.page_states, last_page_state)
|
|
|
|
self.ui:handleEvent(Event:new("EndOfBook"))
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
local blank_area = Geom:new()
|
|
|
|
blank_area:setSizeTo(self.view.visible_area)
|
|
|
|
local overlap = self.overlap
|
|
|
|
local offset = Geom:new{
|
|
|
|
x = 0,
|
|
|
|
y = last_visible_area.h - overlap
|
|
|
|
}
|
|
|
|
self.view.page_states = self:genPageStatesFromTop(last_page_state, blank_area, offset)
|
|
|
|
elseif page_diff < 0 then
|
|
|
|
-- page up, first page should be moved to bottom
|
|
|
|
local blank_area = Geom:new()
|
|
|
|
blank_area:setSizeTo(self.view.visible_area)
|
|
|
|
local overlap = self.overlap
|
|
|
|
local first_page_state = table.remove(self.view.page_states, 1)
|
|
|
|
local offset = Geom:new{
|
|
|
|
x = 0,
|
|
|
|
y = -first_page_state.visible_area.h + overlap
|
|
|
|
}
|
|
|
|
self.view.page_states = self:genPageStatesFromBottom(
|
|
|
|
first_page_state, blank_area, offset)
|
|
|
|
end
|
|
|
|
-- update current pageno to the very last part in current view
|
|
|
|
self:_gotoPage(self.view.page_states[#self.view.page_states].page, "scrolling")
|
|
|
|
UIManager:setDirty(self.view.dialog, "partial")
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoPageRel(diff)
|
|
|
|
logger.dbg("goto relative page:", diff)
|
|
|
|
local new_va = self.visible_area:copy()
|
|
|
|
local x_pan_off, y_pan_off = 0, 0
|
|
|
|
local right_to_left = self.ui.document.configurable.writing_direction and self.ui.document.configurable.writing_direction > 0
|
|
|
|
local bottom_to_top = self.ui.zooming.zoom_bottom_to_top
|
|
|
|
local h_progress = 1 - self.ui.zooming.zoom_overlap_h * (1/100)
|
|
|
|
local v_progress = 1 - self.ui.zooming.zoom_overlap_v * (1/100)
|
|
|
|
local old_va = self.visible_area
|
|
|
|
local old_page = self.current_page
|
|
|
|
local x, y, w, h = "x", "y", "w", "h"
|
|
|
|
local x_diff = diff
|
|
|
|
local y_diff = diff
|
|
|
|
|
|
|
|
-- Adjust directions according to settings
|
|
|
|
if self.ui.zooming.zoom_direction_vertical then -- invert axes
|
|
|
|
y, x, h, w = x, y, w, h
|
|
|
|
h_progress, v_progress = v_progress, h_progress
|
|
|
|
if right_to_left then
|
|
|
|
x_diff, y_diff = -x_diff, -y_diff
|
|
|
|
end
|
|
|
|
if bottom_to_top then
|
|
|
|
x_diff = -x_diff
|
|
|
|
end
|
|
|
|
elseif bottom_to_top then
|
|
|
|
y_diff = -y_diff
|
|
|
|
end
|
|
|
|
if right_to_left then
|
|
|
|
x_diff = -x_diff
|
|
|
|
end
|
|
|
|
|
|
|
|
if self.zoom_mode ~= "free" then
|
|
|
|
x_pan_off = Math.roundAwayFromZero(self.visible_area[w] * h_progress * x_diff)
|
|
|
|
y_pan_off = Math.roundAwayFromZero(self.visible_area[h] * v_progress * y_diff)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Auxiliary functions to (as much as possible) keep things clear
|
|
|
|
-- If going backwards (diff < 0) "end" is equivalent to "beginning", "next" to "previous";
|
|
|
|
-- in column mode, "line" is equivalent to "column".
|
|
|
|
local function at_end(axis)
|
|
|
|
-- returns true if we're at the end of line (axis = x) or page (axis = y)
|
|
|
|
local len, _diff
|
|
|
|
if axis == x then
|
|
|
|
len, _diff = w, x_diff
|
|
|
|
else
|
|
|
|
len, _diff = h, y_diff
|
|
|
|
end
|
|
|
|
return old_va[axis] + old_va[len] + _diff > self.page_area[axis] + self.page_area[len]
|
|
|
|
or old_va[axis] + _diff < self.page_area[axis]
|
|
|
|
end
|
|
|
|
local function goto_end(axis, _diff)
|
|
|
|
-- updates view area to the end of line (axis = x) or page (axis = y)
|
|
|
|
local len = axis == x and w or h
|
|
|
|
_diff = _diff or (axis == x and x_diff or y_diff)
|
|
|
|
new_va[axis] = _diff > 0
|
|
|
|
and old_va[axis] + self.page_area[len] - old_va[len]
|
|
|
|
or self.page_area[axis]
|
|
|
|
end
|
|
|
|
local function goto_next_line()
|
|
|
|
new_va[y] = old_va[y] + y_pan_off
|
|
|
|
goto_end(x, -x_diff)
|
|
|
|
end
|
|
|
|
local function goto_next_page()
|
|
|
|
local new_page
|
|
|
|
if self.ui.document:hasHiddenFlows() then
|
|
|
|
local forward = diff > 0
|
|
|
|
local pdiff = forward and math.ceil(diff) or math.ceil(-diff)
|
|
|
|
new_page = self.current_page
|
|
|
|
for i=1, pdiff do
|
|
|
|
local test_page = forward and self.ui.document:getNextPage(new_page)
|
|
|
|
or self.ui.document:getPrevPage(new_page)
|
|
|
|
if test_page == 0 then -- start or end of document reached
|
|
|
|
if forward then
|
|
|
|
new_page = self.number_of_pages + 1 -- to trigger EndOfBook below
|
|
|
|
else
|
|
|
|
new_page = 0
|
|
|
|
end
|
|
|
|
break
|
|
|
|
end
|
|
|
|
new_page = test_page
|
|
|
|
end
|
|
|
|
else
|
|
|
|
new_page = self.current_page + diff
|
|
|
|
end
|
|
|
|
if new_page > self.number_of_pages then
|
|
|
|
self.ui:handleEvent(Event:new("EndOfBook"))
|
|
|
|
goto_end(y)
|
|
|
|
goto_end(x)
|
|
|
|
elseif new_page > 0 then
|
|
|
|
-- Be sure that the new and old view areas are reset so that no value is carried over to next page.
|
|
|
|
-- Without this, we would have panned_y = new_va.y - old_va.y > 0, and panned_y will be added to the next page's y direction.
|
|
|
|
-- This occurs when the current page has a y > 0 position (for example, a cropped page) and can fit the whole page height,
|
|
|
|
-- while the next page needs scrolling in the height.
|
|
|
|
self:_gotoPage(new_page)
|
|
|
|
new_va = self.visible_area:copy()
|
|
|
|
old_va = self.visible_area
|
|
|
|
goto_end(y, -y_diff)
|
|
|
|
goto_end(x, -x_diff)
|
|
|
|
else
|
|
|
|
goto_end(x)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
Page Overlap: Fix rectangle computation and arrow mode (#7269)
* In ReaderPaging, the panning step pre-PanningUpdate can be wildly overshot near page edges, so, use the corrected value instead by recomputing it after the panning has been effectively computed by ReaderView.
This fixes slight inaccuracies, as well as glaring mistakes when going backwards, or when near page edges.
This is in line with how ReaderRolling computes the value, which I only realized later because I'm an idiot.
* Minor cleanups around the handling of the dim_area Geom object in general.
* Fix the "Arrow" page overlap mode to be painted in the right coordinates when going backward. Issue might not have been terribly clear because of the previous issue ;).
* Center the arrow's point, while we're here.
* Don't use AlphaContainer to make it translucent, because AlphaContainer is horribly broken, and has weird quirks and behavior that make no sense to me unless some very specific and unlikely constraints are met, and they definitely aren't here.
This fixes the arrow copying an arrow-sized square of the original page the book was opened on on the top-left corner of *every* page with an arrow. (lol).
* Do real proper alpha-blending via Icon/ImageWidget from the original icon, instead of faking it via addBlitFrom, in order to avoid the dimming *around* the triangle's shape.
4 years ago
|
|
|
-- Move the view area towards line end
|
|
|
|
new_va[x] = old_va[x] + x_pan_off
|
|
|
|
new_va[y] = old_va[y]
|
|
|
|
|
|
|
|
local prev_page = self.current_page
|
|
|
|
|
|
|
|
-- Handle cases when the view area gets out of page boundaries
|
|
|
|
if not self.page_area:contains(new_va) then
|
|
|
|
if not at_end(x) then
|
|
|
|
goto_end(x)
|
|
|
|
else
|
|
|
|
goto_next_line()
|
|
|
|
if not self.page_area:contains(new_va) then
|
|
|
|
if not at_end(y) then
|
|
|
|
goto_end(y)
|
|
|
|
else
|
|
|
|
goto_next_page()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if self.current_page == prev_page then
|
|
|
|
-- Page number haven't changed when panning inside a page,
|
|
|
|
-- but time may: keep the footer updated
|
|
|
|
self.view.footer:onUpdateFooter(self.view.footer_visible)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- signal panning update
|
Page Overlap: Fix rectangle computation and arrow mode (#7269)
* In ReaderPaging, the panning step pre-PanningUpdate can be wildly overshot near page edges, so, use the corrected value instead by recomputing it after the panning has been effectively computed by ReaderView.
This fixes slight inaccuracies, as well as glaring mistakes when going backwards, or when near page edges.
This is in line with how ReaderRolling computes the value, which I only realized later because I'm an idiot.
* Minor cleanups around the handling of the dim_area Geom object in general.
* Fix the "Arrow" page overlap mode to be painted in the right coordinates when going backward. Issue might not have been terribly clear because of the previous issue ;).
* Center the arrow's point, while we're here.
* Don't use AlphaContainer to make it translucent, because AlphaContainer is horribly broken, and has weird quirks and behavior that make no sense to me unless some very specific and unlikely constraints are met, and they definitely aren't here.
This fixes the arrow copying an arrow-sized square of the original page the book was opened on on the top-left corner of *every* page with an arrow. (lol).
* Do real proper alpha-blending via Icon/ImageWidget from the original icon, instead of faking it via addBlitFrom, in order to avoid the dimming *around* the triangle's shape.
4 years ago
|
|
|
local panned_x, panned_y = math.floor(new_va.x - old_va.x), math.floor(new_va.y - old_va.y)
|
|
|
|
self.view:PanningUpdate(panned_x, panned_y)
|
|
|
|
|
Page Overlap: Fix rectangle computation and arrow mode (#7269)
* In ReaderPaging, the panning step pre-PanningUpdate can be wildly overshot near page edges, so, use the corrected value instead by recomputing it after the panning has been effectively computed by ReaderView.
This fixes slight inaccuracies, as well as glaring mistakes when going backwards, or when near page edges.
This is in line with how ReaderRolling computes the value, which I only realized later because I'm an idiot.
* Minor cleanups around the handling of the dim_area Geom object in general.
* Fix the "Arrow" page overlap mode to be painted in the right coordinates when going backward. Issue might not have been terribly clear because of the previous issue ;).
* Center the arrow's point, while we're here.
* Don't use AlphaContainer to make it translucent, because AlphaContainer is horribly broken, and has weird quirks and behavior that make no sense to me unless some very specific and unlikely constraints are met, and they definitely aren't here.
This fixes the arrow copying an arrow-sized square of the original page the book was opened on on the top-left corner of *every* page with an arrow. (lol).
* Do real proper alpha-blending via Icon/ImageWidget from the original icon, instead of faking it via addBlitFrom, in order to avoid the dimming *around* the triangle's shape.
4 years ago
|
|
|
-- Update dim area in ReaderView
|
|
|
|
if self.view.page_overlap_enable then
|
|
|
|
if self.current_page ~= old_page then
|
Page Overlap: Fix rectangle computation and arrow mode (#7269)
* In ReaderPaging, the panning step pre-PanningUpdate can be wildly overshot near page edges, so, use the corrected value instead by recomputing it after the panning has been effectively computed by ReaderView.
This fixes slight inaccuracies, as well as glaring mistakes when going backwards, or when near page edges.
This is in line with how ReaderRolling computes the value, which I only realized later because I'm an idiot.
* Minor cleanups around the handling of the dim_area Geom object in general.
* Fix the "Arrow" page overlap mode to be painted in the right coordinates when going backward. Issue might not have been terribly clear because of the previous issue ;).
* Center the arrow's point, while we're here.
* Don't use AlphaContainer to make it translucent, because AlphaContainer is horribly broken, and has weird quirks and behavior that make no sense to me unless some very specific and unlikely constraints are met, and they definitely aren't here.
This fixes the arrow copying an arrow-sized square of the original page the book was opened on on the top-left corner of *every* page with an arrow. (lol).
* Do real proper alpha-blending via Icon/ImageWidget from the original icon, instead of faking it via addBlitFrom, in order to avoid the dimming *around* the triangle's shape.
4 years ago
|
|
|
self.view.dim_area:clear()
|
|
|
|
else
|
Page Overlap: Fix rectangle computation and arrow mode (#7269)
* In ReaderPaging, the panning step pre-PanningUpdate can be wildly overshot near page edges, so, use the corrected value instead by recomputing it after the panning has been effectively computed by ReaderView.
This fixes slight inaccuracies, as well as glaring mistakes when going backwards, or when near page edges.
This is in line with how ReaderRolling computes the value, which I only realized later because I'm an idiot.
* Minor cleanups around the handling of the dim_area Geom object in general.
* Fix the "Arrow" page overlap mode to be painted in the right coordinates when going backward. Issue might not have been terribly clear because of the previous issue ;).
* Center the arrow's point, while we're here.
* Don't use AlphaContainer to make it translucent, because AlphaContainer is horribly broken, and has weird quirks and behavior that make no sense to me unless some very specific and unlikely constraints are met, and they definitely aren't here.
This fixes the arrow copying an arrow-sized square of the original page the book was opened on on the top-left corner of *every* page with an arrow. (lol).
* Do real proper alpha-blending via Icon/ImageWidget from the original icon, instead of faking it via addBlitFrom, in order to avoid the dimming *around* the triangle's shape.
4 years ago
|
|
|
-- We're post PanningUpdate, recompute via self.visible_area instead of new_va for accuracy, it'll have been updated via ViewRecalculate
|
|
|
|
panned_x, panned_y = math.floor(self.visible_area.x - old_va.x), math.floor(self.visible_area.y - old_va.y)
|
|
|
|
|
|
|
|
self.view.dim_area.h = self.visible_area.h - math.abs(panned_y)
|
|
|
|
self.view.dim_area.w = self.visible_area.w - math.abs(panned_x)
|
|
|
|
if panned_y < 0 then
|
Page Overlap: Fix rectangle computation and arrow mode (#7269)
* In ReaderPaging, the panning step pre-PanningUpdate can be wildly overshot near page edges, so, use the corrected value instead by recomputing it after the panning has been effectively computed by ReaderView.
This fixes slight inaccuracies, as well as glaring mistakes when going backwards, or when near page edges.
This is in line with how ReaderRolling computes the value, which I only realized later because I'm an idiot.
* Minor cleanups around the handling of the dim_area Geom object in general.
* Fix the "Arrow" page overlap mode to be painted in the right coordinates when going backward. Issue might not have been terribly clear because of the previous issue ;).
* Center the arrow's point, while we're here.
* Don't use AlphaContainer to make it translucent, because AlphaContainer is horribly broken, and has weird quirks and behavior that make no sense to me unless some very specific and unlikely constraints are met, and they definitely aren't here.
This fixes the arrow copying an arrow-sized square of the original page the book was opened on on the top-left corner of *every* page with an arrow. (lol).
* Do real proper alpha-blending via Icon/ImageWidget from the original icon, instead of faking it via addBlitFrom, in order to avoid the dimming *around* the triangle's shape.
4 years ago
|
|
|
self.view.dim_area.y = self.visible_area.h - self.view.dim_area.h
|
|
|
|
else
|
|
|
|
self.view.dim_area.y = 0
|
|
|
|
end
|
|
|
|
if panned_x < 0 then
|
Page Overlap: Fix rectangle computation and arrow mode (#7269)
* In ReaderPaging, the panning step pre-PanningUpdate can be wildly overshot near page edges, so, use the corrected value instead by recomputing it after the panning has been effectively computed by ReaderView.
This fixes slight inaccuracies, as well as glaring mistakes when going backwards, or when near page edges.
This is in line with how ReaderRolling computes the value, which I only realized later because I'm an idiot.
* Minor cleanups around the handling of the dim_area Geom object in general.
* Fix the "Arrow" page overlap mode to be painted in the right coordinates when going backward. Issue might not have been terribly clear because of the previous issue ;).
* Center the arrow's point, while we're here.
* Don't use AlphaContainer to make it translucent, because AlphaContainer is horribly broken, and has weird quirks and behavior that make no sense to me unless some very specific and unlikely constraints are met, and they definitely aren't here.
This fixes the arrow copying an arrow-sized square of the original page the book was opened on on the top-left corner of *every* page with an arrow. (lol).
* Do real proper alpha-blending via Icon/ImageWidget from the original icon, instead of faking it via addBlitFrom, in order to avoid the dimming *around* the triangle's shape.
4 years ago
|
|
|
self.view.dim_area.x = self.visible_area.w - self.view.dim_area.w
|
|
|
|
else
|
|
|
|
self.view.dim_area.x = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onRedrawCurrentPage()
|
|
|
|
self.ui:handleEvent(Event:new("PageUpdate", self.current_page))
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
-- wrapper for bounds checking
|
|
|
|
function ReaderPaging:_gotoPage(number, orig_mode)
|
|
|
|
if number == self.current_page or not number then
|
|
|
|
-- update footer even if we stay on the same page (like when
|
|
|
|
-- viewing the bottom part of a page from a top part view)
|
|
|
|
self.view.footer:onUpdateFooter(self.view.footer_visible)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
if number > self.number_of_pages then
|
|
|
|
logger.warn("page number too high: "..number.."!")
|
|
|
|
number = self.number_of_pages
|
|
|
|
elseif number < 1 then
|
|
|
|
logger.warn("page number too low: "..number.."!")
|
|
|
|
number = 1
|
|
|
|
end
|
|
|
|
-- this is an event to allow other controllers to be aware of this change
|
|
|
|
self.ui:handleEvent(Event:new("PageUpdate", number, orig_mode))
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoPage(number, pos)
|
|
|
|
self:setPagePosition(number, 0)
|
|
|
|
self:_gotoPage(number)
|
|
|
|
if pos then
|
|
|
|
local rect_p = Geom:new{ x = pos.x or 0, y = pos.y or 0 }
|
|
|
|
local rect_s = Geom:new(rect_p):copy()
|
|
|
|
rect_s:transformByScale(self.view.state.zoom)
|
|
|
|
if self.view.page_scroll then
|
|
|
|
self:onScrollPanRel(rect_s.y - self.view.page_area.y)
|
|
|
|
else
|
|
|
|
self.view:PanningUpdate(rect_s.x - self.view.visible_area.x, rect_s.y - self.view.visible_area.y)
|
|
|
|
end
|
|
|
|
elseif number == self.current_page then
|
|
|
|
-- gotoPage emits this event only if the page changes
|
|
|
|
self.ui:handleEvent(Event:new("PageUpdate", self.current_page))
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoRelativePage(number)
|
|
|
|
local new_page = self.current_page
|
|
|
|
local test_page = new_page
|
|
|
|
local forward = number > 0
|
|
|
|
for i=1, math.abs(number) do
|
|
|
|
test_page = forward and self.ui.document:getNextPage(test_page)
|
|
|
|
or self.ui.document:getPrevPage(test_page)
|
|
|
|
if test_page == 0 then -- start or end of document reached
|
|
|
|
break
|
|
|
|
end
|
|
|
|
new_page = test_page
|
|
|
|
end
|
|
|
|
self:_gotoPage(new_page)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoPercentage(percentage)
|
|
|
|
if percentage < 0 then percentage = 0 end
|
|
|
|
if percentage > 1 then percentage = 1 end
|
|
|
|
self:_gotoPage(math.floor(percentage*self.number_of_pages))
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
-- These might need additional work to behave fine in scroll
|
|
|
|
-- mode, and other zoom modes than Fit page
|
|
|
|
function ReaderPaging:onGotoNextChapter()
|
|
|
|
local pageno = self.current_page
|
|
|
|
local new_page = self.ui.toc:getNextChapter(pageno)
|
|
|
|
if new_page then
|
|
|
|
self.ui.link:addCurrentLocationToStack()
|
|
|
|
self:onGotoPage(new_page)
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onGotoPrevChapter()
|
|
|
|
local pageno = self.current_page
|
|
|
|
local new_page = self.ui.toc:getPreviousChapter(pageno)
|
|
|
|
if new_page then
|
|
|
|
self.ui.link:addCurrentLocationToStack()
|
|
|
|
self:onGotoPage(new_page)
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onReflowUpdated()
|
|
|
|
self.ui:handleEvent(Event:new("RedrawCurrentPage"))
|
|
|
|
self.ui:handleEvent(Event:new("RestoreZoomMode"))
|
|
|
|
self.ui:handleEvent(Event:new("InitScrollPageStates"))
|
|
|
|
end
|
|
|
|
|
|
|
|
function ReaderPaging:onToggleReflow()
|
|
|
|
self.view.document.configurable.text_wrap = bit.bxor(self.view.document.configurable.text_wrap, 1)
|
|
|
|
self:onReflowUpdated()
|
|
|
|
end
|
|
|
|
|
|
|
|
return ReaderPaging
|