mirror of
https://github.com/koreader/koreader.git
synced 2025-12-18 12:02:09 +01:00
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.
This commit is contained in:
@@ -889,6 +889,12 @@ function ReaderDictionary:showDict(word, results, box, link)
|
|||||||
self:dismissLookupInfo()
|
self:dismissLookupInfo()
|
||||||
if results and results[1] then
|
if results and results[1] then
|
||||||
UIManager:show(self.dict_window)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,9 @@ function Trapper:info(text, fast_refresh)
|
|||||||
ok_callback = function()
|
ok_callback = function()
|
||||||
coroutine.resume(_coroutine, false)
|
coroutine.resume(_coroutine, false)
|
||||||
end,
|
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)
|
UIManager:show(abort_box)
|
||||||
-- no need to forceRePaint, UIManager will do it when we yield()
|
-- no need to forceRePaint, UIManager will do it when we yield()
|
||||||
@@ -186,10 +189,6 @@ function Trapper:info(text, fast_refresh)
|
|||||||
-- continue processing
|
-- continue processing
|
||||||
end
|
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 option, avoid UIManager refresh overhead
|
||||||
if fast_refresh and self.current_widget and self.current_widget.is_infomessage then
|
if fast_refresh and self.current_widget and self.current_widget.is_infomessage then
|
||||||
local orig_moved_offset = self.current_widget.movable:getMovedOffset()
|
local orig_moved_offset = self.current_widget.movable:getMovedOffset()
|
||||||
@@ -214,7 +213,10 @@ function Trapper:info(text, fast_refresh)
|
|||||||
dismiss_callback = function()
|
dismiss_callback = function()
|
||||||
coroutine.resume(_coroutine, false)
|
coroutine.resume(_coroutine, false)
|
||||||
end,
|
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)
|
logger.dbg("Showing InfoMessage:", text)
|
||||||
UIManager:show(self.current_widget)
|
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
|
return true -- always select "OK" in ConfirmBox if no UI
|
||||||
end
|
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
|
-- Close any previous widget
|
||||||
if self.current_widget then
|
if self.current_widget then
|
||||||
UIManager:close(self.current_widget)
|
UIManager:close(self.current_widget)
|
||||||
@@ -289,6 +287,9 @@ function Trapper:confirm(text, cancel_text, ok_text)
|
|||||||
ok_callback = function()
|
ok_callback = function()
|
||||||
coroutine.resume(_coroutine, true)
|
coroutine.resume(_coroutine, true)
|
||||||
end,
|
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)
|
logger.dbg("Showing ConfirmBox and waiting for answer:", text)
|
||||||
UIManager:show(self.current_widget)
|
UIManager:show(self.current_widget)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ local UIManager = {
|
|||||||
_exit_code = nil,
|
_exit_code = nil,
|
||||||
_prevent_standby_count = 0,
|
_prevent_standby_count = 0,
|
||||||
_prev_prevent_standby_count = 0,
|
_prev_prevent_standby_count = 0,
|
||||||
|
_discard_events_till = nil,
|
||||||
|
|
||||||
event_hook = require("ui/hook_container"):new()
|
event_hook = require("ui/hook_container"):new()
|
||||||
}
|
}
|
||||||
@@ -797,10 +798,48 @@ function UIManager:quit()
|
|||||||
end
|
end
|
||||||
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.
|
--- Transmits an event to an active widget.
|
||||||
function UIManager:sendEvent(event)
|
function UIManager:sendEvent(event)
|
||||||
if #self._window_stack == 0 then return end
|
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]
|
local top_widget = self._window_stack[#self._window_stack]
|
||||||
-- top level widget has first access to the event
|
-- top level widget has first access to the event
|
||||||
if top_widget.widget:handleEvent(event) then
|
if top_widget.widget:handleEvent(event) then
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ local ConfirmBox = InputContainer:new{
|
|||||||
margin = Size.margin.default,
|
margin = Size.margin.default,
|
||||||
padding = Size.padding.default,
|
padding = Size.padding.default,
|
||||||
dismissable = true, -- set to false if any button callback is required
|
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()
|
function ConfirmBox:init()
|
||||||
@@ -182,6 +184,10 @@ function ConfirmBox:onShow()
|
|||||||
UIManager:setDirty(self, function()
|
UIManager:setDirty(self, function()
|
||||||
return "ui", self[1][1].dimen
|
return "ui", self[1][1].dimen
|
||||||
end)
|
end)
|
||||||
|
if self.flush_events_on_show then
|
||||||
|
-- Discard queued and coming up events to avoid accidental dismissal
|
||||||
|
UIManager:discardEvents(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ConfirmBox:onCloseWidget()
|
function ConfirmBox:onCloseWidget()
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ local InfoMessage = InputContainer:new{
|
|||||||
no_refresh_on_close = nil,
|
no_refresh_on_close = nil,
|
||||||
-- Only have it painted after this delay (dismissing still works before it's shown)
|
-- Only have it painted after this delay (dismissing still works before it's shown)
|
||||||
show_delay = nil,
|
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()
|
function InfoMessage:init()
|
||||||
@@ -224,6 +226,10 @@ function InfoMessage:onShow()
|
|||||||
UIManager:setDirty(self, function()
|
UIManager:setDirty(self, function()
|
||||||
return "ui", self[1][1].dimen
|
return "ui", self[1][1].dimen
|
||||||
end)
|
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
|
-- schedule us to close ourself if timeout provided
|
||||||
if self.timeout then
|
if self.timeout then
|
||||||
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
|
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
|
||||||
|
|||||||
Reference in New Issue
Block a user