From: Steinar H. Gunderson Date: Fri, 21 Jun 2019 15:45:54 +0000 (+0200) Subject: Support submenus within theme menus. X-Git-Tag: 1.9.0~20 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=f8da8feaff269b75480625e1384951c20c3a529d Support submenus within theme menus. --- diff --git a/nageru/mainwindow.cpp b/nageru/mainwindow.cpp index 91234ce..f33c57d 100644 --- a/nageru/mainwindow.cpp +++ b/nageru/mainwindow.cpp @@ -881,22 +881,38 @@ void MainWindow::update_eq_label(unsigned bus_index, EQBand band, float gain_db) void MainWindow::setup_theme_menu() { - std::vector 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> &entries, QMenu *menu) +{ + for (const unique_ptr &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); + }); } } diff --git a/nageru/mainwindow.h b/nageru/mainwindow.h index 37b1ce0..a0c090f 100644 --- a/nageru/mainwindow.h +++ b/nageru/mainwindow.h @@ -140,6 +140,7 @@ private: 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> &entries, QMenu *menu); void prev_page(); void next_page(); diff --git a/nageru/mixer.h b/nageru/mixer.h index 5af2016..69b28f5 100644 --- a/nageru/mixer.h +++ b/nageru/mixer.h @@ -413,7 +413,7 @@ public: return httpd.get_num_connected_clients(); } - std::vector 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); } diff --git a/nageru/theme.cpp b/nageru/theme.cpp index d31a4b9..c881dc6 100644 --- a/nageru/theme.cpp +++ b/nageru/theme.cpp @@ -1266,6 +1266,7 @@ Theme::Theme(const string &filename, const vector &search_dirs, Resource Theme::~Theme() { + theme_menu.reset(); lua_close(L); } @@ -1597,25 +1598,73 @@ void Theme::channel_clicked(int preview_num) assert(lua_gettop(L) == 0); } -int Theme::set_theme_menu(lua_State *L) +template +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> create_recursive_theme_menu(lua_State *L); + +unique_ptr create_theme_menu_entry(lua_State *L, int index) +{ + unique_ptr 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> 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> create_recursive_theme_menu(lua_State *L) +{ + vector> 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> 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); diff --git a/nageru/theme.h b/nageru/theme.h index 1bed992..2fac796 100644 --- a/nageru/theme.h +++ b/nageru/theme.h @@ -149,10 +149,27 @@ public: #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> 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> submenu; + }; }; - std::vector 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. @@ -197,7 +214,7 @@ private: html_signal_connections; #endif - std::vector theme_menu; + std::unique_ptr theme_menu; std::function theme_menu_callback; friend class LiveInputWrapper;