Dict, Trapper: prevent dismissal by past events

Add UIManager:discardEvents(), to allow dropping events
for a period of time.
Default duration of 600ms on eInk, 300ms otherwise.
Used when showing the dict result window, so that a tap
happening just when it's being shown won't discard it.
Used with Trapper:confirm()/:info(), to avoid taps made
in a previous processing to dismiss (and so, select the
cancel action) a ConfirmBox not yet painted/visible.
pull/7104/head
poire-z 3 years ago
parent b6323f15fc
commit 6205f26047

@ -889,6 +889,12 @@ function ReaderDictionary:showDict(word, results, box, link)
self:dismissLookupInfo()
if results and results[1] then
UIManager:show(self.dict_window)
if not results[1].lookup_cancelled then
-- Discard queued and coming up events to avoid accidental
-- dismissal (but not if lookup interrupted, so 2 quick
-- taps can discard everything)
UIManager:discardEvents(true)
end
end
end

@ -165,6 +165,9 @@ function Trapper:info(text, fast_refresh)
ok_callback = function()
coroutine.resume(_coroutine, false)
end,
-- flush any pending tap, so past events won't be considered
-- action on the yet to be displayed widget
flush_events_on_show = true,
}
UIManager:show(abort_box)
-- no need to forceRePaint, UIManager will do it when we yield()
@ -186,10 +189,6 @@ function Trapper:info(text, fast_refresh)
-- continue processing
end
--- @todo We should try to flush any pending tap, so past
-- events won't be considered action on the yet to be displayed
-- widget
-- If fast_refresh option, avoid UIManager refresh overhead
if fast_refresh and self.current_widget and self.current_widget.is_infomessage then
local orig_moved_offset = self.current_widget.movable:getMovedOffset()
@ -214,7 +213,10 @@ function Trapper:info(text, fast_refresh)
dismiss_callback = function()
coroutine.resume(_coroutine, false)
end,
is_infomessage = true -- flag on our InfoMessages
is_infomessage = true, -- flag on our InfoMessages
-- flush any pending tap, so past events won't be considered
-- action on the yet to be displayed widget
flush_events_on_show = true,
}
logger.dbg("Showing InfoMessage:", text)
UIManager:show(self.current_widget)
@ -268,10 +270,6 @@ function Trapper:confirm(text, cancel_text, ok_text)
return true -- always select "OK" in ConfirmBox if no UI
end
--- @todo We should try to flush any pending tap, so past
-- events won't be considered action on the yet to be displayed
-- widget
-- Close any previous widget
if self.current_widget then
UIManager:close(self.current_widget)
@ -289,6 +287,9 @@ function Trapper:confirm(text, cancel_text, ok_text)
ok_callback = function()
coroutine.resume(_coroutine, true)
end,
-- flush any pending tap, so past events won't be considered
-- action on the yet to be displayed widget
flush_events_on_show = true,
}
logger.dbg("Showing ConfirmBox and waiting for answer:", text)
UIManager:show(self.current_widget)

@ -39,6 +39,7 @@ local UIManager = {
_exit_code = nil,
_prevent_standby_count = 0,
_prev_prevent_standby_count = 0,
_discard_events_till = nil,
event_hook = require("ui/hook_container"):new()
}
@ -797,10 +798,48 @@ function UIManager:quit()
end
end
--- Request events to be ignored for some duration
function UIManager:discardEvents(set_or_seconds)
if not set_or_seconds then -- remove any previously set
self._discard_events_till = nil
return
end
local usecs
if set_or_seconds == true then
-- Use an adequate delay to account for device refresh duration
-- so any events happening in this delay (ie. before a widget
-- is really painted on screen) are discarded.
if Device:hasEinkScreen() then
-- A screen refresh can take a few 100ms,
-- sometimes > 500ms on some devices/temperatures
usecs = 600000
else
-- On non-eInk screen, it's usually instantaneous
usecs = 300000
end
else -- we expect a number
usecs = set_or_seconds * MILLION
end
local now = { util.gettime() }
local now_us = now[1] * MILLION + now[2]
self._discard_events_till = now_us + usecs
end
--- Transmits an event to an active widget.
function UIManager:sendEvent(event)
if #self._window_stack == 0 then return end
-- Ensure discardEvents
if self._discard_events_till then
local now = { util.gettime() }
local now_us = now[1] * MILLION + now[2]
if now_us < self._discard_events_till then
return
else
self._discard_events_till = nil
end
end
local top_widget = self._window_stack[#self._window_stack]
-- top level widget has first access to the event
if top_widget.widget:handleEvent(event) then

@ -51,6 +51,8 @@ local ConfirmBox = InputContainer:new{
margin = Size.margin.default,
padding = Size.padding.default,
dismissable = true, -- set to false if any button callback is required
flush_events_on_show = false, -- set to true when it might be displayed after
-- some processing, to avoid accidental dismissal
}
function ConfirmBox:init()
@ -182,6 +184,10 @@ function ConfirmBox:onShow()
UIManager:setDirty(self, function()
return "ui", self[1][1].dimen
end)
if self.flush_events_on_show then
-- Discard queued and coming up events to avoid accidental dismissal
UIManager:discardEvents(true)
end
end
function ConfirmBox:onCloseWidget()

@ -69,6 +69,8 @@ local InfoMessage = InputContainer:new{
no_refresh_on_close = nil,
-- Only have it painted after this delay (dismissing still works before it's shown)
show_delay = nil,
-- Set to true when it might be displayed after some processing, to avoid accidental dismissal
flush_events_on_show = false,
}
function InfoMessage:init()
@ -224,6 +226,10 @@ function InfoMessage:onShow()
UIManager:setDirty(self, function()
return "ui", self[1][1].dimen
end)
if self.flush_events_on_show then
-- Discard queued and coming up events to avoid accidental dismissal
UIManager:discardEvents(true)
end
-- schedule us to close ourself if timeout provided
if self.timeout then
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)

Loading…
Cancel
Save