#include "sequencer_track_widget.hpp" #include "sequencer_widget.hpp" #include "sequencer_clip_widget.hpp" #include "sequencer_velocity_widget.hpp" #include "sequencer_group_scope.hpp" #include "theme.hpp" #include "ui.hpp" #include #include SequencerTrackWidget::SequencerTrackWidget(SequencerWidget &owner, const xtc_track_info &track) : UiGroup(owner.x(),owner.y()+28,owner.w(),56), owner_(owner), info_(track) { resizable(nullptr); clips_.reserve(XTC_PROJECT_MAX_CLIPS); velocity_=new SequencerVelocityWidget(owner,track.id); header_=new SequencerTrackHeader(*this); end(); } UiChoice *SequencerTrackWidget::instrument_choice() const { return header_->instrument_choice(); } UiGroup *SequencerTrackWidget::header() const { return header_; } SequencerClipWidget *SequencerTrackWidget::clip_widget(uint64_t clip) const { for(auto *view:clips_) if(view->clip_id()==clip) return view; return nullptr; } void SequencerTrackWidget::sync(size_t row, const xtc_track_info &track) { row_=row; info_=track; resize(owner_.x(),owner_.track_top(row),owner_.w(),owner_.track_height(row)); header_->resize(owner_.header_left(),y(),owner_.header_width_,h()); instrument_choice()->resize(header_->x()+10,y()+30,header_->w()-22,20); if(info_.is_bus) instrument_choice()->hide(); velocity_->resize(owner_.x()+owner_.header_width_,y()+56,owner_.w()-owner_.header_width_,88); if(owner_.track_expanded(info_.id)) velocity_->show(); else velocity_->hide(); for(auto it=clips_.begin();it!=clips_.end();) { xtc_clip_info c{}; if(xtc_project_find_clip(owner_.project_,(*it)->clip_id(),&c)!=XTC_OK || c.track_id!=info_.id) { auto *removed=*it; it=clips_.erase(it); removed->hide(); remove(removed); Ui::delete_widget(removed); } else ++it; } size_t tracks=0,clips=0; xtc_project_get_counts(owner_.project_,&tracks,&clips); SequencerGroupScope scope(this); for(size_t i=0;isync(c,y()); // Match dense model order for overlapping clip drawing and hit testing. insert(*view,header_); } } void SequencerTrackWidget::draw() { ui_push_clip(x(),y(),w(),h()); draw_children(); ui_pop_clip(); } int SequencerTrackWidget::handle(int event) { if(event==UI_PUSH) { if(Ui::event_inside(header_)) return header_->handle(event); if(velocity_->visible() && Ui::event_inside(velocity_)) return velocity_->handle(event); // Preserve the full-row clip hit area, including its small vertical margins. for(int i=children()-1;i>=0;--i) { auto *view=dynamic_cast(child(i)); if(view && Ui::event_x()>=view->x() && Ui::event_x()x()+view->w()) return view->handle(event); } owner_.take_focus(); owner_.track_=info_.id; owner_.clip_=UINT64_MAX; owner_.selection_.clear(); owner_.mode_=0; if(Ui::event_button()==UI_RIGHT_MOUSE) owner_.context_menu(); owner_.redraw(); return 1; } if(event==UI_DRAG || event==UI_RELEASE) return owner_.handle(event); return UiGroup::handle(event); } SequencerTrackHeader::SequencerTrackHeader(SequencerTrackWidget &track) : UiGroup(track.x(),track.y(),180,56), track_(track) { resizable(nullptr); instrument_=new UiChoice(x()+10,y()+30,w()-22,20); xtc_ui::style(instrument_); instrument_->textsize(10); instrument_->textcolor(xtc_ui::text()); instrument_->tooltip("Track instrument: built-in sine, an instrument used in this project, or Load plugin..."); instrument_->callback(SequencerWidget::instrument_callback,&track.owner_); end(); } void SequencerTrackHeader::draw() { auto &owner=track_.owner_; const auto &t=track_.info_; ui_push_clip(x(),y(),w(),h()); ui_color(t.id==owner.track_ ? ui_rgb_color(49,62,70) : xtc_ui::panel()); ui_rectf(x(),y(),w()-2,h()-1); ui_color(xtc_ui::track(t.id)); ui_rectf(x(),y(),3,h()-1); ui_push_clip(x()+9,y(),w()-18,55); ui_color(xtc_ui::text()); ui_font(UI_HELVETICA_BOLD,12); if(owner.track_expanded(t.id)) ui_polygon(x()+10,y()+10,x()+20,y()+10,x()+15,y()+16); else ui_polygon(x()+12,y()+8,x()+12,y()+18,x()+18,y()+13); ui_draw(t.name,x()+26,y()+18); if(t.is_bus) { char detail[64]; std::snprintf(detail,sizeof(detail),"%02zu BUS%s%s",track_.row_+1,t.mute ? " MUTE" : "",t.solo ? " SOLO" : ""); ui_font(UI_HELVETICA,10); ui_color(xtc_ui::muted()); ui_draw(detail,x()+12,y()+42); } if(t.mute || t.solo) { ui_font(UI_HELVETICA,10); ui_color(xtc_ui::muted()); ui_draw(t.mute ? "M" : "S",x()+w()-16,y()+18); } ui_pop_clip(); if(owner.track_expanded(t.id)) { ui_color(xtc_ui::muted()); ui_font(UI_HELVETICA,11); bool audio=track_.velocity_->audio(); ui_draw(audio ? "AUDIO VELOCITY" : "VELOCITY 1..127",x()+12,y()+76); ui_font(UI_HELVETICA,10); ui_draw(audio ? "Level envelope 0..400%" : "Drag bars to edit notes",x()+12,y()+94); char value[32]; if(track_.velocity_->preview_label(value,sizeof(value))) ui_draw(value,x()+12,y()+114); } draw_children(); ui_pop_clip(); } int SequencerTrackHeader::handle(int event) { auto &owner=track_.owner_; if(event==UI_PUSH) { if(instrument_->visible() && Ui::event_inside(instrument_)) return UiGroup::handle(event); owner.take_focus(); if(Ui::event_button()==UI_LEFT_MOUSE && Ui::event_x()>=x()+7 && Ui::event_x()=y()+56) return 1; owner.track_=track_.info_.id; owner.clip_=UINT64_MAX; owner.selection_.clear(); owner.mode_=0; if(Ui::event_button()==UI_RIGHT_MOUSE) owner.context_menu(); owner.redraw(); return 1; } return UiGroup::handle(event); }