-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'vcv-context-menu' into v2.0-dev
- Loading branch information
Showing
20 changed files
with
667 additions
and
85 deletions.
There are no files selected for viewing
Submodule metamodule-plugin-sdk
updated
3 files
+27 −0 | api-symbols.txt | |
+1 −1 | metamodule-core-interface | |
+1 −1 | metamodule-rack-interface |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
#include <string> | ||
#include <vector> | ||
|
||
namespace MetaModule | ||
{ | ||
|
||
struct BasePluginModuleMenu { | ||
virtual ~BasePluginModuleMenu() = default; | ||
|
||
virtual std::vector<std::string> get_items() = 0; | ||
virtual void back_event() = 0; | ||
virtual void click_item(unsigned idx) = 0; | ||
virtual bool is_done() = 0; | ||
}; | ||
|
||
} // namespace MetaModule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma once | ||
#include "CoreModules/CoreProcessor.hh" | ||
#include "gui/module_menu/base_plugin_menu.hh" | ||
#include <memory> | ||
|
||
namespace MetaModule | ||
{ | ||
|
||
// Not implemented yet, just placeholder to demonstrate support for multiple plugin types | ||
struct NativeModuleMenu : BasePluginModuleMenu { | ||
|
||
NativeModuleMenu(CoreProcessor *module) { | ||
} | ||
|
||
~NativeModuleMenu() override = default; | ||
|
||
std::vector<std::string> get_items() override { | ||
return {}; | ||
} | ||
|
||
void back_event() override { | ||
} | ||
|
||
void click_item(unsigned idx) override { | ||
} | ||
|
||
bool is_done() override { | ||
return true; | ||
} | ||
}; | ||
|
||
} // namespace MetaModule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
#pragma once | ||
#include "console/pr_dbg.hh" | ||
#include "gui/helpers/lv_helpers.hh" | ||
#include "gui/helpers/roller_hover_text.hh" | ||
#include "gui/module_menu/base_plugin_menu.hh" | ||
#include "gui/module_menu/native_plugin_menu.hh" | ||
#include "gui/module_menu/vcv_plugin_menu.hh" | ||
#include "gui/slsexport/meta5/ui.h" | ||
#include "lvgl.h" | ||
#include "patch_play/patch_playloader.hh" | ||
#include "util/overloaded.hh" | ||
#include <functional> | ||
|
||
namespace MetaModule | ||
{ | ||
|
||
struct PluginModuleMenu { | ||
PluginModuleMenu(PatchPlayLoader &patch_playloader) | ||
: roller_hover(ui_ElementRollerPanel, ui_ModuleViewExtraMenuRoller) | ||
, patch_playloader{patch_playloader} { | ||
lv_hide(roller); | ||
lv_obj_add_event_cb(roller, roller_click_cb, LV_EVENT_CLICKED, this); | ||
lv_obj_add_event_cb(roller, roller_scrolled_cb, LV_EVENT_KEY, this); | ||
lv_obj_remove_style(roller, nullptr, LV_STATE_EDITED); | ||
lv_obj_remove_style(roller, nullptr, LV_STATE_FOCUS_KEY); | ||
|
||
auto roller_label = lv_obj_get_child(roller, 0); | ||
lv_label_set_recolor(roller_label, true); | ||
|
||
lv_obj_set_style_text_font(roller, &ui_font_MuseoSansRounded70014, LV_PART_MAIN); | ||
lv_obj_set_style_text_font(roller, &ui_font_MuseoSansRounded70014, LV_PART_SELECTED); | ||
|
||
auto hover_label = lv_obj_get_child(roller_hover.get_cont(), 0); | ||
lv_obj_set_style_text_font(hover_label, &ui_font_MuseoSansRounded70014, LV_PART_MAIN); | ||
} | ||
|
||
bool create_options_menu(unsigned this_module_id) { | ||
if (auto rack_module = patch_playloader.get_plugin_module<rack::engine::Module>(this_module_id)) { | ||
plugin_menu = std::make_unique<RackModuleMenu>(rack_module->module_widget); | ||
return populate_menu_items() > 0; | ||
|
||
} else if (auto native_module = patch_playloader.get_plugin_module<CoreProcessor>(this_module_id)) { | ||
plugin_menu = std::make_unique<NativeModuleMenu>(native_module); | ||
return populate_menu_items() > 0; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
void show() { | ||
populate_menu_items(); | ||
|
||
lv_roller_set_selected(roller, 0, LV_ANIM_OFF); | ||
lv_show(roller); | ||
lv_group_focus_obj(roller); | ||
if (auto *group = static_cast<lv_group_t *>(lv_obj_get_group(roller))) | ||
lv_group_set_editing(group, true); | ||
lv_event_send(roller, LV_EVENT_PRESSED, nullptr); | ||
|
||
roller_hover.hide(); | ||
|
||
visible = true; | ||
should_close = false; | ||
} | ||
|
||
void hide() { | ||
if (visible) { | ||
lv_hide(roller); | ||
roller_hover.hide(); | ||
visible = false; | ||
should_close = false; | ||
} | ||
} | ||
|
||
void back_event() { | ||
if (visible) { | ||
plugin_menu->back_event(); | ||
|
||
if (plugin_menu->is_done()) | ||
should_close = true; | ||
else | ||
populate_menu_items(); | ||
} | ||
} | ||
|
||
void update() { | ||
roller_hover.update(); | ||
} | ||
|
||
bool wants_to_close() { | ||
return should_close; | ||
} | ||
|
||
void click(unsigned idx) { | ||
// First item is "< Back" | ||
if (idx == 0) { | ||
back_event(); | ||
} else { | ||
plugin_menu->click_item(idx - 1); | ||
refresh_menu_items(); | ||
} | ||
} | ||
|
||
private: | ||
unsigned populate_menu_items() { | ||
std::string opts = LV_SYMBOL_LEFT + std::string(" Back\n"); | ||
|
||
std::vector<std::string> item_strings; | ||
|
||
if (plugin_menu) { | ||
item_strings = plugin_menu->get_items(); | ||
for (auto const &item : item_strings) { | ||
opts += item + "\n"; | ||
} | ||
} else { | ||
pr_err("plugin_menu not created\n"); | ||
} | ||
|
||
// Remove final /n | ||
if (opts.length() > 0) | ||
opts.pop_back(); | ||
|
||
lv_roller_set_options(roller, opts.c_str(), LV_ROLLER_MODE_NORMAL); | ||
|
||
return item_strings.size(); | ||
} | ||
|
||
// Same as populate_menu_items() but keeps current selection | ||
void refresh_menu_items() { | ||
auto cur_idx = lv_roller_get_selected(roller); | ||
auto num_items = populate_menu_items() + 1; // +1 for "< Back" item | ||
if (cur_idx < num_items) | ||
lv_roller_set_selected(roller, cur_idx, LV_ANIM_OFF); | ||
} | ||
|
||
static void roller_click_cb(lv_event_t *event) { | ||
auto page = static_cast<PluginModuleMenu *>(event->user_data); | ||
auto idx = lv_roller_get_selected(page->roller); | ||
|
||
page->click(idx); | ||
|
||
// keep editing focus on roller after clicking it | ||
lv_group_focus_obj(page->roller); | ||
if (auto *group = static_cast<lv_group_t *>(lv_obj_get_group(page->roller))) | ||
lv_group_set_editing(group, true); | ||
lv_event_send(page->roller, LV_EVENT_PRESSED, nullptr); | ||
|
||
// index might be the same, but content probably changed | ||
page->roller_hover.force_redraw(); | ||
} | ||
|
||
static void roller_scrolled_cb(lv_event_t *event) { | ||
auto page = static_cast<PluginModuleMenu *>(event->user_data); | ||
page->roller_hover.hide(); | ||
} | ||
|
||
std::unique_ptr<BasePluginModuleMenu> plugin_menu; | ||
|
||
lv_obj_t *roller = ui_ModuleViewExtraMenuRoller; | ||
|
||
RollerHoverText roller_hover; | ||
PatchPlayLoader &patch_playloader; | ||
|
||
bool visible = false; | ||
bool should_close = false; | ||
}; | ||
|
||
} // namespace MetaModule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#pragma once | ||
#include "console/pr_dbg.hh" | ||
#include "gui/module_menu/base_plugin_menu.hh" | ||
#include "gui/styles.hh" | ||
#include <app/ModuleWidget.hpp> | ||
#include <ui/MenuItem.hpp> | ||
|
||
namespace MetaModule | ||
{ | ||
|
||
struct RackModuleMenu : BasePluginModuleMenu { | ||
RackModuleMenu(std::shared_ptr<rack::app::ModuleWidget> module_widget) | ||
: module_widget{module_widget} { | ||
} | ||
|
||
std::vector<std::string> get_items() override { | ||
std::vector<std::string> item_strings; | ||
|
||
if (!rack_menu) { | ||
// Initialize | ||
rack_menu = std::make_shared<rack::ui::Menu>(); | ||
current_menu = rack_menu.get(); | ||
rack_menu->parentMenu = nullptr; | ||
|
||
if (auto mw = module_widget.lock()) { | ||
mw->appendContextMenu(rack_menu.get()); | ||
} | ||
|
||
exited = false; | ||
} | ||
|
||
if (current_menu) { | ||
add_menu_items(current_menu->children, item_strings); | ||
} | ||
|
||
return item_strings; | ||
} | ||
|
||
void back_event() override { | ||
if (current_menu && current_menu->parentMenu) { | ||
current_menu = current_menu->parentMenu; | ||
} else { | ||
rack_menu.reset(); | ||
current_menu = nullptr; | ||
exited = true; | ||
} | ||
} | ||
|
||
bool is_done() override { | ||
return exited; | ||
} | ||
|
||
void click_item(unsigned idx) override { | ||
if (!current_menu) | ||
return; | ||
|
||
unsigned i = 0; | ||
for (auto child : current_menu->children) { | ||
if (auto rack_item = dynamic_cast<rack::ui::MenuItem *>(child)) { | ||
if (i == idx) { | ||
// Perform its action | ||
rack_item->doAction(); | ||
|
||
// Remove any existing submenu (from a previous click) | ||
if (current_menu->childMenu) { | ||
current_menu->childMenu->parent->removeChild(current_menu->childMenu); | ||
delete current_menu->childMenu; | ||
current_menu->childMenu = nullptr; | ||
} | ||
|
||
// Enter the submenu if it has one | ||
auto submenu = rack_item->createChildMenu(); | ||
if (submenu) { | ||
// Add the submenu widget so we can delete it later | ||
current_menu->childMenu = submenu; | ||
current_menu->addChild(submenu); | ||
|
||
// point child to parent so we can traverse back | ||
submenu->parentMenu = current_menu; | ||
|
||
// Make the submenu our current menu | ||
current_menu = submenu; | ||
} | ||
break; | ||
} | ||
i++; | ||
} | ||
} | ||
} | ||
|
||
private: | ||
void add_menu_items(std::list<rack::widget::Widget *> children, std::vector<std::string> &menu) { | ||
unsigned num_children = 0; | ||
for (auto child : children) { | ||
if (auto rack_item = dynamic_cast<rack::ui::MenuItem *>(child)) { | ||
child->step(); | ||
|
||
// Reasonable limit to size of menu: | ||
if (num_children++ < 256) { | ||
auto &item = menu.emplace_back(); | ||
// Checkmarks go on left side | ||
if (rack_item->rightText.ends_with(CHECKMARK_STRING)) | ||
item = Gui::yellow_text(CHECKMARK_STRING); | ||
|
||
item += rack_item->text; | ||
|
||
if (rack_item->rightText.length() && !rack_item->rightText.ends_with(CHECKMARK_STRING)) | ||
item += " " + Gui::yellow_text(rack_item->rightText); | ||
} | ||
} | ||
} | ||
} | ||
|
||
std::weak_ptr<rack::app::ModuleWidget> module_widget{}; | ||
std::shared_ptr<rack::ui::Menu> rack_menu; | ||
|
||
rack::ui::Menu *current_menu{}; //can't use smart pointer because must point to a raw pointer in rack API | ||
|
||
bool exited = false; | ||
}; | ||
|
||
} // namespace MetaModule |
Oops, something went wrong.