refactor: add touch zone subsystem to inputcontainer

Touch zone decouples screen size from gesture event registration.

The win here is each individual widget does not need to update
gesture range on screen rotate/resize anymore.

Another advantage is we now have a centralized ordered array to handle
all registered touch event listeners, makes it much easier to resolve
gesture range conflicts between multiple widgets.

This patch also includes the following changes:

* migrate readerpaging to use readerui's touch zone
* migrate readerfooter to use readerui's touch zone
* move inverse read direction setting to touch menu's setting tab
* moved kobolight widget from readerview into readerui
* various dead code cleanups and comments
This commit is contained in:
Qingping Hou
2016-12-03 22:57:57 -08:00
parent 8799b4b6b1
commit 0c49b915de
9 changed files with 355 additions and 201 deletions

View File

@@ -1,17 +1,10 @@
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local UIManager = require("ui/uimanager")
local Geom = require("ui/geometry")
local Event = require("ui/event")
local _ = require("gettext")
--[[--
An InputContainer is an WidgetContainer that handles user input events including multi touches
and key presses.
if require("device"):isAndroid() then
require("jit").off(true, true)
end
See @{InputContainer:registerTouchZones} for example on how to listen for multi touch inputs.
--[[
an InputContainer is an WidgetContainer that handles input events
an example for a key_event is this:
An example for listening on key press input event is this:
PanBy20 = {
{ "Shift", Input.group.Cursor },
@@ -26,9 +19,23 @@ an example for a key_event is this:
},
Quit = { {"Home"} },
it is suggested to reference configurable sequences from another table
It is suggested to reference configurable sequences from another table
and store that table as configuration setting
--]]
]]
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local GestureRange = require("ui/gesturerange")
local UIManager = require("ui/uimanager")
local Screen = require("device").screen
local Geom = require("ui/geometry")
local Event = require("ui/event")
local _ = require("gettext")
if require("device"):isAndroid() then
require("jit").off(true, true)
end
local InputContainer = WidgetContainer:new{
vertical_align = "top",
}
@@ -50,6 +57,8 @@ function InputContainer:_init()
end
end
self.ges_events = new_ges_events
self._touch_zones = {}
self._touch_zone_pos_idx = {}
end
function InputContainer:paintTo(bb, x, y)
@@ -71,6 +80,108 @@ function InputContainer:paintTo(bb, x, y)
end
end
--[[--
Register touch zones into this InputContainer.
See gesturedetector for list of supported gestures.
NOTE: You are responsible for calling self:@{updateTouchZonesOnScreenResize} with the new
screen dimension whenever the screen is rotated or resized.
@tparam table zones list of touch zones to register
@usage
local InputContainer = require("ui/widget/container/inputcontainer")
local test_widget = InputContainer:new{}
test_widget:registerTouchZones({
{
id = "foo_tap",
ges = "tap",
-- This binds handler to the full screen
screen_zone = {
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
},
handler = function(ges)
print('User tapped on screen!')
return true
end
},
{
id = "foo_swipe",
ges = "swipe",
-- This binds handler to bottom half of the screen
screen_zone = {
ratio_x = 0, ratio_y = 0.5, ratio_w = 1, ratio_h = 0.5,
},
handler = function(ges)
print("User swiped at the bottom with direction:", ges.direction)
return true
end
},
})
require("ui/uimanager"):show(test_widget)
]]
function InputContainer:registerTouchZones(zones)
local screen_width, screen_height = Screen:getWidth(), Screen:getHeight()
for _, zone in ipairs(zones) do
if self._touch_zone_pos_idx[zone.id] then
table.remove(self._touch_zones, self._touch_zone_pos_idx[zone.id])
self._touch_zone_pos_idx[zone.id] = nil
end
local tzone = {
def = zone,
handler = zone.handler,
gs_range = GestureRange:new{
ges = zone.ges,
rate = zone.rate,
range = Geom:new{
x = screen_width * zone.screen_zone.ratio_x,
y = screen_height * zone.screen_zone.ratio_y,
w = screen_width * zone.screen_zone.ratio_w,
h = screen_height * zone.screen_zone.ratio_h,
},
},
}
local insert_pos = #self._touch_zones
if insert_pos ~= 0 then
if zone.overrides then
for _, override_id in ipairs(zone.overrides) do
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
if insert_pos == 0 then
table.insert(self._touch_zones, tzone)
self._touch_zone_pos_idx[zone.id] = 1
else
table.insert(self._touch_zones, insert_pos, tzone)
self._touch_zone_pos_idx[zone.id] = insert_pos
end
end
end
--[[--
Update touch zones based on new screen dimension.
@tparam ui.geometry.Geom new_screen_dimen new screen dimension
]]
function InputContainer:updateTouchZonesOnScreenResize(new_screen_dimen)
for _, tzone in ipairs(self._touch_zones) do
local range = tzone.gs_range
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.w = new_screen_dimen.w * tzone.def.screen_zone.ratio_w
range.h = new_screen_dimen.h * tzone.def.screen_zone.ratio_h
end
end
--[[
the following handler handles keypresses and checks if they lead to a command.
if this is the case, we retransmit another event within ourselves
@@ -89,6 +200,11 @@ function InputContainer:onKeyPress(key)
end
function InputContainer:onGesture(ev)
for _, tzone in ipairs(self._touch_zones) do
if tzone.gs_range:match(ev) then
return tzone.handler(ev)
end
end
for name, gsseq in pairs(self.ges_events) do
for _, gs_range in ipairs(gsseq) do
if gs_range:match(ev) then