Skip to content

Commit

Permalink
scripting: add mp.input
Browse files Browse the repository at this point in the history
This lets scripts get textual input from the user using console.lua.
  • Loading branch information
guidocella committed Dec 16, 2023
1 parent f575b3d commit 2d84f3a
Show file tree
Hide file tree
Showing 7 changed files with 409 additions and 42 deletions.
23 changes: 17 additions & 6 deletions DOCS/man/javascript.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ otherwise, the documented Lua options, script directories, loading, etc apply to
JavaScript files too.

Script initialization and lifecycle is the same as with Lua, and most of the Lua
functions at the modules ``mp``, ``mp.utils``, ``mp.msg`` and ``mp.options`` are
available to JavaScript with identical APIs - including running commands,
getting/setting properties, registering events/key-bindings/hooks, etc.
functions in the modules ``mp``, ``mp.utils``, ``mp.msg``, ``mp.options`` and
``mp.input`` are available to JavaScript with identical APIs - including running
commands, getting/setting properties, registering events/key-bindings/hooks,
etc.

Differences from Lua
--------------------

No need to load modules. ``mp``, ``mp.utils``, ``mp.msg`` and ``mp.options``
are preloaded, and you can use e.g. ``var cwd = mp.utils.getcwd();`` without
prior setup.
No need to load modules. ``mp``, ``mp.utils``, ``mp.msg``, ``mp.options`` and
``mp.input`` are preloaded, and you can use e.g. ``var cwd =
mp.utils.getcwd();`` without prior setup.

Errors are slightly different. Where the Lua APIs return ``nil`` for error,
the JavaScript ones return ``undefined``. Where Lua returns ``something, error``
Expand Down Expand Up @@ -195,6 +196,16 @@ meta-paths like ``~~/foo`` (other JS file functions do expand meta paths).
``mp.options.read_options(obj [, identifier [, on_update]])`` (types:
string/boolean/number)

``mp.input.get(obj)`` (LE)

``mp.input.terminate()``

``mp.input.log(message, style)``

``mp.input.log_error(message)``

``mp.input.set_log(log)``

Additional utilities
--------------------

Expand Down
85 changes: 85 additions & 0 deletions DOCS/man/lua.rst
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,91 @@ strictly part of the guaranteed API.
Turn the given value into a string. Formats tables and their contents. This
doesn't do anything special; it is only needed because Lua is terrible.

mp.input functions
--------------------

This module lets scripts get textual input from the user using the console
REPL.

``input.get(table)``
Show the console to let the user enter text.

The following entries of ``table`` are read:

``prompt``
The string to be displayed before the input field.

``submit``
A callback invoked when the user presses Enter. The first argument is
the text in the console. You can close the console from within the
callback by calling ``input.terminate()``. If you don't, the console
stays open and the user can input more text.

``opened``
A callback invoked when the console is shown. This can be used to
present a list of options with ``input.set_log()``.

``edited``
A callback invoked when the text changes. This can be used to filter a
list of options based on what the user typed with ``input.set_log()``,
like dmenu does. The first argument is the text in the console.

``complete``
A callback invoked when the user presses TAB. The first argument is the
text before the cursor. The callback should return a table of the string
candidate completion values and the 1-based cursor position from which
the completion starts. console.lua will filter the suggestions beginning
with the the text between this position and the cursor, sort them
alphabetically, insert their longest common prefix, and show them when
there are multiple ones.

``closed``
A callback invoked when the console is hidden, either because
``input.terminate()`` was invoked from the other callbacks, or because
the user closed it with a key binding. The first argument is the text in
the console, and the second argument is the cursor position.

``default_text``
A string to pre-fill the input field with.

``cursor_position``
The initial cursor position, starting from 1.

``id``
An identifier that determines which input history and log buffer to use
among the ones stored for ``input.get()`` calls. The input histories
and logs are stored in memory and do not persist across different mpv
invocations. Defaults to the calling script name with ``prompt``
appended.

Returns true on success, or ``nil, error`` on error, including if the
console is already open.

``input.terminate()``
Close the console.

``input.log(message, style)``
Add a line to the log buffer. ``style`` can contain additional ASS tags to
apply to ``message``.

``input.log_error(message)``
Helper to add a line to the log buffer with the same color as the one the
console uses for errors. Useful when the user submits invalid input.

``input.set_log(log)``
Replace the entire log buffer.

``log`` is a table of strings, or tables with ``text`` and ``style`` keys.

Example:

::

input.set_log({
"regular text",
{ style = "{\\c&H7a77f2&}", text = "error text" }
})

Events
------

Expand Down
57 changes: 57 additions & 0 deletions player/javascript/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,63 @@ function read_options(opts, id, on_update, conf_override) {

mp.options = { read_options: read_options };

/**********************************************************************
* input
*********************************************************************/
mp.input = {
get: function(t) {
var console_enabled = mp.get_property_native("user-data/console/enabled");
if (console_enabled === undefined) {
mp._set_last_error("The console script is not loaded.");
return;
}
if (console_enabled) {
mp._set_last_error("The console is already open.");
return;
}

mp.commandv("script-message-to", "console", "get-input", mp.script_name,
JSON.stringify({
prompt: t.prompt,
default_text: t.default_text,
cursor_position: t.cursor_position,
id: t.id,
}));

mp.register_script_message("input-event", function (type, text, cursor_position) {
if (t[type]) {
var result = t[type](text, cursor_position);

if (type == "complete" && result) {
mp.commandv("script-message-to", "console", "complete",
JSON.stringify(result[0]), result[1]);
}
}

if (type == "closed") {
mp.unregister_script_message("input-event");
}
})

return true;
},
terminate: function () {
mp.commandv("script-message-to", "console", "disable");
},
log: function (message, style) {
mp.commandv("script-message-to", "console", "log",
JSON.stringify({ text: message, style: style }));
},
log_error: function (message) {
mp.commandv("script-message-to", "console", "log",
JSON.stringify({ text: message, error: true }));
},
set_log: function (log) {
mp.commandv("script-message-to", "console", "set-log",
JSON.stringify(log));
}
}

/**********************************************************************
* various
*********************************************************************/
Expand Down
3 changes: 3 additions & 0 deletions player/lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ static const char * const builtin_lua_scripts[][2] = {
},
{"mp.assdraw",
# include "player/lua/assdraw.lua.inc"
},
{"mp.input",
# include "player/lua/input.lua.inc"
},
{"mp.options",
# include "player/lua/options.lua.inc"
Expand Down
Loading

0 comments on commit 2d84f3a

Please sign in to comment.