InputContainer/Dispatcher: Allow toggling touch input

This is made easier by the fact that only a single method in a single
widget actually handles Gesture, and that we barely ever overload it.
So, apply a bit of monkey-patching trickery to handle the magic :).

Fix #9695
reviewable/pr9750/r10^2
NiLuJe 2 years ago
parent 47734eefef
commit 1ec6fb5fcf

@ -81,7 +81,8 @@ local settingsList = {
reboot = {category="none", event="RequestReboot", title=_("Reboot the device"), device=true, condition=Device:canReboot()},
poweroff = {category="none", event="RequestPowerOff", title=_("Power off"), device=true, condition=Device:canPowerOff(), separator=true},
exit = {category="none", event="Exit", title=_("Exit KOReader"), device=true},
toggle_hold_corners = {category="none", event="IgnoreHoldCorners", title=_("Toggle hold corners"), device=true, separator=true},
toggle_hold_corners = {category="none", event="IgnoreHoldCorners", title=_("Toggle hold corners"), device=true},
toggle_touch_input = {category="none", event="IgnoreTouchInput", title=_("Toggle touch input"), device=true, separator=true},
toggle_rotation = {category="none", event="SwapRotation", title=_("Toggle orientation"), device=true},
invert_rotation = {category="none", event="InvertRotation", title=_("Invert rotation"), device=true},
iterate_rotation = {category="none", event="IterateRotation", title=_("Rotate by 90° CW"), device=true},
@ -237,6 +238,7 @@ local dispatcher_menu_order = {
"poweroff",
"toggle_hold_corners",
"toggle_touch_input",
"toggle_gsensor",
"rotation_mode",
"toggle_rotation",
@ -939,9 +941,8 @@ end
--[[--
Calls the events in a settings list
arguments are:
1) a reference to the uimanager
2) the settings table
3) optionally a `gestures`object
1) the settings table
2) optionally a `gestures` object
--]]--
function Dispatcher:execute(settings, gesture)
if settings.settings ~= nil and settings.settings.show_as_quickmenu == true then

@ -134,7 +134,7 @@ function InputContainer:registerTouchZones(zones)
if self._zones[zone.id] then
self.touch_zone_dg:removeNode(zone.id)
end
self._zones[zone.id]= {
self._zones[zone.id] = {
def = zone,
handler = zone.handler,
gs_range = GestureRange:new{
@ -269,6 +269,76 @@ function InputContainer:onGesture(ev)
end
end
-- Will be overloaded by the Gestures plugin, if enabled, for use in _onGestureFiltered
function InputContainer:_isGestureAlwaysActive(ges, multiswipe_directions)
-- If the plugin isn't enabled, IgnoreTouchInput can still be emitted by Dispatcher (e.g., via Profile or QuickMenu).
-- Regardless of that, we still want to block all gestures anyway, as our own onResume handler will ensure
-- that the standard onGesture handler is restored on the next resume cycle,
-- allowing one to restore input handling automatically.
return false
end
InputContainer.isGestureAlwaysActive = InputContainer._isGestureAlwaysActive
-- Filtered variant that only lets specific touch zones marked as "always active" through.
-- (This is used by the "toggle_touch_input" Dispatcher action).
function InputContainer:_onGestureFiltered(ev)
for _, tzone in ipairs(self._ordered_touch_zones) do
if self:isGestureAlwaysActive(tzone.def.id, ev.multiswipe_directions) and tzone.gs_range:match(ev) and tzone.handler(ev) then
return true
end
end
-- No ges_events at all, although if the need ever arises, we could also support an "always active" marker for those ;).
if self.stop_events_propagation then
return true
end
end
-- NOTE: Monkey-patching InputContainer.onGesture allows us to effectively disable touch input,
-- because barely any InputContainer subclasses implement onGesture, meaning they all inherit this one,
-- making this specific method in this specific widget the only piece of code that handles the Gesture
-- Events sent by GestureDetector.
-- We would need to be slightly more creative if subclassed widgets did overload it in in any meaningful way[1].
-- (i.e., use a broadcast Event, don't stop its propagation, and swap self.onGesture in every instance
-- while still only swapping Input.onGesture once...).
--
-- [1] The most common implementation you'll see is a NOP for ReaderUI modules that defer gesture handling to ReaderUI.
-- Notification also implements a simple one to dismiss notifications on any user input,
-- which is something that doesn't impede our goal, which is why we don't need to deal with it.
function InputContainer:onIgnoreTouchInput(toggle)
local Notification = require("ui/widget/notification")
if toggle == false then
-- Restore the proper onGesture handler if we disabled it
if InputContainer._onGesture then
InputContainer.onGesture = InputContainer._onGesture
InputContainer._onGesture = nil
Notification:notify("Restored touch input")
end
elseif toggle == true then
-- Replace the onGesture handler w/ the minimal one if that's not already the case
if not InputContainer._onGesture then
InputContainer._onGesture = InputContainer.onGesture
InputContainer.onGesture = InputContainer._onGestureFiltered
Notification:notify("Disabled touch input")
end
else
-- Toggle the current state
if InputContainer._onGesture then
self:onIgnoreTouchInput(false)
else
self:onIgnoreTouchInput(true)
end
end
-- We only affect the base class, once is enough ;).
return true
end
function InputContainer:onResume()
-- Always restore touch input on resume, to avoid confusion for scatter-brained users ;).
-- It's also helpful when the IgnoreTouchInput event is emitted by Dispatcher through other means than Gestures.
self:onIgnoreTouchInput(false)
end
function InputContainer:onInput(input, ignore_first_hold_release)
local InputDialog = require("ui/widget/inputdialog")
self.input_dialog = InputDialog:new{

@ -9,6 +9,7 @@ local Geom = require("ui/geometry")
local GestureDetector = require("device/gesturedetector")
local GestureRange = require("ui/gesturerange")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local InputDialog = require("ui/widget/inputdialog")
local LuaSettings = require("luasettings")
local Screen = require("device").screen
@ -120,6 +121,22 @@ Multiswipes allow you to perform complex gestures built up out of multiple swipe
These advanced gestures consist of either straight swipes or diagonal swipes. To ensure accuracy, they can't be mixed.]])
-- If the gesture contains the "toggle_touch_input" action,
-- mark it "always active" to make sure that InputContainer won't block it after the IgnoreTouchInput Event.
function Gestures:isGestureAlwaysActive(ges, multiswipe_directions)
-- Handle multiswipes properly
-- NOTE: This is a bit clunky, as ges comes from the list of registered touch zones,
-- while multiswipe_directions comes from the actual input event.
-- Alas, all our multiswipe gestures are handled by a single "multiswipe" zone.
if self.multiswipes_enabled then
if ges == "multiswipe" and multiswipe_directions then
ges = "multiswipe_" .. self:safeMultiswipeName(multiswipe_directions)
end
end
return self.gestures[ges] and self.gestures[ges].toggle_touch_input
end
function Gestures:init()
local defaults_path = FFIUtil.joinPath(self.path, "defaults.lua")
if not lfs.attributes(gestures_path, "mode") then
@ -184,6 +201,13 @@ function Gestures:init()
self.ui.menu:registerToMainMenu(self)
Dispatcher:init()
self:initGesture()
-- Overload InputContainer's stub to allow it to recognize "always active" gestures
InputContainer.isGestureAlwaysActive = function(this, ges, multiswipe_directions) return self:isGestureAlwaysActive(ges, multiswipe_directions) end
end
function Gestures:onCloseWidget()
-- Restore the stub implementation on teardown, to avoid pinning a stale instance of ourselves
InputContainer.isGestureAlwaysActive = InputContainer._isGestureAlwaysActive
end
function Gestures:gestureTitleFunc(ges)

Loading…
Cancel
Save