Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: PianoRoll Razor #5845

Merged
merged 14 commits into from
Feb 26, 2021
Binary file added data/themes/default/razor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions include/Pattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class LMMS_EXPORT Pattern : public TrackContentObject
Note * addStepNote( int step );
void setStep( int step, bool enabled );

// Split the list of notes on the given position
void splitNotes(NoteVector notes, TimePos pos);

// pattern-type stuff
inline PatternTypes type() const
{
Expand Down
14 changes: 13 additions & 1 deletion include/PianoRoll.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class PianoRoll : public QWidget
ModeErase,
ModeSelect,
ModeEditDetuning,
ModeEditRazor
};

/*! \brief Resets settings to default when e.g. creating a new project */
Expand Down Expand Up @@ -226,7 +227,8 @@ protected slots:
ActionResizeNote,
ActionSelectNotes,
ActionChangeNoteProperty,
ActionResizeNoteEditArea
ActionResizeNoteEditArea,
ActionRazor
};

enum NoteEditMode
Expand Down Expand Up @@ -282,6 +284,9 @@ protected slots:
void playChordNotes(int key, int velocity=-1);
void pauseChordNotes(int key);

void setRazorAction();
void cancelRazorAction();

void updateScrollbars();
void updatePositionLineHeight();

Expand All @@ -304,6 +309,7 @@ protected slots:
static QPixmap * s_toolSelect;
static QPixmap * s_toolMove;
static QPixmap * s_toolOpen;
static QPixmap* s_toolRazor;

static PianoRollKeyTypes prKeyOrder[];

Expand Down Expand Up @@ -389,8 +395,10 @@ protected slots:

EditModes m_editMode;
EditModes m_ctrlMode; // mode they were in before they hit ctrl
EditModes m_razorMode; // mode they where in before entering razor

bool m_mouseDownRight; //true if right click is being held down
bool m_firstRazorSplit; // if it's allowed to cancel razor action on SHIFT release event.

TimeLineWidget * m_timeLine;
bool m_scrollBack;
Expand All @@ -408,6 +416,10 @@ protected slots:
// did we start a mouseclick with shift pressed
bool m_startedWithShift;

// Variable that holds the position in ticks for the razor action
int m_razorTickPos;
void updateRazorPos(QMouseEvent* me);

friend class PianoRollWindow;

StepRecorderWidget m_stepRecorderWidget;
Expand Down
172 changes: 167 additions & 5 deletions src/gui/editors/PianoRoll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ QPixmap * PianoRoll::s_toolErase = NULL;
QPixmap * PianoRoll::s_toolSelect = NULL;
QPixmap * PianoRoll::s_toolMove = NULL;
QPixmap * PianoRoll::s_toolOpen = NULL;
QPixmap* PianoRoll::s_toolRazor = nullptr;

TextFloat * PianoRoll::s_textFloat = NULL;

Expand Down Expand Up @@ -184,6 +185,7 @@ PianoRoll::PianoRoll() :
m_editMode( ModeDraw ),
m_ctrlMode( ModeDraw ),
m_mouseDownRight( false ),
m_firstRazorSplit(false),
m_scrollBack( false ),
m_stepRecorderWidget(this, DEFAULT_PR_PPB, PR_TOP_MARGIN, PR_BOTTOM_MARGIN + m_notesEditHeight, WHITE_KEY_WIDTH, 0),
m_stepRecorder(*this, m_stepRecorderWidget),
Expand Down Expand Up @@ -271,6 +273,10 @@ PianoRoll::PianoRoll() :
{
s_toolOpen = new QPixmap( embed::getIconPixmap( "automation" ) );
}
if (s_toolRazor == nullptr)
{
s_toolRazor = new QPixmap(embed::getIconPixmap("razor"));
}

// init text-float
if( s_textFloat == NULL )
Expand Down Expand Up @@ -1268,8 +1274,16 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke)
break;

case Qt::Key_Escape:
// Same as Ctrl + Shift + A
clearSelectedNotes();
// On the Razor mode, ESC cancels it
if (m_editMode == ModeEditRazor)
{
cancelRazorAction();
}
else
{
// Same as Ctrl + Shift + A
clearSelectedNotes();
}
break;

case Qt::Key_Backspace:
Expand Down Expand Up @@ -1314,6 +1328,12 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke)
}

case Qt::Key_Control:
// Ctrl will not enter selection mode if we are
// in Razor mode, but unquantize it
if (m_editMode == ModeEditRazor)
{
break;
}
// Enter selection mode if:
// -> this window is active
// -> shift is not pressed
Expand Down Expand Up @@ -1353,11 +1373,21 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke )
switch( ke->key() )
{
case Qt::Key_Control:
if (m_editMode == ModeEditRazor)
{
break;
}
computeSelectedNotes( ke->modifiers() & Qt::ShiftModifier);
m_editMode = m_ctrlMode;
update();
break;

case Qt::Key_Shift:
if (m_editMode == ModeEditRazor && !m_firstRazorSplit)
{
cancelRazorAction();
}

// update after undo/redo
case Qt::Key_Z:
case Qt::Key_R:
Expand Down Expand Up @@ -1441,6 +1471,35 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
return;
}

// -- Razor
if (m_editMode == ModeEditRazor && me->button() == Qt::LeftButton)
{
NoteVector n;
Note* note = noteUnderMouse();

if (note)
{
n.append(note);

updateRazorPos(me);

// Call splitNotes for the note
m_pattern->splitNotes(n, TimePos(m_razorTickPos));

// Allow cancel razor mode when shift is released (if hold down).
m_firstRazorSplit = false;
}

// Keep in razor mode while SHIFT is hold during cut
if (!(me->modifiers() & Qt::ShiftModifier))
{
cancelRazorAction();
}

update();
return;
}

if( m_editMode == ModeEditDetuning && noteUnderMouse() )
{
static QPointer<AutomationPattern> detuningPattern = nullptr;
Expand Down Expand Up @@ -1946,6 +2005,25 @@ void PianoRoll::pauseChordNotes(int key)
}
}

void PianoRoll::setRazorAction()
{
if (m_editMode != ModeEditRazor)
{
m_firstRazorSplit = true;
m_razorMode = m_editMode;
m_editMode = ModeEditRazor;
m_action = ActionRazor;
setCursor(Qt::ArrowCursor);
update();
}
}

void PianoRoll::cancelRazorAction()
{
m_editMode = m_razorMode;
m_action = ActionNone;
update();
}



Expand Down Expand Up @@ -2047,6 +2125,12 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me )

s_textFloat->hide();

// Quit razor mode if we pressed and released the right mouse button
if (m_editMode == ModeEditRazor && me->button() == Qt::RightButton)
{
cancelRazorAction();
}

if( me->button() & Qt::LeftButton )
{
mustRepaint = true;
Expand Down Expand Up @@ -2105,7 +2189,11 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me )
}

m_currentNote = NULL;
m_action = ActionNone;

if (m_action != ActionRazor)
{
m_action = ActionNone;
}

if( m_editMode == ModeDraw )
{
Expand All @@ -2131,6 +2219,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )

if( m_action == ActionNone && me->buttons() == 0 )
{
// When cursor is between note editing area and volume/panning
// area show vertical size cursor.
if( me->y() > keyAreaBottom() && me->y() < noteEditTop() )
{
setCursor( Qt::SizeVerCursor );
Expand Down Expand Up @@ -2160,6 +2250,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
return;
}

// Update Razor position if we are on Razor mode
if (m_editMode == ModeEditRazor)
{
updateRazorPos(me);
}

if( me->y() > PR_TOP_MARGIN || m_action != ActionNone )
{
bool edit_note = ( me->y() > noteEditTop() )
Expand Down Expand Up @@ -2439,7 +2535,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
}
}
}
else if (me->buttons() == Qt::NoButton && m_editMode != ModeDraw)
else if (me->buttons() == Qt::NoButton && m_editMode != ModeDraw && m_editMode != ModeEditRazor)
ryuukumar marked this conversation as resolved.
Show resolved Hide resolved
{
// Is needed to restore cursor when it previously was set to
// Qt::SizeVerCursor (between keyAreaBottom and noteEditTop)
Expand Down Expand Up @@ -2535,6 +2631,24 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )



void PianoRoll::updateRazorPos(QMouseEvent* me)
{
// Calculate the TimePos from the mouse
int mouseViewportPos = me->x() - m_whiteKeyWidth;
int mouseTickPos = mouseViewportPos / (m_ppb / TimePos::ticksPerBar()) + m_currentPosition;

// If ctrl is not pressed, quantize the position
if (!(me->modifiers() & Qt::ControlModifier))
{
mouseTickPos = floor(mouseTickPos / quantization()) * quantization();
}

m_razorTickPos = mouseTickPos;
}




void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl )
{
// dragging one or more notes around
Expand Down Expand Up @@ -3232,6 +3346,41 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
}
}

// -- Razor tool (draw cut line)
if (m_action == ActionRazor)
{
auto xCoordOfTick = [this](int tick) {
return m_whiteKeyWidth + (
(tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar());
};
Note* n = noteUnderMouse();
if (n)
{
const int key = n->key() - m_startKey + 1;
int y = y_base - key * m_keyLineHeight;

int x = xCoordOfTick(m_razorTickPos);

if (x > xCoordOfTick(n->pos()) &&
x < xCoordOfTick(n->pos() + n->length()))
{
p.setPen(QPen(QColor("#FF0000"), 1));
p.drawLine(x, y, x, y + m_keyLineHeight);

setCursor(Qt::BlankCursor);
}
else
{
setCursor(Qt::ArrowCursor);
}
}
else
{
setCursor(Qt::ArrowCursor);
}
}
// -- End razor tool

//draw current step recording notes
for( const Note *note : m_stepRecorder.getCurStepNotes() )
{
Expand Down Expand Up @@ -3353,6 +3502,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
case ModeErase: cursor = s_toolErase; break;
case ModeSelect: cursor = s_toolSelect; break;
case ModeEditDetuning: cursor = s_toolOpen; break;
case ModeEditRazor: cursor = s_toolRazor; break;
}
QPoint mousePosition = mapFromGlobal( QCursor::pos() );
if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft())
Expand Down Expand Up @@ -3560,7 +3710,12 @@ void PianoRoll::focusOutEvent( QFocusEvent * )
m_pattern->instrumentTrack()->pianoModel()->setKeyState( i, false );
}
}
m_editMode = m_ctrlMode;
if (m_editMode == ModeEditRazor) {
m_editMode = m_razorMode;
m_action = ActionNone;
} else {
m_editMode = m_ctrlMode;
}
update();
}

Expand Down Expand Up @@ -4443,7 +4598,14 @@ PianoRollWindow::PianoRollWindow() :
connect(glueAction, SIGNAL(triggered()), m_editor, SLOT(glueNotes()));
glueAction->setShortcut( Qt::SHIFT | Qt::Key_G );

// Razor
QAction * razorAction = new QAction(embed::getIconPixmap("razor"),
tr("Razor"), noteToolsButton);
connect(razorAction, &QAction::triggered, m_editor, &PianoRoll::setRazorAction);
razorAction->setShortcut( Qt::SHIFT | Qt::Key_R );

noteToolsButton->addAction(glueAction);
noteToolsButton->addAction(razorAction);

notesActionsToolBar->addWidget(noteToolsButton);

Expand Down
Loading