From 73a3df5766a4907ef75874f3c4a48a49e15b0fc4 Mon Sep 17 00:00:00 2001 From: TJ Jackson Date: Tue, 14 Apr 2026 13:18:52 -0700 Subject: [PATCH] Add rebind and rebindRegex commands Signed-off-by: TJ Jackson --- src/lib/app/MuTwkApp/CommandsModule.cpp | 86 +++++++++++++++++++ .../app/MuTwkApp/MuTwkApp/CommandsModule.h | 2 + src/lib/app/TwkApp/EventTable.cpp | 28 ++++++ src/lib/app/TwkApp/TwkApp/EventTable.h | 9 ++ src/lib/app/mu_rvui/commands.mud | 12 +++ src/lib/app/py_rvui/rv_commands_setup.py | 2 + 6 files changed, 139 insertions(+) diff --git a/src/lib/app/MuTwkApp/CommandsModule.cpp b/src/lib/app/MuTwkApp/CommandsModule.cpp index ea0d6839e..2a0995132 100644 --- a/src/lib/app/MuTwkApp/CommandsModule.cpp +++ b/src/lib/app/MuTwkApp/CommandsModule.cpp @@ -147,6 +147,14 @@ namespace TwkApp new Function(c, "unbindRegex", CommandsModule::unbindRegex, None, Return, "void", Parameters, new Param(c, "modeName", "string"), new Param(c, "tableName", "string"), new Param(c, "eventName", "string"), End), + new Function(c, "rebind", CommandsModule::rebind, None, Return, "void", Parameters, new Param(c, "modeName", "string"), + new Param(c, "tableName", "string"), new Param(c, "oldEventName", "string"), + new Param(c, "newEventName", "string"), End), + + new Function(c, "rebindRegex", CommandsModule::rebindRegex, None, Return, "void", Parameters, + new Param(c, "modeName", "string"), new Param(c, "tableName", "string"), new Param(c, "oldEventPattern", "string"), + new Param(c, "newEventPattern", "string"), End), + new Function(c, "setEventTableBBox", CommandsModule::setTableBBox, None, Return, "void", Parameters, new Param(c, "modeName", "string"), new Param(c, "tableName", "string"), new Param(c, "min", "vector float[2]"), new Param(c, "max", "vector float[2]"), End), @@ -563,6 +571,84 @@ namespace TwkApp } } + NODE_IMPLEMENTATION(CommandsModule::rebind, void) + { + Process* p = NODE_THREAD.process(); + Document* d = currentDocument(); + + String* modeName = NODE_ARG_OBJECT(0, StringType::String); + String* tableName = NODE_ARG_OBJECT(1, StringType::String); + String* oldEventName = NODE_ARG_OBJECT(2, StringType::String); + String* newEventName = NODE_ARG_OBJECT(3, StringType::String); + + if (!modeName || !tableName || !oldEventName || !newEventName) + { + throwBadArgumentException(NODE_THIS, NODE_THREAD, "Nil argument to function"); + } + + if (Mode* mode = d->findModeByName(modeName->c_str())) + { + if (EventTable* table = mode->findTableByName(tableName->c_str())) + { + table->rebind(oldEventName->c_str(), newEventName->c_str()); + // + // currentDocument has a copy of this table, so invalidate + // that copy. + // + d->invalidateEventTables(); + } + else + { + string msg = "No table named " + string(tableName->c_str()); + throwBadArgumentException(NODE_THIS, NODE_THREAD, msg); + } + } + else + { + string msg = "No mode named " + string(modeName->c_str()); + throwBadArgumentException(NODE_THIS, NODE_THREAD, msg); + } + } + + NODE_IMPLEMENTATION(CommandsModule::rebindRegex, void) + { + Process* p = NODE_THREAD.process(); + Document* d = currentDocument(); + + String* modeName = NODE_ARG_OBJECT(0, StringType::String); + String* tableName = NODE_ARG_OBJECT(1, StringType::String); + String* oldEventPattern = NODE_ARG_OBJECT(2, StringType::String); + String* newEventPattern = NODE_ARG_OBJECT(3, StringType::String); + + if (!modeName || !tableName || !oldEventPattern || !newEventPattern) + { + throwBadArgumentException(NODE_THIS, NODE_THREAD, "Nil argument to function"); + } + + if (Mode* mode = d->findModeByName(modeName->c_str())) + { + if (EventTable* table = mode->findTableByName(tableName->c_str())) + { + table->rebindRegex(oldEventPattern->c_str(), newEventPattern->c_str()); + // + // currentDocument has a copy of this table, so invalidate + // that copy. + // + d->invalidateEventTables(); + } + else + { + string msg = "No table named " + string(tableName->c_str()); + throwBadArgumentException(NODE_THIS, NODE_THREAD, msg); + } + } + else + { + string msg = "No mode named " + string(modeName->c_str()); + throwBadArgumentException(NODE_THIS, NODE_THREAD, msg); + } + } + NODE_IMPLEMENTATION(CommandsModule::bindings, Mu::Pointer) { Process* p = NODE_THREAD.process(); diff --git a/src/lib/app/MuTwkApp/MuTwkApp/CommandsModule.h b/src/lib/app/MuTwkApp/MuTwkApp/CommandsModule.h index 751fc7259..2137370e2 100644 --- a/src/lib/app/MuTwkApp/MuTwkApp/CommandsModule.h +++ b/src/lib/app/MuTwkApp/MuTwkApp/CommandsModule.h @@ -43,6 +43,8 @@ namespace TwkApp static NODE_DECLARATION(bindRegex, void); static NODE_DECLARATION(unbind, void); static NODE_DECLARATION(unbindRegex, void); + static NODE_DECLARATION(rebind, void); + static NODE_DECLARATION(rebindRegex, void); static NODE_DECLARATION(setTableBBox, void); static NODE_DECLARATION(pushEventTable, void); static NODE_DECLARATION(popEventTable, void); diff --git a/src/lib/app/TwkApp/EventTable.cpp b/src/lib/app/TwkApp/EventTable.cpp index 2d04f1e5e..7b4a1a1de 100644 --- a/src/lib/app/TwkApp/EventTable.cpp +++ b/src/lib/app/TwkApp/EventTable.cpp @@ -66,6 +66,34 @@ namespace TwkApp } } + void EventTable::rebind(const std::string& oldEvent, const std::string& newEvent) + { + BindingMap::iterator i = m_map.find(oldEvent); + + if (i != m_map.end()) + { + Action* action = i->second; + m_map.erase(i); + unbind(newEvent); + m_map[newEvent] = action; + } + } + + void EventTable::rebindRegex(const std::string& oldEventRegex, const std::string& newEventRegex) + { + for (int i = 0; i < m_reBindings.size(); i++) + { + if (m_reBindings[i].first.pattern() == oldEventRegex) + { + Action* action = m_reBindings[i].second; + m_reBindings.erase(m_reBindings.begin() + i); + unbindRegex(newEventRegex); + m_reBindings.push_back(RegExBinding(newEventRegex, action)); + break; + } + } + } + static void deleteAction(EventTable::Binding& b) { delete b.second; } void EventTable::clear() diff --git a/src/lib/app/TwkApp/TwkApp/EventTable.h b/src/lib/app/TwkApp/TwkApp/EventTable.h index 4b33a07ef..66b2c8931 100644 --- a/src/lib/app/TwkApp/TwkApp/EventTable.h +++ b/src/lib/app/TwkApp/TwkApp/EventTable.h @@ -71,6 +71,15 @@ namespace TwkApp void unbind(const std::string& event); void unbindRegex(const std::string& eventRegex); + // + // Move an existing event to a new event. The action currently bound + // to oldEvent is re-bound to newEvent (replacing any existing binding + // there) and the oldEvent binding is removed. + // + + void rebind(const std::string& oldEvent, const std::string& newEvent); + void rebindRegex(const std::string& oldEventRegex, const std::string& newEventRegex); + // // ROI // diff --git a/src/lib/app/mu_rvui/commands.mud b/src/lib/app/mu_rvui/commands.mud index cf6032baa..65d7b44e4 100644 --- a/src/lib/app/mu_rvui/commands.mud +++ b/src/lib/app/mu_rvui/commands.mud @@ -293,6 +293,18 @@ Removes the regular expression event binding from the given mode's event table. """ +rebind """ +Reassign an existing event binding to a new event in the given mode's +event table. +""" + +rebindRegex """ +Reassign an existing regular expression event binding to a new event +in the given mode's event table. + +See also: rebind() +""" + setEventTableBBox """ Set the bounding box for the given mode's event table. """ diff --git a/src/lib/app/py_rvui/rv_commands_setup.py b/src/lib/app/py_rvui/rv_commands_setup.py index 9a15fcba2..9e49b1465 100644 --- a/src/lib/app/py_rvui/rv_commands_setup.py +++ b/src/lib/app/py_rvui/rv_commands_setup.py @@ -317,6 +317,8 @@ "sourceMediaRepsAndNodes", "devicePixelRatio", "waitForProgressiveLoading", + "rebind", + "rebindRegex", ]