diff --git a/frontend/apps/reader/modules/readerdogear.lua b/frontend/apps/reader/modules/readerdogear.lua index 1b737fd67..0329589ef 100644 --- a/frontend/apps/reader/modules/readerdogear.lua +++ b/frontend/apps/reader/modules/readerdogear.lua @@ -18,6 +18,11 @@ function ReaderDogear:init() dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, widget, } + self:resetLayout() +end + +function ReaderDogear:resetLayout() + self[1].dimen.w = Screen:getWidth() if Device:isTouchDevice() then self.ges_events = { Tap = { diff --git a/frontend/apps/reader/modules/readerflipping.lua b/frontend/apps/reader/modules/readerflipping.lua index f239de88f..62db32b6d 100644 --- a/frontend/apps/reader/modules/readerflipping.lua +++ b/frontend/apps/reader/modules/readerflipping.lua @@ -19,6 +19,11 @@ function ReaderFlipping:init() dimen = Geom:new{w = Screen:getWidth(), h = widget:getSize().h}, widget, } + self:resetLayout() +end + +function ReaderFlipping:resetLayout() + self[1].dimen.w = Screen:getWidth() if Device:isTouchDevice() then self.ges_events = { Tap = { diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index fa5730279..89a855bdb 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -85,7 +85,7 @@ function ReaderFooter:init() text = text_default, face = Font:getFace(self.text_font_face, self.text_font_size), } - local text_width = self.progress_text:getSize().w + self.text_width = self.progress_text:getSize().w local ticks_candidates = {} if self.ui.toc and self.settings.toc_markers then local max_level = self.ui.toc:getMaxDepth() @@ -99,40 +99,67 @@ function ReaderFooter:init() table.sort(ticks_candidates, function(a, b) return #a > #b end) end self.progress_bar = ProgressWidget:new{ - width = math.floor(Screen:getWidth() - text_width - self.padding), + width = nil, -- width will be updated in self:resetLayout() height = self.bar_height, percentage = self.progress_percentage, ticks = ticks_candidates[1] or {}, tick_width = DMINIBAR_TOC_MARKER_WIDTH, last = self.pages, } - local horizontal_group = HorizontalGroup:new{} - local bar_container = RightContainer:new{ - dimen = Geom:new{ w = Screen:getWidth() - text_width, h = self.height }, + self.horizontal_group = HorizontalGroup:new{} + self.bar_container = RightContainer:new{ + dimen = Geom:new{ w = Screen:getWidth() - self.text_width, h = self.height }, self.progress_bar, } local text_container = CenterContainer:new{ - dimen = Geom:new{ w = text_width, h = self.height }, + dimen = Geom:new{ w = self.text_width, h = self.height }, self.progress_text, } if self.settings.progress_bar then - table.insert(horizontal_group, bar_container) + table.insert(self.horizontal_group, self.bar_container) end - table.insert(horizontal_group, text_container) + table.insert(self.horizontal_group, text_container) self[1] = BottomContainer:new{ dimen = Screen:getSize(), BottomContainer:new{ dimen = Geom:new{w = Screen:getWidth(), h = self.height*2}, FrameContainer:new{ - horizontal_group, + self.horizontal_group, background = Blitbuffer.COLOR_WHITE, bordersize = 0, padding = 0, } } } - self.dimen = self[1]:getSize() self:updateFooterPage() + + self.mode = G_reader_settings:readSetting("reader_footer_mode") or self.mode + self:applyFooterMode() + self:resetLayout() + + if self.settings.auto_refresh_time then + if not self.autoRefreshTime then + self.autoRefreshTime = function() + self:updateFooterPage() + UIManager:setDirty(self.view.dialog, "ui", self[1][1][1].dimen) + UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshTime) + end + end + self.onCloseDocument = function() + UIManager:unschedule(self.autoRefreshTime) + end + UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshTime) + end +end + +function ReaderFooter:resetLayout() + self.progress_bar.width = math.floor(Screen:getWidth() - self.text_width - self.padding) + self.horizontal_group:resetLayout() + self.bar_container.dimen.w = Screen:getWidth() - self.text_width + self[1].dimen = Screen:getSize() + self[1][1].dimen.w = Screen:getWidth() + self.dimen = self[1]:getSize() + local range = Geom:new{ x = Screen:getWidth()*DTAP_ZONE_MINIBAR.x, y = Screen:getHeight()*DTAP_ZONE_MINIBAR.y, @@ -155,19 +182,6 @@ function ReaderFooter:init() }, } end - self.mode = G_reader_settings:readSetting("reader_footer_mode") or self.mode - self:applyFooterMode() - if self.settings.auto_refresh_time then - self.autoRefreshTime = function() - self:updateFooterPage() - UIManager:setDirty(self.view.dialog, "ui", self[1][1][1].dimen) - UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshTime) - end - UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshTime) - self.onCloseDocument = function() - UIManager:unschedule(self.autoRefreshTime) - end - end end local options = { @@ -294,7 +308,7 @@ function ReaderFooter:updateFooterPage() if self.settings.chapter_time_to_read then table.insert(info, self:getChapterTimeToRead()) end - self.progress_text.text = table.concat(info, " | ") + self.progress_text:setText(table.concat(info, " | ")) else local info = "" if self.mode == 1 then @@ -312,7 +326,7 @@ function ReaderFooter:updateFooterPage() elseif self.mode == 7 then info = self:getChapterTimeToRead() end - self.progress_text.text = info + self.progress_text:setText(info) end end diff --git a/frontend/apps/reader/modules/readerstatus.lua b/frontend/apps/reader/modules/readerstatus.lua index 3542be215..42e865230 100644 --- a/frontend/apps/reader/modules/readerstatus.lua +++ b/frontend/apps/reader/modules/readerstatus.lua @@ -1,5 +1,5 @@ local InputContainer = require("ui/widget/container/inputcontainer") -local StatusWidget = require("ui/widget/statuswidget") +local BookStatusWidget = require("ui/widget/bookstatuswidget") local UIManager = require("ui/uimanager") local _ = require("gettext") @@ -20,29 +20,27 @@ function ReaderStatus:init() if self.ui.document.is_djvu or self.ui.document.is_pic then self.enabled = false return - end - -- register event listener if enabled - self.onEndOfBook = function() - self:showStatus() - end - self.total_pages = self.document:getPageCount() - self.ui:registerPostInitCallback(function() + else + self.total_pages = self.document:getPageCount() self.ui.menu:registerToMainMenu(self) - end) + -- register event listener if enabled + self.onEndOfBook = function() + self:showStatus() + end + end end function ReaderStatus:addToMainMenu(tab_item_table) - table.insert(tab_item_table.typeset, { - text = _("Status"), + table.insert(tab_item_table.info, { + text = _("Book status"), callback = function() self:showStatus() - UIManager:setDirty("all") end, }) end function ReaderStatus:showStatus() - local status_page = StatusWidget:new { + local status_page = BookStatusWidget:new { thumbnail = self.document:getCoverPageImage(), props = self.document:getProps(), document = self.document, diff --git a/frontend/apps/reader/modules/readerview.lua b/frontend/apps/reader/modules/readerview.lua index ad70102b9..3e437c872 100644 --- a/frontend/apps/reader/modules/readerview.lua +++ b/frontend/apps/reader/modules/readerview.lua @@ -113,7 +113,7 @@ end function ReaderView:resetLayout() for i, widget in ipairs(self) do - widget:init() + widget:resetLayout() end end diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index dc3f82dcb..04991b387 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -171,12 +171,6 @@ function ReaderUI:init() view = self.view, ui = self }, true) - -- history view - self:registerModule("history", FileManagerHistory:new{ - dialog = self.dialog, - menu = self.menu, - ui = self, - }) -- frontlight controller if Device:hasFrontlight() then self:registerModule("frontlight", ReaderFrontLight:new{ @@ -297,13 +291,17 @@ function ReaderUI:init() view = self.view, ui = self }) - + -- book status self:registerModule("status", ReaderStatus:new{ ui = self, document = self.document, view = self.view, }) - + -- history view + self:registerModule("history", FileManagerHistory:new{ + dialog = self.dialog, + ui = self, + }) -- koreader plugins for _,plugin_module in ipairs(PluginLoader:loadPlugins()) do DEBUG("Loaded plugin", plugin_module.name, "at", plugin_module.path) diff --git a/frontend/device/generic/device.lua b/frontend/device/generic/device.lua index 8f20197da..19f465b96 100644 --- a/frontend/device/generic/device.lua +++ b/frontend/device/generic/device.lua @@ -128,25 +128,25 @@ function Device:onPowerEvent(ev) UIManager:scheduleIn(10, self.suspend) elseif (ev == "Power" or ev == "Resume") and self.screen_saver_mode then DEBUG("Resuming...") + local UIManager = require("ui/uimanager") + UIManager:unschedule(self.suspend) self:resume() + Screensaver:close() -- restore to previous rotation mode self.screen:setRotationMode(self.orig_rotation_mode) - local UIManager = require("ui/uimanager") - UIManager:unschedule(self.suspend) if self:needsScreenRefreshAfterResume() then self.screen:refreshFull() end self.screen_saver_mode = false self.powerd:refreshCapacity() - Screensaver:close() self.powerd:afterResume() end end --- Hardware function to suspend the device +-- Hardware specific method to suspend the device function Device:suspend() end --- Hardware function to resume the device +-- Hardware specific method to resume the device function Device:resume() end function Device:usbPlugIn() diff --git a/frontend/dump.lua b/frontend/dump.lua index 242ca1e08..91eff5ebb 100644 --- a/frontend/dump.lua +++ b/frontend/dump.lua @@ -49,7 +49,7 @@ local function _serialize(what, outt, indent, max_lv, history) elseif type(what) == "number" then if isUbuntuTouch then -- FIXME: the `SDL_CreateRenderer` function in Ubuntu touch somehow - -- use a strange locale that formats number like this: 1.10000000000000g+02 + -- use a strange locale that formats number like this: 1.10000000000000g+02 -- which cannot be recognized by loadfile after the number is dumped. -- Here the workaround is to preserve enough precision in "%.13e" format. insert(outt, string.format("%.13e", what)) @@ -59,7 +59,7 @@ local function _serialize(what, outt, indent, max_lv, history) elseif type(what) == "boolean" then insert(outt, tostring(what)) elseif type(what) == "function" then - insert(outt, "nil --[[ FUNCTION ]]") + insert(outt, tostring(what)) elseif type(what) == "nil" then insert(outt, "nil") end diff --git a/frontend/ui/gesturerange.lua b/frontend/ui/gesturerange.lua index fb5b7f4d4..e03fcdb9a 100644 --- a/frontend/ui/gesturerange.lua +++ b/frontend/ui/gesturerange.lua @@ -39,6 +39,7 @@ function GestureRange:match(gs) return false end end + if self.rate then -- This filed restraints the upper limit rate(matches per second). -- It's most useful for e-ink devices with less powerfull CPUs and diff --git a/frontend/ui/rendertext.lua b/frontend/ui/rendertext.lua index 14f5cc47d..13674f451 100644 --- a/frontend/ui/rendertext.lua +++ b/frontend/ui/rendertext.lua @@ -134,7 +134,7 @@ end --- Measure rendered size for a given text. -- -- Note this function does not render the text into a bitmap. Use it if you --- only care about the size for the rendered result. +-- only need the estimated size information. -- -- @int x start position for a given text (within maximum width) -- @int width maximum rendering width in pixels (think of it as size of the bitmap) @@ -170,13 +170,27 @@ function RenderText:sizeUtf8Text(x, width, face, text, kerning, bold) --- RenderText size information -- @table RenderTextSize - -- @field x length of the text on x coordinates - -- @field y_top top offset for the text (relative to center of the text) - -- @field y_bottom bottom offset for the text (relative to center of the text) + -- @field x length of the text on x coordinate + -- @field y_top distance between top-most pixel (scanline) and baseline + -- (bearingY) + -- @field y_bottom distance between bottom-most pixel (scanline) and + -- baseline (height - y_top) return { x = pen_x, y_top = pen_y_top, y_bottom = pen_y_bottom } end -function RenderText:renderUtf8Text(buffer, x, y, face, text, kerning, bold, fgcolor, width) +--- Render a given text into a given BlitBuffer +-- +-- @tparam BlitBuffer dest_bb Buffer to blit into +-- @int x starting x coordinate position within dest_bb +-- @int baseline y coordinate for baseline, within dest_bb +-- @tparam ui.font.FontFaceObj face font face that will be used for rendering +-- @string text text to render +-- @bool[opt=false] kerning whether the text should be measured with kerning +-- @bool[opt=false] bold whether the text should be measured as bold +-- @tparam[opt=BlitBuffer.COLOR_BLACK] BlitBuffer.COLOR fgcolor foreground color +-- @int[opt=nil] width maximum rendering width +-- @return int width of rendered bitmap +function RenderText:renderUtf8Text(dest_bb, x, baseline, face, text, kerning, bold, fgcolor, width) if not text then DEBUG("renderUtf8Text called without text"); return 0 @@ -190,7 +204,7 @@ function RenderText:renderUtf8Text(buffer, x, y, face, text, kerning, bold, fgco -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html local pen_x = 0 local prevcharcode = 0 - local text_width = buffer:getWidth() - x + local text_width = dest_bb:getWidth() - x if width and width < text_width then text_width = width end @@ -200,9 +214,10 @@ function RenderText:renderUtf8Text(buffer, x, y, face, text, kerning, bold, fgco if kerning and (prevcharcode ~= 0) then pen_x = pen_x + face.ftface:getKerning(prevcharcode, charcode) end - buffer:colorblitFrom( + dest_bb:colorblitFrom( glyph.bb, - x + pen_x + glyph.l, y - glyph.t, + x + pen_x + glyph.l, + baseline - glyph.t, 0, 0, glyph.bb:getWidth(), glyph.bb:getHeight(), fgcolor) diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 1e145ddd6..f50bbca89 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -227,9 +227,7 @@ end -- UIManager:unschedule(self.anonymousFunction) function UIManager:unschedule(action) for i = #self._task_queue, 1, -1 do - local task = self._task_queue[i] - if task.action == action then - -- remove from table + if self._task_queue[i].action == action then table.remove(self._task_queue, i) end end diff --git a/frontend/ui/widget/statuswidget.lua b/frontend/ui/widget/bookstatuswidget.lua similarity index 63% rename from frontend/ui/widget/statuswidget.lua rename to frontend/ui/widget/bookstatuswidget.lua index 33a0f5c1d..795db202a 100644 --- a/frontend/ui/widget/statuswidget.lua +++ b/frontend/ui/widget/bookstatuswidget.lua @@ -3,6 +3,7 @@ local FrameContainer = require("ui/widget/container/framecontainer") local CenterContainer = require("ui/widget/container/centercontainer") local LeftContainer = require("ui/widget/container/leftcontainer") local HorizontalGroup = require("ui/widget/horizontalgroup") +local OverlapGroup = require("ui/widget/overlapgroup") local VerticalGroup = require("ui/widget/verticalgroup") local HorizontalSpan = require("ui/widget/horizontalspan") local VerticalSpan = require("ui/widget/verticalspan") @@ -24,7 +25,6 @@ local Blitbuffer = require("ffi/blitbuffer") local Screen = require("device").screen local Font = require("ui/font") local TimeVal = require("ui/timeval") -local RenderText = require("ui/rendertext") local template = require("ffi/util").template local util = require("util") @@ -38,7 +38,8 @@ local _ = require("gettext") ["status"] = "Reading" ["modified"] = "24.01.2016" },]] -local StatusWidget = InputContainer:new{ +local BookStatusWidget = InputContainer:new{ + padding = Screen:scaleBySize(15), settings = nil, thumbnail = nil, props = nil, @@ -56,7 +57,7 @@ local StatusWidget = InputContainer:new{ } } -function StatusWidget:init() +function BookStatusWidget:init() self.stats.pages = self.document:getPageCount() self:getStatisticsSettings() if self.settings then @@ -86,84 +87,25 @@ function StatusWidget:init() self[1] = statusContainer end -function StatusWidget:showStatus() - local main_group = VerticalGroup:new{ align = "left" } - - local img_width = Screen:scaleBySize(132 * 1.5) - local img_height = Screen:scaleBySize(184 * 1.5) - - if Screen:getScreenMode() == "landscape" then - img_width = Screen:scaleBySize(132) - img_height = Screen:scaleBySize(184) - end - - local thumb - if self.thumbnail then - thumb = ImageWidget:new{ - image = self.thumbnail, - width = img_width, - height = img_height, - autoscale = false, - } - end - +function BookStatusWidget:showStatus() local screen_width = Screen:getWidth() - - local cover_with_title_and_author_container = CenterContainer:new{ - dimen = Geom:new{ w = screen_width, h = img_height }, - } - - local cover_with_title_and_author_group = HorizontalGroup:new{ align = "top" } - - local span = HorizontalSpan:new{ width = screen_width * 0.05 } - - table.insert(cover_with_title_and_author_group, span) - - if self.thumbnail then - table.insert(cover_with_title_and_author_group, thumb) - end - table.insert(cover_with_title_and_author_group, - self:generateTitleAuthorProgressGroup(screen_width - span.width - img_width, - img_height, - self.props.title, - self.props.authors, - self.view.state.page, --current page - self.document:getPageCount())) - table.insert(cover_with_title_and_author_container, cover_with_title_and_author_group) - - --portrait mode - local rateHeight = Screen:scaleBySize(60) - local statisticsHeight = Screen:scaleBySize(60) - local summaryHeight = Screen:scaleBySize(140) - local statusHeight = Screen:scaleBySize(105) - - --landscape mode - if Screen:getScreenMode() == "landscape" then - summaryHeight = Screen:scaleBySize(70) - statusHeight = Screen:scaleBySize(60) - end - - local header_group = HorizontalGroup:new{ - align = "center", - self:addHeader(screen_width * 0.95, Screen:scaleBySize(15), _("Progress")), - CloseButton:new{ window = self } + return VerticalGroup:new{ + align = "left", + OverlapGroup:new{ + dimen = Geom:new{ w = screen_width, h = Screen:scaleBySize(30) }, + CloseButton:new{ window = self }, + }, + self:genBookInfoGroup(), + self:genHeader(_("Statistics")), + self:genStatisticsGroup(screen_width), + self:genHeader(_("Review")), + self:genSummaryGroup(screen_width), + self:genHeader(_("Update Status")), + self:generateSwitchGroup(screen_width), } - - table.insert(main_group, header_group) - table.insert(main_group, cover_with_title_and_author_container) - table.insert(main_group, self:addHeader(screen_width, Screen:scaleBySize(25), _("Rate"))) - table.insert(main_group, self:generateRateGroup(screen_width, rateHeight, self.summary.rating)) - table.insert(main_group, self:addHeader(screen_width, Screen:scaleBySize(35), _("Statistics"))) - table.insert(main_group, self:generateStatisticsGroup(screen_width, statisticsHeight, - self:getStatDays(self.stats), self:getStatHours(self.stats), self:getReadPages(self.stats))) - table.insert(main_group, self:addHeader(screen_width, Screen:scaleBySize(35), _("Review"))) - table.insert(main_group, self:generateSummaryGroup(screen_width, summaryHeight, self.summary.note)) - table.insert(main_group, self:addHeader(screen_width, Screen:scaleBySize(25), _("Update Status"))) - table.insert(main_group, self:generateSwitchGroup(screen_width, statusHeight, self.summary.status)) - return main_group end -function StatusWidget:getStatDays(stats) +function BookStatusWidget:getStatDays(stats) if stats and stats.performance_in_pages then local dates = {} for k, v in pairs(stats.performance_in_pages) do @@ -174,129 +116,59 @@ function StatusWidget:getStatDays(stats) return "none" end - -function StatusWidget:getStatHours(stats) +function BookStatusWidget:getStatHours(stats) if stats and stats.total_time_in_sec then return util.secondsToClock(stats.total_time_in_sec, false) end return "none" end - -function StatusWidget:getReadPages(stats) +function BookStatusWidget:getReadPages(stats) if stats and stats.performance_in_pages and stats.pages then return util.tableSize(stats.performance_in_pages) .. "/" .. stats.pages end return "none" end -function StatusWidget:addHeader(width, height, title) - local group = HorizontalGroup:new{ - align = "center", - bordersize = 0, - } - - local bold = false +function BookStatusWidget:genHeader(title) + local width, height = Screen:getWidth(), Screen:scaleBySize(35) - local titleWidget = TextWidget:new{ + local header_title = TextWidget:new{ text = title, - face = self.large_font_face, - bold = bold, + face = self.medium_font_face, + fgcolor = Blitbuffer.gray(0.4), } - local titleSize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.large_font_face, title, true, bold) - local lineWidth = ((width - titleSize.x) * 0.5) + local padding_span = HorizontalSpan:new{ width = self.padding } + local line_width = (width - header_title:getSize().w) / 2 - self.padding * 2 local line_container = LeftContainer:new{ - dimen = Geom:new{ w = lineWidth, h = height }, + dimen = Geom:new{ w = line_width, h = height }, LineWidget:new{ background = Blitbuffer.gray(0.2), dimen = Geom:new{ - w = lineWidth, + w = line_width, h = 2, } } } - local text_container = CenterContainer:new{ - dimen = Geom:new{ w = titleSize.x, h = height }, - titleWidget, - } - - table.insert(group, line_container) - table.insert(group, text_container) - table.insert(group, line_container) - return group -end - -function StatusWidget:generateSwitchGroup(width, height, book_status) - local switch_container = CenterContainer:new{ - dimen = Geom:new{ w = width, h = height }, - } - - local args = { - [1] = "complete", - [2] = "reading", - [3] = "abandoned", - } - - local position = 2 - for k, v in pairs(args) do - if v == book_status then - position = k - end - end - - local config = { - event = "ChangeBookStatus", - default_value = 2, - toggle = { - [1] = _("Complete"), - [2] = _("Reading"), - [3] = _("Abandoned"), - }, - args = args, - default_arg = "reading", - values = { - [1] = 1, - [2] = 2, - [3] = 3, + return VerticalGroup:new{ + VerticalSpan:new{ width = Screen:scaleBySize(25) }, + HorizontalGroup:new{ + align = "center", + padding_span, + line_container, + padding_span, + header_title, + padding_span, + line_container, + padding_span, }, - name = "book_status", - alternate = false, - enabled = true, + VerticalSpan:new{ width = Screen:scaleBySize(5) }, } - - local switch = ToggleSwitch:new{ - width = width * 0.6, - default_value = config.default_value, - name = config.name, - name_text = config.name_text, - event = config.event, - toggle = config.toggle, - args = config.args, - alternate = config.alternate, - default_arg = config.default_arg, - values = config.values, - enabled = config.enable, - config = self, - } - - switch:setPosition(position) - - table.insert(switch_container, switch) - return switch_container end -function StatusWidget:onConfigChoose(values, name, event, args, events, position) - UIManager:scheduleIn(0.05, function() - if values then - self:onChangeBookStatus(args, position) - end - UIManager:setDirty("all") - end) -end - -function StatusWidget:onChangeBookStatus(option_name, option_value) +function BookStatusWidget:onChangeBookStatus(option_name, option_value) local curr_time = TimeVal:now() self.summary.status = option_name[option_value] self.summary.modified = os.date("%Y-%m-%d", curr_time.sec) @@ -304,44 +176,7 @@ function StatusWidget:onChangeBookStatus(option_name, option_value) return true end -function StatusWidget:onUpdateNote() - self.summary.note = self.input_note:getText() - self:saveSummary() - return true -end - - -function StatusWidget:saveSummary() - if self.summary then - self.settings:saveSetting("summary", self.summary) - self.settings:flush() - end -end - - -function StatusWidget:generateSummaryGroup(width, height, text) - - self.input_note = InputText:new{ - text = text, - face = self.medium_font_face, - width = width * 0.95, - height = height * 0.55, - scroll = true, - focused = false, - margin = 5, - padding = 0, - parent = self, - hint = _("A few words about the book"), - } - - local note_container = CenterContainer:new{ - dimen = Geom:new{ w = width, h = height }, - self.input_note - } - return note_container -end - -function StatusWidget:generateRateGroup(width, height, rating) +function BookStatusWidget:generateRateGroup(width, height, rating) self.stars_container = CenterContainer:new{ dimen = Geom:new{ w = width, h = height }, } @@ -350,7 +185,7 @@ function StatusWidget:generateRateGroup(width, height, rating) return self.stars_container end -function StatusWidget:setStar(num) +function BookStatusWidget:setStar(num) --clear previous data self.stars_container:clear() @@ -379,7 +214,106 @@ function StatusWidget:setStar(num) return true end -function StatusWidget:generateStatisticsGroup(width, height, days, average, pages) +function BookStatusWidget:genBookInfoGroup() + local screen_width = Screen:getWidth() + local split_span_width = screen_width * 0.05 + + local img_width, img_height + if Screen:getScreenMode() == "landscape" then + img_width = Screen:scaleBySize(132) + img_height = Screen:scaleBySize(184) + else + img_width = Screen:scaleBySize(132 * 1.5) + img_height = Screen:scaleBySize(184 * 1.5) + end + + local height = img_height + local width = screen_width - split_span_width - img_width + -- title + local book_meta_info_group = VerticalGroup:new{ + align = "center", + VerticalSpan:new{ width = height * 0.2 }, + TextBoxWidget:new{ + text = self.props.title, + width = width, + face = self.medium_font_face, + alignment = "center", + }, + + } + -- author + local text_author = TextWidget:new{ + text = self.props.authors, + face = self.small_font_face, + padding = 2, + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = text_author:getSize().h }, + text_author + } + ) + -- progress bar + local total_pages = self.document:getPageCount() + local read_percentage = self.view.state.page / total_pages + local progress_bar = ProgressWidget:new{ + width = width * 0.7, + height = Screen:scaleBySize(10), + percentage = read_percentage, + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = progress_bar:getSize().h }, + progress_bar + } + ) + -- complete text + local text_complete = TextWidget:new{ + text = template(_("%1% Completed"), + string.format("%1.f", read_percentage * 100)), + face = self.small_font_face, + } + table.insert(book_meta_info_group, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = text_complete:getSize().h }, + text_complete + } + ) + -- rating + table.insert(book_meta_info_group, + VerticalSpan:new{ width = Screen:scaleBySize(30) }) + local rateHeight = Screen:scaleBySize(60) + table.insert(book_meta_info_group, + self:generateRateGroup(screen_width, rateHeight, self.summary.rating)) + + -- build the final group + local book_info_group = HorizontalGroup:new{ + align = "top", + HorizontalSpan:new{ width = split_span_width } + } + -- thumbnail + if self.thumbnail then + table.insert(book_info_group, ImageWidget:new{ + image = self.thumbnail, + width = img_width, + height = img_height, + autoscale = false, + }) + end + + table.insert(book_info_group, CenterContainer:new{ + dimen = Geom:new{ w = width, h = height }, + book_meta_info_group, + }) + + return CenterContainer:new{ + dimen = Geom:new{ w = screen_width, h = img_height }, + book_info_group, + } +end + +function BookStatusWidget:genStatisticsGroup(width) + local height = Screen:scaleBySize(60) local statistics_container = CenterContainer:new{ dimen = Geom:new{ w = width, h = height }, } @@ -414,27 +348,26 @@ function StatusWidget:generateStatisticsGroup(width, height, days, average, page } } - local data_group = HorizontalGroup:new{ align = "center", CenterContainer:new{ dimen = Geom:new{ w = tile_width, h = tile_height }, TextWidget:new{ - text = days, + text = self:getStatDays(self.stats), face = self.medium_font_face, }, }, CenterContainer:new{ dimen = Geom:new{ w = tile_width, h = tile_height }, TextWidget:new{ - text = average, + text = self:getStatHours(self.stats), face = self.medium_font_face, }, }, CenterContainer:new{ dimen = Geom:new{ w = tile_width, h = tile_height }, TextWidget:new{ - text = pages, + text = self:getReadPages(self.stats), face = self.medium_font_face, } } @@ -447,82 +380,130 @@ function StatusWidget:generateStatisticsGroup(width, height, days, average, page return statistics_container end -function StatusWidget:generateTitleAuthorProgressGroup(width, height, title, authors, current_page, total_pages) +function BookStatusWidget:genSummaryGroup(width) + local height + if Screen:getScreenMode() == "landscape" then + height = Screen:scaleBySize(60) + else + height = Screen:scaleBySize(130) + end - local title_author_container = CenterContainer:new{ - dimen = Geom:new{ w = width, h = height }, + local text_padding = 5 + self.input_note = InputText:new{ + text = self.summary.note, + face = self.medium_font_face, + width = width - self.padding * 3, + height = height * 0.75, + scroll = true, + bordersize = 2, + focused = false, + padding = text_padding, + parent = self, + hint = _("A few words about the book"), } - local title_author_progressbar_group = VerticalGroup:new{ - align = "center", - VerticalSpan:new{ width = height * 0.2 }, - TextBoxWidget:new{ - text = title, - width = width, - face = self.medium_font_face, - alignment = "center", + return VerticalGroup:new{ + VerticalSpan:new{ width = Screen:scaleBySize(5) }, + CenterContainer:new{ + dimen = Geom:new{ w = width, h = height }, + self.input_note } } - local text_author = TextWidget:new{ - text = authors, - face = self.small_font_face, - padding = 2, - } +end - local author_container = CenterContainer:new{ - dimen = Geom:new{ w = width, h = text_author:getSize().h }, - text_author - } +function BookStatusWidget:onUpdateNote() + self.summary.note = self.input_note:getText() + self:saveSummary() + return true +end - table.insert(title_author_progressbar_group, author_container) +function BookStatusWidget:saveSummary() + if self.summary then + self.settings:saveSetting("summary", self.summary) + self.settings:flush() + end +end + +function BookStatusWidget:generateSwitchGroup(width) + local height + if Screen:getScreenMode() == "landscape" then + -- landscape mode + height = Screen:scaleBySize(60) + else + -- portrait mode + height = Screen:scaleBySize(105) + end - local read_percentage = current_page / total_pages - local progress_height = Screen:scaleBySize(10) + local args = { "complete", "reading", "abandoned" } - local progress_bar = ProgressWidget:new{ - width = width * 0.7, - height = progress_height, - percentage = read_percentage, - ticks = {}, - tick_width = 0, - last = total_pages, + local current_status = self.summary.status + local position = 2 + for k, v in pairs(args) do + if v == current_status then + position = k + end + end + + local config = { + event = "ChangeBookStatus", + default_value = 2, + args = args, + default_arg = "reading", + toggle = { _("Complete"), _("Reading"), _("Abandoned") }, + values = { 1, 2, 3 }, + name = "book_status", + alternate = false, + enabled = true, } - table.insert(title_author_progressbar_group, - CenterContainer:new{ - dimen = Geom:new{ w = width, h = progress_bar:getSize().h }, - progress_bar - } - ) - local text_complete = TextWidget:new{ - text = template(_("%1% Completed"), - string.format("%1.f", read_percentage * 100)), - face = self.small_font_face, + local switch = ToggleSwitch:new{ + width = width * 0.6, + default_value = config.default_value, + name = config.name, + name_text = config.name_text, + event = config.event, + toggle = config.toggle, + args = config.args, + alternate = config.alternate, + default_arg = config.default_arg, + values = config.values, + enabled = config.enable, + config = self, } + switch:setPosition(position) - local progress_bar_text_container = CenterContainer:new{ - dimen = Geom:new{ w = width, h = text_complete:getSize().h }, - text_complete + return VerticalGroup:new{ + VerticalSpan:new{ width = Screen:scaleBySize(10) }, + CenterContainer:new{ + ignore = "height", + dimen = Geom:new{ w = width, h = height }, + switch, + } } +end - table.insert(title_author_progressbar_group, progress_bar_text_container) - table.insert(title_author_container, title_author_progressbar_group) - return title_author_container +function BookStatusWidget:onConfigChoose(values, name, event, args, events, position) + UIManager:scheduleIn(0.05, function() + if values then + self:onChangeBookStatus(args, position) + end + UIManager:setDirty("all") + end) end -function StatusWidget:onAnyKeyPressed() +function BookStatusWidget:onAnyKeyPressed() return self:onClose() end -function StatusWidget:onClose() +function BookStatusWidget:onClose() self:saveSummary() UIManager:setDirty("all") UIManager:close(self) return true end -function StatusWidget:getStatisticsSettings() +function BookStatusWidget:getStatisticsSettings() if self.settings then local stats = self.settings:readSetting("stats") if stats then @@ -534,8 +515,7 @@ function StatusWidget:getStatisticsSettings() end end - -function StatusWidget:onSwitchFocus(inputbox) +function BookStatusWidget:onSwitchFocus(inputbox) self.note_dialog = InputDialog:new{ title = "Note", input = self.input_note:getText(), @@ -571,10 +551,9 @@ function StatusWidget:onSwitchFocus(inputbox) UIManager:show(self.note_dialog) end -function StatusWidget:closeInputDialog() +function BookStatusWidget:closeInputDialog() self.note_dialog:onClose() UIManager:close(self.note_dialog) end -return StatusWidget - +return BookStatusWidget diff --git a/frontend/ui/widget/closebutton.lua b/frontend/ui/widget/closebutton.lua index 1a19eebc2..8e81fc994 100644 --- a/frontend/ui/widget/closebutton.lua +++ b/frontend/ui/widget/closebutton.lua @@ -14,8 +14,11 @@ Example: local InputContainer = require("ui/widget/container/inputcontainer") local FrameContainer = require("ui/widget/container/framecontainer") +local HorizontalGroup = require("ui/widget/horizontalgroup") +local HorizontalSpan = require("ui/widget/horizontalspan") local TextWidget = require("ui/widget/textwidget") local GestureRange = require("ui/gesturerange") +local Screen = require("device").screen local Font = require("ui/font") local CloseButton = InputContainer:new{ @@ -28,18 +31,24 @@ function CloseButton:init() text = "×", face = Font:getFace("cfont", 32), } + local padding_span = HorizontalSpan:new{ width = Screen:scaleBySize(14) } + self[1] = FrameContainer:new{ bordersize = 0, padding = 0, - text_widget + HorizontalGroup:new{ + padding_span, + text_widget, + padding_span, + } } - self.dimen = text_widget:getSize() - self.ges_events.Close = { GestureRange:new{ ges = "tap", - range = self.dimen, + -- x and y coordinates for the widget is only known after the it is + -- drawn. so use callback to get range at runtime. + range = function() return self.dimen end, }, doc = "Tap on close button", } diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 242ad32f1..cdbb0c5e5 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -126,7 +126,7 @@ function DictQuickLookup:update() text = self.dictionary, face = self.title_face, bold = true, - width = self.width - self.button_padding, + width = self.width, } } -- lookup word @@ -218,7 +218,10 @@ function DictQuickLookup:update() } self.dict_bar = OverlapGroup:new{ - dimen = {w = button_table:getSize().w, h = self.dict_title:getSize().h}, + dimen = { + w = button_table:getSize().w + self.button_padding, + h = self.dict_title:getSize().h + }, self.dict_title, } diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 7008c25ca..0707adfef 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -100,7 +100,7 @@ function InputText:initTextBox(text) text_widget, } self.dimen = self[1]:getSize() - + -- FIXME: self.parent is not always in the widget statck (BookStatusWidget) UIManager:setDirty(self.parent, function() return "ui", self[1].dimen end) diff --git a/frontend/ui/widget/keyvaluepage.lua b/frontend/ui/widget/keyvaluepage.lua index caaf91660..1774133b1 100644 --- a/frontend/ui/widget/keyvaluepage.lua +++ b/frontend/ui/widget/keyvaluepage.lua @@ -77,6 +77,7 @@ function KeyValueTitle:init() else show_title_txt = self.title end + -- title and close button table.insert(self, OverlapGroup:new{ dimen = { w = self.width }, TextWidget:new{ @@ -85,12 +86,14 @@ function KeyValueTitle:init() }, self.close_button, }) + -- page count and separation line self.page_cnt = FrameContainer:new{ padding = 4, margin = 0, bordersize = 0, background = Blitbuffer.COLOR_WHITE, - overlap_offset = {0, -18}, + -- overlap offset x will be updated in setPageCount method + overlap_offset = {0, -15}, TextWidget:new{ text = "", -- page count fgcolor = Blitbuffer.COLOR_GREY, @@ -117,8 +120,7 @@ function KeyValueTitle:setPageCount(curr, total) return end self.page_cnt[1]:setText(curr .. "/" .. total) - self.page_cnt.overlap_offset[1] = (self.width - self.page_cnt:getSize().w - - self.close_button:getSize().w) + self.page_cnt.overlap_offset[1] = (self.width - self.page_cnt:getSize().w - 10) self.title_bottom[2] = self.page_cnt end diff --git a/frontend/ui/widget/progresswidget.lua b/frontend/ui/widget/progresswidget.lua index 8b31600fe..91f176316 100644 --- a/frontend/ui/widget/progresswidget.lua +++ b/frontend/ui/widget/progresswidget.lua @@ -1,10 +1,36 @@ +--[[-- +Widget for displaying progress bar. + +Configurable attributes: + + * width + * height + * margin_v -- vertical margin for solid infill + * margin_h -- horizontal margin for solid infill + * radius + * bordersize + * bordercolor + * bgcolor + * rectcolor -- infill color + * ticks (list) -- default to nil, use this if you want to insert markers + * tick_width + * last -- maximum tick + +Example: + + local foo_bar = ProgressWidget:new{ + width = 400, + height = 10, + percentage = 50/100, + } + UIManager:show(foo_bar) + +]] + local Widget = require("ui/widget/widget") local Geom = require("ui/geometry") local Blitbuffer = require("ffi/blitbuffer") ---[[ -ProgressWidget shows a progress bar ---]] local ProgressWidget = Widget:new{ width = nil, height = nil, @@ -16,7 +42,7 @@ local ProgressWidget = Widget:new{ bgcolor = Blitbuffer.COLOR_WHITE, rectcolor = Blitbuffer.gray(0.7), percentage = nil, - ticks = {}, + ticks = nil, tick_width = 3, last = nil, } @@ -32,18 +58,26 @@ function ProgressWidget:paintTo(bb, x, y) w = my_size.w, h = my_size.h } + -- fill background bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.bgcolor, self.radius) - bb:paintBorder(x, y, my_size.w, my_size.h, - self.bordersize, self.bordercolor, self.radius) - 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) - for i=1, #self.ticks do - local page = self.ticks[i] - bb:paintRect( + -- paint border + bb:paintBorder(x, y, + my_size.w, my_size.h, + self.bordersize, self.bordercolor, self.radius) + -- paint percentage infill + bb:paintRect(x+self.margin_h, math.ceil(y+self.margin_v+self.bordersize), + math.ceil((my_size.w-2*self.margin_h)*self.percentage), + my_size.h-2*(self.margin_v+self.bordersize), self.rectcolor) + if self.ticks then + 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) + y + self.margin_v + self.bordersize, + self.tick_width, + my_size.h-2*(self.margin_v+self.bordersize), + self.bordercolor) + end end end diff --git a/kodev b/kodev index cbdc21a35..d300d6a37 100755 --- a/kodev +++ b/kodev @@ -309,7 +309,9 @@ OPTIONS: if [ ! -z $2 ]; then test_path="${test_path}/$2" fi - busted --lua=./luajit ${opts} -o ./spec/$1/unit/verbose_print --exclude-tags=notest ${test_path} + busted --lua=./luajit ${opts} \ + -o ./spec/$1/unit/verbose_print \ + --exclude-tags=notest ${test_path} popd } diff --git a/spec/unit/gesturerange_spec.lua b/spec/unit/gesturerange_spec.lua new file mode 100644 index 000000000..89be942c7 --- /dev/null +++ b/spec/unit/gesturerange_spec.lua @@ -0,0 +1,39 @@ +require("commonrequire") + +local GestureRange = require("ui/gesturerange") +local Geom = require("ui/geometry") + +describe("gesturerange module", function() + it("should match tap event within range", function() + local g = GestureRange:new{ + ges = "tap", + range = Geom:new{ x = 0, y = 0, w = 200, h = 200}, + } + assert.truthy(g:match({ + ges = "tap", + pos = Geom:new{ x = 1, y = 1, w = 0, h = 0 }, + })) + end) + + it("should not match tap event outside of range", function() + local g = GestureRange:new{ + ges = "tap", + range = Geom:new{ x = 0, y = 0, w = 100, h = 100}, + } + assert.falsy(g:match({ + ges = "tap", + pos = Geom:new{ x = 105, y = 1, w = 0, h = 0 }, + })) + end) + + it("should match any event within nil range", function() + local g = GestureRange:new{ + ges = "tap", + range = nil, + } + assert.truthy(g:match({ + ges = "tap", + pos = Geom:new{ x = 1, y = 1, w = 1000000000000000000, h = 100 }, + })) + end) +end) diff --git a/spec/unit/uimanager_spec.lua b/spec/unit/uimanager_spec.lua index b7b170bcc..ac2676963 100644 --- a/spec/unit/uimanager_spec.lua +++ b/spec/unit/uimanager_spec.lua @@ -113,6 +113,25 @@ describe("UIManager spec", function() assert.are.same('quux', UIManager._task_queue[5].action) end) + it("should unschedule all the tasks with the same action", function() + local now = { util.gettime() } + local noop1 = function() end + UIManager:quit() + UIManager._task_queue = { + { time = {now[1] - 15, now[2] }, action = '3' }, + { time = {now[1] - 10, now[2] }, action = '1' }, + { time = {now[1], now[2] - 6 }, action = '3' }, + { time = {now[1], now[2] - 5 }, action = '2' }, + { time = now, action = '3' }, + } + -- insert into the tail slot + UIManager:unschedule('3') + assert.are.same({ + { time = {now[1] - 10, now[2] }, action = '1' }, + { time = {now[1], now[2] - 5 }, action = '2' }, + }, UIManager._task_queue) + end) + it("should not have race between unschedule and _checkTasks", function() local now = { util.gettime() } local run_count = 0 diff --git a/utils/wbuilder.lua b/utils/wbuilder.lua index 51be431e1..7073affe2 100755 --- a/utils/wbuilder.lua +++ b/utils/wbuilder.lua @@ -340,36 +340,57 @@ local TestInputText = InputText:new{ ----------------------------------------------------- -- key value page ----------------------------------------------------- -local KeyValuePage = require("ui/widget/keyvaluepage") -local kvp = KeyValuePage:new{ - title = 'Statistics This is a very very log item whose length should exceed the width of the men', - kv_pairs = { - {"1 Current period", "00:00:00"}, - {"This is a very very log item whose length should exceed the width of the menu.", "value"}, - {"2 Time to read", "00:00:00 00:00:00 00:00:00 00:00:00"}, - {"2 Time to read", "00:00:00"}, - {"3 Time to read", "00:00:00"}, - {"4 Time to read", "00:00:00"}, - {"5 Time to read", "00:00:00"}, - {"6 Time to read", "00:00:00"}, - {"7 Time to read", "00:00:00"}, - {"8 Time to read", "00:00:00"}, - {"9 Time to read", "00:00:00"}, - {"10 Time to read", "00:00:00"}, - {"11 Time to read", "00:00:00"}, - "----------------------------", - {"12 Time to read", "00:00:00"}, - {"13 Time to read", "00:00:00"}, - {"14 Time to read", "00:00:00"}, - {"15 Time to read", "00:00:00"}, - {"16 Time to read", "00:00:00"}, - {"17 Time to read", "00:00:00"}, - {"18 Time to read", "00:00:00"}, - {"19 Time to read", "00:00:00"}, - {"20 Time to read", "00:00:00"}, - {"21 Time to read", "00:00:00"}, - }, -} +function testKeyValuePage() + local KeyValuePage = require("ui/widget/keyvaluepage") + local kvp = KeyValuePage:new{ + title = 'Statistics This is a very very log item whose length should exceed the width of the men', + kv_pairs = { + {"1 Current period", "00:00:00"}, + {"This is a very very log item whose length should exceed the width of the menu.", "value"}, + {"2 Time to read", "00:00:00 00:00:00 00:00:00 00:00:00"}, + {"2 Time to read", "00:00:00"}, + {"3 Time to read", "00:00:00"}, + {"4 Time to read", "00:00:00"}, + {"5 Time to read", "00:00:00"}, + {"6 Time to read", "00:00:00"}, + {"7 Time to read", "00:00:00"}, + {"8 Time to read", "00:00:00"}, + {"9 Time to read", "00:00:00"}, + {"10 Time to read", "00:00:00"}, + {"11 Time to read", "00:00:00"}, + "----------------------------", + {"12 Time to read", "00:00:00"}, + {"13 Time to read", "00:00:00"}, + {"14 Time to read", "00:00:00"}, + {"15 Time to read", "00:00:00"}, + {"16 Time to read", "00:00:00"}, + {"17 Time to read", "00:00:00"}, + {"18 Time to read", "00:00:00"}, + {"19 Time to read", "00:00:00"}, + {"20 Time to read", "00:00:00"}, + {"21 Time to read", "00:00:00"}, + }, + } + UIManager:show(kvp) +end + +function testBookStatus() + -- doc = DocumentRegistry:openDocument("spec/front/unit/data/juliet.epub") + doc = DocumentRegistry:openDocument("spec/front/unit/data/2col.pdf") + reader = ReaderUI:new{ + dialog = readerwindow, + dimen = Geom:new{ w = Screen:getWidth() - 100, h = Screen:getHeight() - 100 }, + document = doc + } + + local status_page = require("ui/widget/bookstatuswidget"):new { + thumbnail = doc:getCoverPageImage(), + props = doc:getProps(), + document = doc, + view = reader.view, + } + UIManager:show(status_page) +end ----------------------------------------------------------------------- -- you may want to uncomment following show calls to see the changes @@ -385,6 +406,7 @@ UIManager:show(Clock:new()) --UIManager:show(keyboard) --UIManager:show(TestInputText) --TestInputText:onShowKeyboard() ---UIManager:show(kvp) +-- testKeyValuePage() +testBookStatus() UIManager:run()