From eb71ca765f995793c11a659acb85e67dffbf1a6b Mon Sep 17 00:00:00 2001 From: hanez Date: Fri, 6 Nov 2026 01:39:31 +0100 Subject: [PATCH] feat(plugins): inspect and edit selected track effects (phase 86) --- docs/ROADMAP.md | 8 ++++---- docs/TRACK_INSERTS.md | 6 ++++++ src/gui/plugin_dialog.cpp | 5 +++++ src/gui/track_inserts.hpp | 11 +++++++++++ tests/integration/test_gui.cpp | 7 +++++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 2895600..8d1c75e 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1970,12 +1970,12 @@ Implemented: selected insert Up/Down, bypass and removal, preserving effect meta Show selected effect identity and supported generic controls. Provide reset where supported and clearly identify unavailable native editors/parameters. ### Tasks and acceptance criteria -- [ ] Implement the described workflow with validated errors and safe ownership. -- [ ] Pass relevant core and/or GUI tests. -- [ ] Document behavior and limitations; commit this phase separately. +- [x] Implement the described workflow with validated errors and safe ownership. +- [x] Pass relevant core and/or GUI tests. +- [x] Document behavior and limitations; commit this phase separately. ### Implementation status -Planned. +Implemented: selected insert Inspect/edit verifies identity and shows generic controls with reset/defaults through transactional preparation. Zero-parameter adapters explicitly explain default configuration and unavailable native editors; reset is disabled there. Focused real modal reset/apply/cancel, adoption, undo and reload tests passed with a warning-enabled GUI build. --- diff --git a/docs/TRACK_INSERTS.md b/docs/TRACK_INSERTS.md index 4d47fe3..2f152df 100644 --- a/docs/TRACK_INSERTS.md +++ b/docs/TRACK_INSERTS.md @@ -44,3 +44,9 @@ Select a slot in the rack, then use **Up**, **Down**, **Bypass** or **Remove**. The selection follows reordered effects. Bypassed slots retain their settings and appear marked in the rack; their nodes are omitted from the compiled graph. Each successful operation creates one project undo step. + +**Inspect / edit** verifies the selected plugin and opens its identity and generic +controls. Reset defaults clears parameter automation on Apply. Cancel leaves the +project unchanged. Internal effects and LADSPA expose controls; modern adapters +currently expose none and run default configurations. Native plugin editor +windows and opaque plugin-state chunks are not implemented. diff --git a/src/gui/plugin_dialog.cpp b/src/gui/plugin_dialog.cpp index 9421c05..6a449c1 100644 --- a/src/gui/plugin_dialog.cpp +++ b/src/gui/plugin_dialog.cpp @@ -78,11 +78,16 @@ bool xtc_edit_plugin(const xtc_plugin_spec &spec, const xtc_plugin_details &deta ((details.flags[i] & XTC_PLUGIN_LOGARITHMIC) ? "; logarithmic hint" : ""); auto *range = new Fl_Box(25,y+27,715,25); range->copy_label(hint.c_str()); range->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); } + if(!spec.control_count) { + auto *unavailable=new Fl_Box(25,160,730,75,"This adapter exposes no editable parameters. The plugin runs with its default configuration.\nNative plugin editor windows are not available in this host."); + unavailable->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); + } scroll->end(); auto *note = new Fl_Box(15,515,770,30,"Automation overrides static controls. Reset defaults also clears all plugin automation."); note->labelsize(12); struct Reset { const xtc_plugin_details *details; std::array *inputs; unsigned int count; bool used; } reset{&details,&inputs,spec.control_count,false}; auto *defaults = new Fl_Button(20,555,170,30,"Reset defaults"); + if(!spec.control_count)defaults->deactivate(); defaults->callback([](Fl_Widget *, void *data) { auto *r = static_cast(data); r->used = true; for (unsigned int i = 0; i < r->count; ++i) (*r->inputs)[i]->value(r->details->parameters[i].default_value); diff --git a/src/gui/track_inserts.hpp b/src/gui/track_inserts.hpp index 987d924..7ce195a 100644 --- a/src/gui/track_inserts.hpp +++ b/src/gui/track_inserts.hpp @@ -35,6 +35,8 @@ class TrackInsertsPanel : public Fl_Group { auto *button=new Fl_Button(left,y+12,82,28,action.name);left+=90; button->callback([](Fl_Widget *w,void *c){auto *p=static_cast(c);int command=!strcmp(w->label(),"Up") ? -1 : !strcmp(w->label(),"Down") ? 1 : !strcmp(w->label(),"Bypass") ? 2 : 3;p->result(p->edit_slot(command));},this); } + auto *inspect=new Fl_Button(x+850,y+12,120,28,"Inspect / edit"); + inspect->callback([](Fl_Widget *,void *c){auto *p=static_cast(c);p->result(p->inspect_slot());},this); message_=new Fl_Box(x+16,y+50,w-32,32,"Track input > inserts > output gain > track fader > bus/master");message_->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); slots_=new Fl_Hold_Browser(x+16,y+94,230,h-110);slots_->format_char(0); graph_=new GraphWidget(x+260,y+94,w-276,h-110,nullptr,[](void *,const xtc_graph_edit &,size_t *){return XTC_ERR_UNSUPPORTED;},nullptr,message_); @@ -46,6 +48,15 @@ class TrackInsertsPanel : public Fl_Group { void select_track(uint64_t id) {track_=id;refresh(true);} const xtc_graph *graph() const {return model_;} void add(const char *file,unsigned int index) { result(owner_->add_track_effect(track_,file,index)); } + xtc_result inspect_slot() { + xtc_track_chain chain{};auto r=xtc_project_get_chain(owner_->project_.get(),track_,&chain);size_t index=selected_slot(); + if(r!=XTC_OK)return r; + if(index>=chain.count)return XTC_ERR_INVALID_STATE; + xtc_plugin_details details{};xtc_plugin_spec edited{}; + r=xtc_plugin_describe(&chain.slots[index].plugin,owner_->sample_rate(),&details);if(r!=XTC_OK)return r; + if(!xtc_edit_plugin(chain.slots[index].plugin,details,edited))return XTC_OK; + chain.slots[index].plugin=edited;return owner_->set_track_chain(track_,chain); + } /* Commands: -1 up, 1 down, 2 toggle bypass, 3 remove. */ xtc_result edit_slot(int command) { xtc_track_chain chain{};auto r=xtc_project_get_chain(owner_->project_.get(),track_,&chain);size_t index=selected_slot(); diff --git a/tests/integration/test_gui.cpp b/tests/integration/test_gui.cpp index b9de45a..8a5f6f3 100644 --- a/tests/integration/test_gui.cpp +++ b/tests/integration/test_gui.cpp @@ -242,6 +242,13 @@ int track_insert_tests() CHECK(panel->edit_slot(3)==XTC_OK && window.undo()==XTC_OK);panel->refresh(true); CHECK(xtc_project_get_chain(s->project(),track,&chain)==XTC_OK && chain.count==2); CHECK(panel->edit_slot(3)==XTC_OK); + CHECK(xtc_project_get_chain(s->project(),track,&chain)==XTC_OK);chain.slots[0].plugin.values[0]=10; + CHECK(window.set_track_chain(track,chain)==XTC_OK); + Fl::add_timeout(.05,[](void *){auto *dialog=Fl::modal();CHECK(dialog);press(find(dialog,"Reset defaults"));press(find(dialog,"Apply"));}); + CHECK(panel->inspect_slot()==XTC_OK); + CHECK(xtc_project_get_chain(s->project(),track,&chain)==XTC_OK && chain.slots[0].plugin.values[0]!=10); + const auto *cancel_before=s->project(); + Fl::add_timeout(.05,[](void *){press(find(Fl::modal(),"Cancel"));});CHECK(panel->inspect_slot()==XTC_OK && s->project()==cancel_before); CHECK(window.save_project("/tmp/xtc-inserts-gui.xtc")==XTC_OK && window.new_project()==XTC_OK && window.open_project("/tmp/xtc-inserts-gui.xtc")==XTC_OK); CHECK(xtc_project_get_chain(s->project(),track,&chain)==XTC_OK && chain.count==1);unlink("/tmp/xtc-inserts-gui.xtc"); window.do_callback();puts("Track inserts GUI adoption/undo/rollback/reload passed.");return failures ? 1 : 0;