mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
GestureDetector: add Tap interval setting, to avoid bounces
Also fix info_text not displayed, and add more descriptive ones.
This commit is contained in:
@@ -47,22 +47,31 @@ local TimeVal = require("ui/timeval")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
|
||||
-- all the time parameters are in us
|
||||
local ges_double_tap_interval = G_reader_settings:readSetting("ges_double_tap_interval") or 300 * 1000
|
||||
local ges_two_finger_tap_duration = G_reader_settings:readSetting("ges_two_finger_tap_duration") or 300 * 1000
|
||||
local ges_hold_interval = G_reader_settings:readSetting("ges_hold_interval") or 500 * 1000
|
||||
local ges_pan_delayed_interval = G_reader_settings:readSetting("ges_pan_delayed_interval") or 500 * 1000
|
||||
local ges_swipe_interval = G_reader_settings:readSetting("ges_swipe_interval") or 900 * 1000
|
||||
-- default values (all the time parameters are in microseconds)
|
||||
local TAP_INTERVAL = 0 * 1000
|
||||
local DOUBLE_TAP_INTERVAL = 300 * 1000
|
||||
local TWO_FINGER_TAP_DURATION = 300 * 1000
|
||||
local HOLD_INTERVAL = 500 * 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 = {
|
||||
-- must be initialized with the Input singleton class
|
||||
input = nil,
|
||||
-- default values (all the time parameters are in us)
|
||||
DOUBLE_TAP_INTERVAL = 300 * 1000,
|
||||
TWO_FINGER_TAP_DURATION = 300 * 1000,
|
||||
HOLD_INTERVAL = 500 * 1000,
|
||||
PAN_DELAYED_INTERVAL = 500 * 1000,
|
||||
SWIPE_INTERVAL = 900 * 1000,
|
||||
-- default values (accessed for display by plugins/gestures.koplugin)
|
||||
TAP_INTERVAL = TAP_INTERVAL,
|
||||
DOUBLE_TAP_INTERVAL = DOUBLE_TAP_INTERVAL,
|
||||
TWO_FINGER_TAP_DURATION = TWO_FINGER_TAP_DURATION,
|
||||
HOLD_INTERVAL = HOLD_INTERVAL,
|
||||
PAN_DELAYED_INTERVAL = PAN_DELAYED_INTERVAL,
|
||||
SWIPE_INTERVAL = SWIPE_INTERVAL,
|
||||
-- pinch/spread direction table
|
||||
DIRECTION_TABLE = {
|
||||
east = "horizontal",
|
||||
@@ -103,6 +112,7 @@ function GestureDetector:init()
|
||||
-- distance parameters
|
||||
self.TWO_FINGER_TAP_REGION = 20 * scaler
|
||||
self.DOUBLE_TAP_DISTANCE = 50 * scaler
|
||||
self.SINGLE_TAP_BOUNCE_DISTANCE = self.DOUBLE_TAP_DISTANCE
|
||||
self.PAN_THRESHOLD = self.DOUBLE_TAP_DISTANCE
|
||||
self.MULTISWIPE_THRESHOLD = self.DOUBLE_TAP_DISTANCE
|
||||
end
|
||||
@@ -144,6 +154,15 @@ end
|
||||
--[[
|
||||
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)
|
||||
local tv_diff = tap2.timev - tap1.timev
|
||||
return (
|
||||
@@ -244,7 +263,9 @@ function GestureDetector:clearState(slot)
|
||||
end
|
||||
|
||||
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
|
||||
elseif type == "ges_two_finger_tap_duration" then
|
||||
ges_two_finger_tap_duration = interval
|
||||
@@ -258,7 +279,9 @@ function GestureDetector:setNewInterval(type, interval)
|
||||
end
|
||||
|
||||
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
|
||||
elseif type == "ges_two_finger_tap_duration" then
|
||||
return ges_two_finger_tap_duration
|
||||
@@ -330,6 +353,8 @@ function GestureDetector:tapState(tev)
|
||||
self:clearState(slot)
|
||||
end
|
||||
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)
|
||||
else
|
||||
-- last tev in this slot is cleared by last two finger tap
|
||||
@@ -368,6 +393,17 @@ function GestureDetector:handleDoubleTap(tev)
|
||||
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
|
||||
self:isDoubleTap(self.last_taps[slot], cur_tap) then
|
||||
-- it is a double tap
|
||||
@@ -381,16 +417,27 @@ function GestureDetector:handleDoubleTap(tev)
|
||||
-- set current tap to last 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
|
||||
local deadline = cur_tap.timev + TimeVal:new{
|
||||
sec = 0,
|
||||
usec = not self.input.disable_double_tap and ges_double_tap_interval or 0,
|
||||
}
|
||||
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
|
||||
-- user must only tapped once
|
||||
-- user has not double-tap'ed: it's a single tap
|
||||
if self.last_taps[slot] ~= nil then
|
||||
self.last_taps[slot] = nil
|
||||
-- we are using closure here
|
||||
|
||||
@@ -437,6 +437,7 @@ function Gestures:addIntervals(menu_items)
|
||||
sub_item_table = {
|
||||
{
|
||||
text = _("Text selection rate"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
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
|
||||
end
|
||||
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.
|
||||
Higher values mean faster screen updates, but also use more CPU.
|
||||
|
||||
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_min = 1.0,
|
||||
value_max = 60.0,
|
||||
value_step = 1,
|
||||
value_hold_step = 15,
|
||||
ok_text = _("Set rate"),
|
||||
title_text = _("Text selection rate"),
|
||||
default_value = Screen.low_pan_rate and 5.0 or 30.0,
|
||||
callback = function(spin)
|
||||
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,
|
||||
},
|
||||
{
|
||||
text = _("Double tap interval"),
|
||||
text = _("Tap interval"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local GestureDetector = require("device/gesturedetector")
|
||||
local items = SpinWidget:new{
|
||||
text = T(_([[
|
||||
Set double tap interval in milliseconds.
|
||||
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds).
|
||||
title_text = _("Tap interval"),
|
||||
info_text = T(_([[
|
||||
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),
|
||||
width = math.floor(Screen:getWidth() * 0.6),
|
||||
width = math.floor(Screen:getWidth() * 0.75),
|
||||
value = GestureDetector:getInterval("ges_double_tap_interval")/1000,
|
||||
value_min = 100,
|
||||
value_max = 2000,
|
||||
value_step = 100,
|
||||
value_hold_step = 500,
|
||||
ok_text = _("Set interval"),
|
||||
title_text = _("Double tap interval"),
|
||||
default_value = GestureDetector.DOUBLE_TAP_INTERVAL/1000,
|
||||
callback = function(spin)
|
||||
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"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local GestureDetector = require("device/gesturedetector")
|
||||
local items = SpinWidget:new{
|
||||
text = T(_([[
|
||||
Set two finger tap duration in milliseconds.
|
||||
The duration value can range from 100 (0.1 seconds) to 2000 (2 seconds).
|
||||
title_text = _("Two finger tap duration"),
|
||||
info_text = T(_([[
|
||||
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),
|
||||
width = math.floor(Screen:getWidth() * 0.6),
|
||||
width = math.floor(Screen:getWidth() * 0.75),
|
||||
value = GestureDetector:getInterval("ges_two_finger_tap_duration")/1000,
|
||||
value_min = 100,
|
||||
value_max = 2000,
|
||||
value_step = 100,
|
||||
value_hold_step = 500,
|
||||
ok_text = _("Set duration"),
|
||||
title_text = _("Two finger tap duration"),
|
||||
default_value = GestureDetector.TWO_FINGER_TAP_DURATION/1000,
|
||||
callback = function(spin)
|
||||
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"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local GestureDetector = require("device/gesturedetector")
|
||||
local items = SpinWidget:new{
|
||||
text = T(_([[
|
||||
Set hold interval in milliseconds.
|
||||
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds).
|
||||
title_text = _("Hold interval"),
|
||||
info_text = T(_([[
|
||||
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),
|
||||
width = math.floor(Screen:getWidth() * 0.6),
|
||||
width = math.floor(Screen:getWidth() * 0.75),
|
||||
value = GestureDetector:getInterval("ges_hold_interval")/1000,
|
||||
value_min = 100,
|
||||
value_max = 2000,
|
||||
value_step = 100,
|
||||
value_hold_step = 500,
|
||||
ok_text = _("Set interval"),
|
||||
title_text = _("Hold interval"),
|
||||
default_value = GestureDetector.HOLD_INTERVAL/1000,
|
||||
callback = function(spin)
|
||||
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"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local GestureDetector = require("device/gesturedetector")
|
||||
local items = SpinWidget:new{
|
||||
text = T(_([[
|
||||
Set pan delay interval in milliseconds.
|
||||
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds).
|
||||
title_text = _("Pan delay interval"),
|
||||
info_text = T(_([[
|
||||
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),
|
||||
width = math.floor(Screen:getWidth() * 0.6),
|
||||
width = math.floor(Screen:getWidth() * 0.75),
|
||||
value = GestureDetector:getInterval("ges_pan_delayed_interval")/1000,
|
||||
value_min = 100,
|
||||
value_max = 2000,
|
||||
value_step = 100,
|
||||
value_hold_step = 500,
|
||||
ok_text = _("Set interval"),
|
||||
title_text = _("Pan delay interval"),
|
||||
default_value = GestureDetector.PAN_DELAYED_INTERVAL/1000,
|
||||
callback = function(spin)
|
||||
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"),
|
||||
keep_menu_open = true,
|
||||
callback = function()
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local GestureDetector = require("device/gesturedetector")
|
||||
local items = SpinWidget:new{
|
||||
text = T(_([[
|
||||
Set swipe interval in milliseconds.
|
||||
The interval value can range from 100 (0.1 seconds) to 2000 (2 seconds).
|
||||
title_text = _("Swipe interval"),
|
||||
info_text = T(_([[
|
||||
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),
|
||||
width = math.floor(Screen:getWidth() * 0.6),
|
||||
width = math.floor(Screen:getWidth() * 0.75),
|
||||
value = GestureDetector:getInterval("ges_swipe_interval")/1000,
|
||||
value_min = 100,
|
||||
value_max = 2000,
|
||||
value_step = 100,
|
||||
value_hold_step = 500,
|
||||
ok_text = _("Set interval"),
|
||||
title_text = _("Swipe interval"),
|
||||
default_value = GestureDetector.SWIPE_INTERVAL/1000,
|
||||
callback = function(spin)
|
||||
G_reader_settings:saveSetting("ges_swipe_interval", spin.value*1000)
|
||||
|
||||
Reference in New Issue
Block a user