proper multiselect delete

portability/boost
zetaPRIME 2018-12-07 16:13:10 -05:00
parent 8238a15771
commit d107547e1b
9 changed files with 150 additions and 35 deletions

8
notes
View File

@ -50,7 +50,7 @@ TODO {
undo {
- single-row pattern edits
? multi-select edits
- multi-select edits
- channel move
- channel rename
@ -63,11 +63,13 @@ TODO {
- pattern delete (w/ resulting sequence edit)
- generic sequencer edit
make selection follow pattern move where applicable
}
# fix how qt5.12 broke header text (removed elide for now)
/ multiselect editing (at least delete) [ delete partially implemented, pending undo functionality ]
- multiselect editing (at least delete) [ delete done ]
pattern editing - spacebar for strut (param placeholder)
add metadata and pattern properties (artist, song title, project bpm; pattern name, length etc.)
@ -77,6 +79,8 @@ TODO {
at some point {
pattern cut+copy+paste
pattern editor cells can have (dynamic) tool tips; set this up with port names, etc.
? de-hardcode the "» " (probably just make it a static const variable somewhere?)
make everything relevant check if editing is locked

View File

@ -0,0 +1,54 @@
#include "compositecommand.h"
#include "data/project.h"
#include "uisocket.h"
#include "mainwindow.h"
#include "editing/projectcommands.h"
#include "editing/patterncommands.h"
using Xybrid::Data::Project;
using Xybrid::Data::Pattern;
using namespace Xybrid::Editing;
CompositeCommand::CompositeCommand(std::shared_ptr<Project> p) {
project = p;
}
CompositeCommand* CompositeCommand::compose(ProjectCommand* c) {
if (!project) {
project = c->project;
} else if (c->project != project) return this; // fail
if (!children.empty() && children.back()->mergeWith(c)) delete c;
else children.emplace_back(c);
return this;
}
CompositeCommand* CompositeCommand::compose(PatternCommand* c) {
if (!project) {
project = c->pattern->project->socket->window->getProject();
} else if (c->pattern->project != project.get()) return this; // fail
if (!children.empty() && children.back()->mergeWith(c)) delete c;
else children.emplace_back(c);
return this;
}
bool CompositeCommand::commit(QString name) {
if (!project->socket || !project->socket->undoStack) return cancel();
if (!name.isEmpty()) setText(name);
project->socket->undoStack->push(this);
return true;
}
bool CompositeCommand::cancel() {
delete this;
return false;
}
void CompositeCommand::redo() {
for (auto i = children.begin(); i != children.end(); i++) (*i)->redo();
}
void CompositeCommand::undo() {
for (auto i = children.rbegin(); i != children.rend(); i++) (*i)->undo();
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <memory>
#include <vector>
#include <QUndoCommand>
namespace Xybrid::Data {
class Project;
class Pattern;
}
namespace Xybrid::Editing {
class ProjectCommand;
class PatternCommand;
class CompositeCommand : public QUndoCommand {
std::shared_ptr<Data::Project> project;
std::vector<std::unique_ptr<QUndoCommand>> children;
public:
CompositeCommand() = default;
CompositeCommand(std::shared_ptr<Data::Project>);
~CompositeCommand() override = default;
CompositeCommand* compose(ProjectCommand*);
CompositeCommand* compose(PatternCommand*);
bool commit(QString name = QString());
bool cancel();
void redo() override;
void undo() override;
};
}

View File

@ -11,7 +11,7 @@ using namespace Xybrid::Editing;
bool PatternCommand::commit() {
if (!pattern->valid()) return false;
if (!pattern->project->socket || !pattern->project->socket->undoStack) return false;
if (!pattern->project->socket || !pattern->project->socket->undoStack) return cancel();
pattern->project->socket->undoStack->push(this);
return true;
}

View File

@ -5,8 +5,10 @@
#include <QUndoCommand>
namespace Xybrid::Editing {
class CompositeCommand;
class PatternCommand : public QUndoCommand {
//
friend class CompositeCommand;
protected:
std::shared_ptr<Data::Pattern> pattern;

View File

@ -11,7 +11,7 @@ using Xybrid::Data::Pattern;
using namespace Xybrid::Editing;
bool ProjectCommand::commit() {
if (!project->socket || !project->socket->undoStack) return false;
if (!project->socket || !project->socket->undoStack) return cancel();
project->socket->undoStack->push(this);
return true;
}

View File

@ -5,8 +5,10 @@
#include <QUndoCommand>
namespace Xybrid::Editing {
class CompositeCommand;
class ProjectCommand : public QUndoCommand {
//
friend class CompositeCommand;
protected:
std::shared_ptr<Data::Project> project;

View File

@ -9,6 +9,7 @@ using Xybrid::UI::PatternEditorView;
#include "ui/patterneditormodel.h"
using Xybrid::UI::PatternEditorModel;
#include "editing/compositecommand.h"
#include "editing/patterncommands.h"
using namespace Xybrid::Editing;
@ -36,6 +37,33 @@ namespace {
if (static_cast<int>(val) == -1) val = 0;
val = static_cast<T>((static_cast<size_t>(val) & 15) * 16 + (hex & 15));
}
struct SelectionBounds {
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
SelectionBounds(const QModelIndexList& sel) {
x1 = std::numeric_limits<int>::max();
y1 = std::numeric_limits<int>::max();
for (auto s : sel) {
x1 = std::min(x1, s.column());
y1 = std::min(y1, s.row());
x2 = std::max(x2, s.column());
y2 = std::max(y2, s.row());
}
}
[[maybe_unused]] bool portSelected(int c) {
int cx = c * PatternEditorModel::colsPerChannel;
return (cx >= x1 && cx <= x2);
}
[[maybe_unused]] bool noteSelected(int c) {
int cx = (c * PatternEditorModel::colsPerChannel) + 1;
return (cx >= x1 && cx <= x2);
}
[[maybe_unused]] bool paramSelected(int c, int p) {
int cx = (c * PatternEditorModel::colsPerChannel) + 2 + (p*2);
return (cx+1 >= x1 && cx <= x2);
}
};
}
void PatternEditorItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
@ -114,37 +142,26 @@ bool PatternEditorItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *m
} else {
if (sel.size() > 1) {
if (k == Qt::Key_Delete) {
int x1 = std::numeric_limits<int>::max(), y1 = std::numeric_limits<int>::max(), x2 = 0, y2 = 0;
for (auto s : sel) {
x1 = std::min(x1, s.column());
y1 = std::min(y1, s.row());
x2 = std::max(x2, s.column());
y2 = std::max(y2, s.row());
}
int chMin = (x1 - (x1 % PatternEditorModel::colsPerChannel)) / PatternEditorModel::colsPerChannel;
int chMax = (x2 - (x2 % PatternEditorModel::colsPerChannel)) / PatternEditorModel::colsPerChannel;
//qDebug() << "channels " << chMin << " to " << chMax << ", rows " << y1 << " to " << y2;
dc->cancel();
auto* cc = new CompositeCommand();
SelectionBounds s(sel);
int chMin = (s.x1 - (s.x1 % PatternEditorModel::colsPerChannel)) / PatternEditorModel::colsPerChannel;
int chMax = (s.x2 - (s.x2 % PatternEditorModel::colsPerChannel)) / PatternEditorModel::colsPerChannel;
// TODO: make this only delete the relevant columns
for (int c = chMin; c <= chMax; c++) {
for (int r = y1; r <= y2; r++) {
p->rowAt(c, r).port = -1;
p->rowAt(c, r).note = -1;
p->rowAt(c, r).params.reset();
for (int r = s.y1; r <= s.y2; r++) {
auto* dc = new PatternDeltaCommand(p, c, r);
if (s.portSelected(c)) dc->row.port = -1;
if (s.noteSelected(c)) dc->row.note = -1;
for (int i = static_cast<int>(dc->row.numParams()) - 1; i >= 0; i--) {
if (s.paramSelected(c, i)) dc->row.removeParam(static_cast<size_t>(i));
}
cc->compose(dc);
}
}
m->refresh();
/*for (auto i = sel.rbegin(); i != sel.rend(); ++i) {
int cc = index.column() % PatternEditorModel::colsPerChannel;
int ch = (index.column() - cc) / PatternEditorModel::colsPerChannel;
auto& row = p->rowAt(ch, index.row());
//editorEvent(event, model, option, *i);
}
for (auto s : sel) sm->select(s, QItemSelectionModel::Select);*/
delete dc;
return true;
return cc->commit("delete selection");
}
// for all other commands, reset selection to cursor and defer
@ -242,7 +259,7 @@ bool PatternEditorItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *m
}
}
// kill command if unused
delete dc;
dc->cancel();
}
return false;
}//*/

View File

@ -40,7 +40,8 @@ SOURCES += \
config/colorscheme.cpp \
fileops.cpp \
editing/patterncommands.cpp \
editing/projectcommands.cpp
editing/projectcommands.cpp \
editing/compositecommand.cpp
HEADERS += \
mainwindow.h \
@ -57,7 +58,8 @@ HEADERS += \
config/colorscheme.h \
fileops.h \
editing/patterncommands.h \
editing/projectcommands.h
editing/projectcommands.h \
editing/compositecommand.h
FORMS += \
mainwindow.ui