xtc/src/gui/JUCE/sequencer_track_widget.cpp

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);
}