void MainWindow::setup_theme_menu()
{
- std::vector<Theme::MenuEntry> theme_menu_entries = global_mixer->get_theme_menu();
+ Theme::MenuEntry *root_menu = global_mixer->get_theme_menu();
+ // Remove the old menu, if any.
if (theme_menu != nullptr) {
ui->menuBar->removeAction(theme_menu->menuAction());
theme_menu = nullptr;
}
- if (!theme_menu_entries.empty()) {
- theme_menu = new QMenu("&Theme");
- for (const Theme::MenuEntry &entry : theme_menu_entries) {
- QAction *action = theme_menu->addAction(QString::fromStdString(entry.text));
- connect(action, &QAction::triggered, [entry] {
- global_mixer->theme_menu_entry_clicked(entry.lua_ref);
- });
+ if (root_menu != nullptr) {
+ assert(root_menu->is_submenu);
+ if (!root_menu->submenu.empty()) {
+ theme_menu = new QMenu("&Theme");
+ fill_menu_from_theme_menu(root_menu->submenu, theme_menu);
+ ui->menuBar->insertMenu(ui->menu_Help->menuAction(), theme_menu);
+ }
+ }
+}
+
+void MainWindow::fill_menu_from_theme_menu(const vector<unique_ptr<Theme::MenuEntry>> &entries, QMenu *menu)
+{
+ for (const unique_ptr<Theme::MenuEntry> &entry : entries) {
+ if (entry->is_submenu) {
+ QMenu *submenu = new QMenu(QString::fromStdString(entry->text));
+ fill_menu_from_theme_menu(entry->submenu, submenu);
+ menu->addMenu(submenu);
+ continue;
}
- ui->menuBar->insertMenu(ui->menu_Help->menuAction(), theme_menu);
+
+ QAction *action = menu->addAction(QString::fromStdString(entry->text));
+ connect(action, &QAction::triggered, [lua_ref = entry->entry.lua_ref] {
+ global_mixer->theme_menu_entry_clicked(lua_ref);
+ });
}
}
void update_stereo_label(unsigned bus_index, int stereo_width_percent);
void update_eq_label(unsigned bus_index, EQBand band, float gain_db);
void setup_theme_menu();
+ void fill_menu_from_theme_menu(const std::vector<std::unique_ptr<Theme::MenuEntry>> &entries, QMenu *menu);
void prev_page();
void next_page();
return httpd.get_num_connected_clients();
}
- std::vector<Theme::MenuEntry> get_theme_menu() { return theme->get_theme_menu(); }
+ Theme::MenuEntry *get_theme_menu() { return theme->get_theme_menu(); }
void theme_menu_entry_clicked(int lua_ref) { return theme->theme_menu_entry_clicked(lua_ref); }
Theme::~Theme()
{
+ theme_menu.reset();
lua_close(L);
}
assert(lua_gettop(L) == 0);
}
-int Theme::set_theme_menu(lua_State *L)
+template <class T>
+void destroy(T &ref)
+{
+ ref.~T();
+}
+
+Theme::MenuEntry::~MenuEntry()
{
- for (const Theme::MenuEntry &entry : theme_menu) {
- luaL_unref(L, LUA_REGISTRYINDEX, entry.lua_ref);
+ if (is_submenu) {
+ luaL_unref(entry.L, LUA_REGISTRYINDEX, entry.lua_ref);
+ } else {
+ destroy(submenu);
}
- theme_menu.clear();
+}
- int num_elements = lua_gettop(L);
- for (int i = 1; i <= num_elements; ++i) {
- lua_rawgeti(L, i, 1);
- const string text = checkstdstring(L, -1);
- lua_pop(L, 1);
+namespace {
+
+vector<unique_ptr<Theme::MenuEntry>> create_recursive_theme_menu(lua_State *L);
+
+unique_ptr<Theme::MenuEntry> create_theme_menu_entry(lua_State *L, int index)
+{
+ unique_ptr<Theme::MenuEntry> entry;
+
+ lua_rawgeti(L, index, 1);
+ const string text = checkstdstring(L, -1);
+ lua_pop(L, 1);
- lua_rawgeti(L, i, 2);
+ lua_rawgeti(L, index, 2);
+ if (lua_istable(L, -1)) {
+ vector<unique_ptr<Theme::MenuEntry>> submenu = create_recursive_theme_menu(L);
+ entry.reset(new Theme::MenuEntry{ text, move(submenu) });
+ lua_pop(L, 1);
+ } else {
luaL_checktype(L, -1, LUA_TFUNCTION);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ entry.reset(new Theme::MenuEntry{ text, L, ref });
+ }
+ return entry;
+}
+
+vector<unique_ptr<Theme::MenuEntry>> create_recursive_theme_menu(lua_State *L)
+{
+ vector<unique_ptr<Theme::MenuEntry>> menu;
+ size_t num_elements = lua_objlen(L, -1);
+ for (size_t i = 1; i <= num_elements; ++i) {
+ lua_rawgeti(L, -1, i);
+ menu.emplace_back(create_theme_menu_entry(L, -1));
+ lua_pop(L, 1);
+ }
+ return menu;
+}
- theme_menu.push_back(MenuEntry{ text, ref });
+} // namespace
+
+int Theme::set_theme_menu(lua_State *L)
+{
+ theme_menu.reset();
+
+ vector<unique_ptr<MenuEntry>> root_menu;
+ int num_elements = lua_gettop(L);
+ for (int i = 1; i <= num_elements; ++i) {
+ root_menu.emplace_back(create_theme_menu_entry(L, i));
}
+ fprintf(stderr, "now creating a new one\n");
+ theme_menu.reset(new MenuEntry("", move(root_menu)));
+ fprintf(stderr, "DONE reset\n");
+
lua_pop(L, num_elements);
assert(lua_gettop(L) == 0);
#endif
struct MenuEntry {
+ MenuEntry(const std::string &text, lua_State *L, int lua_ref)
+ : text(text), is_submenu(false), entry{L, lua_ref} {}
+ MenuEntry(const std::string &text, std::vector<std::unique_ptr<MenuEntry>> submenu)
+ : text(text), is_submenu(true), submenu(std::move(submenu)) {}
+ ~MenuEntry();
+
std::string text;
- int lua_ref;
+ bool is_submenu;
+
+ union {
+ // is_submenu = false.
+ struct {
+ lua_State *L;
+ int lua_ref;
+ } entry;
+
+ // is_submenu = true.
+ std::vector<std::unique_ptr<MenuEntry>> submenu;
+ };
};
- std::vector<MenuEntry> get_theme_menu() { return theme_menu; } // Can be empty for no menu.
+ MenuEntry *get_theme_menu() { return theme_menu.get(); } // Can be empty for no menu.
void theme_menu_entry_clicked(int lua_ref);
// Will be invoked every time the theme sets a new menu.
html_signal_connections;
#endif
- std::vector<MenuEntry> theme_menu;
+ std::unique_ptr<MenuEntry> theme_menu;
std::function<void()> theme_menu_callback;
friend class LiveInputWrapper;