mirror of
https://github.com/koreader/koreader.git
synced 2025-12-18 12:02:09 +01:00
Dict lookups: have them always interruptible
They should be now interruptible when fuzzy search is disabled and on Android.
This commit is contained in:
@@ -673,41 +673,29 @@ function ReaderDictionary:startSdcv(word, dict_names, fuzzy_search)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local results_str = nil
|
local cmd = util.shell_escape(args)
|
||||||
if Device:isAndroid() then
|
-- cmd = "sleep 7 ; " .. cmd -- uncomment to simulate long lookup time
|
||||||
local A = require("android")
|
|
||||||
results_str = A.stdout(unpack(args))
|
-- Some sdcv lookups, when using fuzzy search with many dictionaries
|
||||||
else
|
-- and a really bad selected text, can take up to 10 seconds.
|
||||||
local cmd = util.shell_escape(args)
|
-- It is nice to be able to cancel it when noticing wrong text was
|
||||||
-- cmd = "sleep 7 ; " .. cmd -- uncomment to simulate long lookup time
|
-- selected.
|
||||||
|
-- Because sdcv starts outputing its output only at the end when it has
|
||||||
|
-- done its work, we can use Trapper:dismissablePopen() to cancel it as
|
||||||
|
-- long as we are waiting for output.
|
||||||
|
-- When fuzzy search is enabled, we have a lookup_progress_msg that can
|
||||||
|
-- be used to catch a tap and trigger cancellation.
|
||||||
|
-- When fuzzy search is disabled, we provide false instead so an
|
||||||
|
-- invisible non-event-forwarding TrapWidget is used to catch a tap
|
||||||
|
-- and trigger cancellation (invisible so there's no need for repaint
|
||||||
|
-- and refresh with the usually fast non-fuzzy search lookups).
|
||||||
|
-- We must ensure we will have some output to be readable (if no
|
||||||
|
-- definition found, sdcv will output some message on stderr, and
|
||||||
|
-- let stdout empty) by appending an "echo":
|
||||||
|
cmd = cmd .. "; echo"
|
||||||
|
local completed, results_str = Trapper:dismissablePopen(cmd, self.lookup_progress_msg or false)
|
||||||
|
lookup_cancelled = not completed
|
||||||
|
|
||||||
if self.lookup_progress_msg then
|
|
||||||
-- Some sdcv lookups, when using fuzzy search with many dictionaries
|
|
||||||
-- and a really bad selected text, can take up to 10 seconds.
|
|
||||||
-- It is nice to be able to cancel it when noticing wrong text was selected.
|
|
||||||
-- As we have a lookup_progress_msg (that can be used to catch a tap
|
|
||||||
-- and trigger cancellation), and because sdcv starts outputing its
|
|
||||||
-- output only at the end when it has done its work, we can
|
|
||||||
-- use Trapper:dismissablePopen() to cancel it as long as we are waiting
|
|
||||||
-- for output.
|
|
||||||
-- We must ensure we will have some output to be readable (if no
|
|
||||||
-- definition found, sdcv will output some message on stderr, and
|
|
||||||
-- let stdout empty) by appending an "echo":
|
|
||||||
cmd = cmd .. "; echo"
|
|
||||||
local completed
|
|
||||||
completed, results_str = Trapper:dismissablePopen(cmd, self.lookup_progress_msg)
|
|
||||||
lookup_cancelled = not completed
|
|
||||||
else
|
|
||||||
-- Fuzzy search disabled, usual option for people who don't want
|
|
||||||
-- a "Looking up..." InfoMessage and usually fast: do a classic
|
|
||||||
-- blocking io.popen()
|
|
||||||
local std_out = io.popen(cmd, "r")
|
|
||||||
if std_out then
|
|
||||||
results_str = std_out:read("*all")
|
|
||||||
std_out:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if results_str and results_str ~= "\n" then -- \n is when lookup was cancelled
|
if results_str and results_str ~= "\n" then -- \n is when lookup was cancelled
|
||||||
local ok, results = pcall(JSON.decode, results_str)
|
local ok, results = pcall(JSON.decode, results_str)
|
||||||
if ok and results then
|
if ok and results then
|
||||||
|
|||||||
@@ -314,8 +314,9 @@ Notes and limitations:
|
|||||||
3) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage},
|
3) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage},
|
||||||
that, as a modal, will catch any @{ui.event|Tap event} happening during
|
that, as a modal, will catch any @{ui.event|Tap event} happening during
|
||||||
`cmd` execution. This can be an existing already displayed widget, or
|
`cmd` execution. This can be an existing already displayed widget, or
|
||||||
provided as a string (a new TrapWidget will be created). If nil, an invisible
|
provided as a string (a new TrapWidget will be created). If nil, true or false,
|
||||||
TrapWidget will be used instead.
|
an invisible TrapWidget will be used instead (if nil or true, the event will be
|
||||||
|
resent; if false, the event will not be resent).
|
||||||
|
|
||||||
If we really need to have more control, we would need to use `select()` via `ffi`
|
If we really need to have more control, we would need to use `select()` via `ffi`
|
||||||
or do low level non-blocking reading on the file descriptor.
|
or do low level non-blocking reading on the file descriptor.
|
||||||
@@ -324,7 +325,7 @@ collect indefinitely, the best option would be to compile any `timeout.c`
|
|||||||
and use it as a wrapper.
|
and use it as a wrapper.
|
||||||
|
|
||||||
@string cmd shell `cmd` to execute and get output from
|
@string cmd shell `cmd` to execute and get output from
|
||||||
@param trap_widget_or_string already shown widget, string or nil
|
@param trap_widget_or_string already shown widget, string, or nil, true or false
|
||||||
@treturn boolean completed (`true` if not interrupted, `false` if dismissed)
|
@treturn boolean completed (`true` if not interrupted, `false` if dismissed)
|
||||||
@treturn string output of command
|
@treturn string output of command
|
||||||
]]
|
]]
|
||||||
@@ -358,10 +359,15 @@ function Trapper:dismissablePopen(cmd, trap_widget_or_string)
|
|||||||
UIManager:show(trap_widget)
|
UIManager:show(trap_widget)
|
||||||
UIManager:forceRePaint()
|
UIManager:forceRePaint()
|
||||||
else
|
else
|
||||||
-- Use an invisible TrapWidget that resend event
|
-- Use an invisible TrapWidget that resend event, but not if
|
||||||
|
-- trap_widget_or_string is false (rather than nil or true)
|
||||||
|
local resend_event = true
|
||||||
|
if trap_widget_or_string == false then
|
||||||
|
resend_event = false
|
||||||
|
end
|
||||||
trap_widget = TrapWidget:new{
|
trap_widget = TrapWidget:new{
|
||||||
text = nil,
|
text = nil,
|
||||||
resend_event = true,
|
resend_event = resend_event,
|
||||||
}
|
}
|
||||||
UIManager:show(trap_widget)
|
UIManager:show(trap_widget)
|
||||||
own_trap_widget_invisible = true
|
own_trap_widget_invisible = true
|
||||||
@@ -472,11 +478,12 @@ Notes and limitations:
|
|||||||
4) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage},
|
4) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage},
|
||||||
that, as a modal, will catch any @{ui.event|Tap event} happening during
|
that, as a modal, will catch any @{ui.event|Tap event} happening during
|
||||||
`cmd` execution. This can be an existing already displayed widget, or
|
`cmd` execution. This can be an existing already displayed widget, or
|
||||||
provided as a string (a new TrapWidget will be created). If nil, an invisible
|
provided as a string (a new TrapWidget will be created). If nil, true or false,
|
||||||
TrapWidget will be used instead.
|
an invisible TrapWidget will be used instead (if nil or true, the event will be
|
||||||
|
resent; if false, the event will not be resent).
|
||||||
|
|
||||||
@function task lua function to execute and get return values from
|
@function task lua function to execute and get return values from
|
||||||
@param trap_widget_or_string already shown widget, string or nil
|
@param trap_widget_or_string already shown widget, string, or nil, true or false
|
||||||
@boolean task_returns_simple_string[opt=false] true if task returns a single string
|
@boolean task_returns_simple_string[opt=false] true if task returns a single string
|
||||||
@treturn boolean completed (`true` if not interrupted, `false` if dismissed)
|
@treturn boolean completed (`true` if not interrupted, `false` if dismissed)
|
||||||
@return ... return values of task
|
@return ... return values of task
|
||||||
@@ -504,10 +511,15 @@ function Trapper:dismissableRunInSubprocess(task, trap_widget_or_string, task_re
|
|||||||
UIManager:show(trap_widget)
|
UIManager:show(trap_widget)
|
||||||
UIManager:forceRePaint()
|
UIManager:forceRePaint()
|
||||||
else
|
else
|
||||||
-- Use an invisible TrapWidget that resend event
|
-- Use an invisible TrapWidget that resend event, but not if
|
||||||
|
-- trap_widget_or_string is false (rather than nil or true)
|
||||||
|
local resend_event = true
|
||||||
|
if trap_widget_or_string == false then
|
||||||
|
resend_event = false
|
||||||
|
end
|
||||||
trap_widget = TrapWidget:new{
|
trap_widget = TrapWidget:new{
|
||||||
text = nil,
|
text = nil,
|
||||||
resend_event = true,
|
resend_event = resend_event,
|
||||||
}
|
}
|
||||||
UIManager:show(trap_widget)
|
UIManager:show(trap_widget)
|
||||||
own_trap_widget_invisible = true
|
own_trap_widget_invisible = true
|
||||||
|
|||||||
@@ -115,16 +115,13 @@ function TrapWidget:_dismissAndResent(evtype, ev)
|
|||||||
self.dismiss_callback()
|
self.dismiss_callback()
|
||||||
UIManager:close(self)
|
UIManager:close(self)
|
||||||
if self.resend_event and evtype and ev then
|
if self.resend_event and evtype and ev then
|
||||||
-- XXX There may be timing problems that could cause crashes, as we
|
-- There may be some timing issues that could cause crashes, as we
|
||||||
-- use nextTick, if the dismiss_callback uses UIManager:scheduleIn()
|
-- use nextTick, if the dismiss_callback uses UIManager:scheduleIn()
|
||||||
-- or has set up some widget that may catch that event while not being
|
-- or has set up some widget that may catch that event while not being
|
||||||
-- yet fully initialiazed.
|
-- yet fully initialiazed.
|
||||||
-- It happened mostly when I had some bug somewhere, and it was a quite
|
-- (It happened mostly when I had some bug somewhere, and it was a quite
|
||||||
-- reliable sign of a bug somewhere, but the stacktrace was unrelated
|
-- reliable sign of a bug somewhere, but the stacktrace was unrelated
|
||||||
-- to the bug location.
|
-- to the bug location.)
|
||||||
-- Fix to avoid crashes: in GestureRange:match(), check that self.range()
|
|
||||||
-- does not return nil before using it:
|
|
||||||
-- if not range or not range:contains(gs.pos) then return false
|
|
||||||
UIManager:nextTick(function() UIManager:handleInputEvent(Event:new(evtype, ev)) end)
|
UIManager:nextTick(function() UIManager:handleInputEvent(Event:new(evtype, ev)) end)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
|||||||
Reference in New Issue
Block a user