13
0

Allow Lua action scripts to provide a button icon

This commit is contained in:
Robin Gareus 2017-02-17 18:56:18 +01:00
parent 149634c2cf
commit 472f14c803
5 changed files with 87 additions and 10 deletions

View File

@ -904,6 +904,7 @@ private:
void pre_release_dialog ();
bool bind_lua_action_script (GdkEventButton*, int);
void update_action_script_btn (int i, const std::string&);
};
#endif /* __ardour_gui_h__ */

View File

@ -304,6 +304,8 @@ ARDOUR_UI::setup_windows ()
main_vpacker.pack_start (status_bar_hpacker, false, false);
#endif
LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::update_action_script_btn));
for (int i = 0; i < 9; ++i) {
std::string const a = string_compose (X_("script-action-%1"), i + 1);
Glib::RefPtr<Action> act = ActionManager::get_action(X_("Editor"), a.c_str());
@ -411,3 +413,14 @@ ARDOUR_UI::bind_lua_action_script (GdkEventButton*ev, int i)
li->interactive_add (LuaScriptInfo::EditorAction, i);
return true;
}
void
ARDOUR_UI::update_action_script_btn (int i, const std::string&)
{
if (LuaInstance::instance()->lua_action_has_icon (i)) {
uintptr_t ii = i;
action_script_call_btn[i].set_icon (&LuaInstance::render_action_icon, (void*)ii);
} else {
action_script_call_btn[i].set_icon (0, 0);
}
}

View File

@ -868,7 +868,6 @@ Editor::Editor ()
setup_fade_images ();
LuaInstance::instance(); // instantiate
LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &Editor::set_script_action_name));
instant_save ();

View File

@ -851,6 +851,7 @@ LuaInstance::LuaInstance ()
LuaInstance::~LuaInstance ()
{
delete _lua_call_action;
delete _lua_render_icon;
delete _lua_add_action;
delete _lua_del_action;
delete _lua_get_action;
@ -866,22 +867,28 @@ LuaInstance::init ()
{
lua.do_command (
"function ScriptManager ()"
" local self = { scripts = {}, instances = {} }"
" local self = { scripts = {}, instances = {}, icons = {} }"
""
" local remove = function (id)"
" self.scripts[id] = nil"
" self.instances[id] = nil"
" self.icons[id] = nil"
" end"
""
" local addinternal = function (i, n, s, f, a)"
" local addinternal = function (i, n, s, f, c, a)"
" assert(type(i) == 'number', 'id must be numeric')"
" assert(type(n) == 'string', 'Name must be string')"
" assert(type(s) == 'string', 'Script must be string')"
" assert(type(f) == 'function', 'Factory is a not a function')"
" assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
" self.scripts[i] = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a }"
" self.scripts[i] = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a, ['c'] = c }"
" local env = _ENV; env.f = nil env.debug = nil os.exit = nil require = nil dofile = nil loadfile = nil package = nil"
" self.instances[i] = load (string.dump(f, true), nil, nil, env)(a)"
" if type(c) == 'function' then"
" self.icons[i] = load (string.dump(c, true), nil, nil, env)(a)"
" else"
" self.icons[i] = nil"
" end"
" end"
""
" local call = function (id)"
@ -895,17 +902,26 @@ LuaInstance::init ()
" collectgarbage()"
" end"
""
" local add = function (i, n, s, b, a)"
" local icon = function (id, ...)"
" if type(self.icons[id]) == 'function' then"
" pcall (self.icons[id], ...)"
" end"
" collectgarbage()"
" end"
""
" local add = function (i, n, s, b, c, a)"
" assert(type(b) == 'string', 'ByteCode must be string')"
" load (b)()" // assigns f
" f = nil load (b)()" // assigns f
" icn = nil load (c)()" // may assign "icn"
" assert(type(f) == 'string', 'Assigned ByteCode must be string')"
" addinternal (i, n, s, load(f), a)"
" addinternal (i, n, s, load(f), type(icn) ~= \"string\" or icn == '' or load(icn), a)"
" end"
""
" local get = function (id)"
" if type(self.scripts[id]) == 'table' then"
" return { ['name'] = self.scripts[id]['n'],"
" ['script'] = self.scripts[id]['s'],"
" ['icon'] = type(self.scripts[id]['c']) == 'function',"
" ['args'] = self.scripts[id]['a'] }"
" end"
" return nil"
@ -934,6 +950,8 @@ LuaInstance::init ()
" return rv;"
" elseif type(value) == \"function\" then"
" return rv .. string.format(\"%q\", string.dump(value, true))"
" elseif type(value) == \"boolean\" then"
" return rv .. tostring (value)"
" else"
" error('cannot save a ' .. type(value))"
" end"
@ -947,6 +965,7 @@ LuaInstance::init ()
" local clear = function ()"
" self.scripts = {}"
" self.instances = {}"
" self.icons = {}"
" collectgarbage()"
" end"
""
@ -954,13 +973,13 @@ LuaInstance::init ()
" clear()"
" load (state)()"
" for i, s in pairs (scripts) do"
" addinternal (i, s['n'], s['s'], load(s['f']), s['a'])"
" addinternal (i, s['n'], s['s'], load(s['f']), type (s['c']) ~= \"string\" or s['c'] == '' or load (s['c']), s['a'])"
" end"
" collectgarbage()"
" end"
""
" return { call = call, add = add, remove = remove, get = get,"
" restore = restore, save = save, clear = clear}"
" restore = restore, save = save, clear = clear, icon = icon}"
" end"
" "
" manager = ScriptManager ()"
@ -978,6 +997,7 @@ LuaInstance::init ()
_lua_del_action = new luabridge::LuaRef(lua_mgr["remove"]);
_lua_get_action = new luabridge::LuaRef(lua_mgr["get"]);
_lua_call_action = new luabridge::LuaRef(lua_mgr["call"]);
_lua_render_icon = new luabridge::LuaRef(lua_mgr["icon"]);
_lua_save = new luabridge::LuaRef(lua_mgr["save"]);
_lua_load = new luabridge::LuaRef(lua_mgr["restore"]);
_lua_clear = new luabridge::LuaRef(lua_mgr["clear"]);
@ -1184,6 +1204,23 @@ LuaInstance::call_action (const int id)
}
}
void
LuaInstance::render_action_icon (cairo_t* cr, int w, int h, uint32_t c, void* i) {
int ii = reinterpret_cast<uintptr_t> (i);
instance()->render_icon (ii, cr, w, h, c);
}
void
LuaInstance::render_icon (int i, cairo_t* cr, int w, int h, uint32_t clr)
{
Cairo::Context ctx (cr);
try {
(*_lua_render_icon)(i + 1, (Cairo::Context *)&ctx, w, h, clr);
} catch (luabridge::LuaException const& e) {
cerr << "LuaException:" << e.what () << endl;
}
}
bool
LuaInstance::set_lua_action (
const int id,
@ -1196,12 +1233,13 @@ LuaInstance::set_lua_action (
// get bytcode of factory-function in a sandbox
// (don't allow scripts to interfere)
const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
const std::string& iconfunc = LuaScripting::get_factory_bytecode (script, "icon", "icn");
luabridge::LuaRef tbl_arg (luabridge::newTable(L));
for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
if ((*i)->optional && !(*i)->is_set) { continue; }
tbl_arg[(*i)->name] = (*i)->value;
}
(*_lua_add_action)(id + 1, name, script, bytecode, tbl_arg);
(*_lua_add_action)(id + 1, name, script, bytecode, iconfunc, tbl_arg);
ActionChanged (id, name); /* EMIT SIGNAL */
} catch (luabridge::LuaException const& e) {
cerr << "LuaException:" << e.what () << endl;
@ -1258,6 +1296,23 @@ LuaInstance::lua_action_names ()
return rv;
}
bool
LuaInstance::lua_action_has_icon (const int id)
{
try {
luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
if (ref.isNil()) {
return false;
}
if (ref["icon"].isBoolean()) {
return ref["icon"].cast<bool>();
}
} catch (luabridge::LuaException const& e) {
cerr << "LuaException:" << e.what () << endl;
}
return false;
}
bool
LuaInstance::lua_action (const int id, std::string& name, std::string& script, LuaScriptParamList& args)
{
@ -1570,6 +1625,8 @@ LuaCallback::init (void)
" return rv;"
" elseif type(value) == \"function\" then"
" return rv .. string.format(\"%q\", string.dump(value, true))"
" elseif type(value) == \"boolean\" then"
" return rv .. tostring (value)"
" else"
" error('cannot save a ' .. type(value))"
" end"

View File

@ -3,6 +3,8 @@
#include <bitset>
#include <cairo.h>
#include "pbd/id.h"
#include "pbd/signals.h"
#include "pbd/xml++.h"
@ -86,6 +88,8 @@ public:
static void register_hooks (lua_State* L);
static void bind_cairo (lua_State* L);
static void render_action_icon (cairo_t* cr, int w, int h, uint32_t c, void* i);
void set_session (ARDOUR::Session* s);
int set_state (const XMLNode&);
@ -96,12 +100,14 @@ public:
/* actions */
void call_action (const int);
void render_icon (int i, cairo_t*, int, int, uint32_t);
bool set_lua_action (const int, const std::string&, const std::string&, const ARDOUR::LuaScriptParamList&);
bool remove_lua_action (const int);
bool lua_action_name (const int, std::string&);
std::vector<std::string> lua_action_names ();
bool lua_action (const int, std::string&, std::string&, ARDOUR::LuaScriptParamList&);
bool lua_action_has_icon (const int);
sigc::signal<void,int,std::string> ActionChanged;
/* callbacks */
@ -125,6 +131,7 @@ private:
LuaState lua;
luabridge::LuaRef * _lua_call_action;
luabridge::LuaRef * _lua_render_icon;
luabridge::LuaRef * _lua_add_action;
luabridge::LuaRef * _lua_del_action;
luabridge::LuaRef * _lua_get_action;