mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
[feat] Add ReaderBack (#3821)
This implements a reasonable facsimile of going back on Android. The back button first goes back in a history of visited pages. When there's no history left, it closes the app. Fixes #3816.
This commit is contained in:
81
frontend/apps/reader/modules/readerback.lua
Normal file
81
frontend/apps/reader/modules/readerback.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
local Device = require("device")
|
||||
local Event = require("ui/event")
|
||||
local EventListener = require("ui/widget/eventlistener")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
|
||||
local ReaderBack = EventListener:new{
|
||||
location_stack = {},
|
||||
-- a limit not intended to be a practical limit but just a failsafe
|
||||
max_stack = 5000,
|
||||
}
|
||||
|
||||
function ReaderBack:init()
|
||||
if Device:hasKeys() then
|
||||
self.ui.key_events.Back = { {"Back"}, doc = "Reader back" }
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderBack:_getCurrentLocation()
|
||||
local current_location
|
||||
|
||||
if self.ui.document.info.has_pages then
|
||||
current_location = self.ui.paging:getBookLocation()
|
||||
else
|
||||
current_location = {
|
||||
xpointer = self.ui.rolling:getBookLocation(),
|
||||
}
|
||||
end
|
||||
|
||||
return current_location
|
||||
end
|
||||
|
||||
local ignore_location
|
||||
|
||||
function ReaderBack:addCurrentLocationToStack()
|
||||
local location_stack = self.location_stack
|
||||
local new_location = self:_getCurrentLocation()
|
||||
|
||||
if util.tableEquals(ignore_location, new_location) then return end
|
||||
|
||||
table.insert(location_stack, new_location)
|
||||
|
||||
if #location_stack > self.max_stack then
|
||||
table.remove(location_stack, 1)
|
||||
end
|
||||
end
|
||||
|
||||
-- Scroll mode crengine
|
||||
function ReaderBack:onPosUpdate()
|
||||
self:addCurrentLocationToStack()
|
||||
end
|
||||
|
||||
-- Paged media
|
||||
function ReaderBack:onPageUpdate()
|
||||
self:addCurrentLocationToStack()
|
||||
end
|
||||
|
||||
-- Called when loading new document
|
||||
function ReaderBack:onReadSettings(config)
|
||||
self.location_stack = {}
|
||||
end
|
||||
|
||||
function ReaderBack:onBack()
|
||||
local location_stack = self.location_stack
|
||||
|
||||
if #location_stack > 1 then
|
||||
local saved_location = table.remove(location_stack)
|
||||
|
||||
if saved_location then
|
||||
ignore_location = self:_getCurrentLocation()
|
||||
logger.dbg("[ReaderBack] restoring:", saved_location)
|
||||
self.ui:handleEvent(Event:new('RestoreBookLocation', saved_location))
|
||||
return true
|
||||
end
|
||||
else
|
||||
logger.dbg("[ReaderBack] no location history, closing")
|
||||
self.ui:handleEvent(Event:new("Close"))
|
||||
end
|
||||
end
|
||||
|
||||
return ReaderBack
|
||||
@@ -17,6 +17,7 @@ local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local InputDialog = require("ui/widget/inputdialog")
|
||||
local PluginLoader = require("pluginloader")
|
||||
local ReaderActivityIndicator = require("apps/reader/modules/readeractivityindicator")
|
||||
local ReaderBack = require("apps/reader/modules/readerback")
|
||||
local ReaderBookmark = require("apps/reader/modules/readerbookmark")
|
||||
local ReaderConfig = require("apps/reader/modules/readerconfig")
|
||||
local ReaderCoptListener = require("apps/reader/modules/readercoptlistener")
|
||||
@@ -97,11 +98,6 @@ function ReaderUI:init()
|
||||
|
||||
if Device:hasKeys() then
|
||||
self.key_events.Home = { {"Home"}, doc = "open file browser" }
|
||||
if Device:isSDL() then
|
||||
--if in the desktop emulator
|
||||
--add the old Back key to exit koreader
|
||||
self.key_events.Close = { {"Back"}, doc = "Exit koreader" }
|
||||
end
|
||||
end
|
||||
|
||||
-- a view container (so it must be child #1!)
|
||||
@@ -299,6 +295,11 @@ function ReaderUI:init()
|
||||
})
|
||||
self.disable_double_tap = G_reader_settings:readSetting("disable_double_tap") ~= false
|
||||
end
|
||||
-- back location stack
|
||||
self:registerModule("back", ReaderBack:new{
|
||||
ui = self,
|
||||
view = self.view,
|
||||
})
|
||||
-- fulltext search
|
||||
self:registerModule("search", ReaderSearch:new{
|
||||
dialog = self.dialog,
|
||||
|
||||
@@ -95,6 +95,46 @@ function util.secondsToClock(seconds, withoutSeconds)
|
||||
end
|
||||
end
|
||||
|
||||
--[[--
|
||||
Compares values in two different tables.
|
||||
|
||||
Source: <a href="https://stackoverflow.com/a/32660766/2470572">https://stackoverflow.com/a/32660766/2470572</a>
|
||||
]]
|
||||
---- @param o1 Lua table
|
||||
---- @param o2 Lua table
|
||||
---- @bool ignore_mt
|
||||
---- @treturn boolean
|
||||
function util.tableEquals(o1, o2, ignore_mt)
|
||||
if o1 == o2 then return true end
|
||||
local o1Type = type(o1)
|
||||
local o2Type = type(o2)
|
||||
if o1Type ~= o2Type then return false end
|
||||
if o1Type ~= 'table' then return false end
|
||||
|
||||
if not ignore_mt then
|
||||
local mt1 = getmetatable(o1)
|
||||
if mt1 and mt1.__eq then
|
||||
--compare using built in method
|
||||
return o1 == o2
|
||||
end
|
||||
end
|
||||
|
||||
local keySet = {}
|
||||
|
||||
for key1, value1 in pairs(o1) do
|
||||
local value2 = o2[key1]
|
||||
if value2 == nil or util.tableEquals(value1, value2, ignore_mt) == false then
|
||||
return false
|
||||
end
|
||||
keySet[key1] = true
|
||||
end
|
||||
|
||||
for key2, _ in pairs(o2) do
|
||||
if not keySet[key2] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Returns number of keys in a table.
|
||||
---- @param T Lua table
|
||||
---- @treturn int number of keys in table T
|
||||
|
||||
@@ -5,37 +5,7 @@ describe("InputText widget module", function()
|
||||
require("commonrequire")
|
||||
InputText = require("ui/widget/inputtext")
|
||||
|
||||
-- thanks to https://stackoverflow.com/a/32660766/2470572
|
||||
equals = function(o1, o2, ignore_mt)
|
||||
if o1 == o2 then return true end
|
||||
local o1Type = type(o1)
|
||||
local o2Type = type(o2)
|
||||
if o1Type ~= o2Type then return false end
|
||||
if o1Type ~= 'table' then return false end
|
||||
|
||||
if not ignore_mt then
|
||||
local mt1 = getmetatable(o1)
|
||||
if mt1 and mt1.__eq then
|
||||
--compare using built in method
|
||||
return o1 == o2
|
||||
end
|
||||
end
|
||||
|
||||
local keySet = {}
|
||||
|
||||
for key1, value1 in pairs(o1) do
|
||||
local value2 = o2[key1]
|
||||
if value2 == nil or equals(value1, value2, ignore_mt) == false then
|
||||
return false
|
||||
end
|
||||
keySet[key1] = true
|
||||
end
|
||||
|
||||
for key2, _ in pairs(o2) do
|
||||
if not keySet[key2] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
equals = require("util").tableEquals
|
||||
end)
|
||||
|
||||
describe("addChars()", function()
|
||||
|
||||
Reference in New Issue
Block a user