mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
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:
@@ -77,6 +77,7 @@ function PageCropDialog:onShow()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local ReaderCropping = InputContainer:new{}
|
||||
|
||||
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 BottomContainer = require("ui/widget/container/bottomcontainer")
|
||||
local FrameContainer = require("ui/widget/container/framecontainer")
|
||||
@@ -6,7 +6,6 @@ local ProgressWidget = require("ui/widget/progresswidget")
|
||||
local HorizontalGroup = require("ui/widget/horizontalgroup")
|
||||
local HorizontalSpan = require("ui/widget/horizontalspan")
|
||||
local TextWidget = require("ui/widget/textwidget")
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local Device = require("device")
|
||||
@@ -88,7 +87,7 @@ local footerTextGeneratorMap = {
|
||||
end,
|
||||
}
|
||||
|
||||
local ReaderFooter = InputContainer:new{
|
||||
local ReaderFooter = WidgetContainer:new{
|
||||
mode = MODE.page_progress,
|
||||
pageno = nil,
|
||||
pages = nil,
|
||||
@@ -152,27 +151,31 @@ function ReaderFooter:init()
|
||||
last = nil, -- last will be initialized in self:updateFooterText
|
||||
}
|
||||
|
||||
local margin_span = HorizontalSpan:new{width = self.horizontal_margin}
|
||||
self.horizontal_group = HorizontalGroup:new{margin_span}
|
||||
local margin_span = HorizontalSpan:new{ width = self.horizontal_margin }
|
||||
self.horizontal_group = HorizontalGroup:new{ margin_span }
|
||||
self.text_container = RightContainer:new{
|
||||
dimen = Geom:new{w = 0, h = self.height},
|
||||
dimen = Geom:new{ w = 0, h = self.height },
|
||||
self.footer_text,
|
||||
}
|
||||
table.insert(self.horizontal_group, self.progress_bar)
|
||||
table.insert(self.horizontal_group, self.text_container)
|
||||
table.insert(self.horizontal_group, margin_span)
|
||||
self[1] = BottomContainer:new{
|
||||
dimen = Geom:new{},
|
||||
BottomContainer:new{
|
||||
dimen = Geom:new{w = 0, h = self.height*2},
|
||||
FrameContainer:new{
|
||||
self.horizontal_group,
|
||||
background = Blitbuffer.COLOR_WHITE,
|
||||
bordersize = 0,
|
||||
padding = 0,
|
||||
}
|
||||
}
|
||||
|
||||
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{},
|
||||
self.footer_container,
|
||||
}
|
||||
self[1] = self.footer_positioner
|
||||
|
||||
self.mode = G_reader_settings:readSetting("reader_footer_mode") or self.mode
|
||||
if self.settings.all_at_once then
|
||||
@@ -181,7 +184,6 @@ function ReaderFooter:init()
|
||||
else
|
||||
self:applyFooterMode()
|
||||
end
|
||||
self:resetLayout()
|
||||
|
||||
if self.settings.auto_refresh_time then
|
||||
self:setupAutoRefreshTime()
|
||||
@@ -201,7 +203,30 @@ function ReaderFooter:setupAutoRefreshTime()
|
||||
UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.autoRefreshTime)
|
||||
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()
|
||||
local new_screen_width = Screen:getWidth()
|
||||
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.horizontal_group:resetLayout()
|
||||
self[1].dimen.w = new_screen_width
|
||||
self[1].dimen.h = new_screen_height
|
||||
self[1][1].dimen.w = new_screen_width
|
||||
self.dimen = self[1]:getSize()
|
||||
self.footer_positioner.dimen.w = new_screen_width
|
||||
self.footer_positioner.dimen.h = new_screen_height
|
||||
self.footer_container.dimen.w = new_screen_width
|
||||
self.dimen = self.footer_positioner:getSize()
|
||||
|
||||
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
|
||||
|
||||
function ReaderFooter:getHeight()
|
||||
@@ -481,7 +484,7 @@ function ReaderFooter:_updateFooterText()
|
||||
self._saved_screen_width - self.text_width - self.horizontal_margin*2)
|
||||
self.text_container.dimen.w = self.text_width
|
||||
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
|
||||
|
||||
function ReaderFooter:onPageUpdate(pageno)
|
||||
@@ -501,6 +504,8 @@ end
|
||||
ReaderFooter.onUpdatePos = ReaderFooter.updateFooter
|
||||
|
||||
function ReaderFooter:onReaderReady()
|
||||
self:setupTouchZones()
|
||||
self:resetLayout() -- set widget dimen
|
||||
self:setTocMarkers()
|
||||
self.updateFooterText = self._updateFooterText
|
||||
self:updateFooter()
|
||||
@@ -574,7 +579,7 @@ function ReaderFooter:onTapFooter(arg, ges)
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderFooter:onHoldFooter(arg, ges)
|
||||
function ReaderFooter:onHoldFooter()
|
||||
if self.mode == MODE.off then return end
|
||||
self.ui:handleEvent(Event:new("ShowGotoDialog"))
|
||||
return true
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local Geom = require("ui/geometry")
|
||||
local Input = require("device").input
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local Device = require("device")
|
||||
local Screen = Device.screen
|
||||
local Event = require("ui/event")
|
||||
@@ -11,6 +10,9 @@ local DEBUG = require("dbg")
|
||||
local _ = require("gettext")
|
||||
|
||||
|
||||
local pan_rate = Screen.eink and 4.0 or 10.0
|
||||
|
||||
|
||||
local function copyPageState(page_state)
|
||||
return {
|
||||
page = page_state.page,
|
||||
@@ -75,64 +77,77 @@ function ReaderPaging:init()
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
|
||||
-- This method will be called in onSetDimensions handler
|
||||
function ReaderPaging:initGesListener()
|
||||
self.ges_events = {
|
||||
TapForward = {
|
||||
GestureRange:new{
|
||||
ges = "tap",
|
||||
range = Geom:new{
|
||||
x = Screen:getWidth()*DTAP_ZONE_FORWARD.x,
|
||||
y = Screen:getHeight()*DTAP_ZONE_FORWARD.y,
|
||||
w = Screen:getWidth()*DTAP_ZONE_FORWARD.w,
|
||||
h = Screen:getHeight()*DTAP_ZONE_FORWARD.h,
|
||||
}
|
||||
}
|
||||
},
|
||||
TapBackward = {
|
||||
GestureRange:new{
|
||||
ges = "tap",
|
||||
range = Geom:new{
|
||||
x = Screen:getWidth()*DTAP_ZONE_BACKWARD.x,
|
||||
y = Screen:getHeight()*DTAP_ZONE_BACKWARD.y,
|
||||
w = Screen:getWidth()*DTAP_ZONE_BACKWARD.w,
|
||||
h = Screen:getHeight()*DTAP_ZONE_BACKWARD.h,
|
||||
}
|
||||
}
|
||||
},
|
||||
Swipe = {
|
||||
GestureRange:new{
|
||||
ges = "swipe",
|
||||
range = Geom:new{
|
||||
x = 0, y = 0,
|
||||
w = Screen:getWidth(),
|
||||
h = Screen:getHeight(),
|
||||
}
|
||||
}
|
||||
},
|
||||
Pan = {
|
||||
GestureRange:new{
|
||||
ges = "pan",
|
||||
range = Geom:new{
|
||||
x = 0, y = 0,
|
||||
w = Screen:getWidth(),
|
||||
h = Screen:getHeight(),
|
||||
},
|
||||
rate = Screen.eink and 4.0 or 10.0,
|
||||
}
|
||||
},
|
||||
PanRelease = {
|
||||
GestureRange:new{
|
||||
ges = "pan_release",
|
||||
range = Geom:new{
|
||||
x = 0, y = 0,
|
||||
w = Screen:getWidth(),
|
||||
h = Screen:getHeight(),
|
||||
},
|
||||
}
|
||||
},
|
||||
function ReaderPaging:onReaderReady()
|
||||
self:setupTouchZones()
|
||||
end
|
||||
|
||||
function ReaderPaging:setupTapTouchZones()
|
||||
local forward_zone = {
|
||||
ratio_x = DTAP_ZONE_FORWARD.x, ratio_y = DTAP_ZONE_FORWARD.y,
|
||||
ratio_w = DTAP_ZONE_FORWARD.w, ratio_h = DTAP_ZONE_FORWARD.h,
|
||||
}
|
||||
self:updateReadOrder()
|
||||
local backward_zone = {
|
||||
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
|
||||
},
|
||||
{
|
||||
id = "tap_backward",
|
||||
ges = "tap",
|
||||
screen_zone = backward_zone,
|
||||
handler = function() return self:onTapBackward() end
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
-- This method will be called in onSetDimensions handler
|
||||
function ReaderPaging:setupTouchZones()
|
||||
-- deligate gesture listener to readerui
|
||||
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
|
||||
},
|
||||
{
|
||||
id = "paging_pan",
|
||||
ges = "pan",
|
||||
rate = pan_rate,
|
||||
screen_zone = {
|
||||
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
|
||||
},
|
||||
handler = function(ges) return self:onPan(nil, ges) end
|
||||
},
|
||||
{
|
||||
id = "paging_pan_release",
|
||||
ges = "pan_release",
|
||||
screen_zone = {
|
||||
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
|
||||
},
|
||||
handler = function(ges) return self:onPanRelease(nil, ges) end
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
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_scroll_mode = config:readSetting("flipping_scroll_mode") or false
|
||||
self.inverse_reading_order = config:readSetting("inverse_reading_order") or false
|
||||
self:updateReadOrder()
|
||||
end
|
||||
|
||||
function ReaderPaging:onSaveSettings()
|
||||
@@ -197,12 +211,12 @@ function ReaderPaging:addToMainMenu(tab_item_table)
|
||||
end,
|
||||
sub_item_table = page_overlap_menu,
|
||||
})
|
||||
table.insert(tab_item_table.typeset, {
|
||||
table.insert(tab_item_table.setting, {
|
||||
text = _("Read from right to left"),
|
||||
checked_func = function() return self.inverse_reading_order end,
|
||||
callback = function()
|
||||
self.inverse_reading_order = not self.inverse_reading_order
|
||||
self:updateReadOrder()
|
||||
self:setupTapTouchZones()
|
||||
end,
|
||||
})
|
||||
end
|
||||
@@ -335,7 +349,7 @@ function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges)
|
||||
UIManager:setDirty(self.view.dialog, "partial")
|
||||
end
|
||||
|
||||
function ReaderPaging:onSwipe(arg, ges)
|
||||
function ReaderPaging:onSwipe(_, ges)
|
||||
if self.bookmark_flipping_mode then
|
||||
self:bookmarkFlipping(self.current_page, ges)
|
||||
elseif self.page_flipping_mode and self.original_page then
|
||||
@@ -358,7 +372,7 @@ function ReaderPaging:onSwipe(arg, ges)
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderPaging:onPan(arg, ges)
|
||||
function ReaderPaging:onPan(_, ges)
|
||||
if self.bookmark_flipping_mode then
|
||||
return true
|
||||
elseif self.page_flipping_mode then
|
||||
@@ -374,7 +388,7 @@ function ReaderPaging:onPan(arg, ges)
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderPaging:onPanRelease(arg, ges)
|
||||
function ReaderPaging:onPanRelease(_, ges)
|
||||
if self.page_flipping_mode then
|
||||
if self.view.zoom_mode == "page" then
|
||||
self:updateFlippingPage(self.current_page)
|
||||
@@ -832,13 +846,6 @@ function ReaderPaging:onRedrawCurrentPage()
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderPaging:onSetDimensions()
|
||||
-- update listening according to new screen dimen
|
||||
if Device:isTouchDevice() then
|
||||
self:initGesListener()
|
||||
end
|
||||
end
|
||||
|
||||
-- wrapper for bounds checking
|
||||
function ReaderPaging:_gotoPage(number, orig_mode)
|
||||
if number == self.current_page or not number then
|
||||
@@ -870,55 +877,4 @@ function ReaderPaging:onGotoPercentage(percentage)
|
||||
return true
|
||||
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
|
||||
|
||||
@@ -12,7 +12,6 @@ local Event = require("ui/event")
|
||||
local dbg = require("dbg")
|
||||
local Blitbuffer = require("ffi/blitbuffer")
|
||||
local _ = require("gettext")
|
||||
local ReaderKoboLight = require("apps/reader/modules/readerkobolight")
|
||||
|
||||
local ReaderView = OverlapGroup:new{
|
||||
document = nil,
|
||||
@@ -115,13 +114,6 @@ function ReaderView:addWidgets()
|
||||
self[1] = self.dogear
|
||||
self[2] = self.footer
|
||||
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
|
||||
|
||||
function ReaderView:resetLayout()
|
||||
@@ -609,7 +601,9 @@ function ReaderView:onSetScreenMode(new_mode, rotation)
|
||||
Screen:setScreenMode(new_mode)
|
||||
end
|
||||
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"))
|
||||
end
|
||||
self.cur_rotation_mode = Screen.cur_rotation_mode
|
||||
|
||||
@@ -317,7 +317,15 @@ function ReaderUI:init()
|
||||
})
|
||||
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
|
||||
self:handleEvent(Event:new("ReadSettings", self.doc_settings))
|
||||
|
||||
@@ -445,8 +453,9 @@ function ReaderUI:closeDialog()
|
||||
UIManager:close(self.password_dialog)
|
||||
end
|
||||
|
||||
function ReaderUI:onSetDimensions(dimen)
|
||||
function ReaderUI:onScreenResize(dimen)
|
||||
self.dimen = dimen
|
||||
self:updateTouchZonesOnScreenResize(dimen)
|
||||
end
|
||||
|
||||
function ReaderUI:saveSettings()
|
||||
|
||||
@@ -4,7 +4,8 @@ local DEBUG = require("dbg")
|
||||
|
||||
--[[
|
||||
Current detectable gestures:
|
||||
* tap
|
||||
* touch (user touched screen)
|
||||
* tap (touch action detected as single tap)
|
||||
* pan
|
||||
* hold
|
||||
* swipe
|
||||
|
||||
@@ -265,9 +265,7 @@ Check size of dimension/rectangle for equality
|
||||
@tparam Geom rect_b
|
||||
]]
|
||||
function Geom:equalSize(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
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@@ -279,12 +277,9 @@ Check if our size is smaller than the size of the given dimension/rectangle
|
||||
@tparam Geom 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
|
||||
DEBUG("lt+")
|
||||
return true
|
||||
end
|
||||
DEBUG("lt-")
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
77
spec/unit/widget_inputcontainer_spec.lua
Normal file
77
spec/unit/widget_inputcontainer_spec.lua
Normal file
@@ -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)
|
||||
Reference in New Issue
Block a user