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
BIN
data/themes/classic/clear_ghost_note.png
Normal file
BIN
data/themes/classic/clear_ghost_note.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
data/themes/classic/ghost_note.png
Normal file
BIN
data/themes/classic/ghost_note.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 452 B |
@@ -127,6 +127,10 @@ PianoRoll {
|
||||
qproperty-noteOpacity: 128;
|
||||
qproperty-noteBorders: true; /* boolean property, set false to have borderless notes */
|
||||
qproperty-selectedNoteColor: rgb( 0, 125, 255 );
|
||||
qproperty-ghostNoteColor: #000000;
|
||||
qproperty-ghostNoteTextColor: #ffffff;
|
||||
qproperty-ghostNoteOpacity: 50;
|
||||
qproperty-ghostNoteBorders: true;
|
||||
qproperty-barColor: #4afd85;
|
||||
qproperty-markedSemitoneColor: rgba( 0, 255, 200, 60 );
|
||||
/* Grid colors */
|
||||
|
||||
BIN
data/themes/default/clear_ghost_note.png
Normal file
BIN
data/themes/default/clear_ghost_note.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
data/themes/default/ghost_note.png
Normal file
BIN
data/themes/default/ghost_note.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -146,6 +146,10 @@ PianoRoll {
|
||||
qproperty-noteOpacity: 165;
|
||||
qproperty-noteBorders: false; /* boolean property, set false to have borderless notes */
|
||||
qproperty-selectedNoteColor: #064d79;
|
||||
qproperty-ghostNoteColor: #000000;
|
||||
qproperty-ghostNoteTextColor: #ffffff;
|
||||
qproperty-ghostNoteOpacity: 50;
|
||||
qproperty-ghostNoteBorders: false;
|
||||
qproperty-barColor: #078f3a;
|
||||
qproperty-markedSemitoneColor: rgba(255, 255, 255, 30);
|
||||
/* Grid colors */
|
||||
|
||||
@@ -187,6 +187,7 @@ public slots:
|
||||
|
||||
protected slots:
|
||||
void openInPianoRoll();
|
||||
void setGhostInPianoRoll();
|
||||
|
||||
void resetName();
|
||||
void changeName();
|
||||
|
||||
@@ -59,7 +59,9 @@ class PianoRoll : public QWidget
|
||||
Q_PROPERTY( QColor lineColor READ lineColor WRITE setLineColor )
|
||||
Q_PROPERTY( QColor noteModeColor READ noteModeColor WRITE setNoteModeColor )
|
||||
Q_PROPERTY( QColor noteColor READ noteColor WRITE setNoteColor )
|
||||
Q_PROPERTY( QColor ghostNoteColor READ ghostNoteColor WRITE setGhostNoteColor )
|
||||
Q_PROPERTY( QColor noteTextColor READ noteTextColor WRITE setNoteTextColor )
|
||||
Q_PROPERTY( QColor ghostNoteTextColor READ ghostNoteTextColor WRITE setGhostNoteTextColor )
|
||||
Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor )
|
||||
Q_PROPERTY( QColor selectedNoteColor READ selectedNoteColor WRITE setSelectedNoteColor )
|
||||
Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor )
|
||||
@@ -68,6 +70,8 @@ class PianoRoll : public QWidget
|
||||
Q_PROPERTY( QColor markedSemitoneColor READ markedSemitoneColor WRITE setMarkedSemitoneColor )
|
||||
Q_PROPERTY( int noteOpacity READ noteOpacity WRITE setNoteOpacity )
|
||||
Q_PROPERTY( bool noteBorders READ noteBorders WRITE setNoteBorders )
|
||||
Q_PROPERTY( int ghostNoteOpacity READ ghostNoteOpacity WRITE setGhostNoteOpacity )
|
||||
Q_PROPERTY( bool ghostNoteBorders READ ghostNoteBorders WRITE setGhostNoteBorders )
|
||||
Q_PROPERTY( QColor backgroundShade READ backgroundShade WRITE setBackgroundShade )
|
||||
public:
|
||||
enum EditModes
|
||||
@@ -87,6 +91,7 @@ public:
|
||||
void showPanTextFloat(panning_t pan, const QPoint &pos, int timeout=-1);
|
||||
|
||||
void setCurrentPattern( Pattern* newPattern );
|
||||
void setGhostPattern( Pattern* newPattern );
|
||||
|
||||
inline void stopRecording()
|
||||
{
|
||||
@@ -141,6 +146,14 @@ public:
|
||||
void setNoteOpacity( const int i );
|
||||
bool noteBorders() const;
|
||||
void setNoteBorders( const bool b );
|
||||
QColor ghostNoteColor() const;
|
||||
void setGhostNoteColor( const QColor & c );
|
||||
QColor ghostNoteTextColor() const;
|
||||
void setGhostNoteTextColor( const QColor & c );
|
||||
int ghostNoteOpacity() const;
|
||||
void setGhostNoteOpacity( const int i );
|
||||
bool ghostNoteBorders() const;
|
||||
void setGhostNoteBorders( const bool b );
|
||||
QColor backgroundShade() const;
|
||||
void setBackgroundShade( const QColor & c );
|
||||
|
||||
@@ -206,9 +219,12 @@ protected slots:
|
||||
|
||||
void selectRegionFromPixels( int xStart, int xEnd );
|
||||
|
||||
void clearGhostPattern();
|
||||
|
||||
|
||||
signals:
|
||||
void currentPatternChanged();
|
||||
void ghostPatternSet(bool);
|
||||
void semiToneMarkerMenuScaleSetEnabled(bool);
|
||||
void semiToneMarkerMenuChordSetEnabled(bool);
|
||||
|
||||
@@ -309,6 +325,7 @@ private:
|
||||
static const QVector<double> m_zoomLevels;
|
||||
|
||||
Pattern* m_pattern;
|
||||
Pattern* m_ghostPattern;
|
||||
QScrollBar * m_leftRightScroll;
|
||||
QScrollBar * m_topBottomScroll;
|
||||
|
||||
@@ -388,6 +405,8 @@ private:
|
||||
QColor m_noteModeColor;
|
||||
QColor m_noteColor;
|
||||
QColor m_noteTextColor;
|
||||
QColor m_ghostNoteColor;
|
||||
QColor m_ghostNoteTextColor;
|
||||
QColor m_barColor;
|
||||
QColor m_selectedNoteColor;
|
||||
QColor m_textColor;
|
||||
@@ -395,7 +414,9 @@ private:
|
||||
QColor m_textShadow;
|
||||
QColor m_markedSemitoneColor;
|
||||
int m_noteOpacity;
|
||||
int m_ghostNoteOpacity;
|
||||
bool m_noteBorders;
|
||||
bool m_ghostNoteBorders;
|
||||
QColor m_backgroundShade;
|
||||
|
||||
signals:
|
||||
@@ -412,7 +433,8 @@ public:
|
||||
PianoRollWindow();
|
||||
|
||||
const Pattern* currentPattern() const;
|
||||
void setCurrentPattern(Pattern* pattern);
|
||||
void setCurrentPattern( Pattern* pattern );
|
||||
void setGhostPattern( Pattern* pattern );
|
||||
|
||||
int quantization() const;
|
||||
|
||||
@@ -445,6 +467,7 @@ signals:
|
||||
|
||||
private slots:
|
||||
void patternRenamed();
|
||||
void ghostPatternSet( bool state );
|
||||
|
||||
private:
|
||||
void focusInEvent(QFocusEvent * event);
|
||||
@@ -456,6 +479,7 @@ private:
|
||||
ComboBox * m_noteLenComboBox;
|
||||
ComboBox * m_scaleComboBox;
|
||||
ComboBox * m_chordComboBox;
|
||||
QPushButton * m_clearGhostButton;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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