From 14bbcde4225fc80a20a715a95259d0a8bab7ac3c Mon Sep 17 00:00:00 2001 From: chrox Date: Sat, 2 Mar 2013 22:21:18 +0800 Subject: [PATCH] add multiple slots in gesture detector So that gesture detecting in one slot won't block gestures in the other slots. --- frontend/ui/gesturedetector.lua | 176 +++++++++++++++++--------------- 1 file changed, 92 insertions(+), 84 deletions(-) diff --git a/frontend/ui/gesturedetector.lua b/frontend/ui/gesturedetector.lua index 63de6cbd0..98cf4f762 100644 --- a/frontend/ui/gesturedetector.lua +++ b/frontend/ui/gesturedetector.lua @@ -59,30 +59,30 @@ GestureDetector = { -- distance parameters DOUBLE_TAP_DISTANCE = 50, PAN_THRESHOLD = 50, - - track_id = {}, - tev_stack = {}, - -- latest feeded touch event - last_tev = {}, - is_on_detecting = false, - first_tev = nil, - state = function(self, tev) - self:switchState("initialState", tev) - end, - - last_tap = nil, -- for single/double tap + + -- states are stored in separated slots + states = {}, + track_ids = {}, + tev_stacks = {}, + -- latest feeded touch event in each slots + last_tevs = {}, + first_tevs = {}, + -- detecting status on each slots + detectings = {}, + -- for single/double tap + last_taps = {}, } function GestureDetector:feedEvent(tev) - -- for now, only handle single point touch - if tev.slot ~= 0 then - return nil + local slot = tev.slot + if not self.states[slot] then + self:clearState(slot) -- initiate state end - re = self.state(self, tev) + local ges = self.states[slot](self, tev) if tev.id ~= -1 then - self.last_tev = tev + self.last_tevs[slot] = tev end - return re + return ges end function GestureDetector:deepCopyEv(tev) @@ -111,14 +111,15 @@ function GestureDetector:isDoubleTap(tap1, tap2) end --[[ -compare last_pan with self.first_tev +compare last_pan with first_tev in this slot if it is a swipe, return direction of swipe gesture. --]] -function GestureDetector:isSwipe() - local tv_diff = self.first_tev.timev - self.last_tev.timev +function GestureDetector:isSwipe(tev) + local slot = tev.slot + local tv_diff = self.first_tevs[slot].timev - self.last_tevs[slot].timev if (tv_diff.sec == 0) and (tv_diff.usec < self.SWIPE_INTERVAL) then - x_diff = self.last_tev.x - self.first_tev.x - y_diff = self.last_tev.y - self.first_tev.y + local x_diff = self.last_tevs[slot].x - self.first_tevs[slot].x + local y_diff = self.last_tevs[slot].y - self.first_tevs[slot].y if x_diff == 0 and y_diff == 0 then return nil end @@ -145,32 +146,33 @@ end Warning! this method won't update self.state, you need to do it in each state method! --]] -function GestureDetector:switchState(state_new, tev) +function GestureDetector:switchState(state_new, tev, param) --@TODO do we need to check whether state is valid? (houqp) - return self[state_new](self, tev) + return self[state_new](self, tev, param) end -function GestureDetector:clearState() - self.state = self.initialState - self.last_tev = {} - self.is_on_detecting = false - self.first_tev = nil +function GestureDetector:clearState(slot) + self.states[slot] = self.initialState + self.last_tevs[slot] = {} + self.detectings[slot] = false + self.first_tevs[slot] = nil end function GestureDetector:initialState(tev) + local slot = tev.slot if tev.id then -- a event ends if tev.id == -1 then - self.is_on_detecting = false + self.detectings[slot] = false else - self.track_id[tev.id] = tev.slot + self.track_ids[slot] = tev.id end end if tev.x and tev.y then -- user starts a new touch motion - if not self.is_on_detecting then - self.is_on_detecting = true - self.first_tev = self:deepCopyEv(tev) + if not self.detectings[slot] then + self.detectings[slot] = true + self.first_tevs[slot] = self:deepCopyEv(tev) -- default to tap state return self:switchState("tapState", tev) end @@ -182,14 +184,15 @@ this method handles both single and double tap --]] function GestureDetector:tapState(tev) DEBUG("in tap state...", tev) + local slot = tev.slot if tev.id == -1 then -- end of tap event local ges_ev = { -- default to single tap ges = "tap", pos = Geom:new{ - x = self.last_tev.x, - y = self.last_tev.y, + x = self.last_tevs[slot].x, + y = self.last_tevs[slot].y, w = 0, h = 0, } } @@ -200,57 +203,58 @@ function GestureDetector:tapState(tev) timev = tev.timev, } - if self.last_tap ~= nil and - self:isDoubleTap(self.last_tap, cur_tap) then + if self.last_taps[slot] ~= nil and + self:isDoubleTap(self.last_taps[slot], cur_tap) then -- it is a double tap - self:clearState() + self:clearState(slot) ges_ev.ges = "double_tap" - self.last_tap = nil - DEBUG("double tap detected") + self.last_taps[slot] = nil + DEBUG("double tap detected in slot", slot) return ges_ev end -- set current tap to last tap - self.last_tap = cur_tap + self.last_taps[slot] = cur_tap DEBUG("set up tap timer") - local deadline = self.last_tev.timev + TimeVal:new{ - sec = 0, usec = self.DOUBLE_TAP_INTERVAL, - } + -- deadline should be calculated by adding current tap time and the interval + local deadline = cur_tap.timev + TimeVal:new{ + sec = 0, usec = self.DOUBLE_TAP_INTERVAL, + } Input:setTimeout(function() - DEBUG("in tap timer", self.last_tap ~= nil) + DEBUG("in tap timer", self.last_taps[slot] ~= 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 + if self.last_taps[slot] ~= nil then + self.last_taps[slot] = nil -- we are using closure here - DEBUG("single tap detected") + DEBUG("single tap detected in slot", slot) 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 + self:clearState(slot) + elseif self.states[slot] ~= self.tapState then -- switched from other state, probably from initialState -- we return nil in this case - self.state = self.tapState + self.states[slot] = self.tapState DEBUG("set up hold timer") local deadline = tev.timev + TimeVal:new{ - sec = 0, usec = self.HOLD_INTERVAL - } + sec = 0, usec = self.HOLD_INTERVAL + } Input:setTimeout(function() - if self.state == self.tapState then + if self.states[slot] == self.tapState then -- timer set in tapState, so we switch to hold - DEBUG("hold gesture detected") - return self:switchState("holdState") + DEBUG("hold gesture detected in slot", slot) + return self:switchState("holdState", tev, true) end end, deadline) else -- it is not end of touch event, see if we need to switch to -- other states - if (tev.x and math.abs(tev.x - self.first_tev.x) >= self.PAN_THRESHOLD) or - (tev.y and math.abs(tev.y - self.first_tev.y) >= self.PAN_THRESHOLD) then + if (tev.x and math.abs(tev.x - self.first_tevs[slot].x) >= self.PAN_THRESHOLD) or + (tev.y and math.abs(tev.y - self.first_tevs[slot].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", tev) @@ -259,18 +263,19 @@ function GestureDetector:tapState(tev) end function GestureDetector:panState(tev) - DEBUG("in pan state...") + DEBUG("in pan state...", tev) + local slot = tev.slot if tev.id == -1 then -- end of pan, signal swipe gesture if necessary - swipe_direct = self:isSwipe() + local swipe_direct = self:isSwipe(tev) if swipe_direct then - DEBUG("swipe detected!") + DEBUG("swipe", swipe_direct, "detected in slot", slot) local start_pos = Geom:new{ - x = self.first_tev.x, - y = self.first_tev.y, + x = self.first_tevs[slot].x, + y = self.first_tevs[slot].y, w = 0, h = 0, } - self:clearState() + self:clearState(slot) return { ges = "swipe", direction = swipe_direct, @@ -279,10 +284,10 @@ function GestureDetector:panState(tev) --@TODO add start and end points? (houqp) } end - self:clearState() + self:clearState(slot) else - if self.state ~= self.panState then - self.state = self.panState + if self.states[slot] ~= self.panState then + self.states[slot] = self.panState end local pan_ev = { @@ -294,40 +299,43 @@ function GestureDetector:panState(tev) }, pos = nil, } - pan_ev.relative.x = tev.x - self.last_tev.x - pan_ev.relative.y = tev.y - self.last_tev.y + pan_ev.relative.x = tev.x - self.last_tevs[slot].x + pan_ev.relative.y = tev.y - self.last_tevs[slot].y pan_ev.pos = Geom:new{ - x = self.last_tev.x, - y = self.last_tev.y, + x = self.last_tevs[slot].x, + y = self.last_tevs[slot].y, w = 0, h = 0, } return pan_ev end end -function GestureDetector:holdState(tev) - DEBUG("in hold state...") - -- when we switch to hold state, we pass no ev - -- so ev = nil - if not tev and self.last_tev.x and self.last_tev.y then - self.state = self.holdState +function GestureDetector:holdState(tev, hold) + DEBUG("in hold state...", tev, hold) + local slot = tev.slot + -- when we switch to hold state, we pass additional param "hold" + if tev.id ~= -1 and hold and self.last_tevs[slot].x and self.last_tevs[slot].y then + self.states[slot] = self.holdState return { ges = "hold", pos = Geom:new{ - x = self.last_tev.x, - y = self.last_tev.y, + x = self.last_tevs[slot].x, + y = self.last_tevs[slot].y, w = 0, h = 0, } } end if tev.id == -1 then -- end of hold, signal hold release - self:clearState() + DEBUG("hold_release detected in slot", slot) + local last_x = self.last_tevs[slot].x + local last_y = self.last_tevs[slot].y + self:clearState(slot) return { ges = "hold_release", pos = Geom:new{ - x = self.last_tev.x, - y = self.last_tev.y, + x = last_x, + y = last_y, w = 0, h = 0, } }