11#pragma once
2+ #include " CoreModules/moduleFactory.hh"
23#include " fat_file_io.hh"
34#include " gui/gui_state.hh"
45#include " gui/helpers/lv_helpers.hh"
56#include " gui/notify/queue.hh"
7+ #include " gui/pages/keyboard_entry.hh"
68#include " gui/pages/module_view/automap.hh"
79#include " gui/pages/page_list.hh"
810#include " gui/pages/roller_popup.hh"
@@ -44,11 +46,27 @@ public:
4446 , reset_params_{patch_mod_queue}
4547 , group(lv_group_create())
4648 , moduleViewActionPresetBut{create_lv_list_button (ui_ModuleViewActionMenu, " Presets" )}
47- , moduleViewActionBypassBut{create_lv_list_button (ui_ModuleViewActionMenu, " Bypass: Off" )} {
49+ , moduleViewActionBypassBut{create_lv_list_button (ui_ModuleViewActionMenu, " Bypass: Off" )}
50+ , moduleViewActionRenameBut{create_lv_list_button (ui_ModuleViewActionMenu, " Rename..." )}
51+ , moduleViewActionResetNameBut{create_lv_list_button (ui_ModuleViewActionMenu, " Reset name" )} {
4852 lv_obj_set_parent (ui_ModuleViewActionMenu, lv_layer_top ());
4953 lv_show (ui_ModuleViewActionMenu);
5054 lv_show (moduleViewActionPresetBut);
5155 lv_show (moduleViewActionBypassBut);
56+ lv_show (moduleViewActionRenameBut);
57+ lv_hide (moduleViewActionResetNameBut);
58+
59+ rename_textarea = lv_textarea_create (lv_layer_top ());
60+ lv_hide (rename_textarea);
61+ lv_textarea_set_max_length (rename_textarea, AliasNameString::capacity);
62+ lv_textarea_set_one_line (rename_textarea, true );
63+ lv_obj_set_width (rename_textarea, 300 ); // screen width (320) minus 10px margin each side
64+ lv_obj_set_style_bg_opa (rename_textarea, LV_OPA_COVER, 0 );
65+ lv_obj_set_style_border_color (rename_textarea, lv_color_white (), LV_PART_CURSOR | LV_STATE_DEFAULT);
66+ lv_obj_set_style_border_opa (rename_textarea, LV_OPA_COVER, LV_PART_CURSOR | LV_STATE_DEFAULT);
67+ lv_obj_set_style_border_width (rename_textarea, 1 , LV_PART_CURSOR | LV_STATE_DEFAULT);
68+ lv_obj_set_style_border_side (rename_textarea, LV_BORDER_SIDE_LEFT, LV_PART_CURSOR | LV_STATE_DEFAULT);
69+ lv_obj_add_event_cb (rename_textarea, textarea_changed_cb, LV_EVENT_VALUE_CHANGED, this );
5270 lv_obj_set_x (ui_ModuleViewActionMenu, 160 );
5371 lv_obj_set_height (ui_ModuleViewActionMenu, 240 );
5472
@@ -64,6 +82,8 @@ public:
6482 lv_obj_add_event_cb (ui_ModuleViewActionResetBut, reset_but_cb, LV_EVENT_CLICKED, this );
6583 lv_obj_add_event_cb (ui_ModuleViewActionMidiBut, midi_but_cb, LV_EVENT_CLICKED, this );
6684 lv_obj_add_event_cb (moduleViewActionBypassBut, bypass_but_cb, LV_EVENT_CLICKED, this );
85+ lv_obj_add_event_cb (moduleViewActionRenameBut, rename_but_cb, LV_EVENT_CLICKED, this );
86+ lv_obj_add_event_cb (moduleViewActionResetNameBut, reset_name_but_cb, LV_EVENT_CLICKED, this );
6787
6888 lv_group_add_obj (group, ui_ModuleViewActionAutopatchBut);
6989 lv_group_add_obj (group, ui_ModuleViewActionAutoKnobSet);
@@ -72,6 +92,8 @@ public:
7292 lv_group_add_obj (group, ui_ModuleViewActionMidiBut);
7393 lv_group_add_obj (group, moduleViewActionPresetBut);
7494 lv_group_add_obj (group, moduleViewActionBypassBut);
95+ lv_group_add_obj (group, moduleViewActionRenameBut);
96+ lv_group_add_obj (group, moduleViewActionResetNameBut);
7597 lv_group_add_obj (group, ui_ModuleViewActionDeleteBut);
7698 lv_group_set_wrap (group, false );
7799 }
@@ -80,6 +102,8 @@ public:
80102 this ->module_idx = module_idx;
81103 base_group = parent_group;
82104 confirm_popup.init (lv_layer_top (), group);
105+ reset_name_popup.init (lv_layer_top (), base_group);
106+ keyboard_entry.prepare_focus (lv_layer_top (), base_group);
83107
84108 const auto module_slug = std::string{patches.get_view_patch ()->module_slugs [module_idx]};
85109 const auto module_name = module_slug.substr (module_slug.find_first_of (' :' ) + 1 );
@@ -129,8 +153,12 @@ public:
129153 preset_popup.hide ();
130154 } else if (confirm_popup.is_visible ()) {
131155 confirm_popup.hide ();
156+ } else if (reset_name_popup.is_visible ()) {
157+ reset_name_popup.hide ();
132158 } else if (auto_map.is_visible ()) {
133159 auto_map.hide ();
160+ } else if (keyboard_entry.is_visible ()) {
161+ keyboard_entry.back ();
134162 } else if (visible) {
135163 hide ();
136164 }
@@ -139,6 +167,7 @@ public:
139167 void hide () {
140168 preset_popup.hide ();
141169 confirm_popup.hide ();
170+ reset_name_popup.hide ();
142171 hide_menu ();
143172 }
144173
@@ -154,6 +183,7 @@ public:
154183
155184 void show () {
156185 update_bypass_text ();
186+ update_reset_name_visibility ();
157187 lv_group_focus_obj (ui_ModuleViewActionAutopatchBut);
158188
159189 if (!visible) {
@@ -170,6 +200,11 @@ public:
170200 return visible;
171201 }
172202
203+ bool keyboard_visible () {
204+ // also covers reset_name_popup which intercepts back button -- consider renaming to modal_visible()
205+ return keyboard_entry.is_visible () || reset_name_popup.is_visible ();
206+ }
207+
173208 void reactivate_group () {
174209 lv_group_activate (group);
175210 }
@@ -333,6 +368,11 @@ private:
333368 lv_label_set_text (label, bypassed ? " Bypass: On" : " Bypass: Off" );
334369 }
335370
371+ void update_reset_name_visibility () {
372+ auto alias = patches.get_view_patch ()->get_module_alias (static_cast <uint16_t >(module_idx));
373+ lv_show (moduleViewActionResetNameBut, !alias.empty ());
374+ }
375+
336376 static void bypass_but_cb (lv_event_t *event) {
337377 if (!event || !event->user_data )
338378 return ;
@@ -351,6 +391,83 @@ private:
351391 lv_label_set_text (label, new_state ? " Bypass: On" : " Bypass: Off" );
352392 }
353393
394+ static void reset_name_but_cb (lv_event_t *event) {
395+ if (!event || !event->user_data )
396+ return ;
397+ auto page = static_cast <ModuleViewActionMenu *>(event->user_data );
398+ std::string msg;
399+ {
400+ auto *pd = page->patches .get_view_patch ();
401+ auto default_name = ModuleFactory::getModuleDisplayName (pd->module_slugs [page->module_idx ]);
402+ msg = " Reset module name to '" + std::string{default_name} + " '?" ;
403+ }
404+
405+ page->hide_menu ();
406+ page->reset_name_popup .show (
407+ [page](unsigned choice) {
408+ if (choice == 1 ) {
409+ auto *pd = page->patches .get_view_patch ();
410+ pd->set_module_alias (static_cast <uint16_t >(page->module_idx ), " " );
411+ page->patches .mark_view_patch_modified ();
412+ auto display = ModuleFactory::getModuleDisplayName (pd->module_slugs [page->module_idx ]);
413+ lv_label_set_text (ui_ElementRollerModuleName, display.data ());
414+ }
415+ },
416+ msg.c_str (),
417+ " Reset" );
418+ }
419+
420+ static void textarea_changed_cb (lv_event_t *event) {
421+ if (!event || !event->user_data )
422+ return ;
423+ auto page = static_cast <ModuleViewActionMenu *>(event->user_data );
424+ std::string_view text = lv_textarea_get_text (page->rename_textarea );
425+ auto *pd = page->patches .get_view_patch ();
426+ std::string_view display = text.empty ()
427+ ? (page->pending_alias .empty ()
428+ ? ModuleFactory::getModuleDisplayName (pd->module_slugs [page->module_idx ])
429+ : std::string_view{page->pending_alias })
430+ : text;
431+ lv_label_set_text (ui_ElementRollerModuleName, display.data ());
432+ }
433+
434+ static void rename_but_cb (lv_event_t *event) {
435+ if (!event || !event->user_data )
436+ return ;
437+ auto page = static_cast <ModuleViewActionMenu *>(event->user_data );
438+
439+ auto *pd = page->patches .get_view_patch ();
440+ auto alias = pd->get_module_alias (static_cast <uint16_t >(page->module_idx ));
441+
442+ // Show current alias (or module name) as greyed-out placeholder; don't pre-fill
443+ std::string_view placeholder = alias.empty ()
444+ ? ModuleFactory::getModuleDisplayName (pd->module_slugs [page->module_idx ])
445+ : alias;
446+ lv_textarea_set_placeholder_text (page->rename_textarea , placeholder.data ());
447+ lv_textarea_set_text (page->rename_textarea , " " );
448+ lv_label_set_text (ui_ElementRollerModuleName, placeholder.data ());
449+
450+ page->hide_menu ();
451+ page->pending_alias = alias;
452+ page->keyboard_entry .show_keyboard (
453+ page->rename_textarea ,
454+ [page](std::string_view text) {
455+ auto *pd = page->patches .get_view_patch ();
456+ // Empty text means user didn't type anything — preserve existing alias
457+ std::string_view actual = text.empty () ? std::string_view{page->pending_alias } : text;
458+ pd->set_module_alias (static_cast <uint16_t >(page->module_idx ), actual);
459+ page->patches .mark_view_patch_modified ();
460+
461+ std::string_view display = actual.empty ()
462+ ? ModuleFactory::getModuleDisplayName (pd->module_slugs [page->module_idx ])
463+ : actual;
464+ lv_label_set_text (ui_ElementRollerModuleName, display.data ());
465+ },
466+ /* hide_field_on_dismiss=*/ true );
467+ lv_show (page->rename_textarea );
468+ lv_obj_align_to (page->rename_textarea , ui_Keyboard, LV_ALIGN_OUT_TOP_MID, 0 , -4 );
469+ }
470+
354471 FatFileIO &ramdisk;
355472 OpenPatchManager &patches;
356473 PageList &page_list;
@@ -360,6 +477,7 @@ private:
360477 NotificationQueue ¬ify_queue;
361478
362479 ConfirmPopup confirm_popup;
480+ ConfirmPopup reset_name_popup;
363481
364482 ModuleViewAutoMapDialog auto_map;
365483 RandomizeParams randomizer;
@@ -372,6 +490,11 @@ private:
372490
373491 lv_obj_t *moduleViewActionPresetBut;
374492 lv_obj_t *moduleViewActionBypassBut;
493+ lv_obj_t *moduleViewActionRenameBut;
494+ lv_obj_t *moduleViewActionResetNameBut;
495+ lv_obj_t *rename_textarea = nullptr ;
496+ std::string pending_alias{};
497+ KeyboardEntry keyboard_entry;
375498 std::string preset_path{};
376499 uint16_t cur_preset_idx{};
377500 std::string presets{};
0 commit comments