add collapsable TOC menu

pull/990/head
chrox 10 years ago
parent 46963df0bb
commit 7c9130744c

@ -1,11 +1,12 @@
local InputContainer = require("ui/widget/container/inputcontainer")
local CenterContainer = require("ui/widget/container/centercontainer")
local GestureRange = require("ui/gesturerange")
local Button = require("ui/widget/button")
local UIManager = require("ui/uimanager")
local Menu = require("ui/widget/menu")
local Geom = require("ui/geometry")
local Screen = require("ui/screen")
local Device = require("ui/device")
local UIManager = require("ui/uimanager")
local Event = require("ui/event")
local Font = require("ui/font")
local DEBUG = require("dbg")
@ -14,6 +15,10 @@ local _ = require("gettext")
local ReaderToc = InputContainer:new{
toc = nil,
ticks = {},
toc_indent = " ",
collapsed_toc = {},
collapse_depth = 2,
expanded_nodes = {},
toc_menu_title = _("Table of contents"),
}
@ -54,6 +59,7 @@ end
function ReaderToc:onUpdateToc()
self.toc = nil
self.ticks = {}
self.collapsed_toc = {}
return true
end
@ -208,29 +214,66 @@ function ReaderToc:getChapterPagesDone(pageno, level)
return previous_chapter
end
function ReaderToc:updateCurrentNode()
if #self.collapsed_toc > 0 then
for i, v in ipairs(self.collapsed_toc) do
if v.page > self.pageno then
self.collapsed_toc.current = i > 1 and i - 1 or 1
break
end
end
end
end
function ReaderToc:onShowToc()
self:fillToc()
local max_depth = self:getMaxDepth()
-- build menu items
if #self.toc > 0 and not self.toc[1].text then
for _,v in ipairs(self.toc) do
v.text = (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title)
v.text = self.toc_indent:rep(v.depth-1)..self:cleanUpTocTitle(v.title)
v.mandatory = v.page
end
end
-- update current entry
if #self.toc > 0 then
for i=1, #self.toc do
v = self.toc[i]
if v.page > self.pageno then
self.toc.current = i > 1 and i - 1 or 1
break
-- update collapsible state
self.expand_button = Button:new{
icon = "resources/icons/appbar.control.expand.png",
bordersize = 0,
show_parent = self,
}
self.collapse_button = Button:new{
icon = "resources/icons/appbar.control.collapse.png",
bordersize = 0,
show_parent = self,
}
if #self.toc > 0 and #self.collapsed_toc == 0 then
local depth = 0
for i = #self.toc, 1, -1 do
local v = self.toc[i]
-- node v has child node(s)
if v.depth < depth then
v.state = self.expand_button:new{
callback = function() self:expandToc(i) end,
indent = self.toc_indent:rep(v.depth-1),
}
end
if v.depth < self.collapse_depth then
table.insert(self.collapsed_toc, 1, v)
end
depth = v.depth
end
end
self:updateCurrentNode()
local button_size = self.expand_button:getSize()
local toc_menu = Menu:new{
title = _("Table of Contents"),
item_table = self.toc,
item_table = self.collapsed_toc,
state_size = button_size,
ui = self.ui,
is_borderless = true,
width = Screen:getWidth(),
@ -264,11 +307,74 @@ function ReaderToc:onShowToc()
toc_menu.show_parent = menu_container
self.toc_menu = toc_menu
UIManager:show(menu_container)
return true
end
-- expand TOC node of index in raw toc table
function ReaderToc:expandToc(index)
table.insert(self.expanded_nodes, index)
local cur_node = self.toc[index]
local cur_depth = cur_node.depth
local collapsed_index = nil
for i, v in ipairs(self.collapsed_toc) do
if v.page == cur_node.page and v.depth == cur_depth
and v.text == cur_node.text then
collapsed_index = i
break
end
end
for i = index + 1, #self.toc do
local v = self.toc[i]
if v.depth == cur_depth + 1 then
collapsed_index = collapsed_index + 1
table.insert(self.collapsed_toc, collapsed_index, v)
elseif v.depth <= cur_depth then
break
end
end
-- change state of current node to expanded
cur_node.state = self.collapse_button:new{
callback = function() self:collapseToc(index) end,
indent = self.toc_indent:rep(cur_depth-1),
}
self:updateCurrentNode()
self.toc_menu:swithItemTable(nil, self.collapsed_toc, -1)
end
-- collapse TOC node of index in raw toc table
function ReaderToc:collapseToc(index)
local cur_node = self.toc[index]
local cur_depth = cur_node.depth
local i = 1
local is_child_node = false
while i <= #self.collapsed_toc do
local v = self.collapsed_toc[i]
if v.page > cur_node.page and v.depth <= cur_depth then
is_child_node = false
end
if is_child_node then
table.remove(self.collapsed_toc, i)
else
i = i + 1
end
if v.page == cur_node.page and v.depth == cur_depth
and v.text == cur_node.text then
is_child_node = true
end
end
-- change state of current node to collapsed
cur_node.state = self.expand_button:new{
callback = function() self:expandToc(index) end,
indent = self.toc_indent:rep(cur_depth-1),
}
self:updateCurrentNode()
self.toc_menu:swithItemTable(nil, self.collapsed_toc, -1)
end
function ReaderToc:addToMainMenu(tab_item_table)
-- insert table to main reader menu
table.insert(tab_item_table.navi, 1, {

@ -160,28 +160,51 @@ function MenuItem:init()
end
local mandatory = self.mandatory and ""..self.mandatory.." " or ""
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face, ""..mandatory, true).x
w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, self.text, true).x
if w + mandatory_w >= self.content_width then
if Device:isTouchDevice() then
else
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face,
""..mandatory, true, self.bold).x
local state_button_width = self.state_size.w or 0
w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
self.text, true, self.bold).x
if w + mandatory_w + state_button_width >= self.content_width then
if Device:hasKeyboard() then
self.active_key_events.ShowItemDetail = {
{"Right"}, doc = "show item detail"
}
end
local indicator = " >> "
local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, indicator, true).x
local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
indicator, true, self.bold).x
self.text = RenderText:getSubTextByWidth(self.text, self.face,
self.content_width - indicator_w - mandatory_w, true) .. indicator
self.content_width - indicator_w - mandatory_w - state_button_width,
true, self.bold) .. indicator
end
local state_button = self.state or HorizontalSpan:new{
width = state_button_width,
}
local state_indent = self.state and self.state.indent or ""
local state_container = LeftContainer:new{
dimen = Geom:new{w = self.content_width/2, h = self.dimen.h},
HorizontalGroup:new{
HorizontalSpan:new{
width = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
state_indent, true, self.bold).x,
},
state_button
}
}
local text_container = LeftContainer:new{
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
TextWidget:new{
text = self.text,
face = self.face,
bold = self.bold,
HorizontalGroup:new{
HorizontalSpan:new{
width = self.state_size.w,
},
TextWidget:new{
text = self.text,
face = self.face,
bold = self.bold,
}
}
}
@ -195,6 +218,7 @@ function MenuItem:init()
}
self._underline_container = UnderlineContainer:new{
vertical_align = "center",
dimen = Geom:new{
w = self.content_width,
h = self.dimen.h
@ -203,16 +227,17 @@ function MenuItem:init()
align = "center",
OverlapGroup:new{
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
state_container,
text_container,
mandatory_container,
},
}
}
self[1] = FrameContainer:new{
bordersize = 0,
padding = 0,
HorizontalGroup:new{
align = "center",
HorizontalSpan:new{ width = 5 },
ItemShortCutIcon:new{
dimen = shortcut_icon_dimen,
@ -569,6 +594,8 @@ function Menu:updateItems(select_number)
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,

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="16"
height="16"
viewBox="0 0 16 16"
enable-background="new 0 0 76.00 76.00"
xml:space="preserve"
id="svg2"
inkscape:version="0.48.4 r9939"
sodipodi:docname="appbar.control.collapse.svg"
inkscape:export-filename="/home/chrox/dev/koreader/resources/icons/appbar.control.collapse.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><metadata
id="metadata10"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs8" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="1015"
id="namedview6"
showgrid="false"
inkscape:zoom="12.421053"
inkscape:cx="30.041478"
inkscape:cy="18.178404"
inkscape:window-x="1280"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><path
d="M 16,3 8,13 8,13 0,3 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1;stroke-width:0.2;stroke-linejoin:round"
sodipodi:nodetypes="ccccc" /></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="16"
height="16"
viewBox="0 0 16 16"
enable-background="new 0 0 76.00 76.00"
xml:space="preserve"
id="svg2"
inkscape:version="0.48.4 r9939"
sodipodi:docname="appbar.control.expand.svg"
inkscape:export-filename="/home/chrox/dev/koreader/resources/icons/appbar.control.expand.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><metadata
id="metadata10"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs8" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="1015"
id="namedview6"
showgrid="false"
inkscape:zoom="12.421053"
inkscape:cx="30.041478"
inkscape:cy="18.178404"
inkscape:window-x="1280"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><path
d="M 3,0 13,8 13,8 3,16 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1;stroke-width:0.2;stroke-linejoin:round"
sodipodi:nodetypes="ccccc" /></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -24,7 +24,7 @@ describe("Readertoc module", function()
local ticks_level_0 = nil
it("should get ticks of level 0", function()
ticks_level_0 = toc:getTocTicks(0)
DEBUG("ticks", ticks_level_0)
--DEBUG("ticks", ticks_level_0)
assert.are.same(28, #ticks_level_0)
end)
local ticks_level_1 = nil
@ -68,4 +68,26 @@ describe("Readertoc module", function()
assert.are.same(0, toc:getChapterPagesDone(100, 0))
assert.are.same(10, toc:getChapterPagesDone(200, 0))
end)
describe("collasible TOC", function()
it("should collapse the secondary toc nodes by default", function()
toc:onShowToc()
assert.are.same(7, #toc.collapsed_toc)
end)
it("should not expand toc nodes that have no child nodes", function()
toc:expandToc(2)
assert.are.same(7, #toc.collapsed_toc)
end)
it("should expand toc nodes that have child nodes", function()
toc:expandToc(3)
assert.are.same(13, #toc.collapsed_toc)
toc:expandToc(18)
assert.are.same(18, #toc.collapsed_toc)
end)
it("should collapse toc nodes that have been expanded", function()
toc:collapseToc(3)
assert.are.same(12, #toc.collapsed_toc)
toc:collapseToc(18)
assert.are.same(7, #toc.collapsed_toc)
end)
end)
end)

Loading…
Cancel
Save