From b09bb87d4e0eb68f9c986359e82f9992294f33db Mon Sep 17 00:00:00 2001 From: chrox Date: Tue, 7 Oct 2014 17:09:37 +0800 Subject: [PATCH] refactoring readertoc and readerfooter so that they don't repeat themselves. --- frontend/apps/reader/modules/readerfooter.lua | 77 ++++--- .../apps/reader/modules/readerrolling.lua | 14 +- frontend/apps/reader/modules/readertoc.lua | 192 +++++++----------- frontend/ui/widget/progresswidget.lua | 18 +- spec/unit/readertoc_spec.lua | 60 ++++++ 5 files changed, 189 insertions(+), 172 deletions(-) create mode 100644 spec/unit/readertoc_spec.lua diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index 443d384b9..ebb296609 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -20,6 +20,7 @@ local ReaderFooter = InputContainer:new{ visible = true, pageno = nil, pages = nil, + toc_level = 0, progress_percentage = 0.0, progress_text = nil, text_font_face = "ffont", @@ -37,35 +38,39 @@ function ReaderFooter:init() self.pageno = self.view.state.page self.pages = self.view.document:getPageCount() - local progress_text_default = "" + local text_default = "" if DMINIBAR_ALL_AT_ONCE then + local info = {} + if DMINIBAR_BATTERY then + table.insert(info, "B:100%") + end if DMINIBAR_TIME then - progress_text_default = progress_text_default .. " | WW:WW" + table.insert(info, "WW:WW") end if DMINIBAR_PAGES then - progress_text_default = progress_text_default .. " | 0000 / 0000" + table.insert(info, "0000 / 0000") end if DMINIBAR_NEXT_CHAPTER then - progress_text_default = progress_text_default .. " | => 000" + table.insert(info, "=> 000") end - if DMINIBAR_BATTERY then - progress_text_default = progress_text_default .. " | B:100%" - end - progress_text_default = string.sub(progress_text_default, 4) + text_default = table.concat(info, " | ") else - progress_text_default = string.format(" %d / %d ", self.pages, self.pages) + text_default = string.format(" %d / %d ", self.pages, self.pages) end self.progress_text = TextWidget:new{ - text = progress_text_default, + text = text_default, face = Font:getFace(self.text_font_face, self.text_font_size), } local text_width = self.progress_text:getSize().w + local ticks = (self.ui.toc and DMINIBAR_PROGRESS_MARKER) + and self.ui.toc:getTocTicks(self.toc_level) or {} self.progress_bar = ProgressWidget:new{ width = math.floor(Screen:getWidth() - text_width - self.padding), height = self.bar_height, percentage = self.progress_percentage, - TOC = self.ui.document:getToc(), + ticks = ticks, + tick_width = DMINIBAR_TOC_MARKER_WIDTH, last = self.pages, } local horizontal_group = HorizontalGroup:new{} @@ -121,42 +126,55 @@ function ReaderFooter:init() self:applyFooterMode() end -function ReaderFooter:fillToc() - self.toc = self.ui.document:getToc() +function ReaderFooter:getBatteryInfo() + local powerd = Device:getPowerDevice() + --local state = powerd:isCharging() and -1 or powerd:getCapacity() + return "B:" .. powerd:getCapacity() .. "%" +end + +function ReaderFooter:getTimeInfo() + return os.date("%H:%M") +end + +function ReaderFooter:getProgressInfo() + return string.format("%d / %d", self.pageno, self.pages) +end + +function ReaderFooter:getNextChapterInfo() + local left = self.ui.toc:getChapterPagesLeft(self.pageno, self.toc_level) + return "=> " .. (left and left or self.pages - self.pageno) end function ReaderFooter:updateFooterPage() if type(self.pageno) ~= "number" then return end self.progress_bar.percentage = self.pageno / self.pages if DMINIBAR_ALL_AT_ONCE then - self.progress_text.text = "" + local info = {} if DMINIBAR_BATTERY then - local powerd = Device:getPowerDevice() - local state = powerd:isCharging() and -1 or powerd:getCapacity() - self.progress_text.text = self.progress_text.text .. " | B:" .. powerd:getCapacity() .. "%" + table.insert(info, self:getBatteryInfo()) end if DMINIBAR_TIME then - self.progress_text.text = self.progress_text.text .. " | " .. os.date("%H:%M") + table.insert(info, self:getTimeInfo()) end if DMINIBAR_PAGES then - self.progress_text.text = self.progress_text.text .. " | " .. string.format("%d / %d", self.pageno, self.pages) + table.insert(info, self:getProgressInfo()) end if DMINIBAR_NEXT_CHAPTER then - self.progress_text.text = self.progress_text.text .. " | => " .. self.ui.toc:_getChapterPagesLeft(self.pageno,self.pages) + table.insert(info, self:getNextChapterInfo()) end - self.progress_text.text = string.sub(self.progress_text.text, 4) + self.progress_text.text = table.concat(info, " | ") else + local info = "" if self.mode == 1 then - self.progress_text.text = string.format("%d / %d", self.pageno, self.pages) + info = self:getProgressInfo() elseif self.mode == 2 then - self.progress_text.text = os.date("%H:%M") + info = self:getTimeInfo() elseif self.mode == 3 then - self.progress_text.text = "=> " .. self.ui.toc:_getChapterPagesLeft(self.pageno,self.pages) + info = self:getNextChapterInfo() elseif self.mode == 4 then - local powerd = Device:getPowerDevice() - local state = powerd:isCharging() and -1 or powerd:getCapacity() - self.progress_text.text = "B:" .. powerd:getCapacity() .. "%" + info = self:getBatteryInfo() end + self.progress_text.text = info end end @@ -166,9 +184,10 @@ function ReaderFooter:updateFooterPos() self.progress_bar.percentage = self.position / self.doc_height if self.show_time then - self.progress_text.text = os.date("%H:%M") + self.progress_text.text = self:getTimeInfo() else - self.progress_text.text = string.format("%1.f", self.progress_bar.percentage*100).."%" + local percentage = self.progress_bar.percentage + self.progress_text.text = string.format("%1.f", percentage*100) .. "%" end end diff --git a/frontend/apps/reader/modules/readerrolling.lua b/frontend/apps/reader/modules/readerrolling.lua index 841f61eee..23b958187 100644 --- a/frontend/apps/reader/modules/readerrolling.lua +++ b/frontend/apps/reader/modules/readerrolling.lua @@ -247,18 +247,12 @@ function ReaderRolling:onResume() end function ReaderRolling:onDoubleTapForward() - local i = self.ui.toc:_getNextChapter(self.current_page+self.ui.document:getVisiblePageCount()) - if i ~= "" then - self:onGotoPage(i) - end + self:onGotoPage(self.ui.toc:getNextChapter(self.current_page)) return true end function ReaderRolling:onDoubleTapBackward() - local i = self.ui.toc:_getPreviousChapter(self.current_page) - if i ~= "" then - self:onGotoPage(i) - end + self:onGotoPage(self.ui.toc:getPreviousChapter(self.current_page)) return true end @@ -414,7 +408,9 @@ function ReaderRolling:gotoPercent(new_percent) end function ReaderRolling:onGotoPage(number) - self:gotoPage(number) + if number then + self:gotoPage(number) + end return true end diff --git a/frontend/apps/reader/modules/readertoc.lua b/frontend/apps/reader/modules/readertoc.lua index a9b42f028..9f49d5393 100644 --- a/frontend/apps/reader/modules/readertoc.lua +++ b/frontend/apps/reader/modules/readertoc.lua @@ -13,6 +13,7 @@ local _ = require("gettext") local ReaderToc = InputContainer:new{ toc = nil, + ticks = {}, toc_menu_title = _("Table of contents"), } @@ -60,30 +61,17 @@ function ReaderToc:onPageUpdate(pageno) end function ReaderToc:fillToc() + if self.toc and #self.toc > 0 then return end self.toc = self.ui.document:getToc() end --- _getTocTitleByPage wrapper, so specific reader --- can tranform pageno according its need function ReaderToc:getTocTitleByPage(pn_or_xp) - local page = pn_or_xp + self:fillToc() + if #self.toc == 0 then return "" end + local pageno = pn_or_xp if type(pn_or_xp) == "string" then - page = self.ui.document:getPageFromXPointer(pn_or_xp) + pageno = self.ui.document:getPageFromXPointer(pn_or_xp) end - return self:_getTocTitleByPage(page) -end - -function ReaderToc:_getTocTitleByPage(pageno) - if not self.toc then - -- build toc when needed. - self:fillToc() - end - - -- no table of content - if #self.toc == 0 then - return "" - end - local pre_entry = self.toc[1] for _k,_v in ipairs(self.toc) do if _v.page > pageno then @@ -98,135 +86,91 @@ function ReaderToc:getTocTitleOfCurrentPage() return self:getTocTitleByPage(self.pageno) end -function ReaderToc:_getChapterPagesLeft(pageno,pages) - local i - local j = 0 - - if not self.toc then - -- build toc when needed. - self:fillToc() - end - - -- no table of content - if #self.toc == 0 then - return "" - end - - if #self.toc > 0 then - for i = 1, #self.toc do - v = self.toc[i] - if v.page > pageno then - j = v.page - break - end +function ReaderToc:getMaxDepth() + self:fillToc() + local max_depth = 0 + for _, v in ipairs(self.toc) do + if v.depth > max_depth then + max_depth = v.depth end end - if j == 0 then - if pages > 0 then - return pages-pageno - else - return "" - end - else - return j-pageno-1 - end + return max_depth end -function ReaderToc:_getChapterPagesDone(pageno) - local i - local j = 0 - - if not self.toc then - -- build toc when needed. - self:fillToc() - end - - -- no table of content - if #self.toc == 0 then - return "" - end +--[[ +TOC ticks is a list of page number in ascending order of TOC nodes at certain level +positive level counts nodes of the depth level (level 1 for depth 1) +non-positive level counts nodes of reversed depth level (level -1 for max_depth-1) +--]] +function ReaderToc:getTocTicks(level) + if self.ticks[level] then return self.ticks[level] end + -- build toc ticks if not found + self:fillToc() + local ticks = {} if #self.toc > 0 then - for i = 1, #self.toc do - v = self.toc[i] - if v.page >= pageno then - break + local depth = nil + if level > 0 then + depth = level + else + depth = self:getMaxDepth() + level + end + for _, v in ipairs(self.toc) do + if v.depth == depth then + table.insert(ticks, v.page) end - j = v.page end + -- normally the ticks are sorted already but in rare cases + -- toc nodes may be not in ascending order + table.sort(ticks) + -- cache ticks only if ticks are available + self.ticks[level] = ticks end - if j < 2 then - return "" - else - return j-pageno - end + return ticks end -function ReaderToc:_getPreviousChapter(pageno) - local i - local j = 0 - - if not self.toc then - -- build toc when needed. - self:fillToc() - end - - -- no table of content - if #self.toc == 0 then - return "" - end - - if #self.toc > 0 then - for i = 1, #self.toc do - v = self.toc[i] - if v.page >= pageno then - break - end - j = v.page +function ReaderToc:getNextChapter(cur_pageno, level) + local ticks = self:getTocTicks(level) + local next_chapter = nil + for i = 1, #ticks do + if ticks[i] > cur_pageno then + next_chapter = ticks[i] + break end end - if j >= pageno then - return "" - else - return j - end + return next_chapter end -function ReaderToc:_getNextChapter(pageno) - local i - local j = 0 - - if not self.toc then - -- build toc when needed. - self:fillToc() +function ReaderToc:getPreviousChapter(cur_pageno, level) + local ticks = self:getTocTicks(level) + local previous_chapter = nil + for i = 1, #ticks do + if ticks[i] >= cur_pageno then + break + end + previous_chapter = ticks[i] end + return previous_chapter +end - -- no table of content - if #self.toc == 0 then - return "" +function ReaderToc:getChapterPagesLeft(pageno, level) + local next_chapter = self:getNextChapter(pageno, level) + if next_chapter then + next_chapter = next_chapter - pageno end + return next_chapter +end - if #self.toc > 0 then - for i = 1, #self.toc do - v = self.toc[i] - if v.page >= pageno then - j = v.page - break - end - end - end - if j < pageno then - return "" - else - return j +function ReaderToc:getChapterPagesDone(pageno, level) + local previous_chapter = self:getPreviousChapter(pageno, level) + if previous_chapter then + previous_chapter = pageno - previous_chapter end + return previous_chapter end - function ReaderToc:onShowToc() - if not self.toc then - self:fillToc() - end + self:fillToc() -- build menu items if #self.toc > 0 and not self.toc[1].text then for _,v in ipairs(self.toc) do diff --git a/frontend/ui/widget/progresswidget.lua b/frontend/ui/widget/progresswidget.lua index 36431d698..09dc49cb7 100644 --- a/frontend/ui/widget/progresswidget.lua +++ b/frontend/ui/widget/progresswidget.lua @@ -11,12 +11,12 @@ local ProgressWidget = Widget:new{ margin_v = 1, radius = 2, bordersize = 1, - toc_marker_width = DMINIBAR_TOC_MARKER_WIDTH, bordercolor = 15, bgcolor = 0, rectcolor = 10, percentage = nil, - TOC = {}, + ticks = {}, + tick_width = 3, last = nil, } @@ -37,14 +37,12 @@ function ProgressWidget:paintTo(bb, x, y) bb:paintRect(x+self.margin_h, y+self.margin_v+self.bordersize, (my_size.w-2*self.margin_h)*self.percentage, (my_size.h-2*(self.margin_v+self.bordersize)), self.rectcolor) - if DMINIBAR_PROGRESS_MARKER then - if #self.TOC > 0 then - for i=1, #self.TOC do - v = self.TOC[i] - bb:paintRect(x+(my_size.w-2*self.margin_h)*(v.page/self.last), y+self.margin_v+self.bordersize, - self.toc_marker_width,(my_size.h-2*(self.margin_v+self.bordersize)), self.bordercolor) - end - end + for i=1, #self.ticks do + local page = self.ticks[i] + bb:paintRect( + x + (my_size.w-2*self.margin_h)*(page/self.last), + y + self.margin_v + self.bordersize, self.tick_width, + (my_size.h-2*(self.margin_v+self.bordersize)), self.bordercolor) end end diff --git a/spec/unit/readertoc_spec.lua b/spec/unit/readertoc_spec.lua new file mode 100644 index 000000000..6798f8df0 --- /dev/null +++ b/spec/unit/readertoc_spec.lua @@ -0,0 +1,60 @@ +require("commonrequire") +local DocumentRegistry = require("document/documentregistry") +local ReaderUI = require("apps/reader/readerui") +local DEBUG = require("dbg") + +describe("Readertoc module", function() + local sample_epub = "spec/front/unit/data/juliet.epub" + local readerui = ReaderUI:new{ + document = DocumentRegistry:openDocument(sample_epub), + } + local toc = readerui.toc + local toc_max_depth = nil + it("should get max toc depth", function() + toc_max_depth = toc:getMaxDepth() + assert.are.same(2, toc_max_depth) + end) + it("should get toc title from page", function() + local title = toc:getTocTitleByPage(56) + assert(title == "Prologue") + local title = toc:getTocTitleByPage(172) + assert(title == "SCENE IV. Hall in Capulet's house.") + end) + describe("getTocTicks API", function() + local ticks_level_0 = nil + it("should get ticks of level 0", function() + ticks_level_0 = toc:getTocTicks(0) + assert.are.same(26, #ticks_level_0) + end) + local ticks_level_1 = nil + it("should get ticks of level 1", function() + ticks_level_1 = toc:getTocTicks(1) + assert.are.same(7, #ticks_level_1) + end) + local ticks_level_m1 = nil + it("should get ticks of level -1", function() + ticks_level_m1 = toc:getTocTicks(1) + assert.are.same(7, #ticks_level_m1) + end) + it("should get the same ticks of level -1 and level 1", function() + if toc_max_depth == 2 then + assert.are.same(ticks_level_1, ticks_level_m1) + end + end) + end) + it("should get page of next chapter", function() + assert.are.same(25, toc:getNextChapter(10, 0)) + assert.are.same(103, toc:getNextChapter(100, 0)) + assert.are.same(nil, toc:getNextChapter(200, 0)) + end) + it("should get page of previous chapter", function() + assert.are.same(9, toc:getPreviousChapter(10, 0)) + assert.are.same(95, toc:getPreviousChapter(100, 0)) + assert.are.same(190, toc:getPreviousChapter(200, 0)) + end) + it("should get page left of chapter", function() + assert.are.same(15, toc:getChapterPagesLeft(10, 0)) + assert.are.same(3, toc:getChapterPagesLeft(100, 0)) + assert.are.same(nil, toc:getChapterPagesLeft(200, 0)) + end) +end)