From 0f9bfbf1476aee157c1498f171b0b193ed637b9b Mon Sep 17 00:00:00 2001 From: Alex Pletnev Date: Tue, 2 Feb 2016 19:38:14 +0200 Subject: [PATCH 1/3] #1730 Create complete book feature --- frontend/apps/reader/modules/readerstatus.lua | 59 ++ frontend/apps/reader/readerui.lua | 6 + frontend/ui/widget/inputdialog.lua | 6 +- frontend/ui/widget/statuswidget.lua | 653 ++++++++++++++++++ resources/icons/src/status.star.empty.svg | 5 + resources/icons/src/status.star.svg | 5 + resources/icons/stats.star.empty.png | Bin 0 -> 928 bytes resources/icons/stats.star.full.png | Bin 0 -> 761 bytes 8 files changed, 733 insertions(+), 1 deletion(-) create mode 100644 frontend/apps/reader/modules/readerstatus.lua create mode 100644 frontend/ui/widget/statuswidget.lua create mode 100644 resources/icons/src/status.star.empty.svg create mode 100644 resources/icons/src/status.star.svg create mode 100644 resources/icons/stats.star.empty.png create mode 100644 resources/icons/stats.star.full.png diff --git a/frontend/apps/reader/modules/readerstatus.lua b/frontend/apps/reader/modules/readerstatus.lua new file mode 100644 index 000000000..d25f50cfc --- /dev/null +++ b/frontend/apps/reader/modules/readerstatus.lua @@ -0,0 +1,59 @@ +local InputContainer = require("ui/widget/container/inputcontainer") +local StatusWidget = require("ui/widget/statuswidget") + +local UIManager = require("ui/uimanager") +local _ = require("gettext") + +local ReaderStatus = InputContainer:new { + document = nil, + summary = { + rating = 0, + note = nil, + status = "", + modified = "", + }, + enabled = true, +} + +function ReaderStatus:init() + if self.ui.document.is_djvu or self.ui.document.is_pdf or self.ui.document.is_pic then + self.enabled = false + return + end + UIManager:scheduleIn(0.1, function() self.ui.menu:registerToMainMenu(self) end) +end + +function ReaderStatus:addToMainMenu(tab_item_table) + table.insert(tab_item_table.typeset, { + text = _("Status"), + callback = function() + self:showStatus() + UIManager:setDirty("all") + end, + }) +end + +function ReaderStatus:showStatus() + local statusWidget = StatusWidget:new { + thumbnail = self.document:getCoverPageImage(), + props = self.document:getProps(), + document = self.document, + settings = self.settings, + } + UIManager:show(statusWidget) +end + +function ReaderStatus:onPageUpdate(pageno) + if self.enabled then + if pageno == self.document:getPageCount() then + self:showStatus() + end + end +end + +function ReaderStatus:onReadSettings(config) + self.settings = config +end + +return ReaderStatus + diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index 646fd8ef8..a6859f61e 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -42,6 +42,7 @@ local ReaderActivityIndicator = require("apps/reader/modules/readeractivityindic local FileManagerHistory = require("apps/filemanager/filemanagerhistory") local ReaderSearch = require("apps/reader/modules/readersearch") local ReaderLink = require("apps/reader/modules/readerlink") +local ReaderStatus = require("apps/reader/modules/readerstatus") local PluginLoader = require("apps/reader/pluginloader") --[[ @@ -300,6 +301,11 @@ function ReaderUI:init() ui = self }) + self:registerModule("status", ReaderStatus:new{ + ui = self, + document = self.document, + }) + -- koreader plugins for _,plugin_module in ipairs(PluginLoader:loadPlugins()) do DEBUG("Loaded plugin", plugin_module.name, "at", plugin_module.path) diff --git a/frontend/ui/widget/inputdialog.lua b/frontend/ui/widget/inputdialog.lua index f8a325d2c..8e8b6a395 100644 --- a/frontend/ui/widget/inputdialog.lua +++ b/frontend/ui/widget/inputdialog.lua @@ -24,6 +24,9 @@ local InputDialog = InputContainer:new{ width = nil, height = nil, + text_width = nil, + text_height = nil, + title_face = Font:getFace("tfont", 22), input_face = Font:getFace("cfont", 20), @@ -58,7 +61,8 @@ function InputDialog:init() text = self.input, hint = self.input_hint, face = self.input_face, - width = self.width * 0.9, + width = self.text_width or self.width * 0.9, + height = self.text_height or nil, input_type = self.input_type, text_type = self.text_type, enter_callback = self.enter_callback, diff --git a/frontend/ui/widget/statuswidget.lua b/frontend/ui/widget/statuswidget.lua new file mode 100644 index 000000000..ee48605b0 --- /dev/null +++ b/frontend/ui/widget/statuswidget.lua @@ -0,0 +1,653 @@ +local InputContainer = require("ui/widget/container/inputcontainer") +local FrameContainer = require("ui/widget/container/framecontainer") +local CenterContainer = require("ui/widget/container/centercontainer") +local RightContainer = require("ui/widget/container/rightcontainer") +local LeftContainer = require("ui/widget/container/leftcontainer") +local HorizontalGroup = require("ui/widget/horizontalgroup") +local VerticalGroup = require("ui/widget/verticalgroup") +local HorizontalSpan = require("ui/widget/horizontalspan") +local VerticalSpan = require("ui/widget/verticalspan") +local InputText = require("ui/widget/inputtext") +local ToggleSwitch = require("ui/widget/toggleswitch") +local Button = require("ui/widget/button") +local ProgressWidget = require("ui/widget/progresswidget") +local LineWidget = require("ui/widget/linewidget") +local TextWidget = require("ui/widget/textwidget") +local ImageWidget = require("ui/widget/imagewidget") +local CloseButton = require("ui/widget/closebutton") +local InputDialog = require("ui/widget/inputdialog") + +local UIManager = require("ui/uimanager") +local Geom = require("ui/geometry") +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 DocSettings = require("docsettings") +local DEBUG = require("dbg") +local util = require("util") +local _ = require("gettext") + +--[[ +--Save into sdr folder addtional section +["summary"] = { + ["rating"] = 5, + ["note"] = "Some text", + ["status"] = "Reading" + ["modified"] = "24.01.2016" +},]] +local StatusWidget = InputContainer:new { + settings = nil, + thumbnail = nil, + props = nil, + star = {}, + summary = { + rating = nil, + note = nil, + status = "", + modified = "", + }, + stats = { + total_time_in_sec = 0, + performance_in_pages = nil, + pages = nil, + + } +} + +function StatusWidget:init() + self.stats.pages = self.document:getPageCount() + self:getStatisticsSettings() + if self.settings then + self.summary = self.settings:readSetting("summary") + end + + self.small_font_face = Font:getFace("ffont", 15) + self.medium_font_face = Font:getFace("ffont", 20) + self.large_font_face = Font:getFace("ffont", 25) + + self.star = Button:new { + icon = "resources/icons/stats.star.empty.png", + bordersize = 0, + radius = 0, + margin = 0, + enabled = true, + show_parent = self, + } + + local statusContainer = FrameContainer:new { + dimen = Screen:getSize(), + background = Blitbuffer.COLOR_WHITE, + bordersize = 0, + padding = 0, + self:showStatus(), + } + 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 = nil + if self.thumbnail then + thumb = ImageWidget:new { + image = self.thumbnail, + width = img_width, + height = img_height, + autoscale = false, + } + end + + 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.document:getCurrentPage(), + 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")) + } + table.insert(header_group, CloseButton:new { window = self }) + + 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) + if stats and stats.performance_in_pages then + local dates = {} + for k, v in pairs(stats.performance_in_pages) do + dates[os.date("%Y-%m-%d", k)] = "" + end + return util.tableSize(dates) + end + return "none" +end + + +function StatusWidget: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) + 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 + + local titleWidget = TextWidget:new { + text = title, + face = self.large_font_face, + bold = bold, + } + local titleSize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.large_font_face, title, true, bold) + local lineWidth = ((width - titleSize.x) * 0.5) + + local line_container = LeftContainer:new { + dimen = Geom:new { w = lineWidth, h = height }, + LineWidget:new { + background = Blitbuffer.gray(0.2), + dimen = Geom:new { + w = lineWidth, + 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, + }, + name = "book_status", + alternate = false, + enabled = true, + } + + 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) + local curr_time = TimeVal:now() + self.summary.status = option_name[option_value] + self.summary.modified = os.date("%Y-%m-%d", curr_time.sec) + self:saveSummary() + return true +end + +function StatusWidget:onUpdateNote() + self.summary.note = self.input_note:getText() + self:saveSummary() + return true +end + + +function StatusWidget:saveSummary() + self.settings:saveSetting("summary", self.summary) + self.settings:flush() +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) + self.stars_container = CenterContainer:new { + dimen = Geom:new { w = width, h = height }, + } + + self:setStar(rating) + return self.stars_container +end + +function StatusWidget:setStar(num) + --clear previous data + self.stars_container:clear() + + local stars_group = HorizontalGroup:new { align = "center" } + if num then + self.summary.rating = num + self:saveSummary() + + for i = 1, num do + table.insert(stars_group, self.star:new { icon = "resources/icons/stats.star.full.png", callback = function() self:setStar(i) end }) + end + else + num = 0 + end + + for i = num + 1, 5 do + table.insert(stars_group, self.star:new { callback = function() self:setStar(i) end }) + end + + table.insert(self.stars_container, stars_group) + + UIManager:setDirty(nil, "partial") + return true +end + +--TODO generate from table +function StatusWidget:generateStatisticsGroup(width, height, days, average, pages) + local statistics_container = CenterContainer:new { + dimen = Geom:new { w = width, h = height }, + } + + local statistics_group = VerticalGroup:new { align = "left" } + local titles_group = HorizontalGroup:new { align = "center" } + local data_group = HorizontalGroup:new { align = "center" } + + local tile_width = width * 0.33333 + local tile_height = height * 0.5 + + local title_days_container = CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = _("Days"), + face = self.small_font_face, + }, + } + local title_time_container = CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = _("Time"), + face = self.small_font_face, + }, + } + local title_read_pages_container = CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = _("Read pages"), + face = self.small_font_face, + } + } + + table.insert(titles_group, title_days_container) + table.insert(titles_group, title_time_container) + table.insert(titles_group, title_read_pages_container) + + + local days_container = CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = days, + face = self.medium_font_face, + }, + } + local average_time_container = CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = average, + face = self.medium_font_face, + }, + } + local read_pages_container = CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = pages, + face = self.medium_font_face, + } + } + + table.insert(data_group, days_container) + table.insert(data_group, average_time_container) + table.insert(data_group, read_pages_container) + + table.insert(statistics_group, titles_group) + table.insert(statistics_group, data_group) + + table.insert(statistics_container, statistics_group) + return statistics_container +end + +function StatusWidget:generateTitleAuthorProgressGroup(width, height, title, authors, current_page, total_pages) + + local title_author_container = CenterContainer:new { + dimen = Geom:new { w = width, h = height }, + } + + local title_author_progressbar_group = VerticalGroup:new { align = "left" } + + table.insert(title_author_progressbar_group, VerticalSpan:new { width = height * 0.2 }) + + local title_text = self:_getVerticalList(title, width, self.medium_font_face, false) + + for i = 1, util.tableSize(title_text) do + local row = {} + for y = 1, util.tableSize(title_text[i]) do + table.insert(row, title_text[i][y].word) + end + + local text_title = TextWidget:new { + text = table.concat(row), + face = self.medium_font_face, + } + local title_text_container = CenterContainer:new { + dimen = Geom:new { w = width, h = text_title:getSize().h }, + text_title + } + table.insert(title_author_progressbar_group, title_text_container) + end + + local text_author = TextWidget:new { + text = authors, + face = self.small_font_face, + padding = 2, + } + + local author_container = CenterContainer:new { + dimen = Geom:new { w = width, h = text_author:getSize().h }, + text_author + } + + table.insert(title_author_progressbar_group, author_container) + + local progressWidget = ProgressWidget:new { + width = width * 0.7, + height = Screen:scaleBySize(10), + percentage = current_page / total_pages, + ticks = {}, + tick_width = 0, + last = total_pages, + } + + local progress_bar_container = CenterContainer:new { + dimen = Geom:new { w = width, h = progressWidget:getSize().h }, + progressWidget + } + + table.insert(title_author_progressbar_group, progress_bar_container) + local text_complete = TextWidget:new { + text = string.format("%1.f", progressWidget.percentage * 100) .. "% " .. _("Completed"), + face = self.small_font_face, + } + + local progress_bar_text_container = CenterContainer:new { + dimen = Geom:new { w = width, h = text_complete:getSize().h }, + text_complete + } + + table.insert(title_author_progressbar_group, progress_bar_text_container) + table.insert(title_author_container, title_author_progressbar_group) + return title_author_container +end + + +function StatusWidget:onAnyKeyPressed() + return self:onClose() +end + +function StatusWidget:onClose() + self:saveSummary() + UIManager:setDirty("all") + UIManager:close(self) + return true +end + + +--TODO: MOVE TO UTILS AND CHANGE ALSO TEXTBOXWIDGET +function StatusWidget:_wrapGreedyAlg(h_list, width) + local cur_line_width = 0 + local cur_line = {} + local v_list = {} + + for k, w in ipairs(h_list) do + w.box = { + x = cur_line_width, + w = w.width, + } + cur_line_width = cur_line_width + w.width + if w.word == "\n" then + if cur_line_width > 0 then + -- hard line break + table.insert(v_list, cur_line) + cur_line = {} + cur_line_width = 0 + end + elseif cur_line_width > width then + -- wrap to next line + table.insert(v_list, cur_line) + cur_line = {} + cur_line_width = w.width + table.insert(cur_line, w) + else + table.insert(cur_line, w) + end + end + -- handle last line + table.insert(v_list, cur_line) + + return v_list +end + +--TODO: MOVE TO UTILS AND CHANGE ALSO TEXTBOXWIDGET +function StatusWidget:_getVerticalList(text, width, face, bold) + -- build horizontal list + local h_list = {} + for line in util.gsplit(text, "\n", true) do + for words in line:gmatch("[\32-\127\192-\255]+[\128-\191]*") do + for word in util.gsplit(words, "%s+", true) do + for w in util.gsplit(word, "%p+", true) do + local word_box = {} + word_box.word = w + word_box.width = RenderText:sizeUtf8Text(0, Screen:getWidth(), face, w, true, bold).x + table.insert(h_list, word_box) + end + end + end + if line:sub(-1) == "\n" then table.insert(h_list, { word = '\n', width = 0 }) end + end + + -- @TODO check alg here 25.04 2012 (houqp) + -- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp) + return self:_wrapGreedyAlg(h_list, width) +end + + +function StatusWidget:getStatisticsSettings() + local lastfile = G_reader_settings:readSetting("lastfile") + if lastfile then + local settings = DocSettings:open(lastfile) + if settings then + self.stats = settings:readSetting("stats") + end + end + if self.settings then + local currStats = self.settings:readSetting("stats") + self.stats.total_time_in_sec = self.stats.total_time_in_sec + currStats.total_time_in_sec + for k, v in pairs(currStats.performance_in_pages) do self.stats.performance_in_pages[k] = v end + end +end + + +function StatusWidget:onSwitchFocus(inputbox) + self.note_dialog = InputDialog:new { + title = "Note", + input = self.input_note:getText(), + input_hint = "", + input_type = "text", + scroll = true, + text_height = Screen:scaleBySize(150), + buttons = { + { + { + text = _("Cancel"), + callback = function() + self:closeInputDialog() + end, + }, + { + text = _("OK"), + callback = function() + self.input_note:setText(self.note_dialog:getInputText()) + self:closeInputDialog() + self:onUpdateNote() + end, + }, + }, + }, + enter_callback = function() + self:closeInputDialog() + end, + width = Screen:getWidth() * 0.8, + height = Screen:getHeight() * 0.2, + } + self.note_dialog:onShowKeyboard() + UIManager:show(self.note_dialog) +end + +function StatusWidget:closeInputDialog() + self.note_dialog:onClose() + UIManager:close(self.note_dialog) +end + +return StatusWidget + diff --git a/resources/icons/src/status.star.empty.svg b/resources/icons/src/status.star.empty.svg new file mode 100644 index 000000000..5b43fd4bd --- /dev/null +++ b/resources/icons/src/status.star.empty.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/icons/src/status.star.svg b/resources/icons/src/status.star.svg new file mode 100644 index 000000000..c4bdaef67 --- /dev/null +++ b/resources/icons/src/status.star.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/icons/stats.star.empty.png b/resources/icons/stats.star.empty.png new file mode 100644 index 0000000000000000000000000000000000000000..75ba56ed5772192c283a28d4e5e02aecec9c4834 GIT binary patch literal 928 zcmV;R17G}!P)W>hA6?IfyI(=sFJ%59bh1X z+)RO2=RH?fS9-?4=bsFaV!j|kb!@xcDk5z&Q)0)yg9bqj$%6O~g($woVxgapBWU@Y zGi?pp4<4Mo;W$`tP_E#e}j$PDfAHw^VElA}l& zZvgVc-=vfeWr6{4!*5W^moiZRxG5*4LMRgrAP1#lC{r7NjZ*af(0MnOGD!dkrEr>o zJ!Vi5h@SA&&?Sj=CmsqK-uUhl;J<}m+4E!MwgaiJ^B3nj5IFTwIx}w3LjR<}-a;aWDYc_m{#fQU+(LCODfs2OPBIzR=^SAOONwC;~0$GB(P% zqGzKNbVHU@&D6H%1_6*%ON5DB8D(4`ij<<*2~g;9R1g63*a!#Vtcx-(AkvcMccI5o z-T=&1v)E-^PP(q6tDunVONAUqc>^%_{cT;w<)rJnoaoa|Lulj;fF&R9*QJaL2$W)T z&G$IU8vs#HwV&w%0j1c7^4U=H2H<+>|GIwh0R8}o$3C9;jNFp|0000(#+P5QawsE*($-I-mknfC|t76`%rCfC^9nDnJFOz<1s~XPwBGWM?%(MIM zWGyhm22v!{I>Z^O>5>=5GS@`+dR=%;)pp1gKW4Y6q^@>+b=; z@lvUz9A7S%-elzM3``~y8Vm*spx5gWKKn3l0FVz3F|1fD(&=>a;Xio+z`-pP3NH@t z_j{^TD&i1c01$P$-R_ICzGUPD0C9i4UcUff^|1@mCJqoKAMG^G0nUS@i33E*M>{+o zj|nd~NfQU?bUL(HEFyjMAtP}BL?6f~&ZK1|3b0zOsMTsEDr7dB5q3$+L;;fIqaAKE z8U#=&69qu-gB-|YNbaO+1Tg@l4M-n2CMhEYu-$G|ws^naIsgChdvdv)sxjd6a|aMP z@WpPo`?!Ni4wTDfb%yi{@t=f-!y%1EqpyUB%BLdFz_&>%D4Foe#vbBtkyKbRYyf@Z zCnd#6!C7JS-I7X@jP(xaAG}LaCdoJhyhBnP$)pCr1EeKoBN-6@JOq+Let)@KQoS>f z6QR^}t<+eDIL<>;?$0GlQzJM7aB_bxK$=q18Gw`fa{`8;>2%6+jC+%-jGO?trDMC@R^gGGp1VxDtN}QsV>X*rJEXf0+!vqA z9KjmEl$|1%LAe+1M00W}?3nF1${HZp_YdnE4q%Xx)Nx2!0Q}B&9AyO%92*6l4@<^$ zaFZmp*=(p*tC=40ylw>$92@B;#7V~FL7b#)Gg2#nuzWa@F@c0bvh6sE4G`@6+e*d+ zfxHS4OZTO0#!+kl-S>Buj0r+d^pD4*)l`!WfTkCsgel6H95r2sq|jXBj-%KB*a6A5 rcoGOCg-FC{0J8y1EBC4EKM(K+ejhN8>)Usb00000NkvXXu0mjfVx~`! literal 0 HcmV?d00001 From 7625621c84549200f484c280379a78221b6cbc0c Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Mon, 8 Feb 2016 23:16:06 -0800 Subject: [PATCH 2/3] feat(ui): support text alignment in TextBoxWidget --- frontend/ui/widget/textboxwidget.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index 853118d65..ad3a172d0 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -180,7 +180,16 @@ function TextBoxWidget:_render(v_list) local y = font_height local pen_x for _,l in ipairs(v_list) do - pen_x = 0 + if self.alignment == "center" then + local line_len = 0 + for _,w in ipairs(l) do + line_len = line_len + w.width + end + pen_x = (self.width - line_len)/2 + else + pen_x = 0 + end + for _,w in ipairs(l) do w.box.y = y - line_height_px - font_height --@TODO Don't use kerning for monospaced fonts. (houqp) From c0a2b3d4d92484f3832dd4a84ea8f8097e785686 Mon Sep 17 00:00:00 2001 From: Alex Pletnev Date: Tue, 9 Feb 2016 14:51:55 +0200 Subject: [PATCH 3/3] #1730 Create complete book feature --- frontend/apps/reader/modules/readerstatus.lua | 9 +- frontend/ui/widget/statuswidget.lua | 206 ++++++------------ 2 files changed, 71 insertions(+), 144 deletions(-) diff --git a/frontend/apps/reader/modules/readerstatus.lua b/frontend/apps/reader/modules/readerstatus.lua index d25f50cfc..580203bba 100644 --- a/frontend/apps/reader/modules/readerstatus.lua +++ b/frontend/apps/reader/modules/readerstatus.lua @@ -13,6 +13,7 @@ local ReaderStatus = InputContainer:new { modified = "", }, enabled = true, + total_pages = 0 } function ReaderStatus:init() @@ -20,6 +21,7 @@ function ReaderStatus:init() self.enabled = false return end + self.total_pages = self.document:getPageCount() UIManager:scheduleIn(0.1, function() self.ui.menu:registerToMainMenu(self) end) end @@ -45,7 +47,12 @@ end function ReaderStatus:onPageUpdate(pageno) if self.enabled then - if pageno == self.document:getPageCount() then + --in case when pageUpdate event generated before _document:render() + if pageno > self.total_pages or self.total_pages == 1 then + self.total_pages = self.document:getPageCount() + end + + if pageno == self.total_pages and self.total_pages ~= 1 then self:showStatus() end end diff --git a/frontend/ui/widget/statuswidget.lua b/frontend/ui/widget/statuswidget.lua index ee48605b0..39ae094a2 100644 --- a/frontend/ui/widget/statuswidget.lua +++ b/frontend/ui/widget/statuswidget.lua @@ -14,6 +14,8 @@ local ProgressWidget = require("ui/widget/progresswidget") local LineWidget = require("ui/widget/linewidget") local TextWidget = require("ui/widget/textwidget") local ImageWidget = require("ui/widget/imagewidget") +local TextBoxWidget = require("ui/widget/textboxwidget") + local CloseButton = require("ui/widget/closebutton") local InputDialog = require("ui/widget/inputdialog") @@ -51,9 +53,8 @@ local StatusWidget = InputContainer:new { }, stats = { total_time_in_sec = 0, - performance_in_pages = nil, - pages = nil, - + performance_in_pages = {}, + pages = 0, } } @@ -146,9 +147,9 @@ function StatusWidget:showStatus() local header_group = HorizontalGroup:new { align = "center", - self:addHeader(screen_width * 0.95, Screen:scaleBySize(15), _("Progress")) + self:addHeader(screen_width * 0.95, Screen:scaleBySize(15), _("Progress")), + CloseButton:new { window = self } } - table.insert(header_group, CloseButton:new { window = self }) table.insert(main_group, header_group) table.insert(main_group, cover_with_title_and_author_container) @@ -375,72 +376,67 @@ function StatusWidget:setStar(num) return true end ---TODO generate from table function StatusWidget:generateStatisticsGroup(width, height, days, average, pages) local statistics_container = CenterContainer:new { dimen = Geom:new { w = width, h = height }, } local statistics_group = VerticalGroup:new { align = "left" } - local titles_group = HorizontalGroup:new { align = "center" } - local data_group = HorizontalGroup:new { align = "center" } - local tile_width = width * 0.33333 - local tile_height = height * 0.5 + local tile_width = width / 3 + local tile_height = height / 2 - local title_days_container = CenterContainer:new { - dimen = Geom:new { w = tile_width, h = tile_height }, - TextWidget:new { - text = _("Days"), - face = self.small_font_face, + local titles_group = HorizontalGroup:new { + align = "center", + CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = _("Days"), + face = self.small_font_face, + }, }, - } - local title_time_container = CenterContainer:new { - dimen = Geom:new { w = tile_width, h = tile_height }, - TextWidget:new { - text = _("Time"), - face = self.small_font_face, + CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = _("Time"), + face = self.small_font_face, + }, }, - } - local title_read_pages_container = CenterContainer:new { - dimen = Geom:new { w = tile_width, h = tile_height }, - TextWidget:new { - text = _("Read pages"), - face = self.small_font_face, + CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = _("Read pages"), + face = self.small_font_face, + } } } - table.insert(titles_group, title_days_container) - table.insert(titles_group, title_time_container) - table.insert(titles_group, title_read_pages_container) - - local days_container = CenterContainer:new { - dimen = Geom:new { w = tile_width, h = tile_height }, - TextWidget:new { - text = days, - face = self.medium_font_face, + local data_group = HorizontalGroup:new { + align = "center", + CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = days, + face = self.medium_font_face, + }, }, - } - local average_time_container = CenterContainer:new { - dimen = Geom:new { w = tile_width, h = tile_height }, - TextWidget:new { - text = average, - face = self.medium_font_face, + CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = average, + face = self.medium_font_face, + }, }, - } - local read_pages_container = CenterContainer:new { - dimen = Geom:new { w = tile_width, h = tile_height }, - TextWidget:new { - text = pages, - face = self.medium_font_face, + CenterContainer:new { + dimen = Geom:new { w = tile_width, h = tile_height }, + TextWidget:new { + text = pages, + face = self.medium_font_face, + } } } - table.insert(data_group, days_container) - table.insert(data_group, average_time_container) - table.insert(data_group, read_pages_container) - table.insert(statistics_group, titles_group) table.insert(statistics_group, data_group) @@ -454,29 +450,16 @@ function StatusWidget:generateTitleAuthorProgressGroup(width, height, title, aut dimen = Geom:new { w = width, h = height }, } - local title_author_progressbar_group = VerticalGroup:new { align = "left" } - - table.insert(title_author_progressbar_group, VerticalSpan:new { width = height * 0.2 }) - - local title_text = self:_getVerticalList(title, width, self.medium_font_face, false) - - for i = 1, util.tableSize(title_text) do - local row = {} - for y = 1, util.tableSize(title_text[i]) do - table.insert(row, title_text[i][y].word) - end - - local text_title = TextWidget:new { - text = table.concat(row), + 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", } - local title_text_container = CenterContainer:new { - dimen = Geom:new { w = width, h = text_title:getSize().h }, - text_title - } - table.insert(title_author_progressbar_group, title_text_container) - end - + } local text_author = TextWidget:new { text = authors, face = self.small_font_face, @@ -532,78 +515,15 @@ function StatusWidget:onClose() return true end - ---TODO: MOVE TO UTILS AND CHANGE ALSO TEXTBOXWIDGET -function StatusWidget:_wrapGreedyAlg(h_list, width) - local cur_line_width = 0 - local cur_line = {} - local v_list = {} - - for k, w in ipairs(h_list) do - w.box = { - x = cur_line_width, - w = w.width, - } - cur_line_width = cur_line_width + w.width - if w.word == "\n" then - if cur_line_width > 0 then - -- hard line break - table.insert(v_list, cur_line) - cur_line = {} - cur_line_width = 0 - end - elseif cur_line_width > width then - -- wrap to next line - table.insert(v_list, cur_line) - cur_line = {} - cur_line_width = w.width - table.insert(cur_line, w) - else - table.insert(cur_line, w) - end - end - -- handle last line - table.insert(v_list, cur_line) - - return v_list -end - ---TODO: MOVE TO UTILS AND CHANGE ALSO TEXTBOXWIDGET -function StatusWidget:_getVerticalList(text, width, face, bold) - -- build horizontal list - local h_list = {} - for line in util.gsplit(text, "\n", true) do - for words in line:gmatch("[\32-\127\192-\255]+[\128-\191]*") do - for word in util.gsplit(words, "%s+", true) do - for w in util.gsplit(word, "%p+", true) do - local word_box = {} - word_box.word = w - word_box.width = RenderText:sizeUtf8Text(0, Screen:getWidth(), face, w, true, bold).x - table.insert(h_list, word_box) - end - end - end - if line:sub(-1) == "\n" then table.insert(h_list, { word = '\n', width = 0 }) end - end - - -- @TODO check alg here 25.04 2012 (houqp) - -- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp) - return self:_wrapGreedyAlg(h_list, width) -end - - function StatusWidget:getStatisticsSettings() - local lastfile = G_reader_settings:readSetting("lastfile") - if lastfile then - local settings = DocSettings:open(lastfile) - if settings then - self.stats = settings:readSetting("stats") - end - end if self.settings then - local currStats = self.settings:readSetting("stats") - self.stats.total_time_in_sec = self.stats.total_time_in_sec + currStats.total_time_in_sec - for k, v in pairs(currStats.performance_in_pages) do self.stats.performance_in_pages[k] = v end + local stats = self.settings:readSetting("stats") + if stats then + self.stats.total_time_in_sec = self.stats.total_time_in_sec + stats.total_time_in_sec + for k, v in pairs(stats.performance_in_pages) do + self.stats.performance_in_pages[k] = v + end + end end end