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

Add Beat Preview to PatternClipView #7559

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 64 additions & 6 deletions src/gui/clips/PatternClipView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@
#include <QMenu>
#include <QPainter>

#include "AutomationClip.h"
#include "Engine.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "MidiClip.h"
#include "PatternClip.h"
#include "PatternStore.h"
#include "PatternTrack.h"
#include "RenameDialog.h"
#include "SampleClip.h"

namespace lmms::gui
{
Expand Down Expand Up @@ -76,93 +80,147 @@



void PatternClipView::paintEvent(QPaintEvent*)
{
QPainter painter( this );

if( !needsUpdate() )
{
painter.drawPixmap( 0, 0, m_paintPixmap );
return;
}

setNeedsUpdate( false );

if (m_paintPixmap.isNull() || m_paintPixmap.size() != size())
{
m_paintPixmap = QPixmap(size());
}

QPainter p( &m_paintPixmap );

QLinearGradient lingrad( 0, 0, 0, height() );
QColor c = getColorForDisplay( painter.background().color() );

lingrad.setColorAt( 0, c.lighter( 130 ) );
lingrad.setColorAt( 1, c.lighter( 70 ) );

// paint a black rectangle under the clip to prevent glitches with transparent backgrounds
p.fillRect( rect(), QColor( 0, 0, 0 ) );

int pixelsPerPattern = Engine::patternStore()->lengthOfPattern(m_patternClip->patternIndex()) * pixelsPerBar();
int offset = static_cast<int>(m_patternClip->startTimeOffset() * (pixelsPerBar() / TimePos::ticksPerBar()))
% pixelsPerPattern;
if (offset < 2) {
offset += pixelsPerPattern;
}

if( gradient() )
{
p.fillRect( rect(), lingrad );
}
else
{
p.fillRect( rect(), c );
}
// draw notes
int patternIndex = static_cast<PatternTrack*>(m_patternClip->getTrack())->patternIndex();
// Count the number of non-empty instrument tracks. Only used midi tracks will be drawn.
int numberIntsrumentTracksUsed = 0;
for (const auto& track : Engine::patternStore()->tracks())
{
Clip* clip = track->getClip(patternIndex);
MidiClip* mClip = dynamic_cast<MidiClip*>(clip);
if (mClip)
{
if (mClip->notes().size() > 0) { numberIntsrumentTracksUsed++; }
}
}

int trackIndex = 0;
for (const auto& track : Engine::patternStore()->tracks())
{
Clip* clip = track->getClip(patternIndex);
MidiClip* mClip = dynamic_cast<MidiClip*>(clip);
SampleClip* sClip = dynamic_cast<SampleClip*>(clip);
AutomationClip* aClip = dynamic_cast<AutomationClip*>(clip);
if (mClip)
{
if (mClip->notes().size() == 0) { continue; } // Continue without updating the track index; empty tracks are not drawn.
// Compare how long the clip view is compared to the underlying pattern. First +1 for ceiling, second +1 for possible previous bar.
int maxPossibleRepeitions = getClip()->length() / mClip->length() + 1 + 1;
for (Note const * note : mClip->notes())
{
QRect noteRect = QRect(
note->pos() * pixelsPerBar() / TimePos::ticksPerBar() + offset,
trackIndex * height() / numberIntsrumentTracksUsed,
pixelsPerBar() / 16,
height() / numberIntsrumentTracksUsed
);
// Loop through all the possible bars this pattern could affect. Starting at -1 for the possibility of start offset
for (int i = -1; i < maxPossibleRepeitions - 1; i++)
{
noteRect.moveLeft(note->pos() * pixelsPerBar() / TimePos::ticksPerBar() + offset + i * pixelsPerPattern);
p.fillRect(noteRect, QColor(255, 255, 255, std::min(255, note->getVolume() * 255 / 100)));
}
}
}
else if (sClip)
{
// TODO, we probably won't be rendering a whole waveform for this; perhaps just a single block given the sample start offset
}
else if (aClip)
{
// TODO, probably do nothing?
}
// Only increment the track index if we actually drew anything; empty tracks do not contribute.
trackIndex++;
}

// bar lines
const int lineSize = 3;
int pixelsPerPattern = Engine::patternStore()->lengthOfPattern(m_patternClip->patternIndex()) * pixelsPerBar();
int offset = static_cast<int>(m_patternClip->startTimeOffset() * (pixelsPerBar() / TimePos::ticksPerBar()))
% pixelsPerPattern;
if (offset < 2) {
offset += pixelsPerPattern;
}

p.setPen( c.darker( 200 ) );

if (pixelsPerPattern > 0)
{
for (int x = offset; x < width() - 2; x += pixelsPerPattern)
{
p.drawLine( x, BORDER_WIDTH, x, BORDER_WIDTH + lineSize );
p.drawLine( x, rect().bottom() - ( BORDER_WIDTH + lineSize ),
x, rect().bottom() - BORDER_WIDTH );
}
}

// clip name
paintTextLabel(m_patternClip->name(), p);

// inner border
p.setPen( c.lighter( 130 ) );
p.drawRect( 1, 1, rect().right() - BORDER_WIDTH,
rect().bottom() - BORDER_WIDTH );

// outer border
p.setPen( c.darker( 300 ) );
p.drawRect( 0, 0, rect().right(), rect().bottom() );

// draw the 'muted' pixmap only if the clip was manualy muted
if (m_patternClip->isMuted())
{
const int spacing = BORDER_WIDTH;
const int size = 14;
p.drawPixmap( spacing, height() - ( size + spacing ),
embed::getIconPixmap( "muted", size, size ) );
}

p.end();

painter.drawPixmap( 0, 0, m_paintPixmap );
}




Check notice on line 223 in src/gui/clips/PatternClipView.cpp

View check run for this annotation

codefactor.io / CodeFactor

src/gui/clips/PatternClipView.cpp#L83-L223

Complex Method
void PatternClipView::openInPatternEditor()
{
Engine::patternStore()->setCurrentPattern(m_patternClip->patternIndex());
Expand Down
Loading