mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Merge pull request #634 from houqp/ges-detect
Touch gesture detection module
This commit is contained in:
@@ -58,6 +58,11 @@ function Device:isKindle2()
|
||||
end
|
||||
end
|
||||
|
||||
function Device:isTouchDevice()
|
||||
local model = self:getModel()
|
||||
return (model == "Kindle4") or (model == "KindlePaperWhite") or util.isEmulated()
|
||||
end
|
||||
|
||||
function Device:intoScreenSaver()
|
||||
--os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt")
|
||||
if self.charging_mode == false and self.screen_saver_mode == false then
|
||||
|
||||
@@ -146,8 +146,8 @@ for points, it is basically an equality check
|
||||
function Geom:contains(rect_b)
|
||||
if self.x <= rect_b.x
|
||||
and self.y <= rect_b.y
|
||||
and self.x + self.w <= rect_b.x + rect_b.w
|
||||
and self.y + self.h <= rect_b.y + rect_b.h
|
||||
and self.x + self.w >= rect_b.x + rect_b.w
|
||||
and self.y + self.h >= rect_b.y + rect_b.h
|
||||
then
|
||||
return true
|
||||
end
|
||||
|
||||
271
frontend/ui/gesturedetector.lua
Normal file
271
frontend/ui/gesturedetector.lua
Normal file
@@ -0,0 +1,271 @@
|
||||
require "ui/geometry"
|
||||
|
||||
-- Synchronization events (SYN.code).
|
||||
SYN_REPORT = 0
|
||||
SYN_CONFIG = 1
|
||||
SYN_MT_REPORT = 2
|
||||
|
||||
-- For multi-touch events (ABS.code).
|
||||
ABS_MT_SLOT = 47
|
||||
ABS_MT_POSITION_X = 53
|
||||
ABS_MT_POSITION_Y = 54
|
||||
ABS_MT_TRACKING_ID = 57
|
||||
ABS_MT_PRESSURE = 58
|
||||
|
||||
|
||||
GestureRange = {
|
||||
ges = nil,
|
||||
range = nil,
|
||||
}
|
||||
|
||||
function GestureRange:new(o)
|
||||
local o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function GestureRange:match(gs)
|
||||
if gs.ges ~= self.ges then
|
||||
return false
|
||||
end
|
||||
|
||||
if self.range:contains(gs.pos) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Single tap event from kernel example:
|
||||
|
||||
MT_TRACK_ID: 0
|
||||
MT_X: 222
|
||||
MT_Y: 207
|
||||
SYN REPORT
|
||||
MT_TRACK_ID: -1
|
||||
SYN REPORT
|
||||
--]]
|
||||
|
||||
GestureDetector = {
|
||||
-- all the time parameters are in us
|
||||
DOUBLE_TAP_INTERVAL = 300 * 1000,
|
||||
HOLD_INTERVAL = 1000 * 1000,
|
||||
-- distance parameters
|
||||
DOUBLE_TAP_DISTANCE = 50,
|
||||
PAN_THRESHOLD = 50,
|
||||
|
||||
track_id = {},
|
||||
ev_stack = {},
|
||||
cur_ev = {},
|
||||
ev_start = false,
|
||||
state = function(self, ev)
|
||||
self:switchState("initialState", ev)
|
||||
end,
|
||||
|
||||
last_ev_timev = nil,
|
||||
|
||||
-- for tap
|
||||
last_tap = nil,
|
||||
}
|
||||
|
||||
function GestureDetector:feedEvent(ev)
|
||||
--DEBUG(ev.type, ev.code, ev.value, ev.time)
|
||||
if ev.type == EV_SYN then
|
||||
if ev.code == SYN_REPORT then
|
||||
self.cur_ev.timev = TimeVal:new(ev.time)
|
||||
local re = self.state(self, self.cur_ev)
|
||||
self.last_ev_timev = self.cur_ev.timev
|
||||
if re ~= nil then
|
||||
return re
|
||||
end
|
||||
self.cur_ev = {}
|
||||
end
|
||||
elseif ev.type == EV_ABS then
|
||||
if ev.code == ABS_MT_SLOT then
|
||||
self.cur_ev.slot = ev.value
|
||||
elseif ev.code == ABS_MT_TRACKING_ID then
|
||||
self.cur_ev.id = ev.value
|
||||
elseif ev.code == ABS_MT_POSITION_X then
|
||||
self.cur_ev.x = ev.value
|
||||
elseif ev.code == ABS_MT_POSITION_Y then
|
||||
self.cur_ev.y = ev.value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
tap2 is the later tap
|
||||
]]
|
||||
function GestureDetector:isDoubleTap(tap1, tap2)
|
||||
local tv_diff = tap2.timev - tap1.timev
|
||||
return (
|
||||
math.abs(tap1.x - tap2.x) < self.DOUBLE_TAP_DISTANCE and
|
||||
math.abs(tap1.y - tap2.y) < self.DOUBLE_TAP_DISTANCE and
|
||||
(tv_diff.sec == 0 and (tv_diff.usec) < self.DOUBLE_TAP_INTERVAL)
|
||||
)
|
||||
end
|
||||
|
||||
--[[
|
||||
Warning! this method won't update self.state, you need to do it
|
||||
in each state method!
|
||||
--]]
|
||||
function GestureDetector:switchState(state_new, ev)
|
||||
--@TODO do we need to check whether state is valid? (houqp)
|
||||
return self[state_new](self, ev)
|
||||
end
|
||||
|
||||
function GestureDetector:clearState()
|
||||
self.cur_x = nil
|
||||
self.cur_y = nil
|
||||
self.state = self.initialState
|
||||
self.cur_ev = {}
|
||||
self.ev_start = false
|
||||
end
|
||||
|
||||
function GestureDetector:initialState(ev)
|
||||
if ev.id then
|
||||
-- a event ends
|
||||
if ev.id == -1 then
|
||||
self.ev_start = false
|
||||
else
|
||||
self.track_id[ev.id] = ev.slot
|
||||
end
|
||||
end
|
||||
if ev.x and ev.y then
|
||||
-- user starts a new touch motion
|
||||
if not self.ev_start then
|
||||
self.ev_start = true
|
||||
-- default to tap state
|
||||
return self:switchState("tapState", ev)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
this method handles both single and double tap
|
||||
--]]
|
||||
function GestureDetector:tapState(ev)
|
||||
DEBUG("in tap state...", ev)
|
||||
if ev.id == -1 then
|
||||
-- end of tap event
|
||||
local ges_ev = {
|
||||
-- default to single tap
|
||||
ges = "tap",
|
||||
pos = Geom:new{
|
||||
x = self.cur_x,
|
||||
y = self.cur_y,
|
||||
w = 0, h = 0,
|
||||
}
|
||||
}
|
||||
-- cur_tap is used for double tap detection
|
||||
local cur_tap = {
|
||||
x = self.cur_x,
|
||||
y = self.cur_y,
|
||||
timev = ev.timev,
|
||||
}
|
||||
|
||||
if self.last_tap ~= nil and
|
||||
self:isDoubleTap(self.last_tap, cur_tap) then
|
||||
-- it is a double tap
|
||||
self:clearState()
|
||||
ges_ev.ges = "double_tap"
|
||||
self.last_tap = nil
|
||||
return ges_ev
|
||||
end
|
||||
|
||||
-- set current tap to last tap
|
||||
self.last_tap = cur_tap
|
||||
|
||||
DEBUG("set up tap timer")
|
||||
local deadline = self.cur_ev.timev + TimeVal:new{
|
||||
sec = 0, usec = self.DOUBLE_TAP_INTERVAL,
|
||||
}
|
||||
Input:setTimeout(function()
|
||||
DEBUG("in tap timer", self.last_tap ~= nil)
|
||||
-- double tap will set last_tap to nil so if it is not, then
|
||||
-- user must only tapped once
|
||||
if self.last_tap ~= nil then
|
||||
self.last_tap = nil
|
||||
-- we are using closure here
|
||||
return ges_ev
|
||||
end
|
||||
end, deadline)
|
||||
-- we are already at the end of touch event
|
||||
-- so reset the state
|
||||
self:clearState()
|
||||
elseif self.state ~= self.tapState then
|
||||
-- switched from other state, probably from initialState
|
||||
-- we return nil in this case
|
||||
self.state = self.tapState
|
||||
self.cur_x = ev.x
|
||||
self.cur_y = ev.y
|
||||
DEBUG("set up hold timer")
|
||||
local deadline = self.cur_ev.timev + TimeVal:new{
|
||||
sec = 0, usec = self.HOLD_INTERVAL
|
||||
}
|
||||
Input:setTimeout(function()
|
||||
print("hold timer", self.state == self.tapState)
|
||||
if self.state == self.tapState then
|
||||
-- timer set in tapState, so we switch to hold
|
||||
return self:switchState("holdState")
|
||||
end
|
||||
end, deadline)
|
||||
else
|
||||
-- it is not end of touch event, see if we need to switch to
|
||||
-- other states
|
||||
if (ev.x and math.abs(ev.x - self.cur_x) >= self.PAN_THRESHOLD) or
|
||||
(ev.y and math.abs(ev.y - self.cur_y) >= self.PAN_THRESHOLD) then
|
||||
-- if user's finger moved long enough in X or
|
||||
-- Y distance, we switch to pan state
|
||||
return self:switchState("panState", ev)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function GestureDetector:panState(ev)
|
||||
DEBUG("in pan state...")
|
||||
if ev.id == -1 then
|
||||
-- end of pan, signal swipe gesture
|
||||
self:clearState()
|
||||
elseif self.state ~= self.panState then
|
||||
self.state = self.panState
|
||||
--@TODO calculate direction here (houqp)
|
||||
else
|
||||
end
|
||||
self.cur_x = ev.x
|
||||
self.cur_y = ev.y
|
||||
end
|
||||
|
||||
function GestureDetector:holdState(ev)
|
||||
DEBUG("in hold state...")
|
||||
-- when we switch to hold state, we pass no ev
|
||||
-- so ev = nil
|
||||
if not ev and self.cur_x and self.cur_y then
|
||||
self.state = self.holdState
|
||||
return {
|
||||
ges = "hold",
|
||||
pos = Geom:new{
|
||||
x = self.cur_x,
|
||||
y = self.cur_y,
|
||||
w = 0, h = 0,
|
||||
}
|
||||
}
|
||||
end
|
||||
if ev.id == -1 then
|
||||
-- end of hold, signal hold release
|
||||
self:clearState()
|
||||
return {
|
||||
ges = "hold_release",
|
||||
pos = Geom:new{
|
||||
x = self.cur_x,
|
||||
y = self.cur_y,
|
||||
w = 0, h = 0,
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
require "ui/event"
|
||||
require "ui/device"
|
||||
require "ui/time"
|
||||
require "ui/gesturedetector"
|
||||
require "settings"
|
||||
|
||||
-- constants from <linux/input.h>
|
||||
@@ -7,18 +9,6 @@ EV_SYN = 0
|
||||
EV_KEY = 1
|
||||
EV_ABS = 3
|
||||
|
||||
-- Synchronization events (SYN.code).
|
||||
SYN_REPORT = 0
|
||||
SYN_CONFIG = 1
|
||||
SYN_MT_REPORT = 2
|
||||
|
||||
-- For multi-touch events (ABS.code).
|
||||
ABS_MT_SLOT = 47
|
||||
ABS_MT_POSITION_X = 53
|
||||
ABS_MT_POSITION_Y = 54
|
||||
ABS_MT_TRACKING_ID = 57
|
||||
ABS_MT_PRESSURE = 58
|
||||
|
||||
-- key press event values (KEY.value)
|
||||
EVENT_VALUE_KEY_PRESS = 1
|
||||
EVENT_VALUE_KEY_REPEAT = 2
|
||||
@@ -235,6 +225,8 @@ Input = {
|
||||
"LPgBack", "RPgBack", "LPgFwd", "RPgFwd"
|
||||
}
|
||||
},
|
||||
|
||||
timer_callbacks = {},
|
||||
}
|
||||
|
||||
function Input:init()
|
||||
@@ -276,11 +268,55 @@ function Input:adjustKindle4EventMap()
|
||||
self.event_map[104] = "LPgFwd"
|
||||
end
|
||||
|
||||
function Input:setTimeout(cb, tv_out)
|
||||
local item = {
|
||||
callback = cb,
|
||||
deadline = tv_out,
|
||||
}
|
||||
for k,v in ipairs(self.timer_callbacks) do
|
||||
if v.deadline > tv_out then
|
||||
table.insert(self.timer_callbacks, k, item)
|
||||
break
|
||||
end
|
||||
end
|
||||
if #self.timer_callbacks <= 0 then
|
||||
self.timer_callbacks[1] = item
|
||||
end
|
||||
end
|
||||
|
||||
function Input:waitEvent(timeout_us, timeout_s)
|
||||
-- wrapper for input.waitForEvents that will retry for some cases
|
||||
local ok, ev
|
||||
local wait_deadline = TimeVal:now() + TimeVal:new{
|
||||
sec = timeout_s,
|
||||
usec = timeout_us
|
||||
}
|
||||
while true do
|
||||
ok, ev = pcall(input.waitForEvent, timeout_us, timeout_s)
|
||||
if #self.timer_callbacks > 0 then
|
||||
-- we don't block if there is any timer, set wait to 10us
|
||||
while #self.timer_callbacks > 0 do
|
||||
ok, ev = pcall(input.waitForEvent, 100)
|
||||
if ok then break end
|
||||
local tv_now = TimeVal:now()
|
||||
if ((not timeout_us and not timeout_s) or tv_now < wait_deadline) then
|
||||
-- check whether timer is up
|
||||
if tv_now >= self.timer_callbacks[1].deadline then
|
||||
local ges = self.timer_callbacks[1].callback()
|
||||
table.remove(self.timer_callbacks, 1)
|
||||
if ges then
|
||||
-- Do we really need to clear all setTimeout after
|
||||
-- decided a gesture? FIXME
|
||||
Input.timer_callbacks = {}
|
||||
return Event:new("Gesture", ges)
|
||||
end -- EOF if ges
|
||||
end -- EOF if deadline reached
|
||||
else
|
||||
break
|
||||
end -- EOF if not exceed wait timeout
|
||||
end -- while #timer_callbacks > 0
|
||||
else
|
||||
ok, ev = pcall(input.waitForEvent, timeout_us)
|
||||
end -- EOF if #timer_callbacks > 0
|
||||
if ok then
|
||||
break
|
||||
end
|
||||
@@ -291,7 +327,7 @@ function Input:waitEvent(timeout_us, timeout_s)
|
||||
elseif ev == "application forced to quit" then
|
||||
os.exit(0)
|
||||
end
|
||||
DEBUG("got error waiting for events:", ev)
|
||||
--DEBUG("got error waiting for events:", ev)
|
||||
if ev ~= "Waiting for input failed: 4\n" then
|
||||
-- we only abort if the error is not EINTR
|
||||
break
|
||||
@@ -341,28 +377,10 @@ function Input:waitEvent(timeout_us, timeout_s)
|
||||
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
|
||||
return Event:new("KeyRelease", key)
|
||||
end
|
||||
elseif ev.type == EV_ABS then
|
||||
if ev.code == ABS_MT_SLOT then
|
||||
DEBUG("MT_SLOT:", ev.value)
|
||||
elseif ev.code == ABS_MT_TRACKING_ID then
|
||||
DEBUG("MT_TRACK_ID:", ev.value)
|
||||
elseif ev.code == ABS_MT_POSITION_X then
|
||||
DEBUG("MT_X:", ev.value)
|
||||
elseif ev.code == ABS_MT_POSITION_Y then
|
||||
DEBUG("MT_Y:", ev.value)
|
||||
else
|
||||
DEBUG("unknown touch event!", ev)
|
||||
return Event:new("UnkonwnTouchEvent", ev)
|
||||
end
|
||||
elseif ev.type == EV_SYN then
|
||||
if ev.code == SYN_REPORT then
|
||||
DEBUG("SYN REPORT")
|
||||
elseif ev.code == SYN_MT_REPORT then
|
||||
DEBUG("SYN MT_REPORT")
|
||||
elseif ev.code == SYN_CONFIG then
|
||||
DEBUG("SYN CONFIG")
|
||||
else
|
||||
DEBUG(ev)
|
||||
elseif ev.type == EV_ABS or ev.type == EV_SYN then
|
||||
local touch_ges = GestureDetector:feedEvent(ev)
|
||||
if touch_ges then
|
||||
return Event:new("Gesture", touch_ges)
|
||||
end
|
||||
else
|
||||
-- some other kind of event that we do not know yet
|
||||
|
||||
@@ -80,6 +80,15 @@ function MenuItem:init()
|
||||
self.active_key_events = {
|
||||
Select = { {"Press"}, doc = "chose selected item" },
|
||||
}
|
||||
self.ges_events = {
|
||||
TapSelect = {
|
||||
GestureRange:new{
|
||||
ges = "tap",
|
||||
range = self.dimen,
|
||||
},
|
||||
doc = "Select Menu Item",
|
||||
},
|
||||
}
|
||||
|
||||
w = sizeUtf8Text(0, self.dimen.w, self.face, self.text, true).x
|
||||
if w >= self.content_width then
|
||||
@@ -136,6 +145,11 @@ function MenuItem:onShowItemDetail()
|
||||
return true
|
||||
end
|
||||
|
||||
function MenuItem:onTapSelect()
|
||||
self.menu:onMenuSelect(self.table)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Widget that displays menu
|
||||
@@ -265,9 +279,11 @@ function Menu:updateItems(select_number)
|
||||
local item_tmp = MenuItem:new{
|
||||
text = self.item_table[i].text,
|
||||
face = self.cface,
|
||||
dimen = self.item_dimen,
|
||||
dimen = self.item_dimen:new(),
|
||||
shortcut = item_shortcut,
|
||||
shortcut_style = shortcut_style,
|
||||
table = self.item_table[i],
|
||||
menu = self,
|
||||
}
|
||||
table.insert(self.item_group, item_tmp)
|
||||
table.insert(self.layout, {item_tmp})
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
ReaderMenu = InputContainer:new{
|
||||
key_events = {
|
||||
ShowMenu = { { "Menu" }, doc = "show menu" },
|
||||
},
|
||||
}
|
||||
ReaderMenu = InputContainer:new{}
|
||||
|
||||
function ReaderMenu:init()
|
||||
if Device:isTouchDevice() then
|
||||
self.ges_events = {
|
||||
TapShowMenu = {
|
||||
GestureRange:new{
|
||||
ges = "tap",
|
||||
range = Geom:new{
|
||||
x = 0, y = 0,
|
||||
w = Screen:getWidth(),
|
||||
h = Screen:getHeight()/2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
self.key_events = {
|
||||
ShowMenu = { { "Menu" }, doc = "show menu" },
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderMenu:onShowMenu()
|
||||
local item_table = {}
|
||||
@@ -49,7 +66,10 @@ function ReaderMenu:onShowMenu()
|
||||
end
|
||||
|
||||
table.insert(item_table, {
|
||||
text = "Return to file browser"
|
||||
text = "Return to file browser",
|
||||
callback = function()
|
||||
self.ui:onClose()
|
||||
end
|
||||
})
|
||||
|
||||
local main_menu = Menu:new{
|
||||
@@ -69,3 +89,9 @@ function ReaderMenu:onShowMenu()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderMenu:onTapShowMenu()
|
||||
self:onShowMenu()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -1,24 +1,65 @@
|
||||
ReaderPaging = InputContainer:new{
|
||||
key_events = {
|
||||
GotoNextPage = { {Input.group.PgFwd}, doc = "go to next page", event = "GotoPageRel", args = 1 },
|
||||
GotoPrevPage = { {Input.group.PgBack}, doc = "go to previous page", event = "GotoPageRel", args = -1 },
|
||||
|
||||
GotoFirst = { {"1"}, doc = "go to start", event = "GotoPercent", args = 0},
|
||||
Goto11 = { {"2"}, doc = "go to 11%", event = "GotoPercent", args = 11},
|
||||
Goto22 = { {"3"}, doc = "go to 22%", event = "GotoPercent", args = 22},
|
||||
Goto33 = { {"4"}, doc = "go to 33%", event = "GotoPercent", args = 33},
|
||||
Goto44 = { {"5"}, doc = "go to 44%", event = "GotoPercent", args = 44},
|
||||
Goto55 = { {"6"}, doc = "go to 55%", event = "GotoPercent", args = 55},
|
||||
Goto66 = { {"7"}, doc = "go to 66%", event = "GotoPercent", args = 66},
|
||||
Goto77 = { {"8"}, doc = "go to 77%", event = "GotoPercent", args = 77},
|
||||
Goto88 = { {"9"}, doc = "go to 88%", event = "GotoPercent", args = 88},
|
||||
GotoLast = { {"0"}, doc = "go to end", event = "GotoPercent", args = 100},
|
||||
},
|
||||
current_page = 0,
|
||||
number_of_pages = 0
|
||||
}
|
||||
|
||||
function ReaderPaging:init()
|
||||
if Device:isTouchDevice() then
|
||||
self.ges_events = {
|
||||
TapForward = {
|
||||
GestureRange:new{
|
||||
ges = "tap",
|
||||
range = Geom:new{
|
||||
x = Screen:getWidth()/2,
|
||||
y = Screen:getHeight()/2,
|
||||
w = Screen:getWidth(),
|
||||
h = Screen:getHeight()
|
||||
}
|
||||
}
|
||||
},
|
||||
TapBackward = {
|
||||
GestureRange:new{
|
||||
ges = "tap",
|
||||
range = Geom:new{
|
||||
x = 0,
|
||||
y = Screen:getHeight()/2,
|
||||
w = Screen:getWidth()/2,
|
||||
h = Screen:getHeight()/2,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
self.key_events = {
|
||||
GotoNextPage = {
|
||||
{Input.group.PgFwd}, doc = "go to next page",
|
||||
event = "GotoPageRel", args = 1 },
|
||||
GotoPrevPage = {
|
||||
{Input.group.PgBack}, doc = "go to previous page",
|
||||
event = "GotoPageRel", args = -1 },
|
||||
|
||||
GotoFirst = {
|
||||
{"1"}, doc = "go to start", event = "GotoPercent", args = 0},
|
||||
Goto11 = {
|
||||
{"2"}, doc = "go to 11%", event = "GotoPercent", args = 11},
|
||||
Goto22 = {
|
||||
{"3"}, doc = "go to 22%", event = "GotoPercent", args = 22},
|
||||
Goto33 = {
|
||||
{"4"}, doc = "go to 33%", event = "GotoPercent", args = 33},
|
||||
Goto44 = {
|
||||
{"5"}, doc = "go to 44%", event = "GotoPercent", args = 44},
|
||||
Goto55 = {
|
||||
{"6"}, doc = "go to 55%", event = "GotoPercent", args = 55},
|
||||
Goto66 = {
|
||||
{"7"}, doc = "go to 66%", event = "GotoPercent", args = 66},
|
||||
Goto77 = {
|
||||
{"8"}, doc = "go to 77%", event = "GotoPercent", args = 77},
|
||||
Goto88 = {
|
||||
{"9"}, doc = "go to 88%", event = "GotoPercent", args = 88},
|
||||
GotoLast = {
|
||||
{"0"}, doc = "go to end", event = "GotoPercent", args = 100},
|
||||
}
|
||||
end
|
||||
self.number_of_pages = self.ui.document.info.number_of_pages
|
||||
end
|
||||
|
||||
@@ -68,3 +109,15 @@ end
|
||||
function ReaderPaging:onCloseDocument()
|
||||
self.ui.doc_settings:saveSetting("last_page", self.current_page)
|
||||
end
|
||||
|
||||
function ReaderPaging:onTapForward()
|
||||
self:onGotoPageRel(1)
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderPaging:onTapBackward()
|
||||
self:onGotoPageRel(-1)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
102
frontend/ui/time.lua
Normal file
102
frontend/ui/time.lua
Normal file
@@ -0,0 +1,102 @@
|
||||
TimeVal = {
|
||||
sec = 0,
|
||||
usec = 0,
|
||||
}
|
||||
|
||||
function TimeVal:new(o)
|
||||
local o = o or {}
|
||||
if o.sec == nil then
|
||||
o.sec = 0
|
||||
end
|
||||
if o.usec == nil then
|
||||
o.usec = 0
|
||||
elseif o.usec > 1000000 then
|
||||
o.sec = o.sec + maht.floor(o.usec/1000000)
|
||||
o.usec = o.usec % 1000000
|
||||
end
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
function TimeVal:__lt(time_b)
|
||||
if self.sec < time_b.sec then
|
||||
return true
|
||||
elseif self.sec > time_b.sec then
|
||||
return false
|
||||
else
|
||||
-- self.sec == time_b.sec
|
||||
if self.usec < time_b.usec then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TimeVal:__le(time_b)
|
||||
if self.sec < time_b.sec then
|
||||
return true
|
||||
elseif self.sec > time_b.sec then
|
||||
return false
|
||||
else
|
||||
-- self.sec == time_b.sec
|
||||
if self.usec > time_b.usec then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TimeVal:__eq(time_b)
|
||||
if self.sec == time_b.sec and self.usec == time_b.usec then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function TimeVal:__sub(time_b)
|
||||
diff = TimeVal:new{}
|
||||
|
||||
diff.sec = self.sec - time_b.sec
|
||||
diff.usec = self.usec - time_b.usec
|
||||
|
||||
if diff.sec < 0 and diff.usec > 0 then
|
||||
diff.sec = diff.sec + 1
|
||||
diff.usec = diff.usec - 1000000
|
||||
elseif diff.sec > 0 and diff.usec < 0 then
|
||||
diff.sec = diff.sec - 1
|
||||
diff.usec = diff.usec + 1000000
|
||||
end
|
||||
|
||||
return diff
|
||||
end
|
||||
|
||||
function TimeVal:__add(time_b)
|
||||
sum = TimeVal:new{}
|
||||
|
||||
sum.sec = self.sec + time_b.sec
|
||||
sum.usec = self.usec + time_b.usec
|
||||
if sum.usec > 1000000 then
|
||||
sum.usec = sum.usec - 1000000
|
||||
sum.sec = sum.sec + 1
|
||||
end
|
||||
|
||||
if sum.sec < 0 and sum.usec > 0 then
|
||||
sum.sec = sum.sec + 1
|
||||
sum.usec = sum.usec - 1000000
|
||||
elseif sum.sec > 0 and sum.usec < 0 then
|
||||
sum.sec = sum.sec - 1
|
||||
sum.usec = sum.usec + 1000000
|
||||
end
|
||||
|
||||
return sum
|
||||
end
|
||||
|
||||
function TimeVal:now()
|
||||
local sec, usec = util.gettime()
|
||||
return TimeVal:new{sec = sec, usec = usec}
|
||||
end
|
||||
@@ -200,6 +200,7 @@ function UIManager:run()
|
||||
|
||||
-- delegate input_event to handler
|
||||
if input_event then
|
||||
DEBUG(input_event)
|
||||
self:sendEvent(input_event)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@ require "ui/graphics"
|
||||
require "ui/image"
|
||||
require "ui/event"
|
||||
require "ui/inputevent"
|
||||
require "ui/gesturedetector"
|
||||
require "ui/font"
|
||||
|
||||
--[[
|
||||
@@ -60,7 +61,7 @@ function WidgetContainer:getSize()
|
||||
-- return size of first child widget
|
||||
return self[1]:getSize()
|
||||
else
|
||||
return { w = 0, h = 0 }
|
||||
return Geom:new{ w = 0, h = 0 }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -513,9 +514,18 @@ it is suggested to reference configurable sequences from another table
|
||||
and store that table as configuration setting
|
||||
]]
|
||||
InputContainer = WidgetContainer:new{
|
||||
key_events = {}
|
||||
key_events = {},
|
||||
ges_events = {},
|
||||
}
|
||||
|
||||
function InputContainer:paintTo(bb, x, y)
|
||||
self.dimen.x = x
|
||||
self.dimen.y = y
|
||||
if self[1] then
|
||||
return self[1]:paintTo(bb, x, y)
|
||||
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
|
||||
@@ -533,3 +543,15 @@ function InputContainer:onKeyPress(key)
|
||||
end
|
||||
end
|
||||
|
||||
function InputContainer:onGesture(ev)
|
||||
for name, gsseq in pairs(self.ges_events) do
|
||||
for _, gs_range in ipairs(gsseq) do
|
||||
if gs_range:match(ev) then
|
||||
--DEBUG(gs_range)
|
||||
local eventname = gsseq.event or name
|
||||
return self:handleEvent(Event:new(eventname, gsseq.args, ev))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
3
input.c
3
input.c
@@ -299,8 +299,9 @@ static int waitForInput(lua_State *L) {
|
||||
}
|
||||
|
||||
int ticks = SDL_GetTicks();
|
||||
if (usecs < 0)
|
||||
if (usecs < 0) {
|
||||
SDL_WaitEvent(&event);
|
||||
}
|
||||
else {
|
||||
while (SDL_GetTicks()-ticks <= usecs/1000) {
|
||||
if (SDL_PollEvent(&event)) break;
|
||||
|
||||
@@ -97,7 +97,7 @@ else
|
||||
DEBUG = function() end
|
||||
end
|
||||
|
||||
if Device.isKindle4() then
|
||||
if Device.isKindle4() or Device:isTouchDevice() then
|
||||
-- remove menu item shortcut for K4
|
||||
Menu.is_enable_shortcut = false
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user