From 6205f26047cdad230cdca40f57bf4863a945df4c Mon Sep 17 00:00:00 2001 From: poire-z Date: Fri, 1 Jan 2021 14:34:55 +0100 Subject: [PATCH] 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. --- .../apps/reader/modules/readerdictionary.lua | 6 +++ frontend/ui/trapper.lua | 19 ++++----- frontend/ui/uimanager.lua | 39 +++++++++++++++++++ frontend/ui/widget/confirmbox.lua | 6 +++ frontend/ui/widget/infomessage.lua | 6 +++ 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/frontend/apps/reader/modules/readerdictionary.lua b/frontend/apps/reader/modules/readerdictionary.lua index ab214814c..8113fe395 100644 --- a/frontend/apps/reader/modules/readerdictionary.lua +++ b/frontend/apps/reader/modules/readerdictionary.lua @@ -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 diff --git a/frontend/ui/trapper.lua b/frontend/ui/trapper.lua index 71291450c..0d825e2bc 100644 --- a/frontend/ui/trapper.lua +++ b/frontend/ui/trapper.lua @@ -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) diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 4242d0e29..f9a361003 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -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 diff --git a/frontend/ui/widget/confirmbox.lua b/frontend/ui/widget/confirmbox.lua index 69ab0684a..68bd463e3 100644 --- a/frontend/ui/widget/confirmbox.lua +++ b/frontend/ui/widget/confirmbox.lua @@ -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() diff --git a/frontend/ui/widget/infomessage.lua b/frontend/ui/widget/infomessage.lua index 0f9a3dfcb..909e44230 100644 --- a/frontend/ui/widget/infomessage.lua +++ b/frontend/ui/widget/infomessage.lua @@ -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)