From 9d45d7879582baecfc9b89df58373c96d4e431ea Mon Sep 17 00:00:00 2001 From: zetaPRIME Date: Fri, 12 Jul 2019 03:19:56 -0400 Subject: [PATCH] middle click to remove sequence entry --- notes | 14 ++++---------- xybrid/audio/audioengine.cpp | 2 +- xybrid/editing/projectcommands.cpp | 12 ++++++------ xybrid/mainwindow.cpp | 21 +++++++++++++++++++-- xybrid/util/lambdaeventfilter.h | 3 ++- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/notes b/notes index f15eff4..0b6fecf 100644 --- a/notes +++ b/notes @@ -32,8 +32,9 @@ parameters { TODO { immediate frontburner { - - add standardized step values for knobs and set a default (int enum) - global pan (PXX) for InstrumentCore? + spacer rows on top/bottom of pattern editor (keep centered) + + global pan (PXX) for InstrumentCore? *default* pan - node function to release unneeded old data when stopping playback ... @@ -47,19 +48,12 @@ TODO { bugs to fix { - -? it sometimes crashes on exit?? - - ... does graph really need to call reset() on its children at this point + playback after stopping immediately after a note in the first pattern played sometimes skips that note } misc features needed before proper release { - - song metadata (title, artist, comment, default bpm) - at *least* js plugin support, with lua+lv2 highly preferable - - SAMPLES and SAMPLING - - gadget widgets (w/container) - at least a knob with nice range and such - different context menu for multiple selected nodes / pack/unpack selection to/from subgraph (partial; can copy/paste a selection) diff --git a/xybrid/audio/audioengine.cpp b/xybrid/audio/audioengine.cpp index 8105bec..0b1ac6e 100644 --- a/xybrid/audio/audioengine.cpp +++ b/xybrid/audio/audioengine.cpp @@ -362,7 +362,7 @@ Pattern* AudioEngine::findPattern(int adv) { if (mode == Rendering && !s) return nullptr; // stop else if (mode == Rendering && s->type == SequenceEntry::LoopTrigger) { seqPos++; continue; } - else if (!s || s->type == SequenceEntry::LoopTrigger) { // off end or explicit loop, find loop point + else if (!s || (s->type == SequenceEntry::LoopTrigger && seqPos > 0)) { // off end or explicit loop, find loop point for (seqPos = std::min(seqPos, static_cast(project->sequence.size()) - 1); seqPos >= 0; --seqPos) if (project->sequence[static_cast(seqPos)].type == SequenceEntry::LoopStart) break; continue; diff --git a/xybrid/editing/projectcommands.cpp b/xybrid/editing/projectcommands.cpp index 4b0aef9..f238a38 100644 --- a/xybrid/editing/projectcommands.cpp +++ b/xybrid/editing/projectcommands.cpp @@ -20,8 +20,8 @@ bool ProjectCommand::cancel() { return false; } -ProjectSequencerDeltaCommand::ProjectSequencerDeltaCommand(const std::shared_ptr& project) { - this->project = project; +ProjectSequencerDeltaCommand::ProjectSequencerDeltaCommand(const std::shared_ptr& project_) { + project = project_; oldSeq = project->sequence; oldSeqSel = project->socket->window->sequenceSelection(); seq = oldSeq; @@ -50,8 +50,8 @@ void ProjectSequencerDeltaCommand::undo() { project->socket->window->sequenceSelection(oldSeqSel); } -ProjectPatternMoveCommand::ProjectPatternMoveCommand(const std::shared_ptr& project, int f, int t) { - this->project = project; +ProjectPatternMoveCommand::ProjectPatternMoveCommand(const std::shared_ptr& project_, int f, int t) { + project = project_; from = f; to = t; setText("move pattern"); @@ -85,8 +85,8 @@ void ProjectPatternMoveCommand::undo() { if (move) project->socket->window->patternSelection(from); } -ProjectPatternAddCommand::ProjectPatternAddCommand(const std::shared_ptr& project, int at, int atSeq, const std::shared_ptr& copyOf) { - this->project = project; +ProjectPatternAddCommand::ProjectPatternAddCommand(const std::shared_ptr& project_, int at, int atSeq, const std::shared_ptr& copyOf) { + project = project_; if (at < 0) at = std::numeric_limits::max(); this->at = std::max(0, std::min(at, static_cast(project->patterns.size()))); this->atSeq = atSeq; diff --git a/xybrid/mainwindow.cpp b/xybrid/mainwindow.cpp index 6854f46..458605c 100644 --- a/xybrid/mainwindow.cpp +++ b/xybrid/mainwindow.cpp @@ -207,6 +207,23 @@ MainWindow::MainWindow(QWidget *parent) : this->selectPatternForEditing(project->sequence[idx].pattern().get()); }); + // middle click + auto mouselmb = [this, seq = ui->patternSequencer](QObject*, QEvent* e) { + if (e->type() == QEvent::MouseButtonRelease) { + auto me = static_cast(e); + if (me->button() == Qt::MouseButton::MiddleButton) { + auto idx = static_cast(seq->indexAt(me->pos()).column()); + if (idx >= project->sequence.size()) return false; // nothing to remove + auto* c = new ProjectSequencerDeltaCommand(project); + c->seq.erase(c->seq.begin() + static_cast(idx)); + c->seqSel = static_cast(idx)-1; + return c->commit(); + } + } + return false; + }; + ui->patternSequencer->viewport()->installEventFilter(new LambdaEventFilter(this, mouselmb)); + // rightclick menu connect(ui->patternSequencer, &QTableView::customContextMenuRequested, this, [this](const QPoint& pt) { size_t idx = static_cast(ui->patternSequencer->indexAt(pt).column()); @@ -223,7 +240,7 @@ MainWindow::MainWindow(QWidget *parent) : menu->addAction("Insert Separator", this, [this, idx] { int si = static_cast(std::min(idx, project->sequence.size())); auto* c = new ProjectSequencerDeltaCommand(project); - c->seq.insert(c->seq.begin() + si, { }); + c->seq.insert(c->seq.begin() + si, SequenceEntry::Separator); c->seqSel = si+1; c->commit(); }); @@ -263,7 +280,7 @@ MainWindow::MainWindow(QWidget *parent) : menu->setAttribute(Qt::WA_DeleteOnClose); menu->popup(ui->patternSequencer->mapToGlobal(pt)); - });//*/ + }); } { /* Set up keyboard shortcuts for pattern view */ } { diff --git a/xybrid/util/lambdaeventfilter.h b/xybrid/util/lambdaeventfilter.h index ede450f..dd146e0 100644 --- a/xybrid/util/lambdaeventfilter.h +++ b/xybrid/util/lambdaeventfilter.h @@ -7,6 +7,7 @@ class LambdaEventFilter : public QObject { Q_OBJECT std::function filter; public: - LambdaEventFilter(QObject* parent, std::function f) : QObject(parent), filter(f) { } + LambdaEventFilter(QObject* parent, const std::function& f) : QObject(parent), filter(f) { } bool eventFilter(QObject* watched, QEvent* event) override { return filter(watched, event); } + static inline LambdaEventFilter* create(QObject* parent, const std::function& f) { return new LambdaEventFilter(parent, f); } };