Module ui.trapper
Trapper module: provides methods for simple interaction with UI, without the need for explicit callbacks, for use by linear jobs between their steps.
Allows code to trap UI (give progress info to UI, ask for user choice), or get trapped by UI (get interrupted). Mostly done with coroutines, but hides their usage for simplicity.
Functions
Trapper:wrap (func) | Executes a function and allows it to be trapped (that is: to use our other methods). |
Trapper:isWrapped () | Returns if code is wrapped |
Trapper:clear () | Clears left-over widget |
Trapper:reset () | Clears left-over widget and resets Trapper state |
Trapper:info (text) | Displays an InfoMessage, and catches dismissal. |
Trapper:setPausedText (text, abort_text, continue_text) | Overrides text and button texts on the Paused ConfirmBox. |
Trapper:confirm (text, cancel_text, ok_text) | Displays a ConfirmBox and gets user's choice. |
Trapper:dismissablePopen (cmd, trap_widget_or_string) | Dismissable wrapper for io.popen(cmd ). |
Trapper:dismissableRunInSubprocess (task, trap_widget_or_string) | Run a function (task) in a sub-process, allowing it to be dismissed, and returns its return value(s). |
Functions
- Trapper:wrap (func)
-
Executes a function and allows it to be trapped (that is: to use our
other methods).
Simple wrapper function for a coroutine, which is a prerequisite for all our methods (this simply abstracts the coroutine business to our callers), and execute it.
(If some code is not wrap()'ed, most of the other methods, when called, will simply log or fallback to a non-UI action or OK choice.)
This call should be the last step in some event processing code, as it may return early (the first coroutine.yield() in any of the other methods will return from this function), and later be resumed by UIManager. So any following (unwrapped) code would be then executed while
func
is half-done, with unintended consequences.Parameters:
- func function reference to function to wrap and execute
- Trapper:isWrapped ()
-
Returns if code is wrapped
Returns:
-
boolean
true if code is wrapped by Trapper, false otherwise
- Trapper:clear ()
- Clears left-over widget
- Trapper:reset ()
- Clears left-over widget and resets Trapper state
- Trapper:info (text)
-
Displays an InfoMessage, and catches dismissal.
Display a InfoMessage with text, or keep existing InfoMessage if text = nil, and return true.
UNLESS the previous widget was itself a InfoMessage and it has been dismissed (by Tap on the screen), in which case the new InfoMessage is not displayed, and false is returned.
One can only know a InfoMessage has been dismissed when trying to display a new one (we can't do better than that with coroutines). So, don't hesitate to call it regularly (each call costs 100ms), between steps of the work, to provide good responsiveness.
Trapper:info() is a shortcut to get dismiss info while keeping the existing InfoMessage displayed.
Optional fast_refresh parameter should only be used when displaying an InfoMessage over a previous InfoMessage of the exact same size.
Parameters:
- text string text to display as an InfoMessage (or nil to keep existing one)
Returns:
-
boolean
true if InfoMessage was not dismissed, false if dismissed
Usage:
Trapper:info("some text about step or progress") go_on = Trapper:info()
- Trapper:setPausedText (text, abort_text, continue_text)
-
Overrides text and button texts on the Paused ConfirmBox.
A ConfirmBox is displayed when an InfoMessage is dismissed in Trapper:info(), with default text "Paused", and default buttons "Abort" and "Continue".
Parameters:
- Trapper:confirm (text, cancel_text, ok_text)
-
Displays a ConfirmBox and gets user's choice.
Display a ConfirmBox with the text and canceltext/oktext buttons, block and wait for user's choice, and return the choice made: false if Cancel tapped or dismissed, true if OK tapped
Parameters:
- text string text to display in a ConfirmBox
- cancel_text string text for ConfirmBox Cancel button
- ok_text string text for ConfirmBox Ok button
Returns:
-
boolean
false if Cancel tapped or dismissed, true if OK tapped
Usage:
go_on = Trapper:confirm("Do you want to go on?") that_selected = Trapper:confirm("Do you want to do this or that?", "this", "that"))
- Trapper:dismissablePopen (cmd, trap_widget_or_string)
-
Dismissable wrapper for io.popen(
cmd
).Notes and limitations:
1) It is dismissable as long as
cmd
as not yet output anything. Once output has started, the reading will block till it is done. (Some shell tricks, included incmd
, could probably be used to accumulatecmd
output in some variable, and to output the whole variable to stdout at the end.)2)
cmd
needs to output something (we will wait till some data is available) If there are chances for it to not output anything, append"; echo"
tocmd
3) We need a TrapWidget or InfoMessage, that, as a modal, will catch any Tap event happening during
cmd
execution. This can be an existing already displayed widget, or provided as a string (a new TrapWidget will be created). If nil, true or false, 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()
viaffi
or do low level non-blocking reading on the file descriptor. If there arecmd
that may not exit, that we would be trying to collect indefinitely, the best option would be to compile anytimeout.c
and use it as a wrapper.Parameters:
- cmd
string
shell
cmd
to execute and get output from - trap_widget_or_string already shown widget, string, or nil, true or false
Returns:
-
boolean
completed (
true
if not interrupted,false
if dismissed) - string output of command
- cmd
string
shell
- Trapper:dismissableRunInSubprocess (task, trap_widget_or_string)
-
Run a function (task) in a sub-process, allowing it to be dismissed,
and returns its return value(s).
Notes and limitations:
1) As function is run in a sub-process, it can't modify the main KOReader process (its parent). It has access to the state of KOReader at the time the sub-process was started. It should not use any service/driver that would make the parent process vision of the device state incoherent (ie: it should not use UIManager, display widgets, change settings, enable wifi...). It is allowed to modify the filesystem, as long as KOreader has not a cached vision of this filesystem part. Its returned value(s) are returned to the parent.
2) task may return complex data structures (but with simple lua types, no function) or a single string. If task returns a string or nil, set taskreturnssimple_string to true, allowing for some optimisations to be made.
3) If dismissed, the sub-process is killed with SIGKILL, and task is aborted without any chance for cleanup work: use of temporary files should so be limited (do some cleanup of dirty files from previous aborted executions at the start of each new execution if needed), and try to keep important operations as atomic as possible.
4) We need a TrapWidget or InfoMessage, that, as a modal, will catch any Tap event happening during
cmd
execution. This can be an existing already displayed widget, or provided as a string (a new TrapWidget will be created). If nil, true or false, an invisible TrapWidget will be used instead (if nil or true, the event will be resent; if false, the event will not be resent).Parameters:
- task lua function to execute and get return values from
- trap_widget_or_string already shown widget, string, or nil, true or false
Returns:
-
boolean
completed (
true
if not interrupted,false
if dismissed) - ... return values of task