2016-12-04 06:57:57 +00:00
|
|
|
--[[--
|
2022-10-27 00:01:51 +00:00
|
|
|
An InputContainer is a WidgetContainer that handles user input events including multi touches and key presses.
|
2016-04-07 15:28:52 +00:00
|
|
|
|
2016-12-13 16:06:02 +00:00
|
|
|
See @{InputContainer:registerTouchZones} for examples of how to listen for multi touch input.
|
2013-10-18 20:38:07 +00:00
|
|
|
|
2022-10-27 00:01:51 +00:00
|
|
|
This example illustrates how to listen for a key press input event via the `key_events` hashmap:
|
2013-10-18 20:38:07 +00:00
|
|
|
|
2022-10-27 00:01:51 +00:00
|
|
|
key_events = {
|
|
|
|
PanBy20 = {
|
|
|
|
{ "Shift", Input.group.Cursor }, -- Shift + (any member of) Cursor
|
|
|
|
event = "Pan",
|
|
|
|
args = 20,
|
|
|
|
is_inactive = true,
|
|
|
|
},
|
|
|
|
PanNormal = {
|
|
|
|
{ Input.group.Cursor }, -- Any member of Cursor (itself an array)
|
|
|
|
event = "Pan",
|
|
|
|
args = 10,
|
|
|
|
},
|
|
|
|
Exit = {
|
|
|
|
{ "Alt", "F4" }, -- Alt + F4
|
|
|
|
{ "Ctrl", "Q" }, -- Ctrl + Q
|
|
|
|
},
|
|
|
|
Home = {
|
|
|
|
{ { "Home", "H" } }, -- Any of Home or H (note the extra nesting!)
|
|
|
|
},
|
|
|
|
End = {
|
|
|
|
{ "End" }, -- NOTE: For a *single* key, we can forgo the nesting (c.f., match @ device/key).
|
|
|
|
},
|
2014-03-13 13:52:43 +00:00
|
|
|
},
|
2013-10-18 20:38:07 +00:00
|
|
|
|
2016-12-13 16:06:02 +00:00
|
|
|
It is recommended to reference configurable sequences from another table
|
|
|
|
and to store that table as a configuration setting.
|
2016-12-04 06:57:57 +00:00
|
|
|
|
|
|
|
]]
|
|
|
|
|
2017-01-23 14:54:14 +00:00
|
|
|
local DepGraph = require("depgraph")
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
local Device = require("device")
|
2016-12-04 06:57:57 +00:00
|
|
|
local Event = require("ui/event")
|
2017-04-07 13:20:57 +00:00
|
|
|
local Geom = require("ui/geometry")
|
|
|
|
local GestureRange = require("ui/gesturerange")
|
|
|
|
local UIManager = require("ui/uimanager")
|
|
|
|
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
2019-02-18 16:07:27 +00:00
|
|
|
local Screen = Device.screen
|
2016-12-04 06:57:57 +00:00
|
|
|
local _ = require("gettext")
|
|
|
|
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
local InputContainer = WidgetContainer:extend{
|
2014-03-13 13:52:43 +00:00
|
|
|
vertical_align = "top",
|
2013-10-18 20:38:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function InputContainer:_init()
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
-- These should be instance-specific
|
|
|
|
if not self.key_events then
|
|
|
|
self.key_events = {}
|
2014-03-13 13:52:43 +00:00
|
|
|
end
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
if not self.ges_events then
|
|
|
|
self.ges_events = {}
|
2014-03-13 13:52:43 +00:00
|
|
|
end
|
2017-01-23 14:54:14 +00:00
|
|
|
self.touch_zone_dg = nil
|
|
|
|
self._zones = {}
|
|
|
|
self._ordered_touch_zones = {}
|
2013-10-18 20:38:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function InputContainer:paintTo(bb, x, y)
|
2016-01-31 22:25:21 +00:00
|
|
|
if self[1] == nil then
|
|
|
|
return
|
|
|
|
end
|
2019-05-05 08:15:13 +00:00
|
|
|
if self.skip_paint then
|
|
|
|
return
|
|
|
|
end
|
2016-01-31 22:25:21 +00:00
|
|
|
|
2015-09-13 08:06:22 +00:00
|
|
|
if not self.dimen then
|
2015-10-26 15:53:07 +00:00
|
|
|
local content_size = self[1]:getSize()
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
self.dimen = Geom:new{x = 0, y = 0, w = content_size.w, h = content_size.h}
|
2015-09-13 08:06:22 +00:00
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
self.dimen.x = x
|
|
|
|
self.dimen.y = y
|
2016-01-31 22:25:21 +00:00
|
|
|
if self.vertical_align == "center" then
|
|
|
|
local content_size = self[1]:getSize()
|
|
|
|
self[1]:paintTo(bb, x, y + math.floor((self.dimen.h - content_size.h)/2))
|
|
|
|
else
|
|
|
|
self[1]:paintTo(bb, x, y)
|
2014-03-13 13:52:43 +00:00
|
|
|
end
|
2013-10-18 20:38:07 +00:00
|
|
|
end
|
|
|
|
|
2016-12-04 06:57:57 +00:00
|
|
|
--[[--
|
|
|
|
|
|
|
|
Register touch zones into this InputContainer.
|
|
|
|
|
2016-12-13 16:06:02 +00:00
|
|
|
See gesturedetector for a list of supported gestures.
|
2016-12-04 06:57:57 +00:00
|
|
|
|
|
|
|
NOTE: You are responsible for calling self:@{updateTouchZonesOnScreenResize} with the new
|
2016-12-13 16:06:02 +00:00
|
|
|
screen dimensions whenever the screen is rotated or resized.
|
2016-12-04 06:57:57 +00:00
|
|
|
|
|
|
|
@tparam table zones list of touch zones to register
|
|
|
|
|
|
|
|
@usage
|
|
|
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
|
|
|
local test_widget = InputContainer:new{}
|
|
|
|
test_widget:registerTouchZones({
|
|
|
|
{
|
|
|
|
id = "foo_tap",
|
|
|
|
ges = "tap",
|
2016-12-13 16:06:02 +00:00
|
|
|
-- This binds the handler to the full screen
|
2016-12-04 06:57:57 +00:00
|
|
|
screen_zone = {
|
|
|
|
ratio_x = 0, ratio_y = 0, ratio_w = 1, ratio_h = 1,
|
|
|
|
},
|
|
|
|
handler = function(ges)
|
|
|
|
print('User tapped on screen!')
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id = "foo_swipe",
|
|
|
|
ges = "swipe",
|
2016-12-13 16:06:02 +00:00
|
|
|
-- This binds the handler to bottom half of the screen
|
2016-12-04 06:57:57 +00:00
|
|
|
screen_zone = {
|
|
|
|
ratio_x = 0, ratio_y = 0.5, ratio_w = 1, ratio_h = 0.5,
|
|
|
|
},
|
|
|
|
handler = function(ges)
|
|
|
|
print("User swiped at the bottom with direction:", ges.direction)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require("ui/uimanager"):show(test_widget)
|
|
|
|
|
|
|
|
]]
|
|
|
|
function InputContainer:registerTouchZones(zones)
|
|
|
|
local screen_width, screen_height = Screen:getWidth(), Screen:getHeight()
|
2017-01-23 14:54:14 +00:00
|
|
|
if not self.touch_zone_dg then self.touch_zone_dg = DepGraph:new{} end
|
2016-12-04 06:57:57 +00:00
|
|
|
for _, zone in ipairs(zones) do
|
2017-01-23 14:54:14 +00:00
|
|
|
-- override touch zone with the same id to support reregistration
|
|
|
|
if self._zones[zone.id] then
|
|
|
|
self.touch_zone_dg:removeNode(zone.id)
|
2016-12-04 06:57:57 +00:00
|
|
|
end
|
2022-11-02 22:20:56 +00:00
|
|
|
self._zones[zone.id] = {
|
2016-12-04 06:57:57 +00:00
|
|
|
def = zone,
|
|
|
|
handler = zone.handler,
|
|
|
|
gs_range = GestureRange:new{
|
|
|
|
ges = zone.ges,
|
|
|
|
rate = zone.rate,
|
|
|
|
range = Geom:new{
|
|
|
|
x = screen_width * zone.screen_zone.ratio_x,
|
|
|
|
y = screen_height * zone.screen_zone.ratio_y,
|
|
|
|
w = screen_width * zone.screen_zone.ratio_w,
|
|
|
|
h = screen_height * zone.screen_zone.ratio_h,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2017-01-23 14:54:14 +00:00
|
|
|
self.touch_zone_dg:addNode(zone.id)
|
2020-01-24 19:05:21 +00:00
|
|
|
-- print("added "..zone.id)
|
2017-01-23 14:54:14 +00:00
|
|
|
if zone.overrides then
|
|
|
|
for _, override_zone_id in ipairs(zone.overrides) do
|
2020-01-24 19:05:21 +00:00
|
|
|
-- print(" override "..override_zone_id)
|
2017-01-23 14:54:14 +00:00
|
|
|
self.touch_zone_dg:addNodeDep(override_zone_id, zone.id)
|
2016-12-04 06:57:57 +00:00
|
|
|
end
|
|
|
|
end
|
2017-01-23 14:54:14 +00:00
|
|
|
end
|
2020-01-24 19:05:21 +00:00
|
|
|
-- print("ordering:")
|
2017-01-23 14:54:14 +00:00
|
|
|
self._ordered_touch_zones = {}
|
|
|
|
for _, zone_id in ipairs(self.touch_zone_dg:serialize()) do
|
|
|
|
table.insert(self._ordered_touch_zones, self._zones[zone_id])
|
2020-01-24 19:05:21 +00:00
|
|
|
-- print(" "..zone_id)
|
2016-12-04 06:57:57 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-09-29 21:15:57 +00:00
|
|
|
function InputContainer:unRegisterTouchZones(zones)
|
|
|
|
if self.touch_zone_dg then
|
|
|
|
for i, zone in ipairs(zones) do
|
|
|
|
if self._zones[zone.id] then
|
|
|
|
self.touch_zone_dg:removeNode(zone.id)
|
|
|
|
if zone.overrides then
|
|
|
|
for _, override_zone_id in ipairs(zone.overrides) do
|
|
|
|
--self.touch_zone_dg:removeNodeDep(override_zone_id, zone.id)
|
|
|
|
self.touch_zone_dg:removeNodeDep(override_zone_id, zone.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for _, id in ipairs(self._ordered_touch_zones) do
|
|
|
|
if id.def.id == zone.id then
|
|
|
|
table.remove(self._ordered_touch_zones, i)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self._ordered_touch_zones = {}
|
|
|
|
if self.touch_zone_dg then
|
|
|
|
for _, zone_id in ipairs(self.touch_zone_dg:serialize()) do
|
|
|
|
table.insert(self._ordered_touch_zones, self._zones[zone_id])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function InputContainer:checkRegisterTouchZone(id)
|
|
|
|
if self.touch_zone_dg then
|
|
|
|
return self.touch_zone_dg:checkNode(id)
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-12-04 06:57:57 +00:00
|
|
|
--[[--
|
2016-12-13 16:06:02 +00:00
|
|
|
Updates touch zones based on new screen dimensions.
|
2016-12-04 06:57:57 +00:00
|
|
|
|
2016-12-13 16:06:02 +00:00
|
|
|
@tparam ui.geometry.Geom new_screen_dimen new screen dimensions
|
2016-12-04 06:57:57 +00:00
|
|
|
]]
|
|
|
|
function InputContainer:updateTouchZonesOnScreenResize(new_screen_dimen)
|
2017-01-23 14:54:14 +00:00
|
|
|
for _, tzone in ipairs(self._ordered_touch_zones) do
|
2016-12-19 04:26:10 +00:00
|
|
|
local range = tzone.gs_range.range
|
2016-12-04 06:57:57 +00:00
|
|
|
range.x = new_screen_dimen.w * tzone.def.screen_zone.ratio_x
|
|
|
|
range.y = new_screen_dimen.h * tzone.def.screen_zone.ratio_y
|
|
|
|
range.w = new_screen_dimen.w * tzone.def.screen_zone.ratio_w
|
|
|
|
range.h = new_screen_dimen.h * tzone.def.screen_zone.ratio_h
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-10-18 20:38:07 +00:00
|
|
|
--[[
|
2016-12-13 16:06:02 +00:00
|
|
|
Handles keypresses and checks if they lead to a command.
|
|
|
|
If this is the case, we retransmit another event within ourselves.
|
2013-10-18 20:38:07 +00:00
|
|
|
--]]
|
|
|
|
function InputContainer:onKeyPress(key)
|
2014-03-13 13:52:43 +00:00
|
|
|
for name, seq in pairs(self.key_events) do
|
2019-01-11 15:39:00 +00:00
|
|
|
if not seq.is_inactive then
|
|
|
|
for _, oneseq in ipairs(seq) do
|
2022-10-27 00:01:51 +00:00
|
|
|
-- NOTE: key is a device/key object, this isn't string.match!
|
2019-01-11 15:39:00 +00:00
|
|
|
if key:match(oneseq) then
|
|
|
|
local eventname = seq.event or name
|
|
|
|
return self:handleEvent(Event:new(eventname, seq.args, key))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- NOTE: Currently a verbatim copy of onKeyPress ;).
|
|
|
|
function InputContainer:onKeyRepeat(key)
|
|
|
|
for name, seq in pairs(self.key_events) do
|
2014-03-13 13:52:43 +00:00
|
|
|
if not seq.is_inactive then
|
|
|
|
for _, oneseq in ipairs(seq) do
|
|
|
|
if key:match(oneseq) then
|
|
|
|
local eventname = seq.event or name
|
|
|
|
return self:handleEvent(Event:new(eventname, seq.args, key))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-10-18 20:38:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function InputContainer:onGesture(ev)
|
2017-01-23 14:54:14 +00:00
|
|
|
for _, tzone in ipairs(self._ordered_touch_zones) do
|
|
|
|
if tzone.gs_range:match(ev) and tzone.handler(ev) then
|
|
|
|
return true
|
2016-12-04 06:57:57 +00:00
|
|
|
end
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
for name, gsseq in pairs(self.ges_events) do
|
|
|
|
for _, gs_range in ipairs(gsseq) do
|
|
|
|
if gs_range:match(ev) then
|
|
|
|
local eventname = gsseq.event or name
|
2017-01-23 14:54:14 +00:00
|
|
|
if self:handleEvent(Event:new(eventname, gsseq.args, ev)) then
|
|
|
|
return true
|
|
|
|
end
|
2014-03-13 13:52:43 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-05-16 10:45:36 +00:00
|
|
|
if self.stop_events_propagation then
|
|
|
|
return true
|
|
|
|
end
|
2013-10-18 20:38:07 +00:00
|
|
|
end
|
|
|
|
|
2022-11-02 22:20:56 +00:00
|
|
|
-- 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
|
|
|
|
|
2019-05-08 08:13:44 +00:00
|
|
|
function InputContainer:onInput(input, ignore_first_hold_release)
|
2015-04-13 06:45:02 +00:00
|
|
|
local InputDialog = require("ui/widget/inputdialog")
|
|
|
|
self.input_dialog = InputDialog:new{
|
|
|
|
title = input.title or "",
|
2020-02-12 22:05:18 +00:00
|
|
|
input = input.input_func and input.input_func() or input.input,
|
2015-04-13 06:45:02 +00:00
|
|
|
input_hint = input.hint_func and input.hint_func() or input.hint or "",
|
|
|
|
input_type = input.type or "number",
|
[RFC] Pagination UI shenanigans (#7335)
* Menu/KeyValuePage/ReaderGoTo: Unify the dialogs. (Generally, "Enter page number" as title, and "Go to page" as OK button).
* Allow *tapping* on pagination buttons, too. Added spacers around the text to accommodate for that.
* Disable input handlers when <= 1 pages, while still printing the label in black.
* Always display both the label and the chevrons, even on single page content. (Menu being an exception, because it can handle showing no content at all, in which case we hide the chevrons).
* KVP: Tweak the pagination buttons layout in order to have consistent centering, regardless of whether the return arrow is enabled or not. (Also, match Menu's layout, more or less).
* Menu: Minor layout tweaks to follow the KVP tweaks above. Fixes, among possibly other things, buttons in (non-FM) "List" menus overlapping the final entry (e.g., OPDS), and popout menus with a border being misaligned (e.g., Calibre, Find a file).
* CalendarView: Minor layout tweaks to follow the KVP tweaks. Ensures the pagination buttons are laid out in the same way as everywhere else (they used to be a wee bit higher).
2021-02-25 04:15:23 +00:00
|
|
|
buttons = input.buttons or {
|
2015-04-13 06:45:02 +00:00
|
|
|
{
|
|
|
|
{
|
2017-04-07 13:20:57 +00:00
|
|
|
text = input.cancel_text or _("Cancel"),
|
2022-03-04 20:20:00 +00:00
|
|
|
id = "close",
|
2015-04-13 06:45:02 +00:00
|
|
|
callback = function()
|
|
|
|
self:closeInputDialog()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
{
|
2017-04-07 13:20:57 +00:00
|
|
|
text = input.ok_text or _("OK"),
|
2016-05-26 06:09:49 +00:00
|
|
|
is_enter_default = true,
|
2015-04-13 06:45:02 +00:00
|
|
|
callback = function()
|
2021-04-10 22:08:29 +00:00
|
|
|
if input.deny_blank_input and self.input_dialog:getInputText() == "" then return end
|
2015-04-13 06:45:02 +00:00
|
|
|
input.callback(self.input_dialog:getInputText())
|
|
|
|
self:closeInputDialog()
|
|
|
|
end,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
UIManager:show(self.input_dialog)
|
2019-05-08 08:13:44 +00:00
|
|
|
self.input_dialog:onShowKeyboard(ignore_first_hold_release)
|
2015-04-13 06:45:02 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function InputContainer:closeInputDialog()
|
|
|
|
UIManager:close(self.input_dialog)
|
|
|
|
end
|
|
|
|
|
2022-11-01 22:22:07 +00:00
|
|
|
function InputContainer:onPhysicalKeyboardDisconnected()
|
|
|
|
-- Clear the key bindings if Device no longer has keys
|
|
|
|
-- NOTE: hasKeys is the lowest common denominator of key-related Device caps,
|
|
|
|
-- hasDPad/hasFewKeys/hasKeyboard all imply hasKeys ;).
|
|
|
|
if not Device:hasKeys() then
|
|
|
|
self.key_events = {}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-10-18 20:38:07 +00:00
|
|
|
return InputContainer
|