diff --git a/frontend/apps/reader/modules/readerback.lua b/frontend/apps/reader/modules/readerback.lua new file mode 100644 index 000000000..3c8a9e250 --- /dev/null +++ b/frontend/apps/reader/modules/readerback.lua @@ -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 diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index 2964d39ec..1b69f22fa 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -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, diff --git a/frontend/util.lua b/frontend/util.lua index 1d324a8b5..3f0db1537 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -95,6 +95,46 @@ function util.secondsToClock(seconds, withoutSeconds) end end +--[[-- +Compares values in two different tables. + +Source: https://stackoverflow.com/a/32660766/2470572 +]] +---- @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 diff --git a/spec/unit/inputtext_spec.lua b/spec/unit/inputtext_spec.lua index ed63c1ccf..a6e9bf466 100644 --- a/spec/unit/inputtext_spec.lua +++ b/spec/unit/inputtext_spec.lua @@ -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()