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
pull/2416/head
Qingping Hou 8 years ago
parent 8799b4b6b1
commit 0c49b915de

@ -77,6 +77,7 @@ function PageCropDialog:onShow()
return true return true
end end
local ReaderCropping = InputContainer:new{} local ReaderCropping = InputContainer:new{}
function ReaderCropping:onPageCrop(mode) function ReaderCropping:onPageCrop(mode)

@ -1,4 +1,4 @@
local InputContainer = require("ui/widget/container/inputcontainer") local WidgetContainer = require("ui/widget/container/widgetcontainer")
local RightContainer = require("ui/widget/container/rightcontainer") local RightContainer = require("ui/widget/container/rightcontainer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
@ -6,7 +6,6 @@ local ProgressWidget = require("ui/widget/progresswidget")
local HorizontalGroup = require("ui/widget/horizontalgroup") local HorizontalGroup = require("ui/widget/horizontalgroup")
local HorizontalSpan = require("ui/widget/horizontalspan") local HorizontalSpan = require("ui/widget/horizontalspan")
local TextWidget = require("ui/widget/textwidget") local TextWidget = require("ui/widget/textwidget")
local GestureRange = require("ui/gesturerange")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local Device = require("device") local Device = require("device")
@ -88,7 +87,7 @@ local footerTextGeneratorMap = {
end, end,
} }
local ReaderFooter = InputContainer:new{ local ReaderFooter = WidgetContainer:new{
mode = MODE.page_progress, mode = MODE.page_progress,
pageno = nil, pageno = nil,
pages = nil, pages = nil,
@ -152,27 +151,31 @@ function ReaderFooter:init()
last = nil, -- last will be initialized in self:updateFooterText last = nil, -- last will be initialized in self:updateFooterText
} }
local margin_span = HorizontalSpan:new{width = self.horizontal_margin} local margin_span = HorizontalSpan:new{ width = self.horizontal_margin }
self.horizontal_group = HorizontalGroup:new{margin_span} self.horizontal_group = HorizontalGroup:new{ margin_span }
self.text_container = RightContainer:new{ self.text_container = RightContainer:new{
dimen = Geom:new{w = 0, h = self.height}, dimen = Geom:new{ w = 0, h = self.height },
self.footer_text, self.footer_text,
} }
table.insert(self.horizontal_group, self.progress_bar) table.insert(self.horizontal_group, self.progress_bar)
table.insert(self.horizontal_group, self.text_container) table.insert(self.horizontal_group, self.text_container)
table.insert(self.horizontal_group, margin_span) table.insert(self.horizontal_group, margin_span)
self[1] = BottomContainer:new{
self.footer_content = FrameContainer:new{
self.horizontal_group,
background = Blitbuffer.COLOR_WHITE,
bordersize = 0,
padding = 0,
}
self.footer_container = BottomContainer:new{
dimen = Geom:new{ w = 0, h = self.height*2 },
self.footer_content,
}
self.footer_positioner = BottomContainer:new{
dimen = Geom:new{}, dimen = Geom:new{},
BottomContainer:new{ self.footer_container,
dimen = Geom:new{w = 0, h = self.height*2},
FrameContainer:new{
self.horizontal_group,
background = Blitbuffer.COLOR_WHITE,
bordersize = 0,
padding = 0,
}
}
} }
self[1] = self.footer_positioner
self.mode = G_reader_settings:readSetting("reader_footer_mode") or self.mode self.mode = G_reader_settings:readSetting("reader_footer_mode") or self.mode
if self.settings.all_at_once then if self.settings.all_at_once then
@ -181,7 +184,6 @@ function ReaderFooter:init()
else else
self:applyFooterMode() self:applyFooterMode()
end end
self:resetLayout()
if self.settings.auto_refresh_time then if self.settings.auto_refresh_time then
self:setupAutoRefreshTime() self:setupAutoRefreshTime()
@ -201,7 +203,30 @@ function ReaderFooter:setupAutoRefreshTime()
UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshTime) UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshTime)
end end
-- call this method whenever the screen size changed function ReaderFooter:setupTouchZones()
if not Device:isTouchDevice() then return end
local footer_screen_zone = {
ratio_x = DTAP_ZONE_MINIBAR.x, ratio_y = DTAP_ZONE_MINIBAR.y,
ratio_w = DTAP_ZONE_MINIBAR.w, ratio_h = DTAP_ZONE_MINIBAR.h,
}
self.ui:registerTouchZones({
{
id = "footer_tap",
ges = "tap",
screen_zone = footer_screen_zone,
handler = function() return self:onTapFooter() end,
overrides = { 'tap_forward', 'tap_backward', },
},
{
id = "footer_hold",
ges = "hold",
screen_zone = footer_screen_zone,
handler = function() return self:onHoldFooter() end,
},
})
end
-- call this method whenever the screen size changes
function ReaderFooter:resetLayout() function ReaderFooter:resetLayout()
local new_screen_width = Screen:getWidth() local new_screen_width = Screen:getWidth()
if new_screen_width == self._saved_screen_width then return end if new_screen_width == self._saved_screen_width then return end
@ -209,34 +234,12 @@ function ReaderFooter:resetLayout()
self.progress_bar.width = math.floor(new_screen_width - self.text_width - self.horizontal_margin*2) self.progress_bar.width = math.floor(new_screen_width - self.text_width - self.horizontal_margin*2)
self.horizontal_group:resetLayout() self.horizontal_group:resetLayout()
self[1].dimen.w = new_screen_width self.footer_positioner.dimen.w = new_screen_width
self[1].dimen.h = new_screen_height self.footer_positioner.dimen.h = new_screen_height
self[1][1].dimen.w = new_screen_width self.footer_container.dimen.w = new_screen_width
self.dimen = self[1]:getSize() self.dimen = self.footer_positioner:getSize()
self._saved_screen_width = new_screen_width self._saved_screen_width = new_screen_width
if Device:isTouchDevice() then
local range = Geom:new{
x = new_screen_width*DTAP_ZONE_MINIBAR.x,
y = new_screen_height*DTAP_ZONE_MINIBAR.y,
w = new_screen_width*DTAP_ZONE_MINIBAR.w,
h = new_screen_height*DTAP_ZONE_MINIBAR.h
}
self.ges_events = {
TapFooter = {
GestureRange:new{
ges = "tap",
range = range,
},
},
HoldFooter = {
GestureRange:new{
ges = "hold",
range = range,
},
},
}
end
end end
function ReaderFooter:getHeight() function ReaderFooter:getHeight()
@ -481,7 +484,7 @@ function ReaderFooter:_updateFooterText()
self._saved_screen_width - self.text_width - self.horizontal_margin*2) self._saved_screen_width - self.text_width - self.horizontal_margin*2)
self.text_container.dimen.w = self.text_width self.text_container.dimen.w = self.text_width
self.horizontal_group:resetLayout() self.horizontal_group:resetLayout()
UIManager:setDirty(self.view.dialog, "ui", self[1][1][1].dimen) UIManager:setDirty(self.view.dialog, "ui", self.footer_content.dimen)
end end
function ReaderFooter:onPageUpdate(pageno) function ReaderFooter:onPageUpdate(pageno)
@ -501,6 +504,8 @@ end
ReaderFooter.onUpdatePos = ReaderFooter.updateFooter ReaderFooter.onUpdatePos = ReaderFooter.updateFooter
function ReaderFooter:onReaderReady() function ReaderFooter:onReaderReady()
self:setupTouchZones()
self:resetLayout() -- set widget dimen
self:setTocMarkers() self:setTocMarkers()
self.updateFooterText = self._updateFooterText self.updateFooterText = self._updateFooterText
self:updateFooter() self:updateFooter()
@ -574,7 +579,7 @@ function ReaderFooter:onTapFooter(arg, ges)
return true return true
end end
function ReaderFooter:onHoldFooter(arg, ges) function ReaderFooter:onHoldFooter()
if self.mode == MODE.off then return end if self.mode == MODE.off then return end
self.ui:handleEvent(Event:new("ShowGotoDialog")) self.ui:handleEvent(Event:new("ShowGotoDialog"))
return true return true

@ -1,7 +1,6 @@
local InputContainer = require("ui/widget/container/inputcontainer") local InputContainer = require("ui/widget/container/inputcontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local Input = require("device").input local Input = require("device").input
local GestureRange = require("ui/gesturerange")
local Device = require("device") local Device = require("device")
local Screen = Device.screen local Screen = Device.screen
local Event = require("ui/event") local Event = require("ui/event")
@ -11,6 +10,9 @@ local DEBUG = require("dbg")
local _ = require("gettext") local _ = require("gettext")
local pan_rate = Screen.eink and 4.0 or 10.0
local function copyPageState(page_state) local function copyPageState(page_state)
return { return {
page = page_state.page, page = page_state.page,
@ -75,64 +77,77 @@ function ReaderPaging:init()
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
-- This method will be called in onSetDimensions handler function ReaderPaging:onReaderReady()
function ReaderPaging:initGesListener() self:setupTouchZones()
self.ges_events = { end
TapForward = {
GestureRange:new{ function ReaderPaging:setupTapTouchZones()
ges = "tap", local forward_zone = {
range = Geom:new{ ratio_x = DTAP_ZONE_FORWARD.x, ratio_y = DTAP_ZONE_FORWARD.y,
x = Screen:getWidth()*DTAP_ZONE_FORWARD.x, ratio_w = DTAP_ZONE_FORWARD.w, ratio_h = DTAP_ZONE_FORWARD.h,
y = Screen:getHeight()*DTAP_ZONE_FORWARD.y, }
w = Screen:getWidth()*DTAP_ZONE_FORWARD.w, local backward_zone = {
h = Screen:getHeight()*DTAP_ZONE_FORWARD.h, ratio_x = DTAP_ZONE_BACKWARD.x, ratio_y = DTAP_ZONE_BACKWARD.y,
} ratio_w = DTAP_ZONE_BACKWARD.w, ratio_h = DTAP_ZONE_BACKWARD.h,
} }
if self.inverse_reading_order then
forward_zone.ratio_x = 1 - forward_zone.ratio_x - forward_zone.ratio_w
backward_zone.ratio_x = 1 - backward_zone.ratio_x - backward_zone.ratio_w
end
self.ui:registerTouchZones({
{
id = "tap_forward",
ges = "tap",
screen_zone = forward_zone,
handler = function() return self:onTapForward() end
}, },
TapBackward = { {
GestureRange:new{ id = "tap_backward",
ges = "tap", ges = "tap",
range = Geom:new{ screen_zone = backward_zone,
x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x, handler = function() return self:onTapBackward() end
y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y,
w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w,
h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h,
}
}
}, },
Swipe = { })
GestureRange:new{ end
ges = "swipe",
range = Geom:new{ -- This method will be called in onSetDimensions handler
x = 0, y = 0, function ReaderPaging:setupTouchZones()
w = Screen:getWidth(), -- deligate gesture listener to readerui
h = Screen:getHeight(), self.ges_events = {}
} self.onGesture = nil
}
if not Device:isTouchDevice() then return end
self:setupTapTouchZones()
self.ui:registerTouchZones({
{
id = "paging_swipe",
ges = "swipe",
screen_zone = {
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
},
handler = function(ges) return self:onSwipe(nil, ges) end
}, },
Pan = { {
GestureRange:new{ id = "paging_pan",
ges = "pan", ges = "pan",
range = Geom:new{ rate = pan_rate,
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:onPan(nil, ges) end
rate = Screen.eink and 4.0 or 10.0,
}
}, },
PanRelease = { {
GestureRange:new{ id = "paging_pan_release",
ges = "pan_release", ges = "pan_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(ges) return self:onPanRelease(nil, ges) end
},
}
}, },
} })
self:updateReadOrder()
end end
function ReaderPaging:onReadSettings(config) function ReaderPaging:onReadSettings(config)
@ -145,7 +160,6 @@ function ReaderPaging:onReadSettings(config)
self.flipping_zoom_mode = config:readSetting("flipping_zoom_mode") or "page" self.flipping_zoom_mode = config:readSetting("flipping_zoom_mode") or "page"
self.flipping_scroll_mode = config:readSetting("flipping_scroll_mode") or false self.flipping_scroll_mode = config:readSetting("flipping_scroll_mode") or false
self.inverse_reading_order = config:readSetting("inverse_reading_order") or false self.inverse_reading_order = config:readSetting("inverse_reading_order") or false
self:updateReadOrder()
end end
function ReaderPaging:onSaveSettings() function ReaderPaging:onSaveSettings()
@ -197,12 +211,12 @@ function ReaderPaging:addToMainMenu(tab_item_table)
end, end,
sub_item_table = page_overlap_menu, sub_item_table = page_overlap_menu,
}) })
table.insert(tab_item_table.typeset, { table.insert(tab_item_table.setting, {
text = _("Read from right to left"), text = _("Read from right to left"),
checked_func = function() return self.inverse_reading_order end, checked_func = function() return self.inverse_reading_order end,
callback = function() callback = function()
self.inverse_reading_order = not self.inverse_reading_order self.inverse_reading_order = not self.inverse_reading_order
self:updateReadOrder() self:setupTapTouchZones()
end, end,
}) })
end end
@ -335,7 +349,7 @@ function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges)
UIManager:setDirty(self.view.dialog, "partial") UIManager:setDirty(self.view.dialog, "partial")
end end
function ReaderPaging:onSwipe(arg, ges) function ReaderPaging:onSwipe(_, ges)
if self.bookmark_flipping_mode then if self.bookmark_flipping_mode then
self:bookmarkFlipping(self.current_page, ges) self:bookmarkFlipping(self.current_page, ges)
elseif self.page_flipping_mode and self.original_page then elseif self.page_flipping_mode and self.original_page then
@ -358,7 +372,7 @@ function ReaderPaging:onSwipe(arg, ges)
end end
end end
function ReaderPaging:onPan(arg, ges) function ReaderPaging:onPan(_, ges)
if self.bookmark_flipping_mode then if self.bookmark_flipping_mode then
return true return true
elseif self.page_flipping_mode then elseif self.page_flipping_mode then
@ -374,7 +388,7 @@ function ReaderPaging:onPan(arg, ges)
return true return true
end end
function ReaderPaging:onPanRelease(arg, ges) function ReaderPaging:onPanRelease(_, ges)
if self.page_flipping_mode then if self.page_flipping_mode then
if self.view.zoom_mode == "page" then if self.view.zoom_mode == "page" then
self:updateFlippingPage(self.current_page) self:updateFlippingPage(self.current_page)
@ -832,13 +846,6 @@ function ReaderPaging:onRedrawCurrentPage()
return true return true
end end
function ReaderPaging:onSetDimensions()
-- update listening according to new screen dimen
if Device:isTouchDevice() then
self:initGesListener()
end
end
-- wrapper for bounds checking -- wrapper for bounds checking
function ReaderPaging:_gotoPage(number, orig_mode) function ReaderPaging:_gotoPage(number, orig_mode)
if number == self.current_page or not number then if number == self.current_page or not number then
@ -870,55 +877,4 @@ function ReaderPaging:onGotoPercentage(percentage)
return true return true
end end
function ReaderPaging:updateReadOrder()
local width, height = Screen:getWidth(), Screen:getHeight()
if self.inverse_reading_order then
self.ges_events.TapForward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = width * (1-DTAP_ZONE_FORWARD.x - DTAP_ZONE_FORWARD.w),
y = height * DTAP_ZONE_FORWARD.y,
w = width * DTAP_ZONE_FORWARD.w,
h = height * DTAP_ZONE_FORWARD.h,
}
}
}
self.ges_events.TapBackward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = width * (1-DTAP_ZONE_BACKWARD.x - DTAP_ZONE_BACKWARD.w),
y = height * DTAP_ZONE_BACKWARD.y,
w = width * DTAP_ZONE_BACKWARD.w,
h = height * DTAP_ZONE_BACKWARD.h,
}
}
}
else
self.ges_events.TapForward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = width * DTAP_ZONE_FORWARD.x,
y = height * DTAP_ZONE_FORWARD.y,
w = width * DTAP_ZONE_FORWARD.w,
h = height * DTAP_ZONE_FORWARD.h,
}
}
}
self.ges_events.TapBackward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = width * DTAP_ZONE_BACKWARD.x,
y = height * DTAP_ZONE_BACKWARD.y,
w = width * DTAP_ZONE_BACKWARD.w,
h = height * DTAP_ZONE_BACKWARD.h,
}
}
}
end
end
return ReaderPaging return ReaderPaging

@ -12,7 +12,6 @@ local Event = require("ui/event")
local dbg = require("dbg") local dbg = require("dbg")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local _ = require("gettext") local _ = require("gettext")
local ReaderKoboLight = require("apps/reader/modules/readerkobolight")
local ReaderView = OverlapGroup:new{ local ReaderView = OverlapGroup:new{
document = nil, document = nil,
@ -115,13 +114,6 @@ function ReaderView:addWidgets()
self[1] = self.dogear self[1] = self.dogear
self[2] = self.footer self[2] = self.footer
self[3] = self.flipping self[3] = self.flipping
if (Device:isKobo() and Device:hasFrontlight()) then
self.kobolight = ReaderKoboLight:new{
view = self,
ui = self.ui,
}
self[4] = self.kobolight
end
end end
function ReaderView:resetLayout() function ReaderView:resetLayout()
@ -609,7 +601,9 @@ function ReaderView:onSetScreenMode(new_mode, rotation)
Screen:setScreenMode(new_mode) Screen:setScreenMode(new_mode)
end end
UIManager:setDirty(self.dialog, "full") UIManager:setDirty(self.dialog, "full")
self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize())) local new_screen_size = Screen:getSize()
self.ui:handleEvent(Event:new("SetDimensions", new_screen_size))
self.ui:onScreenResize(new_screen_size)
self.ui:handleEvent(Event:new("InitScrollPageStates")) self.ui:handleEvent(Event:new("InitScrollPageStates"))
end end
self.cur_rotation_mode = Screen.cur_rotation_mode self.cur_rotation_mode = Screen.cur_rotation_mode

@ -317,7 +317,15 @@ function ReaderUI:init()
}) })
end end
--dbg(self.doc_settings) local ReaderKoboLight = require("apps/reader/modules/readerkobolight")
if (Device:isKobo() and Device:hasFrontlight()) then
self:registerModule('kobolight', ReaderKoboLight:new{
dialog = self.dialog,
view = self.view,
ui = self,
})
end
-- we only read settings after all the widgets are initialized -- we only read settings after all the widgets are initialized
self:handleEvent(Event:new("ReadSettings", self.doc_settings)) self:handleEvent(Event:new("ReadSettings", self.doc_settings))
@ -445,8 +453,9 @@ function ReaderUI:closeDialog()
UIManager:close(self.password_dialog) UIManager:close(self.password_dialog)
end end
function ReaderUI:onSetDimensions(dimen) function ReaderUI:onScreenResize(dimen)
self.dimen = dimen self.dimen = dimen
self:updateTouchZonesOnScreenResize(dimen)
end end
function ReaderUI:saveSettings() function ReaderUI:saveSettings()

@ -4,7 +4,8 @@ local DEBUG = require("dbg")
--[[ --[[
Current detectable gestures: Current detectable gestures:
* tap * touch (user touched screen)
* tap (touch action detected as single tap)
* pan * pan
* hold * hold
* swipe * swipe

@ -265,9 +265,7 @@ Check size of dimension/rectangle for equality
@tparam Geom rect_b @tparam Geom rect_b
]] ]]
function Geom:equalSize(rect_b) function Geom:equalSize(rect_b)
if self.w == rect_b.w if self.w == rect_b.w and self.h == rect_b.h then
and self.h == rect_b.h
then
return true return true
end end
return false return false
@ -279,12 +277,9 @@ Check if our size is smaller than the size of the given dimension/rectangle
@tparam Geom rect_b @tparam Geom rect_b
]] ]]
function Geom:__lt(rect_b) function Geom:__lt(rect_b)
DEBUG("lt:",self,rect_b)
if self.w < rect_b.w and self.h < rect_b.h then if self.w < rect_b.w and self.h < rect_b.h then
DEBUG("lt+")
return true return true
end end
DEBUG("lt-")
return false return false
end end

@ -1,17 +1,10 @@
local WidgetContainer = require("ui/widget/container/widgetcontainer") --[[--
local UIManager = require("ui/uimanager") An InputContainer is an WidgetContainer that handles user input events including multi touches
local Geom = require("ui/geometry") and key presses.
local Event = require("ui/event")
local _ = require("gettext")
if require("device"):isAndroid() then See @{InputContainer:registerTouchZones} for example on how to listen for multi touch inputs.
require("jit").off(true, true)
end
--[[
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 = { PanBy20 = {
{ "Shift", Input.group.Cursor }, { "Shift", Input.group.Cursor },
@ -26,9 +19,23 @@ an example for a key_event is this:
}, },
Quit = { {"Home"} }, 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 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{ local InputContainer = WidgetContainer:new{
vertical_align = "top", vertical_align = "top",
} }
@ -50,6 +57,8 @@ 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_pos_idx = {}
end end
function InputContainer:paintTo(bb, x, y) function InputContainer:paintTo(bb, x, y)
@ -71,6 +80,108 @@ function InputContainer:paintTo(bb, x, y)
end end
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. the following handler handles keypresses and checks if they lead to a command.
if this is the case, we retransmit another event within ourselves if this is the case, we retransmit another event within ourselves
@ -89,6 +200,11 @@ function InputContainer:onKeyPress(key)
end end
function InputContainer:onGesture(ev) 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 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

@ -0,0 +1,77 @@
describe("InputContainer widget", function()
local InputContainer, Screen
setup(function()
require("commonrequire")
InputContainer = require("ui/widget/container/inputcontainer")
Screen = require("device").screen
end)
it("should register touch zones", function()
local ic = InputContainer:new{}
assert.is.same(#ic._touch_zones, 0)
ic:registerTouchZones({
{
id = "foo",
ges = "swipe",
screen_zone = {
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
},
handler = function() end,
},
{
id = "bar",
ges = "tap",
screen_zone = {
ratio_x = 0, ratio_y = 0.1, ratio_w = 0.5, ratio_h = 1,
},
handler = function() end,
},
})
local screen_width, screen_height = Screen:getWidth(), Screen:getHeight()
assert.is.same(#ic._touch_zones, 2)
assert.is.same("foo", ic._touch_zones[1].def.id)
assert.is.same(ic._touch_zones[1].def.handler, ic._touch_zones[1].handler)
assert.is.same("bar", ic._touch_zones[2].def.id)
assert.is.same("tap", ic._touch_zones[2].gs_range.ges)
assert.is.same(0, ic._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_width / 2, ic._touch_zones[2].gs_range.range.w)
assert.is.same(screen_height, ic._touch_zones[2].gs_range.range.h)
end)
it("should support overrides for touch zones", function()
local ic = InputContainer:new{}
ic:registerTouchZones({
{
id = "foo",
ges = "tap",
screen_zone = {
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
},
handler = function() end,
},
{
id = "bar",
ges = "tap",
screen_zone = {
ratio_x = 0, ratio_y = 0, ratio_w = 0.5, ratio_h = 1,
},
handler = function() end,
},
{
id = "baz",
ges = "tap",
screen_zone = {
ratio_x = 0, ratio_y = 0, ratio_w = 0.5, ratio_h = 1,
},
overrides = { 'foo' },
handler = function() end,
},
})
assert.is.same(ic._touch_zones[1].def.id, 'baz')
assert.is.same(ic._touch_zones[2].def.id, 'foo')
assert.is.same(ic._touch_zones[3].def.id, 'bar')
end)
end)
Loading…
Cancel
Save