Add basic ghost notes feature. (#4575)
Lets you set a melody pattern as visible in the background of the Piano Roll as support when building a new pattern. The pattern is visible throughout the session or until cleared via the provided button.
This commit is contained in:
committed by
Oskar Wallgren
parent
68cefc15c4
commit
5126070bb1
@@ -182,6 +182,8 @@ PianoRoll::PianoRoll() :
|
||||
m_lineColor( 0, 0, 0 ),
|
||||
m_noteModeColor( 0, 0, 0 ),
|
||||
m_noteColor( 0, 0, 0 ),
|
||||
m_ghostNoteColor( 0, 0, 0 ),
|
||||
m_ghostNoteTextColor( 0, 0, 0 ),
|
||||
m_barColor( 0, 0, 0 ),
|
||||
m_selectedNoteColor( 0, 0, 0 ),
|
||||
m_textColor( 0, 0, 0 ),
|
||||
@@ -189,7 +191,9 @@ PianoRoll::PianoRoll() :
|
||||
m_textShadow( 0, 0, 0 ),
|
||||
m_markedSemitoneColor( 0, 0, 0 ),
|
||||
m_noteOpacity( 255 ),
|
||||
m_ghostNoteOpacity( 255 ),
|
||||
m_noteBorders( true ),
|
||||
m_ghostNoteBorders( true ),
|
||||
m_backgroundShade( 0, 0, 0 )
|
||||
{
|
||||
// gui names of edit modes
|
||||
@@ -599,6 +603,26 @@ PianoRoll::~PianoRoll()
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::setGhostPattern( Pattern* newPattern )
|
||||
{
|
||||
m_ghostPattern = newPattern;
|
||||
if( newPattern != nullptr )
|
||||
{
|
||||
// make sure to always get informed about the pattern being destroyed
|
||||
connect( m_ghostPattern, SIGNAL( destroyedPattern( Pattern* ) ), this, SLOT( clearGhostPattern() ) );
|
||||
emit ghostPatternSet( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::clearGhostPattern()
|
||||
{
|
||||
setGhostPattern( nullptr );
|
||||
emit ghostPatternSet( false );
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::setCurrentPattern( Pattern* newPattern )
|
||||
{
|
||||
if( hasValidPattern() )
|
||||
@@ -801,6 +825,30 @@ bool PianoRoll::noteBorders() const
|
||||
void PianoRoll::setNoteBorders( const bool b )
|
||||
{ m_noteBorders = b; }
|
||||
|
||||
QColor PianoRoll::ghostNoteColor() const
|
||||
{ return m_ghostNoteColor; }
|
||||
|
||||
void PianoRoll::setGhostNoteColor( const QColor & c )
|
||||
{ m_ghostNoteColor = c; }
|
||||
|
||||
QColor PianoRoll::ghostNoteTextColor() const
|
||||
{ return m_ghostNoteTextColor; }
|
||||
|
||||
void PianoRoll::setGhostNoteTextColor( const QColor & c )
|
||||
{ m_ghostNoteTextColor = c; }
|
||||
|
||||
int PianoRoll::ghostNoteOpacity() const
|
||||
{ return m_ghostNoteOpacity; }
|
||||
|
||||
void PianoRoll::setGhostNoteOpacity( const int i )
|
||||
{ m_ghostNoteOpacity = i; }
|
||||
|
||||
bool PianoRoll::ghostNoteBorders() const
|
||||
{ return m_ghostNoteBorders; }
|
||||
|
||||
void PianoRoll::setGhostNoteBorders( const bool b )
|
||||
{ m_ghostNoteBorders = b; }
|
||||
|
||||
QColor PianoRoll::backgroundShade() const
|
||||
{ return m_backgroundShade; }
|
||||
|
||||
@@ -810,7 +858,6 @@ void PianoRoll::setBackgroundShade( const QColor & c )
|
||||
|
||||
|
||||
|
||||
|
||||
void PianoRoll::drawNoteRect( QPainter & p, int x, int y,
|
||||
int width, const Note * n, const QColor & noteCol, const QColor & noteTextColor,
|
||||
const QColor & selCol, const int noteOpc, const bool borders, bool drawNoteName )
|
||||
@@ -3024,6 +3071,50 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
|
||||
|
||||
QPolygonF editHandles;
|
||||
|
||||
// -- Begin ghost pattern
|
||||
if( m_ghostPattern != nullptr && m_ghostPattern != m_pattern )
|
||||
{
|
||||
for( const Note *note : m_ghostPattern->notes() )
|
||||
{
|
||||
int len_ticks = note->length();
|
||||
|
||||
if( len_ticks == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if( len_ticks < 0 )
|
||||
{
|
||||
len_ticks = 4;
|
||||
}
|
||||
const int key = note->key() - m_startKey + 1;
|
||||
|
||||
int pos_ticks = note->pos();
|
||||
|
||||
int note_width = len_ticks * m_ppt / MidiTime::ticksPerTact();
|
||||
const int x = ( pos_ticks - m_currentPosition ) *
|
||||
m_ppt / MidiTime::ticksPerTact();
|
||||
// skip this note if not in visible area at all
|
||||
if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// is the note in visible area?
|
||||
if( key > 0 && key <= visible_keys )
|
||||
{
|
||||
|
||||
// we've done and checked all, let's draw the
|
||||
// note
|
||||
drawNoteRect( p, x + WHITE_KEY_WIDTH,
|
||||
y_base - key * KEY_LINE_HEIGHT,
|
||||
note_width, note, ghostNoteColor(), ghostNoteTextColor(), selectedNoteColor(),
|
||||
ghostNoteOpacity(), ghostNoteBorders(), drawNoteNames );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// -- End ghost pattern
|
||||
|
||||
for( const Note *note : m_pattern->notes() )
|
||||
{
|
||||
int len_ticks = note->length();
|
||||
@@ -4221,8 +4312,15 @@ PianoRollWindow::PianoRollWindow() :
|
||||
m_chordComboBox = new ComboBox( m_toolBar );
|
||||
m_chordComboBox->setModel( &m_editor->m_chordModel );
|
||||
m_chordComboBox->setFixedSize( 105, 22 );
|
||||
m_chordComboBox->setToolTip( tr( "Chord") );
|
||||
m_chordComboBox->setToolTip( tr( "Chord" ) );
|
||||
|
||||
// -- Clear ghost pattern button
|
||||
m_clearGhostButton = new QPushButton( m_toolBar );
|
||||
m_clearGhostButton->setIcon( embed::getIconPixmap( "clear_ghost_note" ) );
|
||||
m_clearGhostButton->setToolTip( tr( "Clear ghost notes" ) );
|
||||
m_clearGhostButton->setEnabled( false );
|
||||
connect( m_clearGhostButton, SIGNAL( clicked() ), m_editor, SLOT( clearGhostPattern() ) );
|
||||
connect( m_editor, SIGNAL( ghostPatternSet( bool ) ), this, SLOT( ghostPatternSet( bool ) ) );
|
||||
|
||||
zoomAndNotesToolBar->addWidget( zoom_lbl );
|
||||
zoomAndNotesToolBar->addWidget( m_zoomingComboBox );
|
||||
@@ -4243,6 +4341,9 @@ PianoRollWindow::PianoRollWindow() :
|
||||
zoomAndNotesToolBar->addWidget( chord_lbl );
|
||||
zoomAndNotesToolBar->addWidget( m_chordComboBox );
|
||||
|
||||
zoomAndNotesToolBar->addSeparator();
|
||||
zoomAndNotesToolBar->addWidget( m_clearGhostButton );
|
||||
|
||||
// setup our actual window
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
setFocus();
|
||||
@@ -4265,6 +4366,14 @@ const Pattern* PianoRollWindow::currentPattern() const
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::setGhostPattern( Pattern* pattern )
|
||||
{
|
||||
m_editor->setGhostPattern( pattern );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::setCurrentPattern( Pattern* pattern )
|
||||
{
|
||||
m_editor->setCurrentPattern( pattern );
|
||||
@@ -4387,6 +4496,14 @@ void PianoRollWindow::patternRenamed()
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::ghostPatternSet( bool state )
|
||||
{
|
||||
m_clearGhostButton->setEnabled( state );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::focusInEvent( QFocusEvent * event )
|
||||
{
|
||||
// when the window is given focus, also give focus to the actual piano roll
|
||||
|
||||
@@ -637,6 +637,18 @@ void PatternView::openInPianoRoll()
|
||||
|
||||
|
||||
|
||||
|
||||
void PatternView::setGhostInPianoRoll()
|
||||
{
|
||||
gui->pianoRoll()->setGhostPattern( m_pat );
|
||||
gui->pianoRoll()->parentWidget()->show();
|
||||
gui->pianoRoll()->show();
|
||||
gui->pianoRoll()->setFocus();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PatternView::resetName()
|
||||
{
|
||||
m_pat->setName( m_pat->m_instrumentTrack->name() );
|
||||
@@ -663,7 +675,22 @@ void PatternView::constructContextMenu( QMenu * _cm )
|
||||
_cm->insertAction( _cm->actions()[0], a );
|
||||
connect( a, SIGNAL( triggered( bool ) ),
|
||||
this, SLOT( openInPianoRoll() ) );
|
||||
_cm->insertSeparator( _cm->actions()[1] );
|
||||
|
||||
if( gui->pianoRoll()->currentPattern() &&
|
||||
gui->pianoRoll()->currentPattern() != m_pat &&
|
||||
not m_pat->empty() )
|
||||
{
|
||||
QAction * b = new QAction( embed::getIconPixmap( "ghost_note" ),
|
||||
tr( "Set as ghost in piano-roll" ), _cm );
|
||||
_cm->insertAction( _cm->actions()[1], b );
|
||||
connect( b, SIGNAL( triggered( bool ) ),
|
||||
this, SLOT( setGhostInPianoRoll() ) );
|
||||
_cm->insertSeparator( _cm->actions()[2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
_cm->insertSeparator( _cm->actions()[1] );
|
||||
}
|
||||
|
||||
_cm->addSeparator();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user