GestureDetector: add Tap interval setting, to avoid bounces

Also fix info_text not displayed, and add more
descriptive ones.
This commit is contained in:
poire-z
2020-10-18 15:57:40 +02:00
parent d9b658034d
commit b90f6db876
2 changed files with 134 additions and 46 deletions

View File

@@ -47,22 +47,31 @@ local TimeVal = require("ui/timeval")
local logger = require("logger") local logger = require("logger")
local util = require("util") local util = require("util")
-- all the time parameters are in us -- default values (all the time parameters are in microseconds)
local ges_double_tap_interval = G_reader_settings:readSetting("ges_double_tap_interval") or 300 * 1000 local TAP_INTERVAL = 0 * 1000
local ges_two_finger_tap_duration = G_reader_settings:readSetting("ges_two_finger_tap_duration") or 300 * 1000 local DOUBLE_TAP_INTERVAL = 300 * 1000
local ges_hold_interval = G_reader_settings:readSetting("ges_hold_interval") or 500 * 1000 local TWO_FINGER_TAP_DURATION = 300 * 1000
local ges_pan_delayed_interval = G_reader_settings:readSetting("ges_pan_delayed_interval") or 500 * 1000 local HOLD_INTERVAL = 500 * 1000
local ges_swipe_interval = G_reader_settings:readSetting("ges_swipe_interval") or 900 * 1000 local PAN_DELAYED_INTERVAL = 500 * 1000
local SWIPE_INTERVAL = 900 * 1000
-- current values
local ges_tap_interval = G_reader_settings:readSetting("ges_tap_interval") or TAP_INTERVAL
local ges_double_tap_interval = G_reader_settings:readSetting("ges_double_tap_interval") or DOUBLE_TAP_INTERVAL
local ges_two_finger_tap_duration = G_reader_settings:readSetting("ges_two_finger_tap_duration") or TWO_FINGER_TAP_DURATION
local ges_hold_interval = G_reader_settings:readSetting("ges_hold_interval") or HOLD_INTERVAL
local ges_pan_delayed_interval = G_reader_settings:readSetting("ges_pan_delayed_interval") or PAN_DELAYED_INTERVAL
local ges_swipe_interval = G_reader_settings:readSetting("ges_swipe_interval") or SWIPE_INTERVAL
local GestureDetector = { local GestureDetector = {
-- must be initialized with the Input singleton class -- must be initialized with the Input singleton class
input = nil, input = nil,
-- default values (all the time parameters are in us) -- default values (accessed for display by plugins/gestures.koplugin)
DOUBLE_TAP_INTERVAL = 300 * 1000, TAP_INTERVAL = TAP_INTERVAL,
TWO_FINGER_TAP_DURATION = 300 * 1000, DOUBLE_TAP_INTERVAL = DOUBLE_TAP_INTERVAL,
HOLD_INTERVAL = 500 * 1000, TWO_FINGER_TAP_DURATION = TWO_FINGER_TAP_DURATION,
PAN_DELAYED_INTERVAL = 500 * 1000, HOLD_INTERVAL = HOLD_INTERVAL,
SWIPE_INTERVAL = 900 * 1000, PAN_DELAYED_INTERVAL = PAN_DELAYED_INTERVAL,
SWIPE_INTERVAL = SWIPE_INTERVAL,
-- pinch/spread direction table -- pinch/spread direction table
DIRECTION_TABLE = { DIRECTION_TABLE = {
east = "horizontal", east = "horizontal",
@@ -103,6 +112,7 @@ function GestureDetector:init()
-- distance parameters -- distance parameters
self.TWO_FINGER_TAP_REGION = 20 * scaler self.TWO_FINGER_TAP_REGION = 20 * scaler
self.DOUBLE_TAP_DISTANCE = 50 * scaler self.DOUBLE_TAP_DISTANCE = 50 * scaler
self.SINGLE_TAP_BOUNCE_DISTANCE = self.DOUBLE_TAP_DISTANCE
self.PAN_THRESHOLD = self.DOUBLE_TAP_DISTANCE self.PAN_THRESHOLD = self.DOUBLE_TAP_DISTANCE
self.MULTISWIPE_THRESHOLD = self.DOUBLE_TAP_DISTANCE self.MULTISWIPE_THRESHOLD = self.DOUBLE_TAP_DISTANCE
end end
@@ -144,6 +154,15 @@ end
--[[ --[[
tap2 is the later tap tap2 is the later tap
--]] --]]
function GestureDetector:isTapBounce(tap1, tap2)
local tv_diff = tap2.timev - tap1.timev
return (
math.abs(tap1.x - tap2.x) < self.SINGLE_TAP_BOUNCE_DISTANCE and
math.abs(tap1.y - tap2.y) < self.SINGLE_TAP_BOUNCE_DISTANCE and
(tv_diff.sec == 0 and (tv_diff.usec) < ges_tap_interval)
)
end
function GestureDetector:isDoubleTap(tap1, tap2) function GestureDetector:isDoubleTap(tap1, tap2)
local tv_diff = tap2.timev - tap1.timev local tv_diff = tap2.timev - tap1.timev
return ( return (
@@ -244,7 +263,9 @@ function GestureDetector:clearState(slot)
end end
function GestureDetector:setNewInterval(type, interval) function GestureDetector:setNewInterval(type, interval)
if type == "ges_double_tap_interval" then if type == "ges_tap_interval" then
ges_tap_interval = interval
elseif type == "ges_double_tap_interval" then
ges_double_tap_interval = interval ges_double_tap_interval = interval
elseif type == "ges_two_finger_tap_duration" then elseif type == "ges_two_finger_tap_duration" then
ges_two_finger_tap_duration = interval ges_two_finger_tap_duration = interval
@@ -258,7 +279,9 @@ function GestureDetector:setNewInterval(type, interval)
end end
function GestureDetector:getInterval(type) function GestureDetector:getInterval(type)
if type == "ges_double_tap_interval" then if type == "ges_tap_interval" then
return ges_tap_interval
elseif type == "ges_double_tap_interval" then
return ges_double_tap_interval return ges_double_tap_interval
elseif type == "ges_two_finger_tap_duration" then elseif type == "ges_two_finger_tap_duration" then
return ges_two_finger_tap_duration return ges_two_finger_tap_duration
@@ -330,6 +353,8 @@ function GestureDetector:tapState(tev)
self:clearState(slot) self:clearState(slot)
end end
elseif self.last_tevs[slot] ~= nil then elseif self.last_tevs[slot] ~= nil then
-- Normal single tap seems to always go thru here
-- (the next 'else' might be there for edge cases)
return self:handleDoubleTap(tev) return self:handleDoubleTap(tev)
else else
-- last tev in this slot is cleared by last two finger tap -- last tev in this slot is cleared by last two finger tap
@@ -368,6 +393,17 @@ function GestureDetector:handleDoubleTap(tev)
timev = tev.timev, timev = tev.timev,
} }
-- We do tap bounce detection even when double tap is enabled (so, double tap
-- is triggered when: ges_tap_interval <= delay < ges_double_tap_interval)
if ges_tap_interval > 0 and self.last_taps[slot] ~= nil and self:isTapBounce(self.last_taps[slot], cur_tap) then
logger.dbg("tap bounce detected in slot", slot, ": ignored")
-- Simply ignore it, and clear state as this is the end of a touch event
-- (this doesn't clear self.last_taps[slot], so a 3rd tap can be detected
-- as a double tap)
self:clearState(slot)
return
end
if not self.input.disable_double_tap and self.last_taps[slot] ~= nil and if not self.input.disable_double_tap and self.last_taps[slot] ~= nil and
self:isDoubleTap(self.last_taps[slot], cur_tap) then self:isDoubleTap(self.last_taps[slot], cur_tap) then
-- it is a double tap -- it is a double tap
@@ -381,16 +417,27 @@ function GestureDetector:handleDoubleTap(tev)
-- set current tap to last tap -- set current tap to last tap
self.last_taps[slot] = cur_tap self.last_taps[slot] = cur_tap
logger.dbg("set up tap timer") if self.input.disable_double_tap then
-- We can send the event immediately (no need for the
-- timer stuff needed for double tap support)
logger.dbg("single tap detected in slot", slot, ges_ev.pos)
self:clearState(slot)
return ges_ev
end
-- Double tap enabled: we can't send this single tap immediately as it
-- may be the start of a double tap. We'll send it as a single tap after
-- a timer if no second tap happened in the double tap delay.
logger.dbg("set up single/double tap timer")
-- deadline should be calculated by adding current tap time and the interval -- deadline should be calculated by adding current tap time and the interval
local deadline = cur_tap.timev + TimeVal:new{ local deadline = cur_tap.timev + TimeVal:new{
sec = 0, sec = 0,
usec = not self.input.disable_double_tap and ges_double_tap_interval or 0, usec = not self.input.disable_double_tap and ges_double_tap_interval or 0,
} }
self.input:setTimeout(function() self.input:setTimeout(function()
logger.dbg("in tap timer", self.last_taps[slot] ~= nil) logger.dbg("in single/double tap timer", self.last_taps[slot] ~= nil)
-- double tap will set last_tap to nil so if it is not, then -- double tap will set last_tap to nil so if it is not, then
-- user must only tapped once -- user has not double-tap'ed: it's a single tap
if self.last_taps[slot] ~= nil then if self.last_taps[slot] ~= nil then
self.last_taps[slot] = nil self.last_taps[slot] = nil
-- we are using closure here -- we are using closure here

View File

@@ -437,6 +437,7 @@ function Gestures:addIntervals(menu_items)
sub_item_table = { sub_item_table = {
{ {
text = _("Text selection rate"), text = _("Text selection rate"),
keep_menu_open = true,
callback = function() callback = function()
local SpinWidget = require("ui/widget/spinwidget") local SpinWidget = require("ui/widget/spinwidget")
local current_value = G_reader_settings:readSetting("hold_pan_rate") local current_value = G_reader_settings:readSetting("hold_pan_rate")
@@ -444,18 +445,19 @@ function Gestures:addIntervals(menu_items)
current_value = Screen.low_pan_rate and 5.0 or 30.0 current_value = Screen.low_pan_rate and 5.0 or 30.0
end end
local items = SpinWidget:new{ local items = SpinWidget:new{
text = T(_([[ title_text = _("Text selection rate"),
info_text = T(_([[
The rate is how often screen will be refreshed per second while selecting text. The rate is how often screen will be refreshed per second while selecting text.
Higher values mean faster screen updates, but also use more CPU. Higher values mean faster screen updates, but also use more CPU.
Default value: %1]]), Screen.low_pan_rate and 5.0 or 30.0), Default value: %1]]), Screen.low_pan_rate and 5.0 or 30.0),
width = math.floor(Screen:getWidth() * 0.6), width = math.floor(Screen:getWidth() * 0.75),
value = current_value, value = current_value,
value_min = 1.0, value_min = 1.0,
value_max = 60.0, value_max = 60.0,
value_step = 1, value_step = 1,
value_hold_step = 15, value_hold_step = 15,
ok_text = _("Set rate"), ok_text = _("Set rate"),
title_text = _("Text selection rate"),
default_value = Screen.low_pan_rate and 5.0 or 30.0, default_value = Screen.low_pan_rate and 5.0 or 30.0,
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("hold_pan_rate", spin.value) G_reader_settings:saveSetting("hold_pan_rate", spin.value)
@@ -467,23 +469,54 @@ Default value: %1]]), Screen.low_pan_rate and 5.0 or 30.0),
separator = true, separator = true,
}, },
{ {
text = _("Double tap interval"), text = _("Tap interval"),
keep_menu_open = true,
callback = function() callback = function()
local SpinWidget = require("ui/widget/spinwidget") local SpinWidget = require("ui/widget/spinwidget")
local GestureDetector = require("device/gesturedetector") local GestureDetector = require("device/gesturedetector")
local items = SpinWidget:new{ local items = SpinWidget:new{
text = T(_([[ title_text = _("Tap interval"),
Set double tap interval in milliseconds. info_text = T(_([[
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds). Any other taps made within this interval after a first tap will be considered accidental and ignored.
The interval value is in milliseconds and can range from 0 (0 second) to 2000 (2 seconds).
Default value: %1]]), GestureDetector.TAP_INTERVAL/1000),
width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_tap_interval")/1000,
value_min = 0,
value_max = 2000,
value_step = 50,
value_hold_step = 200,
ok_text = _("Set interval"),
default_value = GestureDetector.TAP_INTERVAL/1000,
callback = function(spin)
G_reader_settings:saveSetting("ges_tap_interval", spin.value*1000)
GestureDetector:setNewInterval("ges_tap_interval", spin.value*1000)
end
}
UIManager:show(items)
end,
},
{
text = _("Double tap interval"),
keep_menu_open = true,
callback = function()
local SpinWidget = require("ui/widget/spinwidget")
local GestureDetector = require("device/gesturedetector")
local items = SpinWidget:new{
title_text = _("Double tap interval"),
info_text = T(_([[
When double tap is enabled, this sets the time to wait for the second tap. A single tap will take at least this long to be detected.
The interval value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).
Default value: %1]]), GestureDetector.DOUBLE_TAP_INTERVAL/1000), Default value: %1]]), GestureDetector.DOUBLE_TAP_INTERVAL/1000),
width = math.floor(Screen:getWidth() * 0.6), width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_double_tap_interval")/1000, value = GestureDetector:getInterval("ges_double_tap_interval")/1000,
value_min = 100, value_min = 100,
value_max = 2000, value_max = 2000,
value_step = 100, value_step = 100,
value_hold_step = 500, value_hold_step = 500,
ok_text = _("Set interval"), ok_text = _("Set interval"),
title_text = _("Double tap interval"),
default_value = GestureDetector.DOUBLE_TAP_INTERVAL/1000, default_value = GestureDetector.DOUBLE_TAP_INTERVAL/1000,
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("ges_double_tap_interval", spin.value*1000) G_reader_settings:saveSetting("ges_double_tap_interval", spin.value*1000)
@@ -495,22 +528,24 @@ Default value: %1]]), GestureDetector.DOUBLE_TAP_INTERVAL/1000),
}, },
{ {
text = _("Two finger tap duration"), text = _("Two finger tap duration"),
keep_menu_open = true,
callback = function() callback = function()
local SpinWidget = require("ui/widget/spinwidget") local SpinWidget = require("ui/widget/spinwidget")
local GestureDetector = require("device/gesturedetector") local GestureDetector = require("device/gesturedetector")
local items = SpinWidget:new{ local items = SpinWidget:new{
text = T(_([[ title_text = _("Two finger tap duration"),
Set two finger tap duration in milliseconds. info_text = T(_([[
The duration value can range from 100 (0.1 seconds) to 2000 (2 seconds). This sets the allowed duration of any of the two fingers touch/release for the combined event to be considered a two finger tap.
The duration value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).
Default value: %1]]), GestureDetector.TWO_FINGER_TAP_DURATION/1000), Default value: %1]]), GestureDetector.TWO_FINGER_TAP_DURATION/1000),
width = math.floor(Screen:getWidth() * 0.6), width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_two_finger_tap_duration")/1000, value = GestureDetector:getInterval("ges_two_finger_tap_duration")/1000,
value_min = 100, value_min = 100,
value_max = 2000, value_max = 2000,
value_step = 100, value_step = 100,
value_hold_step = 500, value_hold_step = 500,
ok_text = _("Set duration"), ok_text = _("Set duration"),
title_text = _("Two finger tap duration"),
default_value = GestureDetector.TWO_FINGER_TAP_DURATION/1000, default_value = GestureDetector.TWO_FINGER_TAP_DURATION/1000,
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("ges_two_finger_tap_duration", spin.value*1000) G_reader_settings:saveSetting("ges_two_finger_tap_duration", spin.value*1000)
@@ -522,22 +557,24 @@ Default value: %1]]), GestureDetector.TWO_FINGER_TAP_DURATION/1000),
}, },
{ {
text = _("Hold interval"), text = _("Hold interval"),
keep_menu_open = true,
callback = function() callback = function()
local SpinWidget = require("ui/widget/spinwidget") local SpinWidget = require("ui/widget/spinwidget")
local GestureDetector = require("device/gesturedetector") local GestureDetector = require("device/gesturedetector")
local items = SpinWidget:new{ local items = SpinWidget:new{
text = T(_([[ title_text = _("Hold interval"),
Set hold interval in milliseconds. info_text = T(_([[
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds). If a touch is not released in this interval, it is considered a hold (or long-press). On document's text, single word selection is then triggered.
The interval value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).
Default value: %1]]), GestureDetector.HOLD_INTERVAL/1000), Default value: %1]]), GestureDetector.HOLD_INTERVAL/1000),
width = math.floor(Screen:getWidth() * 0.6), width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_hold_interval")/1000, value = GestureDetector:getInterval("ges_hold_interval")/1000,
value_min = 100, value_min = 100,
value_max = 2000, value_max = 2000,
value_step = 100, value_step = 100,
value_hold_step = 500, value_hold_step = 500,
ok_text = _("Set interval"), ok_text = _("Set interval"),
title_text = _("Hold interval"),
default_value = GestureDetector.HOLD_INTERVAL/1000, default_value = GestureDetector.HOLD_INTERVAL/1000,
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("ges_hold_interval", spin.value*1000) G_reader_settings:saveSetting("ges_hold_interval", spin.value*1000)
@@ -549,22 +586,24 @@ Default value: %1]]), GestureDetector.HOLD_INTERVAL/1000),
}, },
{ {
text = _("Pan delay interval"), text = _("Pan delay interval"),
keep_menu_open = true,
callback = function() callback = function()
local SpinWidget = require("ui/widget/spinwidget") local SpinWidget = require("ui/widget/spinwidget")
local GestureDetector = require("device/gesturedetector") local GestureDetector = require("device/gesturedetector")
local items = SpinWidget:new{ local items = SpinWidget:new{
text = T(_([[ title_text = _("Pan delay interval"),
Set pan delay interval in milliseconds. info_text = T(_([[
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds). This is used where necessary to reduce potential activation of panning when swiping is intended (e.g., for the menu or for multiswipe).
The interval value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).
Default value: %1]]), GestureDetector.PAN_DELAYED_INTERVAL/1000), Default value: %1]]), GestureDetector.PAN_DELAYED_INTERVAL/1000),
width = math.floor(Screen:getWidth() * 0.6), width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_pan_delayed_interval")/1000, value = GestureDetector:getInterval("ges_pan_delayed_interval")/1000,
value_min = 100, value_min = 100,
value_max = 2000, value_max = 2000,
value_step = 100, value_step = 100,
value_hold_step = 500, value_hold_step = 500,
ok_text = _("Set interval"), ok_text = _("Set interval"),
title_text = _("Pan delay interval"),
default_value = GestureDetector.PAN_DELAYED_INTERVAL/1000, default_value = GestureDetector.PAN_DELAYED_INTERVAL/1000,
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("ges_pan_delayed_interval", spin.value*1000) G_reader_settings:saveSetting("ges_pan_delayed_interval", spin.value*1000)
@@ -576,22 +615,24 @@ Default value: %1]]), GestureDetector.PAN_DELAYED_INTERVAL/1000),
}, },
{ {
text = _("Swipe interval"), text = _("Swipe interval"),
keep_menu_open = true,
callback = function() callback = function()
local SpinWidget = require("ui/widget/spinwidget") local SpinWidget = require("ui/widget/spinwidget")
local GestureDetector = require("device/gesturedetector") local GestureDetector = require("device/gesturedetector")
local items = SpinWidget:new{ local items = SpinWidget:new{
text = T(_([[ title_text = _("Swipe interval"),
Set swipe interval in milliseconds. info_text = T(_([[
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds). This sets the maximum delay between the start and the end of a swipe for it to be considered a swipe. Above this interval, it's considered panning.
The interval value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).
Default value: %1]]), GestureDetector.SWIPE_INTERVAL/1000), Default value: %1]]), GestureDetector.SWIPE_INTERVAL/1000),
width = math.floor(Screen:getWidth() * 0.6), width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_swipe_interval")/1000, value = GestureDetector:getInterval("ges_swipe_interval")/1000,
value_min = 100, value_min = 100,
value_max = 2000, value_max = 2000,
value_step = 100, value_step = 100,
value_hold_step = 500, value_hold_step = 500,
ok_text = _("Set interval"), ok_text = _("Set interval"),
title_text = _("Swipe interval"),
default_value = GestureDetector.SWIPE_INTERVAL/1000, default_value = GestureDetector.SWIPE_INTERVAL/1000,
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("ges_swipe_interval", spin.value*1000) G_reader_settings:saveSetting("ges_swipe_interval", spin.value*1000)