146 lines
6.2 KiB
C++
146 lines
6.2 KiB
C++
#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 <algorithm>
|
|
#include <cstdio>
|
|
|
|
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;i<clips;++i) {
|
|
xtc_clip_info c{}; xtc_project_get_clip(owner_.project_,i,&c);
|
|
if(c.track_id!=info_.id) continue;
|
|
auto *view=clip_widget(c.id);
|
|
if(!view) {
|
|
view=new SequencerClipWidget(owner_,c);
|
|
clips_.push_back(view); // Capacity reserved before creating child widgets.
|
|
}
|
|
view->sync(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<SequencerClipWidget *>(child(i));
|
|
if(view && Ui::event_x()>=view->x() && Ui::event_x()<view->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()<x()+24 && Ui::event_y()<y()+26) {
|
|
owner.expand_track(track_.info_.id,!owner.track_expanded(track_.info_.id)); return 1;
|
|
}
|
|
// The velocity label area does not change clip selection.
|
|
if(Ui::event_y()>=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);
|
|
}
|