refactor: use new KeyValuePage widget for displaying statistics

pull/1817/head
Qingping Hou 8 years ago
parent 65455cee23
commit 11ee8d6fcc

@ -71,7 +71,7 @@ script:
- make all
- travis_retry make testfront
- luajit $(which luacheck) --no-color -q frontend | tee ./luacheck.out
- test $(grep Total ./luacheck.out | awk '{print $2}') -le 238
- test $(grep Total ./luacheck.out | awk '{print $2}') -le 220
after_success:
- make coverage

@ -22,6 +22,10 @@ function DataStorage:getDataDir()
return data_dir
end
function DataStorage:getHistoryDir()
return self:getDataDir() .. "/history"
end
local function initDataDir()
local data_dir = DataStorage:getDataDir()
local sub_data_dirs = {"cache", "clipboard", "data", "history", "ota", "screenshots"}

@ -6,10 +6,11 @@ local DataStorage = require("datastorage")
local UIManager = require("ui/uimanager")
local DocSettings = require("docsettings")
local Menu = require("ui/widget/menu")
local joinPath = require("ffi/util").joinPath
local Screen = require("device").screen
local _ = require("gettext")
local history_dir = DataStorage:getDataDir() .. "/history/"
local history_dir = DataStorage:getHistoryDir()
local FileManagerHistory = InputContainer:extend{
hist_menu_title = _("History"),
@ -30,7 +31,7 @@ function FileManagerHistory:onMenuHold(item)
{
text = _("Remove this item from history"),
callback = function()
os.remove(history_dir..item.histfile)
os.remove(joinPath(history_dir, item.histfile))
self._manager:updateItemTable()
UIManager:close(self.histfile_dialog)
end,
@ -82,7 +83,7 @@ function FileManagerHistory:updateItemTable()
self.hist = {}
for f in lfs.dir(history_dir) do
local path = history_dir..f
local path = joinPath(history_dir, f)
if lfs.attributes(path, "mode") == "file" then
local name = DocSettings:getNameFromHistory(f)
table.insert(self.hist, {

@ -12,7 +12,6 @@ local BBoxWidget = require("ui/widget/bboxwidget")
local HorizontalSpan = require("ui/widget/horizontalspan")
local Button = require("ui/widget/button")
local Math = require("optmath")
local DEBUG = require("dbg")
local Blitbuffer = require("ffi/blitbuffer")
local PageCropDialog = VerticalGroup:new{

@ -2,7 +2,6 @@ local InputContainer = require("ui/widget/container/inputcontainer")
local RightContainer = require("ui/widget/container/rightcontainer")
local ImageWidget = require("ui/widget/imagewidget")
local GestureRange = require("ui/gesturerange")
local UIManager = require("ui/uimanager")
local Device = require("device")
local Geom = require("ui/geometry")
local Screen = require("device").screen

@ -8,7 +8,6 @@ local Screen = require("device").screen
local Input = require("device").input
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
local DEBUG = require("dbg")
local T = require("ffi/util").template
local _ = require("gettext")

@ -14,7 +14,6 @@ local Screen = require("device").screen
local Geom = require("ui/geometry")
local Event = require("ui/event")
local Font = require("ui/font")
local DEBUG = require("dbg")
local _ = require("gettext")
local util = require("util")
@ -54,7 +53,7 @@ function ReaderFooter:init()
book_time_to_read = true,
chapter_time_to_read = true,
}
local text_default = ""
local text_default
if self.settings.all_at_once then
local info = {}
if self.settings.battery then

@ -1,5 +1,6 @@
local InputContainer = require("ui/widget/container/inputcontainer")
local UIManager = require("ui/uimanager")
local JSON = require("json")
local InfoMessage = require("ui/widget/infomessage")
local T = require("ffi/util").template
local _ = require("gettext")
@ -11,32 +12,35 @@ local ReaderHyphenation = InputContainer:new{
}
function ReaderHyphenation:init()
local lang_data_file = assert(io.open("./data/hyph/languages.json"), "r")
lang_data = json.decode(lang_data_file:read("*all"))
self.lang_table = {}
self.hyph_table = {}
self.hyph_alg = cre.getSelectedHyphDict()
for k,v in ipairs(lang_data) do
table.insert(self.hyph_table, {
text = v.name,
callback = function()
self.hyph_alg = v.filename
UIManager:show(InfoMessage:new{
text = T( _("Changed hyphenation to %1."), v.name),
})
self.ui.document:setHyphDictionary(v.filename)
self.ui.toc:onUpdateToc()
end,
checked_func = function()
return v.filename == self.hyph_alg
end
})
self.lang_table[v.language] = v.filename
if v.aliases then
for i,alias in ipairs(v.aliases) do
self.lang_table[alias] = v.filename
local lang_data_file = assert(io.open("./data/hyph/languages.json"), "r")
local ok, lang_data = pcall(JSON.decode, lang_data_file:read("*all"))
if ok and lang_data then
for k,v in ipairs(lang_data) do
table.insert(self.hyph_table, {
text = v.name,
callback = function()
self.hyph_alg = v.filename
UIManager:show(InfoMessage:new{
text = T(_("Changed hyphenation to %1."), v.name),
})
self.ui.document:setHyphDictionary(v.filename)
self.ui.toc:onUpdateToc()
end,
checked_func = function()
return v.filename == self.hyph_alg
end
})
self.lang_table[v.language] = v.filename
if v.aliases then
for i,alias in ipairs(v.aliases) do
self.lang_table[alias] = v.filename
end
end
end
end

@ -6,7 +6,6 @@ local Geom = require("ui/geometry")
local Screen = require("device").screen
local Device = require("device")
local Event = require("ui/event")
local DEBUG = require("dbg")
local _ = require("gettext")
local ReaderLink = InputContainer:new{

@ -318,17 +318,9 @@ function ReaderPaging:onSwipe(arg, ges)
elseif self.page_flipping_mode and self.original_page then
self:gotoPage(self.original_page)
elseif ges.direction == "west" then
if DCHANGE_WEST_SWIPE_TO_EAST then
self:onPagingRel(-1)
else
self:onPagingRel(1)
end
self:onPagingRel(1)
elseif ges.direction == "east" then
if DCHANGE_EAST_SWIPE_TO_WEST then
self:onPagingRel(1)
else
self:onPagingRel(-1)
end
self:onPagingRel(-1)
else
-- trigger full refresh
UIManager:setDirty(nil, "full")

@ -14,8 +14,7 @@ local ReaderPanning = InputContainer:new{
}
function ReaderPanning:init()
if Device:isTouchDevice() then
else
if Device:hasKeyboard() then
self.key_events = {
-- these will all generate the same event, just with different arguments
MoveUp = {

@ -261,17 +261,9 @@ end
function ReaderRolling:onSwipe(arg, ges)
if ges.direction == "west" or ges.direction == "north" then
if DCHANGE_WEST_SWIPE_TO_EAST then
self:onGotoViewRel(-1)
else
self:onGotoViewRel(1)
end
self:onGotoViewRel(1)
elseif ges.direction == "east" or ges.direction == "south" then
if DCHANGE_EAST_SWIPE_TO_WEST then
self:onGotoViewRel(1)
else
self:onGotoViewRel(-1)
end
self:onGotoViewRel(-1)
end
end

@ -424,6 +424,12 @@ function GestureDetector:handleSwipe(tev)
y = self.first_tevs[slot].y,
w = 0, h = 0,
}
-- TODO: dirty hack for some weird devices, replace it with better solution
if swipe_direction == "west" and DCHANGE_WEST_SWIPE_TO_EAST then
swipe_direction = "east"
elseif swipe_direction == "east" and DCHANGE_EAST_SWIPE_TO_WEST then
swipe_direction = "west"
end
DEBUG("swipe", swipe_direction, swipe_distance, "detected in slot", slot)
self:clearState(slot)
return {

@ -4,14 +4,14 @@ local dump = require("dump")
local DocSettings = {}
local history_dir = DataStorage:getDataDir() .. "/history/"
local history_dir = DataStorage:getHistoryDir()
function DocSettings:getSidecarDir(doc_path)
return doc_path:match("(.*)%.")..".sdr"
end
function DocSettings:getHistoryPath(fullpath)
return history_dir .. "[" .. fullpath:gsub("(.*/)([^/]+)","%1] %2"):gsub("/","#") .. ".lua"
return history_dir .. "/[" .. fullpath:gsub("(.*/)([^/]+)","%1] %2"):gsub("/","#") .. ".lua"
end
function DocSettings:getPathFromHistory(hist_name)

@ -31,9 +31,10 @@ function PdfDocument:init()
else
self:_readMetadata()
end
if not (self.info.number_of_pages > 0) then
-- TODO: handle this
-- if not (self.info.number_of_pages > 0) then
--error("No page found in PDF file")
end
-- end
end
function PdfDocument:unlock(password)

@ -10,6 +10,7 @@ local PicDocument = Document:new{
function PicDocument:init()
if not pic then pic = require("ffi/pic") end
local ok
ok, self._document = pcall(pic.openDocument, self.file)
if not ok then
error("Failed to open jpeg image")

@ -11,8 +11,8 @@ local GestureRange = {
scale = nil,
}
function GestureRange:new(o)
local o = o or {}
function GestureRange:new(from_o)
local o = from_o or {}
setmetatable(o, self)
self.__index = self
return o
@ -29,7 +29,7 @@ function GestureRange:match(gs)
-- e.g. range = function() return self.dimen end
-- for inputcontainer given that the x and y field of `self.dimen` is only
-- filled when the inputcontainer is painted into blitbuffer
local range = nil
local range
if type(self.range) == "function" then
range = self.range()
else

@ -18,7 +18,7 @@ local GlyphCache = Cache:new{
}
-- iterator over UTF8 encoded characters in a string
local function utf8Chars(input)
local function utf8Chars(input_text)
local function read_next_glyph(input, pos)
if string.len(input) < pos then return nil end
local value = string.byte(input, pos)
@ -60,7 +60,7 @@ local function utf8Chars(input)
return pos+bytes_left+1, glyph, string.sub(input, pos, pos+bytes_left)
end
end
return read_next_glyph, input, 1
return read_next_glyph, input_text, 1
end
function RenderText:getGlyph(face, charcode, bold)
@ -81,7 +81,7 @@ function RenderText:getGlyph(face, charcode, bold)
rendered_glyph = fb_face.ftface:renderGlyph(charcode, bold)
--DEBUG("fallback to font", font)
break
end
end
end
end
end

@ -9,7 +9,6 @@ local UIManager = require("ui/uimanager")
local Geom = require("ui/geometry")
local Device = require("device")
local Font = require("ui/font")
local DEBUG = require("dbg")
local _ = require("gettext")
--[[

@ -1,14 +1,24 @@
--[[--
Button widget that shows an "×" and handles closing window when tapped
Example:
local parent_widget = HorizontalGroup:new{}
table.insert(parent_widget, CloseButton:new{
window = parent_widget,
})
UIManager:show(parent_widget)
]]
local InputContainer = require("ui/widget/container/inputcontainer")
local FrameContainer = require("ui/widget/container/framecontainer")
local TextWidget = require("ui/widget/textwidget")
local GestureRange = require("ui/gesturerange")
local Font = require("ui/font")
--[[
a button widget that shows an "×" and handles closing window when tapped
--]]
local CloseButton = InputContainer:new{
align = "right",
overlap_align = "right",
window = nil,
}
@ -23,7 +33,7 @@ function CloseButton:init()
text_widget
}
self.dimen = text_widget:getSize():copy()
self.dimen = text_widget:getSize()
self.ges_events.Close = {
GestureRange:new{

@ -8,11 +8,15 @@ local WidgetContainer = Widget:new()
function WidgetContainer:init()
if self.dimen then
if not self.dimen.w then
self.dimen.w = self[1].getSize().w
end
if not self.dimen.h then
self.dimen.h = self[1].getSize().h
if self.initDimen then
self:initDimen()
else
if not self.dimen.w then
self.dimen.w = self[1].getSize().w
end
if not self.dimen.h then
self.dimen.h = self[1].getSize().h
end
end
end
end

@ -91,9 +91,7 @@ function DictQuickLookup:init()
},
}
table.insert(self.dict_bar,
CloseButton:new{
window = self,
})
CloseButton:new{ window = self, })
end
end

@ -39,6 +39,8 @@ function HorizontalGroup:paintTo(bb, x, y)
widget:paintTo(bb, x + self._offsets[i].x, y)
elseif self.align == "bottom" then
widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y)
else
print("[!] invalid alignment for HorizontalGroup", self.align)
end
end
end

@ -0,0 +1,273 @@
--[[--
Widget that presents a multi-page to show key value pairs.
Example:
local Foo = KeyValuePage:new{
title = "Statistics",
kv_pairs = {
{"Current period", "00:00:00"},
-- single or more "-" will generate a solid line
"----------------------------",
{"Page to read", "5"},
{"Time to read", "00:01:00"},
{"Press me", "will invoke the callback",
callback = function() print("hello") end },
},
}
UIManager:show(Foo)
]]
local InputContainer = require("ui/widget/container/inputcontainer")
local FrameContainer = require("ui/widget/container/framecontainer")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local OverlapGroup = require("ui/widget/overlapgroup")
local LeftContainer = require("ui/widget/container/leftcontainer")
local RightContainer = require("ui/widget/container/rightcontainer")
local LineWidget = require("ui/widget/linewidget")
local Blitbuffer = require("ffi/blitbuffer")
local CloseButton = require("ui/widget/closebutton")
local UIManager = require("ui/uimanager")
local TextWidget = require("ui/widget/textwidget")
local GestureRange = require("ui/gesturerange")
local Geom = require("ui/geometry")
local Font = require("ui/font")
local Device = require("device")
local Screen = Device.screen
local KeyValueTitle = VerticalGroup:new{
kv_page = nil,
title = "",
align = "left",
}
function KeyValueTitle:init()
self.close_button = CloseButton:new{ window = self }
table.insert(self, OverlapGroup:new{
dimen = { w = self.width },
TextWidget:new{
text = self.title,
face = Font:getFace("tfont", 26),
},
self.close_button,
})
self.page_cnt = FrameContainer:new{
padding = 4,
margin = 0,
bordersize = 0,
background = Blitbuffer.COLOR_WHITE,
overlap_offset = {0, -18},
TextWidget:new{
text = "", -- page count
fgcolor = Blitbuffer.COLOR_GREY,
face = Font:getFace("ffont", 16),
},
}
self.title_bottom = OverlapGroup:new{
dimen = { w = self.width, h = Screen:scaleBySize(2) },
LineWidget:new{
dimen = Geom:new{ w = self.width, h = Screen:scaleBySize(2) },
background = Blitbuffer.COLOR_GREY,
style = "solid",
},
self.page_cnt,
}
table.insert(self, self.title_bottom)
table.insert(self, VerticalSpan:new{ width = Screen:scaleBySize(5) })
end
function KeyValueTitle:setPageCount(curr, total)
if total == 1 then
-- remove page count if there is only one page
table.remove(self.title_bottom, 2)
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.title_bottom[2] = self.page_cnt
end
function KeyValueTitle:onClose()
self.kv_page:onClose()
return true
end
local KeyValueItem = InputContainer:new{
key = nil,
value = nil,
cface = Font:getFace("cfont", 24),
width = nil,
height = nil,
}
function KeyValueItem:init()
self.dimen = Geom:new{w = self.width, h = self.height}
if self.callback and Device:isTouchDevice() then
self.ges_events.Tap = {
GestureRange:new{
ges = "tap",
range = self.dimen,
}
}
end
self[1] = OverlapGroup:new{
dimen = self.dimen:copy(),
LeftContainer:new{
dimen = self.dimen:copy(),
TextWidget:new{
text = self.key,
face = self.cface,
}
},
RightContainer:new{
dimen = self.dimen:copy(),
TextWidget:new{
text = self.value,
face = self.cface,
}
}
}
end
function KeyValueItem:onTap()
self.callback()
return true
end
local KeyValuePage = InputContainer:new{
title = "",
width = nil,
height = nil,
-- index for the first item to show
show_page = 1,
}
function KeyValuePage:init()
self.dimen = Geom:new{
w = self.width or Screen:getWidth(),
h = self.height or Screen:getHeight(),
}
if Device:isTouchDevice() then
self.ges_events.Swipe = {
GestureRange:new{
ges = "swipe",
range = self.dimen,
}
}
end
local padding = Screen:scaleBySize(10)
self.item_width = self.dimen.w - 2 * padding
self.item_height = Screen:scaleBySize(30)
-- setup title bar
self.title_bar = KeyValueTitle:new{
title = self.title,
width = self.item_width,
height = self.item_height,
kv_page = self,
}
-- setup main content
self.item_padding = self.item_height / 4
local line_height = self.item_height + 2 * self.item_padding
local content_height = self.dimen.h - self.title_bar:getSize().h
self.items_per_page = math.floor(content_height / line_height)
self.pages = math.ceil(#self.kv_pairs / self.items_per_page)
self.main_content = VerticalGroup:new{}
self:_populateItems()
-- assemble page
self[1] = FrameContainer:new{
height = self.dimen.h,
padding = padding,
bordersize = 0,
background = Blitbuffer.COLOR_WHITE,
VerticalGroup:new{
self.title_bar,
self.main_content,
},
}
end
function KeyValuePage:nextPage()
local new_page = math.min(self.show_page+1, self.pages)
if new_page > self.show_page then
self.show_page = new_page
self:_populateItems()
end
end
function KeyValuePage:prevPage()
local new_page = math.max(self.show_page-1, 1)
if new_page < self.show_page then
self.show_page = new_page
self:_populateItems()
end
end
-- make sure self.item_padding and self.item_height are set before calling this
function KeyValuePage:_populateItems()
self.main_content:clear()
local idx_offset = (self.show_page - 1) * self.items_per_page
for idx = 1, self.items_per_page do
local entry = self.kv_pairs[idx_offset + idx]
if entry == nil then break end
table.insert(self.main_content,
VerticalSpan:new{ width = self.item_padding })
if type(entry) == "table" then
table.insert(
self.main_content,
KeyValueItem:new{
height = self.item_height,
width = self.item_width,
key = entry[1],
value = entry[2],
callback = entry.callback,
}
)
elseif type(entry) == "string" then
local c = string.sub(entry, 1, 1)
if c == "-" then
table.insert(self.main_content, LineWidget:new{
background = Blitbuffer.COLOR_LIGHT_GREY,
dimen = Geom:new{
w = self.item_width,
h = Screen:scaleBySize(2)
},
style = "solid",
})
end
end
table.insert(self.main_content,
VerticalSpan:new{ width = self.item_padding })
end
self.title_bar:setPageCount(self.show_page, self.pages)
UIManager:setDirty(self, function()
return "ui", self.dimen
end)
end
function KeyValuePage:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then
self:nextPage()
return true
elseif ges_ev.direction == "east" then
self:prevPage()
return true
end
end
function KeyValuePage:onClose()
UIManager:close(self)
return true
end
return KeyValuePage

@ -19,13 +19,13 @@ function LineWidget:paintTo(bb, x, y)
else
if self.empty_segments then
bb:paintRect(x, y,
self.empty_segments[1].s,
self.dimen.h,
self.background)
self.empty_segments[1].s,
self.dimen.h,
self.background)
bb:paintRect(x + self.empty_segments[1].e, y,
self.dimen.w - x - self.empty_segments[1].e,
self.dimen.h,
self.background)
self.dimen.w - x - self.empty_segments[1].e,
self.dimen.h,
self.background)
else
bb:paintRect(x, y, self.dimen.w, self.dimen.h, self.background)
end

@ -8,7 +8,6 @@ local BottomContainer = require("ui/widget/container/bottomcontainer")
local UnderlineContainer = require("ui/widget/container/underlinecontainer")
local FocusManager = require("ui/widget/focusmanager")
local TextWidget = require("ui/widget/textwidget")
local LineWidget = require("ui/widget/linewidget")
local OverlapGroup = require("ui/widget/overlapgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local HorizontalSpan = require("ui/widget/horizontalspan")
@ -82,7 +81,7 @@ NOTICE:
@menu entry must be provided in order to close the menu
--]]
local MenuCloseButton = InputContainer:new{
align = "right",
overlap_align = "right",
menu = nil,
dimen = Geom:new{},
}
@ -94,7 +93,10 @@ function MenuCloseButton:init()
}
local text_size = self[1]:getSize()
self.dimen.w, self.dimen.h = text_size.w*2, text_size.h*2
self.dimen = Geom:new{
w = text_size.w*2,
h = text_size.h*2,
}
self.ges_events.Close = {
GestureRange:new{
@ -110,45 +112,6 @@ function MenuCloseButton:onClose()
return true
end
--[[
Widget that displays a solid line in menu
--]]
local SeparatorMenuItem = InputContainer:new{
style = "solid",
dimen = nil,
_line_container = nil,
}
function SeparatorMenuItem:init()
self._line_container = CenterContainer:new{
vertical_align = "center",
dimen = Geom:new {
w = self.dimen.w,
h = self.dimen.h
},
HorizontalGroup:new {
align = "center",
OverlapGroup:new {
dimen = Geom:new { w = self.dimen.w, h = Screen:scaleBySize(2) },
LineWidget:new {
style = self.style,
dimen = Geom:new { w = self.dimen.w - 30, h = Screen:scaleBySize(2) },
}
},
}
}
self[1] = FrameContainer:new {
bordersize = 0,
padding = 0,
HorizontalGroup:new {
align = "center",
HorizontalSpan:new { width = 15 },
self._line_container
}
}
end
--[[
Widget that displays an item for menu
--]]
@ -430,7 +393,7 @@ function Menu:init()
-- start to set up widget layout --
-----------------------------------
self.menu_title = TextWidget:new{
align = "center",
overlap_align = "center",
text = self.title,
face = self.tface,
}
@ -578,9 +541,7 @@ function Menu:init()
if Device:isTouchDevice() then
if self.has_close_button then
table.insert(self.title_bar,
MenuCloseButton:new{
menu = self,
})
MenuCloseButton:new{ menu = self })
end
-- watch for outer region if it's a self contained widget
if self.is_popout then
@ -681,27 +642,20 @@ function Menu:updateItems(select_number)
item_shortcut = "Ent"
end
end
local item_tmp
if self.item_table[i].text == "-" then
item_tmp = SeparatorMenuItem:new{
dimen = self.item_dimen:new{ h = Screen:scaleBySize(10) },
}
else
item_tmp = MenuItem:new{
show_parent = self.show_parent,
state = self.item_table[i].state,
state_size = self.state_size or {},
text = self.item_table[i].text,
mandatory = self.item_table[i].mandatory,
bold = self.item_table.current == i,
face = self.cface,
dimen = self.item_dimen:new(),
shortcut = item_shortcut,
shortcut_style = shortcut_style,
table = self.item_table[i],
menu = self,
}
end
local item_tmp = MenuItem:new{
show_parent = self.show_parent,
state = self.item_table[i].state,
state_size = self.state_size or {},
text = self.item_table[i].text,
mandatory = self.item_table[i].mandatory,
bold = self.item_table.current == i,
face = self.cface,
dimen = self.item_dimen:new(),
shortcut = item_shortcut,
shortcut_style = shortcut_style,
table = self.item_table[i],
menu = self,
}
table.insert(self.item_group, item_tmp)
-- this is for focus manager
table.insert(self.layout, {item_tmp})
@ -923,17 +877,9 @@ end
function Menu:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then
if DCHANGE_WEST_SWIPE_TO_EAST then
self:onPrevPage()
else
self:onNextPage()
end
self:onNextPage()
elseif ges_ev.direction == "east" then
if DCHANGE_WEST_SWIPE_TO_EAST then
self:onNextPage()
else
self:onPrevPage()
end
self:onPrevPage()
end
end

@ -22,14 +22,22 @@ function OverlapGroup:getSize()
end
end
return self._size
end
function OverlapGroup:initDimen()
self:getSize() -- populate self._size
-- sync self._size with self.dimen, self.dimen has higher priority
if self.dimen.w then
self._size.w = self.dimen.w
else
self.dimen.w = self._size.w
end
if self.dimen.h then
self._size.h = self.dimen.h
else
self.dimen.h = self._size.h
end
return self._size
end
function OverlapGroup:paintTo(bb, x, y)
@ -37,10 +45,12 @@ function OverlapGroup:paintTo(bb, x, y)
for i, wget in ipairs(self) do
local wget_size = wget:getSize()
if wget.align == "right" then
if wget.overlap_align == "right" then
wget:paintTo(bb, x+size.w-wget_size.w, y)
elseif wget.align == "center" then
elseif wget.overlap_align == "center" then
wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y)
elseif wget.overlap_offset then
wget:paintTo(bb, x+wget.overlap_offset[1], y+wget.overlap_offset[2])
else
-- default to left
wget:paintTo(bb, x, y)

@ -25,23 +25,33 @@ local TextWidget = Widget:new{
--self._length = RenderText:renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, true, self.bold)
--end
function TextWidget:updateSize()
local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold)
if not tsize then
self._length = 0
else
self._length = tsize.x
end
self._height = self.face.size * 1.5
end
function TextWidget:getSize()
--if not self._bb then
--self:_render()
--end
--return { w = self._length, h = self._bb:getHeight() }
local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true, self.bold)
if not tsize then
return Geom:new{}
end
self._length = tsize.x
self._height = self.face.size * 1.5
self:updateSize()
return Geom:new{
w = self._length,
h = self._height,
}
end
function TextWidget:setText(text)
self.text = text
self:updateSize()
end
function TextWidget:paintTo(bb, x, y)
--if not self._bb then
--self:_render()

@ -94,16 +94,13 @@ function TouchMenuItem:onTapSelect(arg, ges)
end
if enabled == false then return end
UIManager:scheduleIn(0.0, function()
self.item_frame.invert = true
UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen
end)
self.item_frame.invert = true
UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen
end)
-- yield to main UI loop to invert item
UIManager:scheduleIn(0.1, function()
self.menu:onMenuSelect(self.item)
end)
UIManager:scheduleIn(0.5, function()
self.item_frame.invert = false
UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen

@ -1,21 +1,22 @@
local InputContainer = require("ui/widget/container/inputcontainer")
local MultiInputDialog = require("ui/widget/multiinputdialog")
local CenterContainer = require("ui/widget/container/centercontainer")
local KeyValuePage = require("ui/widget/keyvaluepage")
local UIManager = require("ui/uimanager")
local Screen = require("device").screen
local Menu = require("ui/widget/menu")
local Font = require("ui/font")
local TimeVal = require("ui/timeval")
local DataStorage = require("datastorage")
local lfs = require("libs/libkoreader-lfs")
local DEBUG = require("dbg")
local T = require("ffi/util").template
local joinPath = require("ffi/util").joinPath
local _ = require("gettext")
local util = require("util")
local tableutil = require("tableutil")
local statistics_dir = DataStorage:getDataDir() .. "/statistics/"
local history_dir = DataStorage:getDataDir() .. "/history/"
local history_dir = DataStorage:getHistoryDir()
local ReaderStatistics = InputContainer:new {
last_time = nil,
@ -51,37 +52,37 @@ function ReaderStatistics:init()
self.last_time = TimeVal:now()
end
function ReaderStatistics:getBookProperties()
local props = self.view.document:getProps()
if props.title == "No document" or props.title == "" then
-- FIXME: sometimes crengine returns "No document", try one more time
props = self.view.document:getProps()
end
return props
end
function ReaderStatistics:initData(config)
--first execution
-- first execution
if self.is_enabled then
local book_properties = self:getBookProperties()
self:savePropertiesInToData(book_properties)
if not self.data then
--first time merge data
self:inplaceMigration();
self.data = { performance_in_pages= {} }
self:inplaceMigration(); -- first time merge data
end
local book_properties = self:getBookProperties()
self.data.title = book_properties.title
self.data.authors = book_properties.authors
self.data.language = book_properties.language
self.data.series = book_properties.series
self.data.pages = self.view.document:getPageCount()
return
end
end
function ReaderStatistics:addToMainMenu(tab_item_table)
table.insert(tab_item_table.plugins, {
text = _("Statistics"),
sub_item_table = {
self:getStatisticEnabledMenuTable(),
self:getStatisticSettingsMenuTable(),
self:getStatisticForCurrentBookMenuTable(),
self:getStatisticTotalStatisticMenuTable(),
}
})
end
function ReaderStatistics:getStatisticEnabledMenuTable()
function ReaderStatistics:getStatisticEnabledMenuItem()
return {
text_func = function()
return _("Enabled")
end,
text = _("Enabled"),
checked_func = function() return self.is_enabled end,
callback = function()
-- if was enabled, have to save data to file
@ -99,18 +100,6 @@ function ReaderStatistics:getStatisticEnabledMenuTable()
}
end
function ReaderStatistics:getStatisticSettingsMenuTable()
return {
text_func = function()
return _("Settings")
end,
checked_func = function() return false end,
callback = function()
self:updateSettings()
end,
}
end
function ReaderStatistics:updateSettings()
self.settings_dialog = MultiInputDialog:new {
title = _("Statistics settings"),
@ -138,9 +127,9 @@ function ReaderStatistics:updateSettings()
{
text = _("Apply"),
callback = function()
self:saveSettings(MultiInputDialog:getFields())
self.settings_dialog:onClose()
UIManager:close(self.settings_dialog)
self:saveSettings(MultiInputDialog:getFields())
end
},
},
@ -153,101 +142,74 @@ function ReaderStatistics:updateSettings()
UIManager:show(self.settings_dialog)
end
function ReaderStatistics:getStatisticForCurrentBookMenuTable()
self.status_menu = {}
local book_status = Menu:new {
title = _("Status"),
item_table = self:updateCurrentStat(),
is_borderless = true,
is_popout = false,
is_enable_shortcut = false,
width = Screen:getWidth(),
height = Screen:getHeight(),
cface = Font:getFace("cfont", 20),
}
self.status_menu = CenterContainer:new {
dimen = Screen:getSize(),
book_status,
}
book_status.close_callback = function()
UIManager:close(self.status_menu)
end
book_status.show_parent = self.status_menu
return {
text = _("Current"),
enabled_func = function() return true end,
checked_func = function() return false end,
callback = function()
book_status:swithItemTable(nil, self:updateCurrentStat())
UIManager:show(self.status_menu)
return true
end
}
function ReaderStatistics:addToMainMenu(tab_item_table)
table.insert(tab_item_table.plugins, {
text = _("Statistics"),
sub_item_table = {
self:getStatisticEnabledMenuItem(),
{
text = _("Settings"),
callback = function() self:updateSettings() end,
},
{
text = _("Current book"),
callback = function()
UIManager:show(KeyValuePage:new{
title = _("Statistics"),
kv_pairs = self:getCurrentStat(),
})
end
},
{
text = _("All books"),
callback = function()
total_msg, kv_pairs = self:getTotalStat()
UIManager:show(KeyValuePage:new{
title = total_msg,
kv_pairs = kv_pairs,
})
end
},
},
})
end
function ReaderStatistics:getStatisticTotalStatisticMenuTable()
self.total_status = Menu:new {
title = _("Total"),
item_table = self:updateTotalStat(),
is_borderless = true,
is_popout = false,
is_enable_shortcut = false,
width = Screen:getWidth(),
height = Screen:getHeight(),
cface = Font:getFace("cfont", 20),
}
self.total_menu = CenterContainer:new {
dimen = Screen:getSize(),
self.total_status,
}
self.total_status.close_callback = function()
UIManager:close(self.total_menu)
function ReaderStatistics:getCurrentStat()
local dates = {}
for k, v in pairs(self.data.performance_in_pages) do
dates[os.date("%Y-%m-%d", k)] = ""
end
local total_days = util.tableSize(dates)
self.total_status.show_parent = self.total_menu
local read_pages = util.tableSize(self.data.performance_in_pages)
local current_page = self.view.state.page -- get current page from the view
local avg_time_per_page = self.data.total_time_in_sec / read_pages
return {
text = _("Total"),
callback = function()
self.total_status:swithItemTable(nil, self:updateTotalStat())
UIManager:show(self.total_menu)
return true
end
{ _("Current period"), util.secondsToClock(self.current_period, false) },
{ _("Time to read"), util.secondsToClock((self.data.pages - current_page) * avg_time_per_page, false) },
{ _("Total time"), util.secondsToClock(self.data.total_time_in_sec, false) },
{ _("Total highlights"), self.data.highlights },
{ _("Total notes"), self.data.notes },
{ _("Total days"), total_days },
{ _("Average time per page"), util.secondsToClock(avg_time_per_page, false) },
{ _("Read pages/Total pages"), read_pages .. "/" .. self.data.pages },
}
end
function ReaderStatistics:updateCurrentStat()
local stats = {}
local dates = {}
for k, v in pairs(self.data.performance_in_pages) do
dates[os.date("%Y-%m-%d", k)] = ""
function generateReadBooksTable(title, dates)
local result = {}
for k, v in tableutil.spairs(dates, function(t, a, b) return t[b].date < t[a].date end) do
table.insert(result, {
k,
T(_("Pages (%1) Time: %2"), v.count, util.secondsToClock(v.read, false))
})
end
local read_pages = util.tableSize(self.data.performance_in_pages)
local current_page = self.view.state.page --get current page from the view
local average_time_per_page = self.data.total_time_in_sec / read_pages
table.insert(stats, { text = _("Current period"), mandatory = util.secondsToClock(self.current_period, false) })
table.insert(stats, { text = _("Time to read"), mandatory = util.secondsToClock((self.data.pages - current_page) * average_time_per_page, false) })
table.insert(stats, { text = _("Total time"), mandatory = util.secondsToClock(self.data.total_time_in_sec, false) })
table.insert(stats, { text = _("Total highlights"), mandatory = self.data.highlights })
table.insert(stats, { text = _("Total notes"), mandatory = self.data.notes })
table.insert(stats, { text = _("Total days"), mandatory = util.tableSize(dates) })
table.insert(stats, { text = _("Average time per page"), mandatory = util.secondsToClock(average_time_per_page, false) })
table.insert(stats, { text = _("Read pages/Total pages"), mandatory = read_pages .. "/" .. self.data.pages })
return stats
return result
end
-- For backward compatibility
function ReaderStatistics:getDatesForBookOldFormat(book)
function getDatesForBookOldFormat(book)
local dates = {}
for k, v in pairs(book.details) do
@ -267,11 +229,10 @@ function ReaderStatistics:getDatesForBookOldFormat(book)
end
end
return self:generateReadBooksTable(book.title, dates)
return generateReadBooksTable(book.title, dates)
end
function ReaderStatistics:getDatesForBook(book)
function getDatesForBook(book)
local dates = {}
for k, v in pairs(book.performance_in_pages) do
@ -283,107 +244,93 @@ function ReaderStatistics:getDatesForBook(book)
count = 1
}
else
dates[date_text] = {
read = dates[date_text].read + v,
count = dates[date_text].count + 1,
date = dates[date_text].date
}
-- TODO: test this
local entry = dates[date_text]
entry.read = entry.read + v
entry.count = entry.count + 1
end
end
return self:generateReadBooksTable(book.title, dates)
end
function ReaderStatistics:generateReadBooksTable(title, dates)
local result = {}
table.insert(result, { text = title })
for k, v in tableutil.spairs(dates, function(t, a, b) return t[b].date < t[a].date end) do
table.insert(result, { text = k, mandatory = T(_("Pages (%1) Time: %2"), v.count, util.secondsToClock(v.read, false)) })
end
return result
return generateReadBooksTable(book.title, dates)
end
function ReaderStatistics:getTotalStat()
local total_stats = {
{
self.data.title,
util.secondsToClock(self.data.total_time_in_sec, false),
callback = function()
UIManager:show(KeyValuePage:new{
title = self.data.title,
kv_pairs = getDatesForBook(self.data),
})
end,
}
}
function ReaderStatistics:updateTotalStat()
local total_stats = {}
local total_books_time = 0
local proceded_titles = self:getStatisticsFromHistory(total_stats, total_books_time)
self:getOldStatisticsFromDirectory(proceded_titles, total_stats, total_books_time)
-- find stats for all other books in history
local proceded_titles, total_books_time = self:getStatisticsFromHistory(total_stats)
total_books_time = total_books_time + self:getOldStatisticsFromDirectory(proceded_titles, total_stats)
total_books_time = total_books_time + tonumber(self.data.total_time_in_sec)
table.insert(total_stats, 1, { text = _("Total hours read"), mandatory = util.secondsToClock(total_books_time, false) })
table.insert(total_stats, 2, { text = "-" })
table.insert(total_stats, 3, {
text = self.data.title,
mandatory = util.secondsToClock(self.data.total_time_in_sec, false),
callback = function()
self.total_status:swithItemTable(nil, self:getDatesForBook(self.data))
UIManager:show(self.total_menu)
return true
end,
})
return total_stats
return T(_("Total hours read %1"),
util.secondsToClock(total_books_time, false)),
total_stats
end
function ReaderStatistics:getStatisticsFromHistory(total_stats, total_books_time)
function ReaderStatistics:getStatisticsFromHistory(total_stats)
local titles = {}
local total_books_time = 0
for curr_file in lfs.dir(history_dir) do
local path = history_dir .. curr_file
local path = joinPath(history_dir, curr_file)
if lfs.attributes(path, "mode") == "file" then
local book_result = self:importFromFile(history_dir, curr_file)
local book_stats = book_result.stats
if book_stats and book_stats.title ~= self.data.title then
titles[book_stats.title] = true
table.insert(total_stats, {
text = book_stats.title,
mandatory = util.secondsToClock(book_stats.total_time_in_sec, false),
book_stats.title,
util.secondsToClock(book_stats.total_time_in_sec, false),
callback = function()
self.total_status:swithItemTable(nil, self:getDatesForBook(book_stats))
UIManager:show(self.total_menu)
return true
UIManager:show(KeyValuePage:new{
title = book_stats.title,
kv_pairs = getDatesForBook(book_stats),
})
end,
})
total_books_time = total_books_time + tonumber(book_stats.total_time_in_sec)
end
end
end
return titles
return titles, total_books_time
end
-- For backward compatibility
function ReaderStatistics:getOldStatisticsFromDirectory(exlude_titles, total_stats, total_books_time)
function ReaderStatistics:getOldStatisticsFromDirectory(exlude_titles, total_stats)
if lfs.attributes(statistics_dir, "mode") ~= "directory" then
return
return 0
end
local total_books_time = 0
for curr_file in lfs.dir(statistics_dir) do
local path = statistics_dir .. curr_file
if lfs.attributes(path, "mode") == "file" then
local book_result = self:importFromFile(statistics_dir, curr_file)
if book_result and book_result.title ~= self.data.title and not exlude_titles[book_result.title] then
table.insert(total_stats, {
text = book_result.title,
mandatory = util.secondsToClock(book_result.total_time, false),
book_result.title,
util.secondsToClock(book_result.total_time, false),
callback = function()
self.total_status:swithItemTable(nil, self:getDatesForBookOldFormat(book_result))
UIManager:show(self.total_menu)
return true
UIManager:show(KeyValuePage:new{
title = book_result.title,
kv_pairs = getDatesForBookOldFormat(book_result),
})
end,
})
total_books_time = total_books_time + tonumber(book_result.total_time)
end
end
end
end
function ReaderStatistics:getBookProperties()
local props = self.view.document:getProps()
if props.title == "No document" or props.title == "" then --sometime crengine returns "No document" try to get one more time
props = self.view.document:getProps()
end
return props
return total_books_time
end
function ReaderStatistics:onPageUpdate(pageno)
@ -411,14 +358,6 @@ function ReaderStatistics:onPageUpdate(pageno)
end
end
function ReaderStatistics:savePropertiesInToData(item)
self.data.title = item.title
self.data.authors = item.authors
self.data.language = item.language
self.data.series = item.series
end
-- For backward compatibility
function ReaderStatistics:inplaceMigration()
local oldData = self:importFromFile(statistics_dir, self.data.title .. ".stat")
@ -431,12 +370,12 @@ end
-- For backward compatibility
function ReaderStatistics:importFromFile(base_path, item)
item = string.gsub(item, "^%s*(.-)%s*$", "%1") --trim
if lfs.attributes(base_path .. item, "mode") == "directory" then
item = string.gsub(item, "^%s*(.-)%s*$", "%1") -- trim
local statistic_file = joinPath(base_path, item)
if lfs.attributes(statistic_file, "mode") == "directory" then
return
end
local statisticFile = base_path .. item
local ok, stored = pcall(dofile, statisticFile)
local ok, stored = pcall(dofile, statistic_file)
if ok then
return stored
else

@ -64,8 +64,7 @@ function TestGrid:paintTo(bb)
end
function TestVisible:paintTo(bb)
--Draw three lines at the borders to assess what the maximum visible coordinates are
-- Draw three lines at the borders to assess what the maximum visible coordinates are
v_line = math.floor(bb:getWidth() / 50)
h_line = math.floor(bb:getHeight() / 50)
-- Paint white background for higher contrast
@ -165,8 +164,6 @@ function Background:onQuitApplication()
UIManager:quit()
end
-----------------------------------------------------
-- example widget: a clock
-----------------------------------------------------
@ -185,7 +182,6 @@ function Clock:schedFunc()
self[1][1]:free()
self[1][1] = self:getTextWidget()
UIManager:setDirty(self)
-- reschedule
-- TODO: wait until next real second shift
UIManager:scheduleIn(1, function() self:schedFunc() end)
end
@ -250,7 +246,6 @@ M = Menu:new{
height = 600,
}
-----------------------------------------------------
-- a reader view widget
-----------------------------------------------------
@ -277,72 +272,62 @@ touch_menu = TouchMenu:new{
icon = "resources/icons/appbar.pokeball.png",
{
text = "item1",
callback = function()
end,
callback = function() end,
},
{
text = "item2",
callback = function()
end,
callback = function() end,
},
{
text = "item3",
callback = function()
end,
callback = function() end,
},
{
text = "item4",
callback = function()
end,
callback = function() end,
},
{
text = "item5",
callback = function()
end,
callback = function() end,
},
{
text = "item6",
callback = function()
end,
callback = function() end,
},
{
text = "item7",
callback = function()
end,
callback = function() end,
},
{
text = "item8",
callback = function()
end,
callback = function() end,
},
{
text = "item9",
callback = function()
end,
callback = function() end,
},
},
{
icon = "resources/icons/appbar.page.corner.bookmark.png",
{
text = "item10",
callback = function()
end,
callback = function() end,
},
{
text = "item11",
callback = function()
end,
callback = function() end,
},
},
{
icon = "resources/icons/appbar.home.png",
callback = function()
DEBUG("hello world!")
end
callback = function() DEBUG("hello world!") end
}
},
}
-----------------------------------------------------
-- input box widget
-----------------------------------------------------
local TestInputText = InputText:new{
width = 400,
enter_callback = function() print("Entered") end,
@ -353,11 +338,43 @@ local TestInputText = InputText:new{
},
}
-----------------------------------------------------
-- key value page
-----------------------------------------------------
local KeyValuePage = require("ui/widget/keyvaluepage")
local kvp = KeyValuePage:new{
title = 'Statistics',
kv_pairs = {
{"1 Current period", "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"},
},
}
-----------------------------------------------------------------------
-- you may want to uncomment following show calls to see the changes
-----------------------------------------------------------------------
--UIManager:show(Background:new())
-- UIManager:show(TestGrid)
--UIManager:show(TestGrid)
UIManager:show(TestVisible)
UIManager:show(Clock:new())
--UIManager:show(M)
@ -365,7 +382,9 @@ UIManager:show(Clock:new())
--UIManager:show(readerwindow)
--UIManager:show(touch_menu)
--UIManager:show(keyboard)
UIManager:show(TestInputText)
TestInputText:onShowKeyboard()
--UIManager:show(TestInputText)
--TestInputText:onShowKeyboard()
UIManager:show(kvp)
UIManager:run()

Loading…
Cancel
Save