support channel reordering via dragging header

portability/boost
zetaPRIME 2018-11-29 22:57:36 -05:00
parent e7a8093b8c
commit 14f1f2d783
4 changed files with 54 additions and 7 deletions

33
notes
View File

@ -45,6 +45,11 @@ project data {
TODO {
immediate frontburner {
- allow swapping channels around
double click to rename channel
^ QHeaderView is a QAbstractItemView as of qt5, so can set an item delegate
create new Project and hook that up to the main window instead of just making an empty pattern
( Project::new(), Pattern::new(Project*) )
make and hook up UI for displaying all existing patterns by index, and for displaying the sequence
@ -54,18 +59,12 @@ TODO {
- make pattern editor detect ctrl and alt modifiers
make everything relevant check if editing is locked
- figure out what library to use for messagepack
- figure out what library to use for messagepack (official msgpack-c)
implement saving (probably a FileIO helper class)
hook up new/open/save menu items
}
? de-hardcode the "» " (probably just make it a static const variable somewhere?)
- implement (top) header span and return channel names
double click to rename channel
QHeaderView::setSectionsMovable(bool movable)
QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
^ need to re-reposition the thing?
QHeaderView is a QAbstractItemView as of qt5, so can set an item delegate
pattern background colors (and time signature, duh)
@ -84,6 +83,12 @@ TODO {
use signals/slots to signal project/pattern updates etc.
saving and loading {
msgpack object
probably have a field of raw data for (some) node saves, and keep it resident in case of plugin crashes
}
resampler object {
one used internally for each note
reference to sample
@ -104,6 +109,12 @@ JS example of sine panning, -1.0 to +1.0 {
}
}
plugin registry {
queuing function for static construction
PluginInfo base class, virtual function for creating a node of said plugin
(separate function for loading from save file?)
}
lv2 support: lilv (duh) for actual plugin loading, suil for UI embedding
eventually hook luajit up with TLSF allocator to aid in real time (maybe look at nedmalloc too)
@ -121,6 +132,14 @@ graph+node+port system {
something something ready flags (all input nodes and containing graph; main graph and command ports always have the bit set)
graphs are also nodes
can use locks on processing node-ports since they'll basically never be contended; this allows for editing during playback
^ or just double-buffer the connections
UI thread takes full responsibility for both replacement and eventual collection; atomic bool marking is enough threadsafety to deflect the collection timer
have to signal playback thread that queues have been invalidated
...or just use locks and a flag since it's probably not worth the added complexity of implementing lockfree to avoid hangups when *fiddling with connections*
though we do want a queue system for plugin parameter changes! don't want xruns from fiddling with instruments or mixing
(most built-in gadgets can avoid this by nature of aligned power-of-two types (<=8bytes) inherently atomic on modern CPUs)
}
keybinds {

View File

@ -36,6 +36,7 @@ MainWindow::MainWindow(QWidget *parent) :
pattern->channels[0].rows[0].addParam('v', 255);
pattern->channels[1].rows[0].port = 1;
pattern->channels[1].rows[0].note = 0;
pattern->channels[5].name = "I have a name";
auto pe = t->findChild<Xybrid::UI::PatternEditorView*>("patternEditor");

View File

@ -26,9 +26,13 @@ PatternEditorView::PatternEditorView(QWidget *parent) : QTableView(parent) {
hdr->setSectionResizeMode(QHeaderView::Fixed);//ResizeToContents);
hdr->setTextElideMode(Qt::ElideMiddle);
hdr->setStretchLastSection(true);
hdr->setSectionsMovable(true);
hdr->setFirstSectionMovable(true);
// hook up scrolling to update header position
connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &PatternEditorView::updateHeaderOffset);
// and header swap
connect(hdr.get(), &QHeaderView::sectionMoved, this, &PatternEditorView::headerMoved);
horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
@ -99,3 +103,25 @@ void PatternEditorView::updateHeader(bool full) {
void PatternEditorView::updateHeaderOffset(int) {
updateHeader(false);
}
void PatternEditorView::headerMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) {
if (logicalIndex == newVisualIndex) return; // assume moving back
hdr->moveSection(newVisualIndex, logicalIndex); // maintain straight-through order
if (logicalIndex >= hdr->count() - 1) return; // no dragging the endcap :|
int min = std::min(oldVisualIndex, newVisualIndex);
int max = std::max(oldVisualIndex, newVisualIndex);
int nf = 0;
if (newVisualIndex > oldVisualIndex) nf = min + 1;
else nf = max;
auto& chn = mdl->getPattern().channels;
std::rotate(chn.begin() + min, chn.begin() + nf, chn.begin() + max + 1);
this->dataChanged( // update everything
mdl->index(0, 0, QModelIndex()),
mdl->index(mdl->rowCount() - 1, mdl->columnCount() - 1, QModelIndex())
);
mdl->updateColumnDisplay(); // update geometries and hide flags
// and make sure header follows
emit hdr->model()->headerDataChanged(Qt::Horizontal, 0, static_cast<int>(chn.size()) - 1);
}

View File

@ -31,6 +31,7 @@ namespace Xybrid::UI {
void updateGeometries() override;
void updateHeader(bool full = false);
void updateHeaderOffset(int);
void headerMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
void keyboardSearch(const QString&) override {} // disable accidental search