fix: override readerhighlight hold in readerfooter

Also fix touch zone dependency graph generation code.

ReaderHighlight has now been migrated to use touch zone

Inputcontainer's touch event handling logic changed to only stop
propagation when handler returns `true`. Previously, it stops
propagation when a handler is found. This is needed to support
both readerhighlight_tap and tap_forward touch zones.
pull/2522/head
Qingping Hou 7 years ago
parent 0b9c9d6ddb
commit 9b7aba3fba

@ -228,6 +228,7 @@ function ReaderFooter:setupTouchZones()
ges = "hold", ges = "hold",
screen_zone = footer_screen_zone, screen_zone = footer_screen_zone,
handler = function() return self:onHoldFooter() end, handler = function() return self:onHoldFooter() end,
overrides = {'readerhighlight_hold'},
}, },
}) })
end end

@ -1,7 +1,4 @@
local InputContainer = require("ui/widget/container/inputcontainer") local InputContainer = require("ui/widget/container/inputcontainer")
local GestureRange = require("ui/gesturerange")
local Geom = require("ui/geometry")
local Screen = require("device").screen
local Device = require("device") local Device = require("device")
local Event = require("ui/event") local Event = require("ui/event")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
@ -18,50 +15,53 @@ function ReaderHighlight:init()
end) end)
end end
function ReaderHighlight:initGesListener() function ReaderHighlight:setupTouchZones()
self.ges_events = { -- deligate gesture listener to readerui
Tap = { self.ges_events = {}
GestureRange:new{ self.onGesture = nil
ges = "tap",
range = Geom:new{ if not Device:isTouchDevice() then return end
x = 0, y = 0,
w = Screen:getWidth(), self.ui:registerTouchZones({
h = Screen:getHeight() {
} id = "readerhighlight_tap",
} ges = "tap",
screen_zone = {
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
},
overrides = { 'tap_forward', 'tap_backward', },
handler = function(ges) return self:onTap(nil, ges) end
}, },
Hold = { {
GestureRange:new{ id = "readerhighlight_hold",
ges = "hold", ges = "hold",
range = Geom:new{ screen_zone = {
x = 0, y = 0, ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
w = Screen:getWidth(), },
h = Screen:getHeight() handler = function(ges) return self:onHold(nil, ges) end
}
}
}, },
HoldRelease = { {
GestureRange:new{ id = "readerhighlight_hold_release",
ges = "hold_release", ges = "hold_release",
range = Geom:new{ screen_zone = {
x = 0, y = 0, ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
w = Screen:getWidth(), },
h = Screen:getHeight() handler = function() return self:onHoldRelease() end
}
}
}, },
HoldPan = { {
GestureRange:new{ id = "readerhighlight_hold_pan",
ges = "hold_pan", ges = "hold_pan",
range = Geom:new{ rate = 2.0,
x = 0, y = 0, screen_zone = {
w = Screen:getWidth(), ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
h = Screen:getHeight() },
}, handler = function(ges) return self:onHoldPan(nil, ges) end
rate = 2.0,
}
}, },
} })
end
function ReaderHighlight:onReaderReady()
self:setupTouchZones()
end end
function ReaderHighlight:addToMainMenu(tab_item_table) function ReaderHighlight:addToMainMenu(tab_item_table)
@ -109,13 +109,6 @@ function ReaderHighlight:genHighlightDrawerMenu()
} }
end end
function ReaderHighlight:onSetDimensions(dimen)
-- update listening according to new screen dimen
if Device:isTouchDevice() then
self:initGesListener()
end
end
function ReaderHighlight:clear() function ReaderHighlight:clear()
if self.ui.document.info.has_pages then if self.ui.document.info.has_pages then
self.view.highlight.temp = {} self.view.highlight.temp = {}
@ -161,15 +154,16 @@ function ReaderHighlight:onTapPageSavedHighlight(ges)
local pos = self.view:screenToPageTransform(ges.pos) local pos = self.view:screenToPageTransform(ges.pos)
for key, page in pairs(pages) do for key, page in pairs(pages) do
local items = self.view.highlight.saved[page] local items = self.view.highlight.saved[page]
if not items then items = {} end if items then
for i = 1, #items do for i = 1, #items do
local pos0, pos1 = items[i].pos0, items[i].pos1 local pos0, pos1 = items[i].pos0, items[i].pos1
local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1) local boxes = self.ui.document:getPageBoxesFromPositions(page, pos0, pos1)
if boxes then if boxes then
for index, box in pairs(boxes) do for index, box in pairs(boxes) do
if inside_box(pos, box) then if inside_box(pos, box) then
logger.dbg("Tap on hightlight") logger.dbg("Tap on hightlight")
return self:onShowHighlightDialog(page, i) return self:onShowHighlightDialog(page, i)
end
end end
end end
end end
@ -181,15 +175,16 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges)
local pos = self.view:screenToPageTransform(ges.pos) local pos = self.view:screenToPageTransform(ges.pos)
for page, _ in pairs(self.view.highlight.saved) do for page, _ in pairs(self.view.highlight.saved) do
local items = self.view.highlight.saved[page] local items = self.view.highlight.saved[page]
if not items then items = {} end if items then
for i = 1, #items do for i = 1, #items do
local pos0, pos1 = items[i].pos0, items[i].pos1 local pos0, pos1 = items[i].pos0, items[i].pos1
local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1) local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1)
if boxes then if boxes then
for index, box in pairs(boxes) do for index, box in pairs(boxes) do
if inside_box(pos, box) then if inside_box(pos, box) then
logger.dbg("Tap on hightlight") logger.dbg("Tap on hightlight")
return self:onShowHighlightDialog(page, i) return self:onShowHighlightDialog(page, i)
end
end end
end end
end end

@ -89,6 +89,7 @@ function ReaderMenu:onReaderReady()
ratio_x = DTAP_ZONE_MENU.x, ratio_y = DTAP_ZONE_MENU.y, ratio_x = DTAP_ZONE_MENU.x, ratio_y = DTAP_ZONE_MENU.y,
ratio_w = DTAP_ZONE_MENU.w, ratio_h = DTAP_ZONE_MENU.h, ratio_w = DTAP_ZONE_MENU.w, ratio_h = DTAP_ZONE_MENU.h,
}, },
overrides = { "tap_forward", "tap_backward", },
handler = function() return self:onTapShowMenu() end, handler = function() return self:onTapShowMenu() end,
}, },
}) })

@ -0,0 +1,106 @@
--[[--
DepGraph module.
Library for constructing dependency graphs.
Example:
local dg = DepGraph:new{}
dg:addNode('a1', {'a2', 'b1'})
dg:addNode('b1', {'a2', 'c1'})
dg:addNode('c1')
-- The return value of dg:serialize() will be:
-- {'a2', 'c1', 'b1', 'a1'}
]]
local DepGraph = {}
function DepGraph:new(new_o)
local o = new_o or {}
o.nodes = {}
setmetatable(o, self)
self.__index = self
return o
end
function DepGraph:addNode(node_key, deps)
if not self.nodes[node_key] then
self.nodes[node_key] = {}
end
if not deps then return end
local node_deps = {}
for _,dep_node_key in ipairs(deps) do
if not self.nodes[dep_node_key] then
self.nodes[dep_node_key] = {}
end
table.insert(node_deps, dep_node_key)
end
self.nodes[node_key].deps = node_deps
end
function DepGraph:removeNode(node_key)
self.nodes[node_key] = nil
for curr_node_key, curr_node in pairs(self.nodes) do
if curr_node.deps then
local remove_idx
for idx, dep_node_key in ipairs(self.nodes) do
if dep_node_key == node_key then
remove_idx = idx
break
end
end
if remove_idx then table.remove(curr_node.deps, remove_idx) end
end
end
end
function DepGraph:addNodeDep(node_key, dep_node_key)
local node = self.nodes[node_key]
if not node then
node = {}
self.nodes[node_key] = node
end
if not node.deps then node.deps = {} end
table.insert(node.deps, dep_node_key)
end
function DepGraph:serialize()
local visited = {}
local ordered_nodes = {}
for node_key,_ in pairs(self.nodes) do
if not visited[node_key] then
local queue = {node_key}
while #queue > 0 do
local pos = #queue
local curr_node_key = queue[pos]
local curr_node = self.nodes[curr_node_key]
local all_deps_visited = true
if curr_node.deps then
for _, dep_node_key in ipairs(curr_node.deps) do
if not visited[dep_node_key] then
-- only insert to queue for later process if node
-- has dependencies
if self.nodes[dep_node_key].deps then
table.insert(queue, dep_node_key)
else
table.insert(ordered_nodes, dep_node_key)
end
visited[dep_node_key] = true
all_deps_visited = false
break
end
end
end
if all_deps_visited then
visited[curr_node_key] = true
table.remove(queue, pos)
table.insert(ordered_nodes, curr_node_key)
end
end
end
end
return ordered_nodes
end
return DepGraph

@ -28,6 +28,7 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer")
local GestureRange = require("ui/gesturerange") local GestureRange = require("ui/gesturerange")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local Screen = require("device").screen local Screen = require("device").screen
local DepGraph = require("depgraph")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local Event = require("ui/event") local Event = require("ui/event")
local _ = require("gettext") local _ = require("gettext")
@ -57,8 +58,9 @@ function InputContainer:_init()
end end
end end
self.ges_events = new_ges_events self.ges_events = new_ges_events
self._touch_zones = {} self.touch_zone_dg = nil
self._touch_zone_pos_idx = {} self._zones = {}
self._ordered_touch_zones = {}
end end
function InputContainer:paintTo(bb, x, y) function InputContainer:paintTo(bb, x, y)
@ -125,12 +127,13 @@ require("ui/uimanager"):show(test_widget)
]] ]]
function InputContainer:registerTouchZones(zones) function InputContainer:registerTouchZones(zones)
local screen_width, screen_height = Screen:getWidth(), Screen:getHeight() local screen_width, screen_height = Screen:getWidth(), Screen:getHeight()
if not self.touch_zone_dg then self.touch_zone_dg = DepGraph:new{} end
for _, zone in ipairs(zones) do for _, zone in ipairs(zones) do
if self._touch_zone_pos_idx[zone.id] then -- override touch zone with the same id to support reregistration
table.remove(self._touch_zones, self._touch_zone_pos_idx[zone.id]) if self._zones[zone.id] then
self._touch_zone_pos_idx[zone.id] = nil self.touch_zone_dg:removeNode(zone.id)
end end
local tzone = { self._zones[zone.id]= {
def = zone, def = zone,
handler = zone.handler, handler = zone.handler,
gs_range = GestureRange:new{ gs_range = GestureRange:new{
@ -144,26 +147,16 @@ function InputContainer:registerTouchZones(zones)
}, },
}, },
} }
local insert_pos = #self._touch_zones self.touch_zone_dg:addNode(zone.id)
if insert_pos ~= 0 then if zone.overrides then
if zone.overrides then for _, override_zone_id in ipairs(zone.overrides) do
for _, override_id in ipairs(zone.overrides) do self.touch_zone_dg:addNodeDep(override_zone_id, zone.id)
local zone_idx = self._touch_zone_pos_idx[override_id]
if zone_idx and zone_idx < insert_pos then
insert_pos = zone_idx
end
end
else
insert_pos = 0
end end
end end
if insert_pos == 0 then end
table.insert(self._touch_zones, tzone) self._ordered_touch_zones = {}
self._touch_zone_pos_idx[zone.id] = 1 for _, zone_id in ipairs(self.touch_zone_dg:serialize()) do
else table.insert(self._ordered_touch_zones, self._zones[zone_id])
table.insert(self._touch_zones, insert_pos, tzone)
self._touch_zone_pos_idx[zone.id] = insert_pos
end
end end
end end
@ -173,7 +166,7 @@ Updates touch zones based on new screen dimensions.
@tparam ui.geometry.Geom new_screen_dimen new screen dimensions @tparam ui.geometry.Geom new_screen_dimen new screen dimensions
]] ]]
function InputContainer:updateTouchZonesOnScreenResize(new_screen_dimen) function InputContainer:updateTouchZonesOnScreenResize(new_screen_dimen)
for _, tzone in ipairs(self._touch_zones) do for _, tzone in ipairs(self._ordered_touch_zones) do
local range = tzone.gs_range.range local range = tzone.gs_range.range
range.x = new_screen_dimen.w * tzone.def.screen_zone.ratio_x range.x = new_screen_dimen.w * tzone.def.screen_zone.ratio_x
range.y = new_screen_dimen.h * tzone.def.screen_zone.ratio_y range.y = new_screen_dimen.h * tzone.def.screen_zone.ratio_y
@ -200,16 +193,18 @@ function InputContainer:onKeyPress(key)
end end
function InputContainer:onGesture(ev) function InputContainer:onGesture(ev)
for _, tzone in ipairs(self._touch_zones) do for _, tzone in ipairs(self._ordered_touch_zones) do
if tzone.gs_range:match(ev) then if tzone.gs_range:match(ev) and tzone.handler(ev) then
return tzone.handler(ev) return true
end end
end end
for name, gsseq in pairs(self.ges_events) do for name, gsseq in pairs(self.ges_events) do
for _, gs_range in ipairs(gsseq) do for _, gs_range in ipairs(gsseq) do
if gs_range:match(ev) then if gs_range:match(ev) then
local eventname = gsseq.event or name local eventname = gsseq.event or name
return self:handleEvent(Event:new(eventname, gsseq.args, ev)) if self:handleEvent(Event:new(eventname, gsseq.args, ev)) then
return true
end
end end
end end
end end

@ -2,6 +2,11 @@ require "defaults"
package.path = "?.lua;common/?.lua;rocks/share/lua/5.1/?.lua;frontend/?.lua;" .. package.path package.path = "?.lua;common/?.lua;rocks/share/lua/5.1/?.lua;frontend/?.lua;" .. package.path
package.cpath = "?.so;common/?.so;/usr/lib/lua/?.so;rocks/lib/lua/5.1/?.so;" .. package.cpath package.cpath = "?.so;common/?.so;/usr/lib/lua/?.so;rocks/lib/lua/5.1/?.so;" .. package.cpath
-- turn off debug by default and set log level to warning
require("dbg"):turnOff()
local logger = require("logger")
logger:setLevel(logger.levels.warn)
-- global reader settings -- global reader settings
local DataStorage = require("datastorage") local DataStorage = require("datastorage")
os.remove(DataStorage:getDataDir().."/settings.reader.lua") os.remove(DataStorage:getDataDir().."/settings.reader.lua")
@ -20,9 +25,6 @@ Screen:init()
local Input = require("device").input local Input = require("device").input
Input.dummy = true Input.dummy = true
-- turn on debug
--require("dbg"):turnOn()
function assertAlmostEquals(expected, actual, margin) function assertAlmostEquals(expected, actual, margin)
if type(actual) ~= 'number' or type(expected) ~= 'number' if type(actual) ~= 'number' or type(expected) ~= 'number'
or type(margin) ~= 'number' then or type(margin) ~= 'number' then

@ -0,0 +1,102 @@
describe("DepGraph module", function()
local DepGraph, logger
setup(function()
require("commonrequire")
logger = require("logger")
DepGraph = require("depgraph")
end)
it("should serialize simple graph", function()
local dg = DepGraph:new{}
dg:addNode('a1', {'a2', 'b1'})
dg:addNode('b1', {'a2', 'c1'})
dg:addNode('c1')
assert.are.same({'a2', 'c1', 'b1', 'a1'}, dg:serialize())
end)
it("should serialize complex graph", function()
local dg = DepGraph:new{}
dg:addNode('readerfooter_tap')
dg:addNode('readerfooter_hold', {})
dg:addNode('readerhighlight_tap', {'tap_backward', 'tap_forward'})
dg:addNode('tap_backward', {'readerfooter_tap', 'readermenu_tap'})
dg:addNode('tap_forward', {'readerfooter_tap', 'readermenu_tap'})
dg:addNode('readerhighlight_hold', {'readerfooter_hold'})
dg:addNode('readerhighlight_hold_release', {})
dg:addNode('readerhighlight_hold_pan', {})
dg:addNode('readermenu_tap', {'readerfooter_tap'})
dg:addNode('paging_swipe', {})
dg:addNode('paging_pan', {})
dg:addNode('paging_pan_release', {})
assert.are.same({
'readerfooter_tap',
'readermenu_tap',
'tap_backward',
'readerhighlight_hold_pan',
'paging_pan_release',
'readerfooter_hold',
'readerhighlight_hold',
'paging_pan',
'paging_swipe',
'tap_forward',
'readerhighlight_tap',
'readerhighlight_hold_release',
}, dg:serialize())
end)
it("should serialize complex graph2", function()
local dg = DepGraph:new{}
dg:addNode('readerfooter_tap')
dg:addNode('readerfooter_hold', {})
dg:addNode('readerhighlight_tap', {})
dg:addNode('tap_backward', {'readerfooter_tap', 'readermenu_tap', 'readerhighlight_tap'})
dg:addNode('tap_forward', {'readerfooter_tap', 'readermenu_tap', 'readerhighlight_tap'})
dg:addNode('readerhighlight_hold', {'readerfooter_hold'})
dg:addNode('readerhighlight_hold_release', {})
dg:addNode('readerhighlight_hold_pan', {})
dg:addNode('readermenu_tap', {'readerfooter_tap'})
assert.are.same({
'readerfooter_tap',
'readermenu_tap',
'readerhighlight_tap',
'tap_backward',
'readerhighlight_hold_pan',
'readerfooter_hold',
'readerhighlight_hold',
'tap_forward',
'readerhighlight_hold_release',
}, dg:serialize())
end)
it("should serialize complex graph with duplicates", function()
local dg = DepGraph:new{}
dg:addNode('readerfooter_tap', {})
dg:addNode('readerfooter_hold', {})
dg:addNode('readerhighlight_tap',
{'tap_backward', 'tap_backward', 'tap_forward'})
dg:addNode('tap_backward', {'readerfooter_tap', 'readermenu_tap'})
dg:addNode('tap_forward', {'readerfooter_tap', 'readermenu_tap'})
dg:addNode('readerhighlight_hold', {'readerfooter_hold'})
dg:addNode('readerhighlight_hold_release', {})
dg:addNode('readerhighlight_hold_pan', {})
dg:addNode('readermenu_tap', {'readerfooter_tap'})
dg:addNode('paging_swipe', {})
dg:addNode('paging_pan', {})
dg:addNode('paging_pan_release', {})
assert.are.same({
'readerfooter_tap',
'readermenu_tap',
'tap_backward',
'readerhighlight_hold_pan',
'paging_pan_release',
'readerfooter_hold',
'readerhighlight_hold',
'paging_pan',
'paging_swipe',
'tap_forward',
'readerhighlight_tap',
'readerhighlight_hold_release',
}, dg:serialize())
end)
end)

@ -8,7 +8,7 @@ describe("InputContainer widget", function()
it("should register touch zones", function() it("should register touch zones", function()
local ic = InputContainer:new{} local ic = InputContainer:new{}
assert.is.same(#ic._touch_zones, 0) assert.is.same(#ic._ordered_touch_zones, 0)
ic:registerTouchZones({ ic:registerTouchZones({
{ {
@ -30,15 +30,15 @@ describe("InputContainer widget", function()
}) })
local screen_width, screen_height = Screen:getWidth(), Screen:getHeight() local screen_width, screen_height = Screen:getWidth(), Screen:getHeight()
assert.is.same(#ic._touch_zones, 2) assert.is.same(#ic._ordered_touch_zones, 2)
assert.is.same("foo", ic._touch_zones[1].def.id) assert.is.same("foo", ic._ordered_touch_zones[1].def.id)
assert.is.same(ic._touch_zones[1].def.handler, ic._touch_zones[1].handler) assert.is.same(ic._ordered_touch_zones[1].def.handler, ic._ordered_touch_zones[1].handler)
assert.is.same("bar", ic._touch_zones[2].def.id) assert.is.same("bar", ic._ordered_touch_zones[2].def.id)
assert.is.same("tap", ic._touch_zones[2].gs_range.ges) assert.is.same("tap", ic._ordered_touch_zones[2].gs_range.ges)
assert.is.same(0, ic._touch_zones[2].gs_range.range.x) assert.is.same(0, ic._ordered_touch_zones[2].gs_range.range.x)
assert.is.same(screen_height * 0.1, ic._touch_zones[2].gs_range.range.y) assert.is.same(screen_height * 0.1, ic._ordered_touch_zones[2].gs_range.range.y)
assert.is.same(screen_width / 2, ic._touch_zones[2].gs_range.range.w) assert.is.same(screen_width / 2, ic._ordered_touch_zones[2].gs_range.range.w)
assert.is.same(screen_height, ic._touch_zones[2].gs_range.range.h) assert.is.same(screen_height, ic._ordered_touch_zones[2].gs_range.range.h)
end) end)
it("should support overrides for touch zones", function() it("should support overrides for touch zones", function()
@ -70,8 +70,86 @@ describe("InputContainer widget", function()
handler = function() end, handler = function() end,
}, },
}) })
assert.is.same(ic._touch_zones[1].def.id, 'baz') assert.is.same(ic._ordered_touch_zones[1].def.id, 'baz')
assert.is.same(ic._touch_zones[2].def.id, 'foo') assert.is.same(ic._ordered_touch_zones[2].def.id, 'foo')
assert.is.same(ic._touch_zones[3].def.id, 'bar') assert.is.same(ic._ordered_touch_zones[3].def.id, 'bar')
end)
it("should support indirect overrides for touch zones", function()
local ic = InputContainer:new{}
local dummy_screen_size = {ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,}
ic:registerTouchZones({
{
id = "readerfooter_tap",
ges = "tap",
screen_zone = dummy_screen_size,
overrides = {
'tap_forward', 'tap_backward', 'readermenu_tap',
},
handler = function() end,
},
{
id = "readerfooter_hold",
ges = "hold",
screen_zone = dummy_screen_size,
overrides = {'readerhighlight_hold'},
handler = function() end,
},
{
id = "readerhighlight_tap",
ges = "tap",
screen_zone = dummy_screen_size,
overrides = { 'tap_forward', 'tap_backward', },
handler = function() end,
},
{
id = "readerhighlight_hold",
ges = "hold",
screen_zone = dummy_screen_size,
handler = function() end,
},
{
id = "readerhighlight_hold_release",
ges = "hold_release",
screen_zone = dummy_screen_size,
handler = function() end,
},
{
id = "readerhighlight_hold_pan",
ges = "hold_pan",
rate = 2.0,
screen_zone = dummy_screen_size,
handler = function() end,
},
{
id = "tap_forward",
ges = "tap",
screen_zone = dummy_screen_size,
handler = function() end,
},
{
id = "tap_backward",
ges = "tap",
screen_zone = dummy_screen_size,
handler = function() end,
},
{
id = "readermenu_tap",
ges = "tap",
overrides = { "tap_forward", "tap_backward", },
screen_zone = dummy_screen_size,
handler = function() end,
},
})
assert.is.same('readerfooter_tap', ic._ordered_touch_zones[1].def.id)
assert.is.same('readerhighlight_tap', ic._ordered_touch_zones[2].def.id)
assert.is.same('readermenu_tap', ic._ordered_touch_zones[3].def.id)
assert.is.same('tap_backward', ic._ordered_touch_zones[4].def.id)
assert.is.same('readerhighlight_hold_pan', ic._ordered_touch_zones[5].def.id)
assert.is.same('readerfooter_hold', ic._ordered_touch_zones[6].def.id)
assert.is.same('readerhighlight_hold', ic._ordered_touch_zones[7].def.id)
assert.is.same('tap_forward', ic._ordered_touch_zones[8].def.id)
assert.is.same('readerhighlight_hold_release', ic._ordered_touch_zones[9].def.id)
end) end)
end) end)

Loading…
Cancel
Save