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:
https://gitlab.com/users/CYBERDEViLNL
2019-01-17 19:07:52 +01:00
committed by Oskar Wallgren
parent 68cefc15c4
commit 5126070bb1
10 changed files with 181 additions and 4 deletions

View File

@@ -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

View File

@@ -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();