Skip to content
Snippets Groups Projects
Unverified Commit 5379ade4 authored by Igor Korsukov's avatar Igor Korsukov Committed by GitHub
Browse files

Merge pull request #6441 from igorkorsukov/clipsview/wave_optima

[AU4] made wave draw optimization
parents 846d30d3 63dfec95
Branches
No related tags found
No related merge requests found
Showing with 323 additions and 540 deletions
...@@ -22,8 +22,16 @@ public: ...@@ -22,8 +22,16 @@ public:
QColor highlight; QColor highlight;
}; };
struct Geometry {
double clipHeight = 0.0; // clip view height
double clipWidth = 0.0; // clip view width
double relClipLeft = 0.0; // relatively to frameLeft
double frameLeft = 0.0; // track line shift
double frameWidth = 0.0; // track line visible width
};
struct Params { struct Params {
QRect viewRect; Geometry geometry;
double zoom = 0.0; double zoom = 0.0;
Style style; Style style;
}; };
......
...@@ -8,15 +8,11 @@ ...@@ -8,15 +8,11 @@
#include <wx/utils.h> #include <wx/utils.h>
#include "ClipInterface.h" #include "ClipInterface.h"
#include "CodeConversions.h"
#include "SelectedRegion.h"
#include "WaveClip.h" #include "WaveClip.h"
#include "WaveTrack.h" #include "WaveTrack.h"
#include "ZoomInfo.h" #include "ZoomInfo.h"
#include "Envelope.h" #include "Envelope.h"
#include "FrameStatistics.h" #include "FrameStatistics.h"
#include "SyncLock.h"
#include "ViewInfo.h"
#include "WaveformScale.h" #include "WaveformScale.h"
#include "graphics/Color.h" #include "graphics/Color.h"
...@@ -25,11 +21,7 @@ ...@@ -25,11 +21,7 @@
#include "domaccessor.h" #include "domaccessor.h"
constexpr int kClipDetailedViewMinimumWidth{ 3 }; constexpr double CLIPVIEW_WIDTH_MIN = 4; // px
constexpr int FrameRadius { 4 };
constexpr int HeaderHeight { 0 };
constexpr int HeaderVMargin { 2 };
using Style = au::au3::Au3WavePainter::Style; using Style = au::au3::Au3WavePainter::Style;
...@@ -120,19 +112,25 @@ public: ...@@ -120,19 +112,25 @@ public:
clip, dataCache, clip, dataCache,
[] { return std::make_unique<WaveBitmapCacheElementQt>(); }); [] { return std::make_unique<WaveBitmapCacheElementQt>(); });
mChannelCaches.push_back( mChannelCaches.push_back({ std::move(dataCache), std::move(bitmapCache) });
{ std::move(dataCache), std::move(bitmapCache) });
} }
return *this; return *this;
} }
struct Geometry
{
double top = 0.0;
double left = 0.0;
double height = 0.0;
double width = 0.0; // not used for draw, just info
};
void Draw(int channelIndex, void Draw(int channelIndex,
QPainter& painter, const QPainter& painter, const
WavePaintParameters& params, WavePaintParameters& params,
const ZoomInfo& zoomInfo, const Geometry& geometry,
const QRect& targetRect, double zoom,
int leftOffset,
double from, double from,
double to) double to)
{ {
...@@ -144,26 +142,24 @@ public: ...@@ -144,26 +142,24 @@ public:
auto& bitmapCache = mChannelCaches[channelIndex].BitmapCache; auto& bitmapCache = mChannelCaches[channelIndex].BitmapCache;
bitmapCache->SetPaintParameters(params); bitmapCache->SetPaintParameters(params);
auto range = bitmapCache->PerformLookup(zoomInfo, from, to); const ZoomInfo zoomInfo(0.0, zoom);
auto left = targetRect.x() + leftOffset; auto range = bitmapCache->PerformLookup(zoomInfo, from, to);
auto height = targetRect.height();
const auto top = targetRect.y(); int left = geometry.left;
int height = geometry.height;
for (auto it = range.begin(); it != range.end(); ++it) { for (auto it = range.begin(); it != range.end(); ++it) {
const auto elementLeftOffset = it.GetLeftOffset(); const auto elementLeftOffset = it.GetLeftOffset();
const auto elementRightOffset = it.GetRightOffset(); const auto elementRightOffset = it.GetRightOffset();
const auto width = WaveBitmapCache::CacheElementWidth const auto width = WaveBitmapCache::CacheElementWidth - elementLeftOffset - elementRightOffset;
- elementLeftOffset - elementRightOffset;
const auto drawableWidth = std::min<int32_t>( const auto drawableWidth = std::min<int32_t>(width, it->Width() - elementLeftOffset);
width, it->Width() - elementLeftOffset);
const auto image = static_cast<const WaveBitmapCacheElementQt&>(*it).GetImage(); const auto image = static_cast<const WaveBitmapCacheElementQt&>(*it).GetImage();
painter.drawImage( painter.drawImage(
QRectF(left, targetRect.y(), drawableWidth, height), QRectF(left, geometry.top, drawableWidth, height),
image, image,
QRectF( QRectF(
elementLeftOffset, elementLeftOffset,
...@@ -365,170 +361,57 @@ int GetWaveYPos(float value, float min, float max, ...@@ -365,170 +361,57 @@ int GetWaveYPos(float value, float min, float max,
return (int)(value * (height - 1) + 0.5); return (int)(value * (height - 1) + 0.5);
} }
struct WaveGeometry
{
double waveTop = 0.0; // wave channel view top
double waveHeight = 0.0; // wave channel view height
double clipWidth = 0.0; // clip view width
double relClipLeft = 0.0; // relatively to frameLeft
double frameLeft = 0.0; // track line shift
double frameWidth = 0.0; // track line visible width
};
struct ClipParameters struct ClipParameters
{ {
// Do a bunch of calculations common to waveform and spectrum drawing. // Do a bunch of calculations common to waveform and spectrum drawing.
ClipParameters(const ClipTimes& clip, const QRect& rect, const ZoomInfo& zoomInfo); ClipParameters(const WaveGeometry& geometry, double zoom);
const double trackRectT0; // absolute time of left edge of track const WaveGeometry geometry;
WaveformPainter::Geometry drawGeometry;
// Lower and upper visible time boundaries (relative to clip). If completely // Lower and upper visible time boundaries (relative to clip). If completely
// off-screen, `t0 == t1`. // off-screen, `t0 == t1`.
double t0; double t0 = 0.0;
double t1; double t1 = 0.0;
const double averagePixelsPerSecond;
const bool showIndividualSamples;
QRect hiddenMid;
int hiddenLeftOffset;
QRect mid;
int leftOffset;
// returns a clip rectangle restricted by viewRect,
// and with clipOffsetX - clip horizontal origin offset within view rect
static QRect GetClipRect(
const ClipTimes& clip, const ZoomInfo& zoomInfo, const QRect& viewRect, bool* outShowSamples = nullptr);
}; };
ClipParameters::ClipParameters( ClipParameters::ClipParameters(const WaveGeometry& geomet, double zoom)
const ClipTimes& clip, const QRect& rect, const ZoomInfo& zoomInfo) : geometry(geomet)
: trackRectT0{zoomInfo.PositionToTime(0, 0, true)}
, averagePixelsPerSecond{GetPixelsPerSecond(rect, zoomInfo)}
, showIndividualSamples{ShowIndividualSamples(
clip.GetRate(), clip.GetStretchRatio(), averagePixelsPerSecond)}
{ {
const auto trackRectT1 = zoomInfo.PositionToTime(rect.width(), 0, true); double drawWidth = std::min(geometry.relClipLeft + geometry.clipWidth, geometry.frameWidth) - geometry.relClipLeft;
const auto playStartTime = clip.GetPlayStartTime(); double drawLeft = 0.0;
if (geometry.relClipLeft < 0) {
const double clipLength = clip.GetPlayEndTime() - clip.GetPlayStartTime(); drawLeft = -geometry.relClipLeft;
// Hidden duration because too far left.
const auto tpre = trackRectT0 - playStartTime;
const auto tpost = trackRectT1 - playStartTime;
const auto blank = GetBlankSpaceBeforePlayEndTime(clip);
// Calculate actual selection bounds so that t0 > 0 and t1 < the
// end of the track
t0 = std::max(tpre, .0);
t1 = std::min(tpost, clipLength - blank)
+ CalculateAdjustmentForZoomLevel(
averagePixelsPerSecond, showIndividualSamples);
// Make sure t1 (the right bound) is greater than 0
if (t1 < 0.0) {
t1 = 0.0;
}
// Make sure t1 is greater than t0
if (t0 > t1) {
t0 = t1;
}
// The variable "hiddenMid" will be the rectangle containing the
// actual waveform, as opposed to any blank area before
// or after the track, as it would appear without the fisheye.
hiddenMid = rect;
// If the left edge of the track is to the right of the left
// edge of the display, then there's some unused area to the
// left of the track. Reduce the "hiddenMid"
hiddenLeftOffset = 0;
if (tpre < 0) {
// Fix Bug #1296 caused by premature conversion to (int).
wxInt64 time64 = zoomInfo.TimeToPosition(playStartTime, 0, true);
if (time64 < 0) {
time64 = 0;
}
hiddenLeftOffset = (time64 < rect.width()) ? (int)time64 : rect.width();
hiddenMid.setLeft(hiddenMid.left() + hiddenLeftOffset);
}
// If the right edge of the track is to the left of the right
// edge of the display, then there's some unused area to the right
// of the track. Reduce the "hiddenMid" rect by the
// size of the blank area.
if (tpost > t1) {
wxInt64 time64 = zoomInfo.TimeToPosition(playStartTime + t1, 0, true);
if (time64 < 0) {
time64 = 0;
} }
const int hiddenRightOffset = (time64 < rect.width()) ? (int)time64 : rect.width();
hiddenMid.setWidth(std::max(0, hiddenRightOffset - hiddenLeftOffset)); drawGeometry.top = geometry.waveTop;
} drawGeometry.left = drawLeft;
// The variable "mid" will be the rectangle containing the drawGeometry.height = geometry.waveHeight;
// actual waveform, as distorted by the fisheye, drawGeometry.width = drawWidth;
// as opposed to any blank area before or after the track.
mid = rect;
// If the left edge of the track is to the right of the left t0 = drawLeft / zoom;
// edge of the display, then there's some unused area to the t1 = drawWidth / zoom;
// left of the track. Reduce the "mid"
leftOffset = 0;
if (tpre < 0) {
wxInt64 time64 = 0;//zoomInfo.TimeToPosition(playStartTime, 0, false);
if (time64 < 0) {
time64 = 0;
}
leftOffset = (time64 < rect.width()) ? (int)time64 : rect.width();
mid.setLeft(mid.left() + leftOffset); LOGDA() << " relClipLeft: " << geometry.relClipLeft
} << " clipWidth: " << geometry.clipWidth
<< " frameLeft: " << geometry.frameLeft
// If the right edge of the track is to the left of the right << " frameWidth: " << geometry.frameWidth
// edge of the display, then there's some unused area to the right << " draw width: " << drawWidth
// of the track. Reduce the "mid" rect by the << " draw left: " << drawLeft
// size of the blank area. << " t0: " << t0
if (tpost > t1) { << " t1: " << t1
wxInt64 time64 = zoomInfo.TimeToPosition(playStartTime + t1, 0, false); ;
if (time64 < 0) {
time64 = 0;
}
const int distortedRightOffset = (time64 < rect.width()) ? (int)time64 : rect.width();
mid.setWidth(std::max(0, distortedRightOffset - leftOffset));
}
}
QRect ClipParameters::GetClipRect(const ClipTimes& clip,
const ZoomInfo& zoomInfo, const QRect& viewRect, bool* outShowSamples)
{
const auto pixelsPerSecond = GetPixelsPerSecond(viewRect, zoomInfo);
const auto showIndividualSamples = ShowIndividualSamples(
clip.GetRate(), clip.GetStretchRatio(), pixelsPerSecond);
const auto clipEndingAdjustment
=CalculateAdjustmentForZoomLevel(pixelsPerSecond, showIndividualSamples);
if (outShowSamples != nullptr) {
*outShowSamples = showIndividualSamples;
}
constexpr auto edgeLeft
=static_cast<ZoomInfo::int64>(std::numeric_limits<int>::min());
constexpr auto edgeRight
=static_cast<ZoomInfo::int64>(std::numeric_limits<int>::max());
const auto left = std::clamp(
zoomInfo.TimeToPosition(clip.GetPlayStartTime(), viewRect.x(), true),
edgeLeft, edgeRight);
const auto right = std::clamp(
zoomInfo.TimeToPosition(
clip.GetPlayEndTime() - GetBlankSpaceBeforePlayEndTime(clip)
+ clipEndingAdjustment,
viewRect.x(), true),
edgeLeft, edgeRight);
if (right >= left) {
// after clamping we can expect that left and right
// are small enough to be put into int
return {
static_cast<int>(left),
viewRect.y(),
std::max(1, static_cast<int>(right - left)),
viewRect.height()
};
}
return {};
} }
void DrawIndividualSamples(int channelIndex, QPainter& painter, const QRect& rect, void DrawIndividualSamples(int channelIndex, QPainter& painter, const QRect& rect,
...@@ -538,10 +421,8 @@ void DrawIndividualSamples(int channelIndex, QPainter& painter, const QRect& rec ...@@ -538,10 +421,8 @@ void DrawIndividualSamples(int channelIndex, QPainter& painter, const QRect& rec
int leftOffset, int leftOffset,
float zoomMin, float zoomMax, float zoomMin, float zoomMax,
bool dB, float dBRange, bool dB, float dBRange,
bool showPoints, bool muted, bool highlight) bool showPoints, bool highlight)
{ {
UNUSED(muted);
const double toffset = clip.GetPlayStartTime(); const double toffset = clip.GetPlayStartTime();
double rate = clip.GetRate(); double rate = clip.GetRate();
const double t0 = std::max(0.0, zoomInfo.PositionToTime(0, -leftOffset) - toffset); const double t0 = std::max(0.0, zoomInfo.PositionToTime(0, -leftOffset) - toffset);
...@@ -657,167 +538,22 @@ void DrawIndividualSamples(int channelIndex, QPainter& painter, const QRect& rec ...@@ -657,167 +538,22 @@ void DrawIndividualSamples(int channelIndex, QPainter& painter, const QRect& rec
} }
} }
void DrawWaveformBackground(QPainter& painter, const QRect& rect,
const Style& style,
const ZoomInfo& zoomInfo,
const double* env, int leftOffset,
float zoomMin, float zoomMax,
double t0, double t1,
bool dB, float dBRange,
int zeroLevelYCoordinate,
bool bIsSyncLockSelected,
bool highlightEnvelope)
{
// Visually (one vertical slice of the waveform background, on its side;
// the "*" is the actual waveform background we're drawing
//
//1.0 0.0 -1.0
// |--------------------------------|--------------------------------|
// *************** ***************
// | | | |
// maxtop maxbot mintop minbot
int h = rect.height();
int halfHeight = wxMax(h / 2, 1);
int maxtop, lmaxtop = 0;
int mintop, lmintop = 0;
int maxbot, lmaxbot = 0;
int minbot, lminbot = 0;
bool sel, lsel = false;
int xx, lx = 0;
int l, w;
painter.setPen(Qt::NoPen);
painter.setBrush(style.blankBrush);
painter.drawRect(rect);
// Bug 2389 - always draw at least one pixel of selection.
int selectedX = zoomInfo.TimeToPosition(t0, -leftOffset);
double time = zoomInfo.PositionToTime(0, -leftOffset), nextTime;
for (xx = 0; xx < rect.width(); ++xx, time = nextTime) {
nextTime = zoomInfo.PositionToTime(xx + 1, -leftOffset);
// First we compute the truncated shape of the waveform background.
// If drawEnvelope is true, then we compute the lower border of the
// envelope.
maxtop = GetWaveYPos(env[xx], zoomMin, zoomMax,
h, dB, true, dBRange, true);
maxbot = GetWaveYPos(env[xx], zoomMin, zoomMax,
h, dB, false, dBRange, true);
mintop = GetWaveYPos(-env[xx], zoomMin, zoomMax,
h, dB, false, dBRange, true);
minbot = GetWaveYPos(-env[xx], zoomMin, zoomMax,
h, dB, true, dBRange, true);
// Make sure it's odd so that a that max and min mirror each other
mintop +=1;
minbot +=1;
//TODO: uncomment and fix
//const auto drawEnvelope = artist->drawEnvelope;
const auto drawEnvelope = false;
if (!drawEnvelope || maxbot > mintop) {
maxbot = halfHeight;
mintop = halfHeight;
}
sel = (t0 <= time && nextTime < t1);
sel = sel || (xx == selectedX);
// We don't draw selection color for sync-lock selected tracks.
sel = sel && !bIsSyncLockSelected;
if (lmaxtop == maxtop
&& lmintop == mintop
&& lmaxbot == maxbot
&& lminbot == minbot
&& lsel == sel) {
continue;
}
painter.setBrush(style.blankBrush);
l = rect.x() + lx;
w = xx - lx;
if (lmaxbot < lmintop - 1) {
painter.drawRect(l, rect.y() + lmaxtop, w, lmaxbot - lmaxtop);
painter.drawRect(l, rect.y() + lmintop, w, lminbot - lmintop);
} else {
painter.drawRect(l, rect.y() + lmaxtop, w, lminbot - lmaxtop);
}
if (highlightEnvelope && lmaxbot < lmintop - 1) {
painter.setBrush(style.highlight);
painter.drawRect(l, rect.y() + lmaxbot, w, lmintop - lmaxbot);
}
lmaxtop = maxtop;
lmintop = mintop;
lmaxbot = maxbot;
lminbot = minbot;
lsel = sel;
lx = xx;
}
painter.setBrush(style.blankBrush);
l = rect.x() + lx;
w = xx - lx;
if (lmaxbot < lmintop - 1) {
painter.drawRect(l, rect.y() + lmaxtop, w, lmaxbot - lmaxtop);
painter.drawRect(l, rect.y() + lmintop, w, lminbot - lmintop);
} else {
painter.drawRect(l, rect.y() + lmaxtop, w, lminbot - lmaxtop);
}
if (highlightEnvelope && lmaxbot < lmintop - 1) {
painter.setBrush(style.highlight);
painter.drawRect(l, rect.y() + lmaxbot, w, lmintop - lmaxbot);
}
//TODO: uncomment and fix
// If sync-lock selected, draw in linked graphics.
/*if (bIsSyncLockSelected && t0 < t1) {
const int begin = std::max(0, std::min(rect.width, (int)(zoomInfo.TimeToPosition(t0, -leftOffset))));
const int end = std::max(0, std::min(rect.width, (int)(zoomInfo.TimeToPosition(t1, -leftOffset))));
TrackArt::DrawSyncLockTiles( context,
{ rect.x + begin, rect.y, end - 1 - begin, rect.height } );
}*/
//OK, the display bounds are between min and max, which
//is spread across rect.height. Draw the line at the proper place.
if (zeroLevelYCoordinate >= rect.top()
&& zeroLevelYCoordinate <= rect.bottom()) {
painter.setPen(Qt::black);
painter.drawLine(rect.x(), zeroLevelYCoordinate,
rect.x() + rect.width() - 1, zeroLevelYCoordinate);
}
}
void DrawMinMaxRMS(int channelIndex, QPainter& painter, void DrawMinMaxRMS(int channelIndex, QPainter& painter,
const QRect& rect, const ClipParameters& params,
double zoom,
const Style& style, const Style& style,
const ZoomInfo& zoomInfo,
const WaveClip& clip, const WaveClip& clip,
double t0, double t1, int leftOffset,
double zoomMin, double zoomMax, double zoomMin, double zoomMax,
bool dB, double dbRange, bool dB, double dbRange)
bool muted)
{ {
UNUSED(muted);
const ZoomInfo localZoomInfo(0.0, zoomInfo.GetZoom());
auto& waveformPainter = WaveformPainter::Get(clip); auto& waveformPainter = WaveformPainter::Get(clip);
const auto trimLeft = clip.GetTrimLeft();
WavePaintParameters paintParameters; WavePaintParameters paintParameters;
paintParameters paintParameters
.SetDisplayParameters( .SetDisplayParameters(
//TODO: uncomment and fix //TODO: uncomment and fix
rect.height(), zoomMin, zoomMax, false /*artist->mShowClipping*/) params.drawGeometry.height, zoomMin, zoomMax, false /*artist->mShowClipping*/)
.SetDBParameters(dbRange, dB) .SetDBParameters(dbRange, dB)
.SetBlankColor(ColorFromQColor(style.blankBrush)) .SetBlankColor(ColorFromQColor(style.blankBrush))
.SetSampleColors( .SetSampleColors(
...@@ -834,37 +570,35 @@ void DrawMinMaxRMS(int channelIndex, QPainter& painter, ...@@ -834,37 +570,35 @@ void DrawMinMaxRMS(int channelIndex, QPainter& painter,
ColorFromQColor(style.clippedPen)) ColorFromQColor(style.clippedPen))
.SetEnvelope(clip.GetEnvelope()); .SetEnvelope(clip.GetEnvelope());
//TODO: uncomment and fix const auto trimLeft = clip.GetTrimLeft();
//clipPainter.SetSelection(
// zoomInfo, artist->pSelectedRegion->t0() - sequenceStartTime,
// artist->pSelectedRegion->t1() - sequenceStartTime);
waveformPainter.Draw(channelIndex, painter, paintParameters, localZoomInfo, rect, leftOffset, t0 + trimLeft, t1 + trimLeft); waveformPainter.Draw(channelIndex, painter, paintParameters, params.drawGeometry, zoom, params.t0 + trimLeft, params.t1 + trimLeft);
} }
static bool ClipDetailsVisible(const ClipTimes& clip, const ZoomInfo& zoomInfo, const QRect& viewRect) static bool showIndividualSamples(const WaveClip& clip, bool zoom)
{ {
//Do not fold clips to line at sample zoom level, as const double sampleRate = clip.GetRate();
//it may become impossible to 'unfold' it when clip is trimmed const double stretchRatio = clip.GetStretchRatio();
//to a single sample
bool showSamples{ false }; // Require at least 1/2 pixel per sample for drawing individual samples.
auto clipRect = ClipParameters::GetClipRect(clip, zoomInfo, viewRect, &showSamples); const double threshold1 = 0.5 * sampleRate / stretchRatio;
return showSamples || clipRect.width() >= kClipDetailedViewMinimumWidth;
bool showIndividualSamples = zoom > threshold1;
return showIndividualSamples;
} }
void DrawWaveform(int channelIndex, static void DrawWaveform(int channelIndex,
QPainter& painter, QPainter& painter,
WaveTrack& track, WaveTrack& track,
const WaveClip& clip, const WaveClip& clip,
const ZoomInfo& zoomInfo, const WaveGeometry& geometry,
const SelectedRegion& selectedRegion, double zoom,
const QRect& rect,
const Style& style, const Style& style,
bool dB, bool muted, bool selected) bool dB)
{ {
//If clip is "too small" draw a placeholder instead of //If clip is "too small" draw a placeholder instead of
//attempting to fit the contents into a few pixels //attempting to fit the contents into a few pixels
if (!ClipDetailsVisible(clip, zoomInfo, rect)) { if (geometry.clipWidth < CLIPVIEW_WIDTH_MIN) {
//TODO: uncomment and fix me //TODO: uncomment and fix me
/* /*
auto clipRect = ClipParameters::GetClipRect(clip, zoomInfo, rect); auto clipRect = ClipParameters::GetClipRect(clip, zoomInfo, rect);
...@@ -873,29 +607,11 @@ void DrawWaveform(int channelIndex, ...@@ -873,29 +607,11 @@ void DrawWaveform(int channelIndex,
return; return;
} }
bool highlightEnvelope = false; const ClipParameters params(geometry, zoom);
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING if (params.drawGeometry.width < 0.1) {
auto target = dynamic_cast<EnvelopeHandle*>(context.target.get());
highlightEnvelope = target && target->GetEnvelope() == clip.GetEnvelope();
#endif
const ClipParameters params(clip, rect, zoomInfo);
const auto& hiddenMid = params.hiddenMid;
// The "hiddenMid" rect contains the part of the display actually
// containing the waveform, as it appears without the fisheye. If it's empty, we're done.
if (hiddenMid.width() <= 0) {
return; return;
} }
const double t0 = params.t0;
const double sampleRate = clip.GetRate();
const double playStartTime = clip.GetPlayStartTime();
const double trackRectT0 = params.trackRectT0;
const double stretchRatio = clip.GetStretchRatio();
const double t1 = params.t1;
const int leftOffset = params.leftOffset;
const QRect mid = params.mid;
auto& settings = WaveformSettings::Get(track); auto& settings = WaveformSettings::Get(track);
const float dBRange = settings.dBRange; const float dBRange = settings.dBRange;
...@@ -907,99 +623,28 @@ void DrawWaveform(int channelIndex, ...@@ -907,99 +623,28 @@ void DrawWaveform(int channelIndex,
auto& cache = WaveformScale::Get(track); auto& cache = WaveformScale::Get(track);
cache.GetDisplayBounds(zoomMin, zoomMax); cache.GetDisplayBounds(zoomMin, zoomMax);
std::vector<double> vEnv(mid.width()); if (!showIndividualSamples(clip, zoom)) {
double* const env = &vEnv[0]; DrawMinMaxRMS(channelIndex, painter,
GetEnvelopeValues( params, zoom,
clip.GetEnvelope(),
playStartTime,
// PRL: change back to make envelope evaluate only at sample times
// and then interpolate the display
0, // 1.0 / sampleRate,
env, mid.width(), leftOffset, zoomInfo
);
// Draw the background of the track, outlining the shape of
// the envelope and using a colored pen for the selected
// part of the waveform
{
double tt0, tt1;
if (SyncLock::IsSelectedOrSyncLockSelected(track)) {
tt0 = track.LongSamplesToTime(track.TimeToLongSamples(selectedRegion.t0())),
tt1 = track.LongSamplesToTime(track.TimeToLongSamples(selectedRegion.t1()));
} else {
tt0 = tt1 = 0.0;
}
DrawWaveformBackground(
painter, mid,
style,
zoomInfo,
env, leftOffset,
zoomMin, zoomMax,
tt0, tt1,
dB, dBRange,
cache.ZeroLevelYCoordinate({ mid.x(), mid.y(), mid.width(), mid.height() }),
!track.GetSelected(), highlightEnvelope);
}
//const double pps =
// averagePixelsPerSample * rate;
// Require at least 1/2 pixel per sample for drawing individual samples.
const double threshold1 = 0.5 * sampleRate / stretchRatio;
// Require at least 3 pixels per sample for drawing the draggable points.
const double threshold2 = 3 * sampleRate / stretchRatio;
const bool showIndividualSamples = zoomInfo.GetZoom() > threshold1;
const bool showPoints = zoomInfo.GetZoom() > threshold2;
if (!showIndividualSamples) {
DrawMinMaxRMS(channelIndex,
painter, rect,
style, style,
zoomInfo,
clip, clip,
t0, t1, leftOffset,
zoomMin, zoomMax, zoomMin, zoomMax,
dB, dBRange, muted); dB, dBRange);
} else { } else {
bool highlight = false; // Require at least 3 pixels per sample for drawing the draggable points.
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING // const double threshold2 = 3 * sampleRate / stretchRatio;
auto target = dynamic_cast<SampleHandle*>(context.target.get()); // const bool showPoints =zoom > threshold2;
highlight = target && target->GetTrack().get() == track; // bool highlight = false;
#endif // DrawIndividualSamples(
DrawIndividualSamples( // channelIndex,
channelIndex, // painter, rect,
painter, rect, // style,
style, // zoomInfo,
zoomInfo, // clip, leftOffset,
clip, leftOffset, // zoomMin, zoomMax,
zoomMin, zoomMax, // dB, dBRange,
dB, dBRange, // showPoints, highlight);
showPoints, muted, highlight);
}
//TODO: uncomment and fix me
/*const auto drawEnvelope = artist->drawEnvelope;
if (drawEnvelope) {
DrawEnvelope(
context, mid, env, zoomMin, zoomMax, dB, dBRange, highlightEnvelope );
EnvelopeEditor::DrawPoints( *clip->GetEnvelope(),
context, mid, dB, dBRange, zoomMin, zoomMax, true, rect.x - mid.x );
}*/
// Draw arrows on the left side if the track extends to the left of the
// beginning of time. :)
//TODO: uncomment and fix me
/*if (trackRectT0 == 0.0 && playStartTime < 0.0) {
TrackArt::DrawNegativeOffsetTrackArrows( context, rect );
} }
{
auto clipRect = ClipParameters::GetClipRect(*clip, zoomInfo, rect);
TrackArt::DrawClipEdges(context.painter, clipRect, selected);
}*/
} }
using namespace au::au3; using namespace au::au3;
...@@ -1012,6 +657,12 @@ AudacityProject& Au3WavePainter::projectRef() const ...@@ -1012,6 +657,12 @@ AudacityProject& Au3WavePainter::projectRef() const
void Au3WavePainter::paint(QPainter& painter, const processing::ClipKey& clipKey, const Params& params) void Au3WavePainter::paint(QPainter& painter, const processing::ClipKey& clipKey, const Params& params)
{ {
//! NOTE Please don't remove, need for debug
// if (!(clipKey.trackId == 2 && clipKey.index == 0)) {
// return;
// }
// LOGD() << "trackId: " << clipKey.trackId << ", clip: " << clipKey.index;
WaveTrack* track = DomAccessor::findWaveTrack(projectRef(), TrackId(clipKey.trackId)); WaveTrack* track = DomAccessor::findWaveTrack(projectRef(), TrackId(clipKey.trackId));
IF_ASSERT_FAILED(track) { IF_ASSERT_FAILED(track) {
return; return;
...@@ -1027,30 +678,26 @@ void Au3WavePainter::paint(QPainter& painter, const processing::ClipKey& clipKey ...@@ -1027,30 +678,26 @@ void Au3WavePainter::paint(QPainter& painter, const processing::ClipKey& clipKey
void Au3WavePainter::doPaint(QPainter& painter, const WaveTrack* _track, const WaveClip* clip, const Params& params) void Au3WavePainter::doPaint(QPainter& painter, const WaveTrack* _track, const WaveClip* clip, const Params& params)
{ {
// debug
// const auto& vr = params.viewRect;
// LOGDA() << "viewRect: w: " << vr.width() << ", h: " << vr.height() << ", x: " << vr.x() << ", y: " << vr.y();
auto sw = FrameStatistics::CreateStopwatch(FrameStatistics::SectionID::WaveformView); auto sw = FrameStatistics::CreateStopwatch(FrameStatistics::SectionID::WaveformView);
WaveTrack* track = const_cast<WaveTrack*>(_track); WaveTrack* track = const_cast<WaveTrack*>(_track);
bool highlightEnvelope = false;
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
auto target = dynamic_cast<EnvelopeHandle*>(context.target.get());
highlightEnvelope = target && target->GetEnvelope() == clip->GetEnvelope();
#endif
const bool dB = !WaveformSettings::Get(*track).isLinear(); const bool dB = !WaveformSettings::Get(*track).isLinear();
auto top = params.viewRect.top(); const auto channelHeight = (params.geometry.clipHeight) / static_cast<int>(clip->NChannels());
const auto channelHeight = (params.viewRect.height()) / static_cast<int>(clip->NChannels());
const Geometry& g = params.geometry;
WaveGeometry cg;
cg.waveHeight = channelHeight;
cg.clipWidth = g.clipWidth;
cg.relClipLeft = g.relClipLeft;
cg.frameLeft = g.frameLeft;
cg.frameWidth = g.frameWidth;
ZoomInfo zoomInfo{ clip->GetPlayStartTime(), params.zoom }; cg.waveTop = 0.0;
SelectedRegion selectedRegion{};
for (unsigned i = 0; i < clip->NChannels(); ++i) { for (unsigned i = 0; i < clip->NChannels(); ++i) {
QRect rect = QRect(params.viewRect.left(), top, params.viewRect.width(), channelHeight); DrawWaveform(i, painter, *track, *clip, cg, params.zoom, params.style, dB);
DrawWaveform(i, painter, *track, *clip, zoomInfo, selectedRegion, rect, params.style, dB, track->GetMute(), false); cg.waveTop += channelHeight;
top += channelHeight;
} }
} }
...@@ -55,5 +55,7 @@ Rectangle { ...@@ -55,5 +55,7 @@ Rectangle {
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.margins: 1 anchors.margins: 1
clipLeft: root.x
} }
} }
...@@ -13,6 +13,18 @@ Rectangle { ...@@ -13,6 +13,18 @@ Rectangle {
height: 76 height: 76
color: ui.theme.backgroundPrimaryColor color: ui.theme.backgroundPrimaryColor
//! NOTE This element must be the same width as the track wave visible area.
//! If this is different, than appropriate changes must be made.
onWidthChanged: {
timelineContext.onResizeFrameWidth(root.width)
}
Component.onCompleted: {
timelineContext.init(root.width)
}
//! ~~~ TimelineContext ~~~
//! NOTE See comment in TimelineContext (.h)
function onWheel(y) { function onWheel(y) {
timelineContext.onWheel(y) timelineContext.onWheel(y)
} }
...@@ -28,11 +40,15 @@ Rectangle { ...@@ -28,11 +40,15 @@ Rectangle {
TimelineContext { TimelineContext {
id: timelineContext id: timelineContext
} }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
StyledTextLabel { StyledTextLabel {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: 16 anchors.leftMargin: 16
text: "zoom: " + timelineContext.zoom + ", offset: " + timelineContext.offset text: "zoom: " + timelineContext.zoom
+ ", frame start time: " + timelineContext.frameStartTime
+ ", end time: " + timelineContext.frameEndTime
+ ", root.width: " + root.width
} }
SeparatorLine { anchors.bottom: parent.bottom } SeparatorLine { anchors.bottom: parent.bottom }
......
...@@ -4,12 +4,14 @@ import Muse.UiComponents ...@@ -4,12 +4,14 @@ import Muse.UiComponents
import Audacity.ProjectScene import Audacity.ProjectScene
Item { Rectangle {
id: root id: root
clip: true clip: true
color: ui.theme.backgroundPrimaryColor
TracksListClipsModel { TracksListClipsModel {
id: tracksModel id: tracksModel
} }
......
...@@ -25,7 +25,7 @@ void ClipsListModel::load() ...@@ -25,7 +25,7 @@ void ClipsListModel::load()
if (m_context) { if (m_context) {
connect(m_context, &TimelineContext::zoomChanged, this, &ClipsListModel::onTimelineContextValuesChanged); connect(m_context, &TimelineContext::zoomChanged, this, &ClipsListModel::onTimelineContextValuesChanged);
connect(m_context, &TimelineContext::offsetChanged, this, &ClipsListModel::onTimelineContextValuesChanged); connect(m_context, &TimelineContext::frameTimeChanged, this, &ClipsListModel::onTimelineContextValuesChanged);
} }
beginResetModel(); beginResetModel();
...@@ -91,7 +91,7 @@ QVariant ClipsListModel::data(const QModelIndex& index, int role) const ...@@ -91,7 +91,7 @@ QVariant ClipsListModel::data(const QModelIndex& index, int role) const
bool ClipsListModel::setData(const QModelIndex& index, const QVariant& value, int role) bool ClipsListModel::setData(const QModelIndex& index, const QVariant& value, int role)
{ {
LOGD() << "" << index.row() << ", value: " << value << ", role: " << role; //LOGD() << "" << index.row() << ", value: " << value << ", role: " << role;
switch (role) { switch (role) {
case ClipLeftRole: { case ClipLeftRole: {
return changeClipStartTime(index, value); return changeClipStartTime(index, value);
...@@ -104,6 +104,10 @@ bool ClipsListModel::setData(const QModelIndex& index, const QVariant& value, in ...@@ -104,6 +104,10 @@ bool ClipsListModel::setData(const QModelIndex& index, const QVariant& value, in
void ClipsListModel::onTimelineContextValuesChanged() void ClipsListModel::onTimelineContextValuesChanged()
{ {
// LOGDA() << "zoom: " << m_context->zoom()
// << " frameStartTime: " << m_context->frameStartTime()
// << " frameEndTime: " << m_context->frameEndTime();
for (size_t i = 0; i < m_clipList.size(); ++i) { for (size_t i = 0; i < m_clipList.size(); ++i) {
QModelIndex idx = this->index(int(i)); QModelIndex idx = this->index(int(i));
emit dataChanged(idx, idx, { ClipWidthRole, ClipLeftRole }); emit dataChanged(idx, idx, { ClipWidthRole, ClipLeftRole });
......
...@@ -8,40 +8,64 @@ ...@@ -8,40 +8,64 @@
static constexpr double ZOOM_MIN = 0.1; static constexpr double ZOOM_MIN = 0.1;
TimelineContext::TimelineContext(QQuickItem* parent) TimelineContext::TimelineContext(QObject* parent)
: QQuickItem(parent) : QObject(parent)
{ {
} }
void TimelineContext::init(double frameWidth)
{
m_zoom = 2.0;//{ 44100.0 / 512.0 };
emit zoomChanged();
m_frameStartTime = 0.0;
emit frameStartTimeChanged();
m_frameEndTime = positionToTime(frameWidth);
emit frameEndTimeChanged();
emit frameTimeChanged();
}
void TimelineContext::onWheel(double y) void TimelineContext::onWheel(double y)
{ {
Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
if (modifiers.testFlag(Qt::ControlModifier)) { if (modifiers.testFlag(Qt::ControlModifier)) {
changeZoom(y < 0 ? -1 : 1); changeZoom(y < 0 ? -1 : 1);
} else if (modifiers.testFlag(Qt::ShiftModifier)) { } else if (modifiers.testFlag(Qt::ShiftModifier)) {
changeOffset(y < 0 ? -1 : 1); shiftFrameTime(y < 0 ? -1 : 1);
} }
} }
void TimelineContext::changeZoom(int direction) void TimelineContext::changeZoom(int direction)
{ {
double step = std::max(zoom() / 2.0, ZOOM_MIN); double step = (muse::is_equal(m_zoom, 1.0) && direction < 0) ? 0.1 : 1.0;
double _zoom = zoom() + (step * direction); double zoom = m_zoom + (step * direction);
_zoom = std::floor(_zoom * 10.0) / 10.0; zoom = std::max(zoom, ZOOM_MIN);
if (_zoom > 4) {
_zoom = std::floor(_zoom); setZoom(zoom);
}
void TimelineContext::onResizeFrameWidth(double frameWidth)
{
m_frameWidth = frameWidth;
updateFrameTime();
} }
_zoom = std::max(_zoom, ZOOM_MIN); void TimelineContext::shiftFrameTime(int direction)
{
double step = 10.0;
double shift = step * direction;
setFrameStartTime(m_frameStartTime + shift);
setFrameEndTime(m_frameEndTime + shift);
setZoom(_zoom); emit frameTimeChanged();
} }
void TimelineContext::changeOffset(int direction) void TimelineContext::updateFrameTime()
{ {
double step = 10; setFrameEndTime(positionToTime(m_frameWidth));
setOffset(offset() + (step * direction)); emit frameTimeChanged();
} }
void TimelineContext::onSelection(double x1, double x2) void TimelineContext::onSelection(double x1, double x2)
...@@ -65,35 +89,16 @@ void TimelineContext::onSelectionTime(double startTime, double endTime) ...@@ -65,35 +89,16 @@ void TimelineContext::onSelectionTime(double startTime, double endTime)
setSelectionActive(!muse::is_zero(startTime) && !muse::is_zero(endTime)); setSelectionActive(!muse::is_zero(startTime) && !muse::is_zero(endTime));
} }
qint64 TimelineContext::timeToPosition(double time) const double TimelineContext::timeToPosition(double time) const
{
double t = 0.5 + m_zoom * (time - m_offset);
if (t < INT64_MIN) {
return INT64_MIN;
}
if (t > INT64_MAX) {
return INT64_MAX;
}
t = floor(t);
return static_cast<qint64>(t);
}
double TimelineContext::positionToTime(qint64 position) const
{
return m_offset + position / m_zoom;
}
double TimelineContext::offset() const
{ {
return m_offset; double p = 0.5 + m_zoom * (time - m_frameStartTime);
p = std::floor(p);
return p;
} }
void TimelineContext::setOffset(double newOffset) double TimelineContext::positionToTime(double position) const
{ {
if (m_offset != newOffset) { return m_frameStartTime + position / m_zoom;
m_offset = newOffset;
emit offsetChanged();
}
} }
double TimelineContext::zoom() const double TimelineContext::zoom() const
...@@ -106,6 +111,7 @@ void TimelineContext::setZoom(double zoom) ...@@ -106,6 +111,7 @@ void TimelineContext::setZoom(double zoom)
if (m_zoom != zoom) { if (m_zoom != zoom) {
m_zoom = zoom; m_zoom = zoom;
emit zoomChanged(); emit zoomChanged();
updateFrameTime();
} }
} }
...@@ -148,3 +154,31 @@ void TimelineContext::setSelectionActive(bool newSelectionActive) ...@@ -148,3 +154,31 @@ void TimelineContext::setSelectionActive(bool newSelectionActive)
m_selectionActive = newSelectionActive; m_selectionActive = newSelectionActive;
emit selectionActiveChanged(); emit selectionActiveChanged();
} }
double TimelineContext::frameStartTime() const
{
return m_frameStartTime;
}
void TimelineContext::setFrameStartTime(double newFrameStartTime)
{
if (qFuzzyCompare(m_frameStartTime, newFrameStartTime)) {
return;
}
m_frameStartTime = newFrameStartTime;
emit frameStartTimeChanged();
}
double TimelineContext::frameEndTime() const
{
return m_frameEndTime;
}
void TimelineContext::setFrameEndTime(double newFrameEndTime)
{
if (qFuzzyCompare(m_frameEndTime, newFrameEndTime)) {
return;
}
m_frameEndTime = newFrameEndTime;
emit frameEndTimeChanged();
}
#pragma once #pragma once
#include <QQuickItem> #include <QObject>
class TimelineContext : public QQuickItem //! NOTE This class does two things:
//! 1. This is a context that is passed to other classes
//! 2. This is a controller that interprets mouse and view resize events into context values
//!
//! If this class becomes more complex,
//! or we notice that its "controller" methods are being called in unexpected places,
//! then we should split it into two separate classes.
class TimelineContext : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(double offset READ offset WRITE setOffset NOTIFY offsetChanged FINAL) // 0 sec visible frame end
// | ~~~~~ ~~~~ ~~~|
Q_PROPERTY(double frameStartTime READ frameStartTime NOTIFY frameStartTimeChanged FINAL)
Q_PROPERTY(double frameEndTime READ frameEndTime NOTIFY frameEndTimeChanged FINAL)
Q_PROPERTY(double zoom READ zoom WRITE setZoom NOTIFY zoomChanged FINAL) Q_PROPERTY(double zoom READ zoom WRITE setZoom NOTIFY zoomChanged FINAL)
Q_PROPERTY(double selectionStartTime READ selectionStartTime NOTIFY selectionStartTimeChanged FINAL) Q_PROPERTY(double selectionStartTime READ selectionStartTime NOTIFY selectionStartTimeChanged FINAL)
...@@ -15,17 +26,11 @@ class TimelineContext : public QQuickItem ...@@ -15,17 +26,11 @@ class TimelineContext : public QQuickItem
public: public:
TimelineContext(QQuickItem* parent = nullptr); TimelineContext(QObject* parent = nullptr);
Q_INVOKABLE void onWheel(double y);
Q_INVOKABLE void onSelection(double x1, double x2);
Q_INVOKABLE void resetSelection();
Q_INVOKABLE qint64 timeToPosition(double time) const; double frameStartTime() const;
Q_INVOKABLE double positionToTime(qint64 position) const; double frameEndTime() const;
double offset() const;
void setOffset(double newOffset);
double zoom() const; double zoom() const;
void setZoom(double zoom); void setZoom(double zoom);
...@@ -35,9 +40,23 @@ public: ...@@ -35,9 +40,23 @@ public:
void setSelectionEndTime(double time); void setSelectionEndTime(double time);
bool selectionActive() const; bool selectionActive() const;
Q_INVOKABLE void init(double frameWidth);
Q_INVOKABLE void onResizeFrameWidth(double frameWidth);
Q_INVOKABLE void onWheel(double y);
Q_INVOKABLE void onSelection(double x1, double x2);
Q_INVOKABLE void resetSelection();
Q_INVOKABLE double timeToPosition(double time) const;
Q_INVOKABLE double positionToTime(double position) const;
signals: signals:
void offsetChanged(); void frameStartTimeChanged();
void frameEndTimeChanged();
void frameTimeChanged(); // any or both together
void zoomChanged(); void zoomChanged();
void selectionStartTimeChanged(); void selectionStartTimeChanged();
...@@ -46,15 +65,22 @@ signals: ...@@ -46,15 +65,22 @@ signals:
private: private:
void shiftFrameTime(int direction);
void setFrameStartTime(double newFrameStartTime);
void setFrameEndTime(double newFrameEndTime);
void updateFrameTime();
void changeZoom(int direction); void changeZoom(int direction);
void changeOffset(int direction);
void onSelectionTime(double t1, double t2); void onSelectionTime(double t1, double t2);
void updateSelectionActive(); void updateSelectionActive();
void setSelectionActive(bool newSelectionActive); void setSelectionActive(bool newSelectionActive);
double m_offset = 0.0; double m_frameWidth = 0.0;
double m_zoom = 2.0;//{ 44100.0 / 512.0 }; double m_frameStartTime = 0.0;
double m_frameEndTime = 0.0;
double m_zoom = 1.0; // see init
double m_selecitonStartTime = 0.0; double m_selecitonStartTime = 0.0;
double m_selectionEndTime = 0.0; double m_selectionEndTime = 0.0;
......
...@@ -29,7 +29,12 @@ void WaveView::setClipKey(const ClipKey& newClipKey) ...@@ -29,7 +29,12 @@ void WaveView::setClipKey(const ClipKey& newClipKey)
void WaveView::paint(QPainter* painter) void WaveView::paint(QPainter* painter)
{ {
au3::IAu3WavePainter::Params params; au3::IAu3WavePainter::Params params;
params.viewRect = QRect(0, 0, width(), height()); params.geometry.clipHeight = height();
params.geometry.clipWidth = width();
params.geometry.relClipLeft = m_clipLeft;
params.geometry.frameLeft = m_context->frameStartTime() * m_context->zoom();
params.geometry.frameWidth = (m_context->frameEndTime() - m_context->frameStartTime()) * m_context->zoom();
params.zoom = m_context->zoom(); params.zoom = m_context->zoom();
const WaveStyle& style = configuration()->waveStyle(); const WaveStyle& style = configuration()->waveStyle();
...@@ -60,7 +65,36 @@ void WaveView::setTimelineContext(TimelineContext* newContext) ...@@ -60,7 +65,36 @@ void WaveView::setTimelineContext(TimelineContext* newContext)
return; return;
} }
//! TODO Subscribe on context props changes if (m_context) {
disconnect(m_context, nullptr, this, nullptr);
}
m_context = newContext; m_context = newContext;
if (m_context) {
connect(m_context, &TimelineContext::frameTimeChanged, this, &WaveView::onFrameTimeChanged);
}
emit timelineContextChanged(); emit timelineContextChanged();
} }
void WaveView::onFrameTimeChanged()
{
update();
}
double WaveView::clipLeft() const
{
return m_clipLeft;
}
void WaveView::setClipLeft(double newClipLeft)
{
if (qFuzzyCompare(m_clipLeft, newClipLeft)) {
return;
}
m_clipLeft = newClipLeft;
emit clipLeftChanged();
update();
}
...@@ -17,6 +17,11 @@ class WaveView : public QQuickPaintedItem ...@@ -17,6 +17,11 @@ class WaveView : public QQuickPaintedItem
Q_PROPERTY(TimelineContext * context READ timelineContext WRITE setTimelineContext NOTIFY timelineContextChanged FINAL) Q_PROPERTY(TimelineContext * context READ timelineContext WRITE setTimelineContext NOTIFY timelineContextChanged FINAL)
Q_PROPERTY(ClipKey clipKey READ clipKey WRITE setClipKey NOTIFY clipKeyChanged FINAL) Q_PROPERTY(ClipKey clipKey READ clipKey WRITE setClipKey NOTIFY clipKeyChanged FINAL)
//! NOTE In a static position, the slip start time corresponds,
//! but it can change while dragging the clip, with the not changing start time
//! (until the end of dragging)
Q_PROPERTY(double clipLeft READ clipLeft WRITE setClipLeft NOTIFY clipLeftChanged FINAL)
muse::Inject<au3::IAu3WavePainter> wavePainter; muse::Inject<au3::IAu3WavePainter> wavePainter;
muse::Inject<IProjectSceneConfiguration> configuration; muse::Inject<IProjectSceneConfiguration> configuration;
...@@ -28,17 +33,22 @@ public: ...@@ -28,17 +33,22 @@ public:
void setClipKey(const ClipKey& newClipKey); void setClipKey(const ClipKey& newClipKey);
TimelineContext* timelineContext() const; TimelineContext* timelineContext() const;
void setTimelineContext(TimelineContext* newContext); void setTimelineContext(TimelineContext* newContext);
double clipLeft() const;
void setClipLeft(double newClipLeft);
void paint(QPainter* painter) override; void paint(QPainter* painter) override;
signals: signals:
void clipKeyChanged(); void clipKeyChanged();
void timelineContextChanged(); void timelineContextChanged();
void clipLeftChanged();
private: private:
void UpdateItemsCache(TimelineContext& trackPanelView);
void onFrameTimeChanged();
ClipKey m_clipKey; ClipKey m_clipKey;
TimelineContext* m_context = nullptr; TimelineContext* m_context = nullptr;
double m_clipLeft = 0;
}; };
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment