feat(gui): reorder, bypass and remove track inserts (phase 85)

This commit is contained in:
Johannes Findeisen 2026-11-06 01:38:55 +01:00
commit ccb1d64b9f
4 changed files with 40 additions and 4 deletions

View file

@ -1955,12 +1955,12 @@ Implemented: case-insensitive path/name search and All/LADSPA/VST2/VST3/CLAP fil
Expose move up/down, remove and bypass with one undo step per action and safe prepared-plan adoption. Expose move up/down, remove and bypass with one undo step per action and safe prepared-plan adoption.
### Tasks and acceptance criteria ### Tasks and acceptance criteria
- [ ] Implement the described workflow with validated errors and safe ownership. - [x] Implement the described workflow with validated errors and safe ownership.
- [ ] Pass relevant core and/or GUI tests. - [x] Pass relevant core and/or GUI tests.
- [ ] Document behavior and limitations; commit this phase separately. - [x] Document behavior and limitations; commit this phase separately.
### Implementation status ### Implementation status
Planned. Implemented: selected insert Up/Down, bypass and removal, preserving effect metadata and selection on reorder. Every successful edit prepares a candidate and records one undo step. Focused GUI checks cover boundary rejection, order, bypass, removal/undo and saved chain reload; warning-enabled build passed.
--- ---

View file

@ -39,3 +39,8 @@ The library searches configured Preferences paths recursively (including VST3
bundle contents), with case-insensitive search and format filters. LADSPA and bundle contents), with case-insensitive search and format filters. LADSPA and
VST2 both use `.so`, so those filters share candidates until inspection identifies VST2 both use `.so`, so those filters share candidates until inspection identifies
the ABI. Listing files executes no plugin code. Only inspect trusted binaries. the ABI. Listing files executes no plugin code. Only inspect trusted binaries.
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.

View file

@ -29,6 +29,12 @@ class TrackInsertsPanel : public Fl_Group {
add->add("Internal Delay",0,[](Fl_Widget *,void *c){static_cast<TrackInsertsPanel *>(c)->add(XTC_BUILTIN_PATH,2);},this); add->add("Internal Delay",0,[](Fl_Widget *,void *c){static_cast<TrackInsertsPanel *>(c)->add(XTC_BUILTIN_PATH,2);},this);
add->add("Internal Reverb",0,[](Fl_Widget *,void *c){static_cast<TrackInsertsPanel *>(c)->add(XTC_BUILTIN_PATH,3);},this); add->add("Internal Reverb",0,[](Fl_Widget *,void *c){static_cast<TrackInsertsPanel *>(c)->add(XTC_BUILTIN_PATH,3);},this);
add->add("Plugin library...",0,[](Fl_Widget *,void *c){auto *p=static_cast<TrackInsertsPanel *>(c);std::string file;if(!xtc_choose_plugin_file(p->owner_->settings(),file))return;unsigned int index=0;if(xtc_choose_plugin(file.c_str(),p->owner_->sample_rate(),index))p->add(file.c_str(),index);},this); add->add("Plugin library...",0,[](Fl_Widget *,void *c){auto *p=static_cast<TrackInsertsPanel *>(c);std::string file;if(!xtc_choose_plugin_file(p->owner_->settings(),file))return;unsigned int index=0;if(xtc_choose_plugin(file.c_str(),p->owner_->sample_rate(),index))p->add(file.c_str(),index);},this);
struct Action {const char *name;int command;};
int left=x+486;
for(const auto &action:{Action{"Up",-1},Action{"Down",1},Action{"Bypass",2},Action{"Remove",3}}) {
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<TrackInsertsPanel *>(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);
}
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); 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); 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_); 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_);
@ -40,6 +46,22 @@ class TrackInsertsPanel : public Fl_Group {
void select_track(uint64_t id) {track_=id;refresh(true);} void select_track(uint64_t id) {track_=id;refresh(true);}
const xtc_graph *graph() const {return model_;} const xtc_graph *graph() const {return model_;}
void add(const char *file,unsigned int index) { result(owner_->add_track_effect(track_,file,index)); } void add(const char *file,unsigned int index) { result(owner_->add_track_effect(track_,file,index)); }
/* 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();
if(r!=XTC_OK)return r;
if(index>=chain.count)return XTC_ERR_INVALID_STATE;
size_t selected=index;
if(command==-1 || command==1) {
if((command==-1 && !index) || (command==1 && index+1>=chain.count))return XTC_ERR_INVALID_ARGUMENT;
selected=command==-1 ? index-1 : index+1;std::swap(chain.slots[index],chain.slots[selected]);
}else if(command==2)chain.slots[index].enabled=!chain.slots[index].enabled;
else if(command==3) {for(size_t i=index+1;i<chain.count;++i)chain.slots[i-1]=chain.slots[i];chain.slots[--chain.count]={};}
else return XTC_ERR_INVALID_ARGUMENT;
r=owner_->set_track_chain(track_,chain);
if(r==XTC_OK && chain.count)slots_->select(static_cast<int>(std::min(selected,chain.count-1)+1));
return r;
}
void refresh(bool force=false) { void refresh(bool force=false) {
const xtc_project *project=owner_->project_.get();size_t count=0,clips=0;xtc_project_get_counts(project,&count,&clips); const xtc_project *project=owner_->project_.get();size_t count=0,clips=0;xtc_project_get_counts(project,&count,&clips);
std::vector<uint64_t> ids;std::vector<std::string> names; std::vector<uint64_t> ids;std::vector<std::string> names;

View file

@ -233,6 +233,15 @@ int track_insert_tests()
auto *before=s->project();submit_result=XTC_ERR_INVALID_STATE; auto *before=s->project();submit_result=XTC_ERR_INVALID_STATE;
CHECK(window.add_track_effect(track,XTC_BUILTIN_PATH,3)==XTC_ERR_INVALID_STATE && s->project()==before);submit_result=XTC_OK; CHECK(window.add_track_effect(track,XTC_BUILTIN_PATH,3)==XTC_ERR_INVALID_STATE && s->project()==before);submit_result=XTC_OK;
CHECK(window.add_track_effect(track,"/missing/plugin.so",0)!=XTC_OK && s->project()==before); CHECK(window.add_track_effect(track,"/missing/plugin.so",0)!=XTC_OK && s->project()==before);
CHECK(window.add_track_effect(track,XTC_BUILTIN_PATH,3)==XTC_OK);
CHECK(panel->edit_slot(-1)==XTC_ERR_INVALID_ARGUMENT);
CHECK(panel->edit_slot(1)==XTC_OK && panel->selected_slot()==1);
CHECK(xtc_project_get_chain(s->project(),track,&chain)==XTC_OK && chain.slots[1].plugin.index==2);
CHECK(panel->edit_slot(2)==XTC_OK);
CHECK(xtc_project_get_chain(s->project(),track,&chain)==XTC_OK && !chain.slots[1].enabled);
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(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(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"); 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; window.do_callback();puts("Track inserts GUI adoption/undo/rollback/reload passed.");return failures ? 1 : 0;