timeline: added thumbnail modes that affect how much is rendered
This commit is contained in:
@@ -312,6 +312,11 @@ TimeRangeListFrameIterator::TimeRangeListFrameIterator(const TimeRangeList &list
|
||||
UpdateIndexIfNecessary();
|
||||
}
|
||||
|
||||
rational TimeRangeListFrameIterator::Snap(const rational &r) const
|
||||
{
|
||||
return Timecode::snap_time_to_timebase(r, timebase_, Timecode::kFloor);
|
||||
}
|
||||
|
||||
bool TimeRangeListFrameIterator::GetNext(rational *out)
|
||||
{
|
||||
if (!HasNext()) {
|
||||
@@ -368,7 +373,7 @@ void TimeRangeListFrameIterator::UpdateIndexIfNecessary()
|
||||
range_index_++;
|
||||
|
||||
if (range_index_ < list_.size()) {
|
||||
current_ = Timecode::snap_time_to_timebase(list_.at(range_index_).in(), timebase_, Timecode::kCeil);
|
||||
current_ = Snap(list_.at(range_index_).in());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +207,8 @@ public:
|
||||
TimeRangeListFrameIterator();
|
||||
TimeRangeListFrameIterator(const TimeRangeList &list, const rational &timebase);
|
||||
|
||||
rational Snap(const rational &r) const;
|
||||
|
||||
bool GetNext(rational *out);
|
||||
|
||||
bool HasNext() const;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "common/filefunctions.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "core.h"
|
||||
#include "timeline/timelinecommon.h"
|
||||
#include "ui/colorcoding.h"
|
||||
#include "ui/style/style.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
@@ -104,6 +105,9 @@ void Config::SetDefaults()
|
||||
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
|
||||
|
||||
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailOn);
|
||||
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled);
|
||||
|
||||
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
||||
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
|
||||
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1)));
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "clip.h"
|
||||
|
||||
#include "config/config.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
@@ -108,12 +109,14 @@ void ClipBlock::set_length_and_media_in(const rational &length)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
set_media_in(SequenceToMediaTime(this->length() - length, kSTMIgnoreLoop));
|
||||
}
|
||||
rational old_length = this->length();
|
||||
|
||||
super::set_length_and_media_in(length);
|
||||
|
||||
if (!reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
set_media_in(SequenceToMediaTime(old_length - length, kSTMIgnoreLoop));
|
||||
}
|
||||
}
|
||||
|
||||
rational ClipBlock::media_in() const
|
||||
@@ -124,6 +127,8 @@ rational ClipBlock::media_in() const
|
||||
void ClipBlock::set_media_in(const rational &media_in)
|
||||
{
|
||||
SetStandardValue(kMediaInInput, QVariant::fromValue(media_in));
|
||||
|
||||
RequestInvalidatedFromConnected();
|
||||
}
|
||||
|
||||
void ClipBlock::SetAutocache(bool e)
|
||||
@@ -208,16 +213,22 @@ void ClipBlock::RequestRangeFromConnected(const TimeRange &range)
|
||||
|
||||
if (type == Track::kVideo || type == Track::kAudio) {
|
||||
if (Node *connected = GetConnectedOutput(kBufferIn)) {
|
||||
TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
|
||||
TimeRange max_range = media_range();
|
||||
if (type == Track::kVideo) {
|
||||
// Handle thumbnails
|
||||
RequestRangeForCache(connected->thumbnail_cache(), max_range, range, true, true);
|
||||
RequestRangeForCache(connected->thumbnail_cache(), max_range, range, true, false);
|
||||
{
|
||||
TimeRange thumb_range = range;
|
||||
if (GetAdjustedThumbnailRange(&thumb_range)) {
|
||||
emit connected->thumbnail_cache()->Request(thumb_range);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle video cache
|
||||
RequestRangeForCache(connected->video_frame_cache(), max_range, range, true, IsAutocaching());
|
||||
} else if (type == Track::kAudio) {
|
||||
// Handle waveforms
|
||||
RequestRangeForCache(connected->waveform_cache(), max_range, range, true, true);
|
||||
RequestRangeForCache(connected->waveform_cache(), max_range, range, true, (OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled));
|
||||
|
||||
// Handle audio cache
|
||||
RequestRangeForCache(connected->audio_playback_cache(), max_range, range, true, IsAutocaching());
|
||||
@@ -232,10 +243,13 @@ void ClipBlock::RequestInvalidatedFromConnected()
|
||||
|
||||
if (type == Track::kVideo || type == Track::kAudio) {
|
||||
if (Node *connected = GetConnectedOutput(kBufferIn)) {
|
||||
TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
|
||||
TimeRange max_range = media_range();
|
||||
if (type == Track::kVideo) {
|
||||
// Handle thumbnails
|
||||
RequestInvalidatedForCache(connected->thumbnail_cache(), max_range);
|
||||
TimeRange thumb_range = max_range;
|
||||
if (GetAdjustedThumbnailRange(&thumb_range)) {
|
||||
RequestInvalidatedForCache(connected->thumbnail_cache(), thumb_range);
|
||||
}
|
||||
|
||||
// Handle video cache
|
||||
if (IsAutocaching()) {
|
||||
@@ -243,7 +257,9 @@ void ClipBlock::RequestInvalidatedFromConnected()
|
||||
}
|
||||
} else if (type == Track::kAudio) {
|
||||
// Handle waveforms
|
||||
RequestInvalidatedForCache(connected->waveform_cache(), max_range);
|
||||
if (OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled) {
|
||||
RequestInvalidatedForCache(connected->waveform_cache(), max_range);
|
||||
}
|
||||
|
||||
// Handle audio cache
|
||||
if (IsAutocaching()) {
|
||||
@@ -280,6 +296,34 @@ void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange
|
||||
}
|
||||
}
|
||||
|
||||
bool ClipBlock::GetAdjustedThumbnailRange(TimeRange *r) const
|
||||
{
|
||||
switch (static_cast<Timeline::ThumbnailMode>(OLIVE_CONFIG("TimelineThumbnailMode").toInt())) {
|
||||
case Timeline::kThumbnailOff:
|
||||
// Don't cache any range
|
||||
return false;
|
||||
case Timeline::kThumbnailInOut:
|
||||
{
|
||||
// Only cache in point
|
||||
rational in = this->media_range().in();
|
||||
if (r->Contains(in)) {
|
||||
// Cache only the in point
|
||||
*r = TimeRange(in, in + thumbnail_cache()->GetTimebase());
|
||||
return true;
|
||||
} else {
|
||||
// Cache nothing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case Timeline::kThumbnailOn:
|
||||
// Cache entire range
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
@@ -379,8 +423,10 @@ void ClipBlock::InputValueChangedEvent(const QString &input, int element)
|
||||
if (Node *connected = GetConnectedOutput(kBufferIn)) {
|
||||
if (type == Track::kVideo) {
|
||||
emit connected->video_frame_cache()->CancelAll();
|
||||
//emit connected->thumbnail_cache()->CancelAll();
|
||||
} else if (type == Track::kAudio) {
|
||||
emit connected->audio_playback_cache()->CancelAll();
|
||||
//emit connected->waveform_cache()->CancelAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +232,8 @@ private:
|
||||
void RequestRangeForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool invalidate, bool request);
|
||||
void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range);
|
||||
|
||||
bool GetAdjustedThumbnailRange(TimeRange *r) const;
|
||||
|
||||
QVector<Block*> block_links_;
|
||||
|
||||
TransitionBlock* in_transition_;
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
ThumbnailCache(QObject* parent = nullptr) :
|
||||
FrameHashCache(parent)
|
||||
{
|
||||
SetTimebase(rational(1, 10));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -459,8 +459,16 @@ void PreviewAutoCacher::StartCachingRange(const TimeRange &range, TimeRangeList
|
||||
void PreviewAutoCacher::StartCachingVideoRange(PlaybackCache *cache, const TimeRange &range)
|
||||
{
|
||||
Node *node = cache->parent();
|
||||
pending_video_jobs_.push_back({node, cache, range, TimeRangeListFrameIterator({range}, viewer_node_->GetVideoParams().frame_rate_as_time_base())});
|
||||
video_cache_data_[cache].job_tracker.insert(range, graph_changed_time_);
|
||||
rational using_tb;
|
||||
if (ThumbnailCache *thumbs = dynamic_cast<ThumbnailCache*>(cache)) {
|
||||
using_tb = thumbs->GetTimebase();
|
||||
} else {
|
||||
using_tb = viewer_node_->GetVideoParams().frame_rate_as_time_base();
|
||||
}
|
||||
|
||||
TimeRangeListFrameIterator iterator({range}, using_tb);
|
||||
pending_video_jobs_.push_back({node, cache, range, iterator});
|
||||
video_cache_data_[cache].job_tracker.insert(TimeRange(iterator.Snap(range.in()), range.out()), graph_changed_time_);
|
||||
TryRender();
|
||||
}
|
||||
|
||||
@@ -679,8 +687,6 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational&
|
||||
rvp.video_params.set_divider(VideoParams::GetDividerForTargetResolution(rvp.video_params.width(), rvp.video_params.height(), 160, 120));
|
||||
rvp.force_color_output = display_color_processor_;
|
||||
rvp.force_format = VideoParams::kFormatUnsigned8;
|
||||
|
||||
wave_cache->SetTimebase(rational(1, 10));
|
||||
} else {
|
||||
frame_cache->SetTimebase(viewer_node_->GetVideoParams().frame_rate_as_time_base());
|
||||
}
|
||||
|
||||
@@ -38,6 +38,17 @@ public:
|
||||
kTrimOut
|
||||
};
|
||||
|
||||
enum ThumbnailMode {
|
||||
kThumbnailOff,
|
||||
kThumbnailInOut,
|
||||
kThumbnailOn
|
||||
};
|
||||
|
||||
enum WaveformMode {
|
||||
kWaveformsDisabled,
|
||||
kWaveformsEnabled
|
||||
};
|
||||
|
||||
static bool IsATrimMode(MovementMode mode) {return mode == kTrimIn || mode == kTrimOut;}
|
||||
|
||||
struct EditToInfo {
|
||||
|
||||
@@ -1140,14 +1140,20 @@ void TimelineWidget::ShowContextMenu()
|
||||
toggle_audio_units->setChecked(use_audio_time_units_);
|
||||
connect(toggle_audio_units, &QAction::triggered, this, &TimelineWidget::SetUseAudioTimeUnits);
|
||||
|
||||
QAction* show_thumbnails = menu.addAction(tr("Show Thumbnails"));
|
||||
show_thumbnails->setCheckable(true);
|
||||
show_thumbnails->setChecked(views_.first()->view()->GetShowThumbnails());
|
||||
connect(show_thumbnails, &QAction::triggered, this, &TimelineWidget::SetViewThumbnailsEnabled);
|
||||
{
|
||||
Menu *thumbnail_menu = new Menu(tr("Show Thumbnails"), &menu);
|
||||
menu.addMenu(thumbnail_menu);
|
||||
|
||||
thumbnail_menu->AddActionWithData(tr("Disabled"), Timeline::kThumbnailOff, OLIVE_CONFIG("TimelineThumbnailMode"));
|
||||
thumbnail_menu->AddActionWithData(tr("Only At In/Out Points"), Timeline::kThumbnailInOut, OLIVE_CONFIG("TimelineThumbnailMode"));
|
||||
thumbnail_menu->AddActionWithData(tr("Enabled"), Timeline::kThumbnailOn, OLIVE_CONFIG("TimelineThumbnailMode"));
|
||||
|
||||
connect(thumbnail_menu, &Menu::triggered, this, &TimelineWidget::SetViewThumbnailsEnabled);
|
||||
}
|
||||
|
||||
QAction* show_waveforms = menu.addAction(tr("Show Waveforms"));
|
||||
show_waveforms->setCheckable(true);
|
||||
show_waveforms->setChecked(views_.first()->view()->GetShowWaveforms());
|
||||
show_waveforms->setChecked(OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled);
|
||||
connect(show_waveforms, &QAction::triggered, this, &TimelineWidget::SetViewWaveformsEnabled);
|
||||
|
||||
menu.addSeparator();
|
||||
@@ -1211,16 +1217,14 @@ void TimelineWidget::AddableObjectChanged()
|
||||
|
||||
void TimelineWidget::SetViewWaveformsEnabled(bool e)
|
||||
{
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
tview->view()->SetShowWaveforms(e);
|
||||
}
|
||||
OLIVE_CONFIG("TimelineWaveformMode") = e ? Timeline::kWaveformsEnabled : Timeline::kWaveformsDisabled;
|
||||
UpdateViewports();
|
||||
}
|
||||
|
||||
void TimelineWidget::SetViewThumbnailsEnabled(bool e)
|
||||
void TimelineWidget::SetViewThumbnailsEnabled(QAction *action)
|
||||
{
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
tview->view()->SetShowThumbnails(e);
|
||||
}
|
||||
OLIVE_CONFIG("TimelineThumbnailMode") = action->data();
|
||||
UpdateViewports();
|
||||
}
|
||||
|
||||
void TimelineWidget::FrameRateChanged()
|
||||
|
||||
@@ -423,7 +423,7 @@ private slots:
|
||||
|
||||
void SetViewWaveformsEnabled(bool e);
|
||||
|
||||
void SetViewThumbnailsEnabled(bool e);
|
||||
void SetViewThumbnailsEnabled(QAction *action);
|
||||
|
||||
void FrameRateChanged();
|
||||
|
||||
|
||||
@@ -46,8 +46,6 @@ TimelineView::TimelineView(Qt::Alignment vertical_alignment, QWidget *parent) :
|
||||
ghosts_(nullptr),
|
||||
show_beam_cursor_(false),
|
||||
connected_track_list_(nullptr),
|
||||
show_thumbnails_(true),
|
||||
show_waveforms_(true),
|
||||
transition_overlay_out_(nullptr),
|
||||
transition_overlay_in_(nullptr)
|
||||
{
|
||||
@@ -486,7 +484,6 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
|
||||
int text_height = fm.height();
|
||||
int text_padding = text_height/4; // This ties into the track minimum height being 1.5
|
||||
int text_total_height = text_height + text_padding + text_padding;
|
||||
Q_UNUSED(text_total_height)
|
||||
|
||||
if (foreground) {
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
@@ -524,43 +521,52 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
|
||||
QRect preview_rect = r.toRect();
|
||||
|
||||
// Draw clip thumbnails
|
||||
if (clip->GetTrackType() == Track::kVideo && show_thumbnails_ && preview_rect.height() > r.height()/3) {
|
||||
if (clip->GetTrackType() == Track::kVideo
|
||||
&& OLIVE_CONFIG("TimelineThumbnailMode").toInt() != Timeline::kThumbnailOff
|
||||
&& preview_rect.height() > r.height()/3) {
|
||||
if (const FrameHashCache *thumbs = clip->thumbnails()) {
|
||||
// Start thumbnails underneath clip name
|
||||
preview_rect.adjust(0, text_total_height, 0, 0);
|
||||
|
||||
QRect thumb_rect;
|
||||
painter->setClipRect(preview_rect);
|
||||
painter->setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
painter->setClipRect(preview_rect);
|
||||
|
||||
Sequence *s = clip->track()->sequence();
|
||||
int width = s->GetVideoParams().width();
|
||||
int height = s->GetVideoParams().height();
|
||||
int start;
|
||||
if (height > 0) { // Prevent divide by zero/invalid params
|
||||
double scale = double(preview_rect.height())/double(height);
|
||||
thumb_rect.setWidth(width * scale);
|
||||
start = (preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width() + qFloor(block_in);
|
||||
} else {
|
||||
start = preview_rect.left();
|
||||
}
|
||||
if (OLIVE_CONFIG("TimelineThumbnailMode") == Timeline::kThumbnailOn) {
|
||||
|
||||
for (int i=start; i<preview_rect.right(); i+=thumb_rect.width()+1) {
|
||||
rational time_here = SceneToTime(i - block_in, GetScale(), connected_track_list_->parent()->GetVideoParams().frame_rate_as_time_base()) + media_in;
|
||||
QString thumbnail = thumbs->GetValidCacheFilename(time_here);
|
||||
|
||||
if (!thumbnail.isEmpty()) {
|
||||
QImage img;
|
||||
if (img.load(thumbnail, "jpg")) {
|
||||
double scale = double(preview_rect.height())/double(img.height());
|
||||
thumb_rect = QRect(i, preview_rect.top(), img.width() * scale, preview_rect.height());
|
||||
painter->drawImage(thumb_rect, img);
|
||||
}
|
||||
Sequence *s = clip->track()->sequence();
|
||||
int width = s->GetVideoParams().width();
|
||||
int height = s->GetVideoParams().height();
|
||||
int start;
|
||||
if (height > 0) { // Prevent divide by zero/invalid params
|
||||
double scale = double(preview_rect.height())/double(height);
|
||||
thumb_rect.setWidth(width * scale);
|
||||
start = (((preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width()) * thumb_rect.width()) + qFloor(block_in);
|
||||
} else {
|
||||
start = preview_rect.left();
|
||||
}
|
||||
|
||||
for (int i=start; i<preview_rect.right(); i+=thumb_rect.width()+1) {
|
||||
rational time_here = SceneToTime(i - block_in, GetScale(), connected_track_list_->parent()->GetVideoParams().frame_rate_as_time_base()) + media_in;
|
||||
DrawThumbnail(painter, thumbs, time_here, i, preview_rect, &thumb_rect);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
rational time = clip->media_range().in();
|
||||
time = Timecode::snap_time_to_timebase(time, thumbs->GetTimebase(), Timecode::kFloor);
|
||||
DrawThumbnail(painter, thumbs, time, block_left, preview_rect, &thumb_rect);
|
||||
|
||||
}
|
||||
|
||||
painter->setClipping(false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Draw waveform
|
||||
if (clip->GetTrackType() == Track::kAudio && show_waveforms_) {
|
||||
if (clip->GetTrackType() == Track::kAudio
|
||||
&& OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled) {
|
||||
if (const AudioWaveformCache *wave = clip->waveform()) {
|
||||
rational waveform_start = SceneToTime(block_left - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base()) + media_in;
|
||||
painter->setPen(shadow_color);
|
||||
@@ -733,6 +739,20 @@ qreal TimelineView::GetTimelineRightBound() const
|
||||
return GetTimelineLeftBound() + viewport()->width();
|
||||
}
|
||||
|
||||
void TimelineView::DrawThumbnail(QPainter *painter, const FrameHashCache *thumbs, const rational &time, int x, const QRect &preview_rect, QRect *thumb_rect) const
|
||||
{
|
||||
QString thumbnail = thumbs->GetValidCacheFilename(time);
|
||||
|
||||
if (!thumbnail.isEmpty()) {
|
||||
QImage img;
|
||||
if (img.load(thumbnail, "jpg")) {
|
||||
double scale = double(preview_rect.height())/double(img.height());
|
||||
*thumb_rect = QRect(x, preview_rect.top(), img.width() * scale, preview_rect.height());
|
||||
painter->drawImage(*thumb_rect, img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TimelineView::GetTrackY(int track_index) const
|
||||
{
|
||||
if (!connected_track_list_ || !connected_track_list_->GetTrackCount()) {
|
||||
|
||||
@@ -73,28 +73,6 @@ public:
|
||||
|
||||
Block* GetItemAtScenePos(const rational& time, int track_index) const;
|
||||
|
||||
bool GetShowWaveforms() const
|
||||
{
|
||||
return show_waveforms_;
|
||||
}
|
||||
|
||||
void SetShowWaveforms(bool e)
|
||||
{
|
||||
show_waveforms_ = e;
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
bool GetShowThumbnails() const
|
||||
{
|
||||
return show_thumbnails_;
|
||||
}
|
||||
|
||||
void SetShowThumbnails(bool e)
|
||||
{
|
||||
show_thumbnails_ = e;
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
signals:
|
||||
void MousePressed(TimelineViewMouseEvent* event);
|
||||
void MouseMoved(TimelineViewMouseEvent* event);
|
||||
@@ -154,6 +132,8 @@ private:
|
||||
|
||||
qreal GetTimelineRightBound() const;
|
||||
|
||||
void DrawThumbnail(QPainter *painter, const FrameHashCache *thumbs, const rational &time, int x, const QRect &preview_rect, QRect *thumb_rect) const;
|
||||
|
||||
QHash<Track::Reference, TimeRangeList>* selections_;
|
||||
|
||||
QVector<TimelineViewGhostItem*>* ghosts_;
|
||||
@@ -164,9 +144,6 @@ private:
|
||||
|
||||
TrackList* connected_track_list_;
|
||||
|
||||
bool show_thumbnails_;
|
||||
bool show_waveforms_;
|
||||
|
||||
ClipBlock *transition_overlay_out_;
|
||||
ClipBlock *transition_overlay_in_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user