From f3dbafe3251a6613433debfdde48dc814f5423a1 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 27 Feb 2018 00:50:02 +0100 Subject: [PATCH] Add a system where themes can present a simple menu to the user. --- mainwindow.cpp | 24 +++++++++++++++++++ mainwindow.h | 2 ++ mixer.h | 9 ++++++++ theme.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++----- theme.h | 19 ++++++++++++++++ 5 files changed, 110 insertions(+), 6 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 6c1c790..7235e8e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -414,6 +414,9 @@ void MainWindow::mixer_created(Mixer *mixer) analyzer.reset(new Analyzer); + global_mixer->set_theme_menu_callback(bind(&MainWindow::setup_theme_menu, this)); + setup_theme_menu(); + struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = schedule_cut_signal; @@ -808,6 +811,27 @@ 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(); + + 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); + }); + } + ui->menuBar->insertMenu(ui->menu_Help->menuAction(), theme_menu); + } +} + void MainWindow::limiter_threshold_knob_changed(int value) { float threshold_dbfs = value * 0.1f; diff --git a/mainwindow.h b/mainwindow.h index 9e10ed9..62551d9 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -128,6 +128,7 @@ private: void set_white_balance(int channel_number, int x, int y); void update_cutoff_labels(float cutoff_hz); void update_eq_label(unsigned bus_index, EQBand band, float gain_db); + void setup_theme_menu(); // Called from DiskSpaceEstimator. void report_disk_space(off_t free_bytes, double estimated_seconds_left); @@ -158,6 +159,7 @@ private: Ui::MainWindow *ui; QLabel *disk_free_label; + QMenu *theme_menu = nullptr; QPushButton *transition_btn1, *transition_btn2, *transition_btn3; std::vector previews; std::vector audio_miniviews; diff --git a/mixer.h b/mixer.h index 940dc13..effc2a3 100644 --- a/mixer.h +++ b/mixer.h @@ -403,6 +403,15 @@ public: return httpd.get_num_connected_clients(); } + std::vector get_theme_menu() { return theme->get_theme_menu(); } + + void theme_menu_entry_clicked(int lua_ref) { return theme->theme_menu_entry_clicked(lua_ref); } + + void set_theme_menu_callback(std::function callback) + { + theme->set_theme_menu_callback(callback); + } + private: struct CaptureCard; diff --git a/theme.cpp b/theme.cpp index 485fa43..bb0bcaf 100644 --- a/theme.cpp +++ b/theme.cpp @@ -48,6 +48,18 @@ using namespace movit; extern Mixer *global_mixer; +Theme *get_theme_updata(lua_State* L) +{ + luaL_checktype(L, lua_upvalueindex(1), LUA_TLIGHTUSERDATA); + return (Theme *)lua_touserdata(L, lua_upvalueindex(1)); +} + +int ThemeMenu_set(lua_State *L) +{ + Theme *theme = get_theme_updata(L); + return theme->set_theme_menu(L); +} + namespace { // Contains basically the same data as InputState, but does not hold on to @@ -136,12 +148,6 @@ int wrap_lua_object_nonowned(lua_State* L, const char *class_name, Args&&... arg return 1; } -Theme *get_theme_updata(lua_State* L) -{ - luaL_checktype(L, lua_upvalueindex(1), LUA_TLIGHTUSERDATA); - return (Theme *)lua_touserdata(L, lua_upvalueindex(1)); -} - Effect *get_effect(lua_State *L, int idx) { if (luaL_testudata(L, idx, "WhiteBalanceEffect") || @@ -782,6 +788,11 @@ const luaL_Reg InputStateInfo_funcs[] = { { NULL, NULL } }; +const luaL_Reg ThemeMenu_funcs[] = { + { "set", ThemeMenu_set }, + { NULL, NULL } +}; + } // namespace LiveInputWrapper::LiveInputWrapper(Theme *theme, EffectChain *chain, bmusb::PixelFormat pixel_format, bool override_bounce, bool deinterlace) @@ -1022,6 +1033,7 @@ Theme::Theme(const string &filename, const vector &search_dirs, Resource register_class("MultiplyEffect", MultiplyEffect_funcs); register_class("MixEffect", MixEffect_funcs); register_class("InputStateInfo", InputStateInfo_funcs); + register_class("ThemeMenu", ThemeMenu_funcs); // Run script. Search through all directories until we find a file that will load // (as in, does not return LUA_ERRFILE); then run it. We store load errors @@ -1340,3 +1352,41 @@ void Theme::channel_clicked(int preview_num) } assert(lua_gettop(L) == 0); } + +int Theme::set_theme_menu(lua_State *L) +{ + for (const Theme::MenuEntry &entry : theme_menu) { + luaL_unref(L, LUA_REGISTRYINDEX, entry.lua_ref); + } + 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); + + lua_rawgeti(L, i, 2); + luaL_checktype(L, -1, LUA_TFUNCTION); + int ref = luaL_ref(L, LUA_REGISTRYINDEX); + + theme_menu.push_back(MenuEntry{ text, ref }); + } + lua_pop(L, num_elements); + assert(lua_gettop(L) == 0); + + if (theme_menu_callback != nullptr) { + theme_menu_callback(); + } + + return 0; +} + +void Theme::theme_menu_entry_clicked(int lua_ref) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref); + if (lua_pcall(L, 0, 0, 0) != 0) { + fprintf(stderr, "error running menu callback: %s\n", lua_tostring(L, -1)); + exit(1); + } +} diff --git a/theme.h b/theme.h index f7897c7..8712bf8 100644 --- a/theme.h +++ b/theme.h @@ -104,9 +104,24 @@ public: } #endif + struct MenuEntry { + std::string text; + int lua_ref; + }; + std::vector get_theme_menu() { return theme_menu; } // 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. + // Is not invoked for a menu that exists at the time of the callback. + void set_theme_menu_callback(std::function callback) + { + theme_menu_callback = callback; + } + private: void register_constants(); void register_class(const char *class_name, const luaL_Reg *funcs); + int set_theme_menu(lua_State *L); std::mutex m; lua_State *L; // Protected by . @@ -125,7 +140,11 @@ private: std::vector> html_signal_connections; #endif + std::vector theme_menu; + std::function theme_menu_callback; + friend class LiveInputWrapper; + friend int ThemeMenu_set(lua_State *L); }; // LiveInputWrapper is a facade on top of an YCbCrInput, exposed to -- 2.39.2