From 94be06e33e7b6dca876b1b4eb9d3a6f2e1732298 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 00:10:02 +1100 Subject: [PATCH 01/10] use O2 optimization for now due to performance and stability concerns --- app/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e33bd2b7d..2370e85d7 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -68,7 +68,7 @@ if(MSVC) # Enabling this threw warnings from FFmpeg headers that broke compilation. See if this is fixable. #target_compile_options(${OLIVE_TARGET} PRIVATE /W4 /WX) else() - target_compile_options(${OLIVE_TARGET} PRIVATE -O3 -Werror -Wuninitialized -pedantic-errors -Wall -Wextra -Wconversion -Wsign-conversion) + target_compile_options(${OLIVE_TARGET} PRIVATE -O2 -Werror -Wuninitialized -pedantic-errors -Wall -Wextra -Wconversion -Wsign-conversion) endif() target_include_directories( From 2fe03debecc63b411a23a2411b57701d9a1361a3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 00:10:43 +1100 Subject: [PATCH 02/10] reimplement string timecode to frame number conversion --- app/common/timecodefunctions.cpp | 186 ++++++++++++++++++++++++++----- app/common/timecodefunctions.h | 3 + 2 files changed, 164 insertions(+), 25 deletions(-) diff --git a/app/common/timecodefunctions.cpp b/app/common/timecodefunctions.cpp index 2783fd237..2e9b294df 100644 --- a/app/common/timecodefunctions.cpp +++ b/app/common/timecodefunctions.cpp @@ -24,7 +24,7 @@ #include "config/config.h" -QString padded(int arg, int padding) { +QString padded(int64_t arg, int padding) { return QString("%1").arg(arg, padding, 10, QChar('0')); } @@ -35,21 +35,11 @@ QString olive::timestamp_to_timecode(const int64_t ×tamp, { double timestamp_dbl = (rational(timestamp) * timebase).toDouble(); - // Determine what symbol to separate frames (";" is used for drop frame, ":" is non-drop frame) - QString frame_token = ";"; - switch (display) { case kTimecodeNonDropFrame: - frame_token = ":"; - // Convert timestamp from drop frame to non-drop frame - // FIXME: There's probably a better way to do this - if (timebase == rational(1001, 30000)) { - timestamp_dbl = timestamp_dbl / (30000.0/1001.0) * 30.0; - } else if (timebase == rational(1001, 60000)) { - timestamp_dbl = timestamp_dbl / (60000.0/1001.0) * 60.0; - } else if (timebase == rational(1001, 24000)) { - timestamp_dbl = timestamp_dbl / (24000.0/1001.0) * 24.0; + if (timebase.numerator() == 1001) { + timestamp_dbl = timestamp_dbl / timebase.flipped().toDouble() * (static_cast(timebase.denominator())/1000.0); } /* fall-through */ case kTimecodeDropFrame: @@ -63,16 +53,15 @@ QString olive::timestamp_to_timecode(const int64_t ×tamp, prefix = "+"; } - timestamp_dbl = qAbs(timestamp_dbl); - - int total_seconds = qFloor(timestamp_dbl); - - int hours = total_seconds / 3600; - int mins = total_seconds / 60 - hours * 60; - int secs = total_seconds - mins * 60; - if (display == kTimecodeSeconds) { - int fraction = qRound((timestamp_dbl - total_seconds) * 1000); + timestamp_dbl = qAbs(timestamp_dbl); + + int64_t total_seconds = qFloor(timestamp_dbl); + + int64_t hours = total_seconds / 3600; + int64_t mins = total_seconds / 60 - hours * 60; + int64_t secs = total_seconds - mins * 60; + int64_t fraction = qRound64((timestamp_dbl - static_cast(total_seconds)) * 1000); return QString("%1%2:%3:%4.%5").arg(prefix, padded(hours, 2), @@ -80,9 +69,49 @@ QString olive::timestamp_to_timecode(const int64_t ×tamp, padded(secs, 2), padded(fraction, 3)); } else { - rational frame_rate = timebase.flipped(); + // Determine what symbol to separate frames (";" is used for drop frame, ":" is non-drop frame) + QString frame_token; + double frame_rate = timebase.flipped().toDouble(); + int rounded_frame_rate = qRound(frame_rate); + int64_t frames, secs, mins, hours; + int64_t f = timestamp; - int frames = qRound((timestamp_dbl - total_seconds) * frame_rate.toDouble()); + if (display == kTimecodeDropFrame && timebase.numerator() == 1001) { + frame_token = ";"; + + /** + * CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE + * + * Code by David Heidelberger, adapted from Andrew Duncan, further adapted for Olive by Olive Team + * Given an int called framenumber and a double called framerate + * Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off. + */ + + // If frame number is greater than 24 hrs, next operation will rollover clock + f %= (qRound(frame_rate*3600)*24); + + // Number of frames per ten minutes + int64_t framesPer10Minutes = qRound(frame_rate * 600); + int64_t d = f / framesPer10Minutes; + int64_t m = f % framesPer10Minutes; + + // Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate + int64_t dropFrames = qRound(frame_rate * (2.0/30.0)); + + // Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames + f += dropFrames*9*d; + if (m > dropFrames) { + f += dropFrames * ((m - dropFrames) / (qRound(frame_rate)*60 - dropFrames)); + } + } else { + frame_token = ":"; + } + + // non-drop timecode + hours = f / (3600*rounded_frame_rate); + mins = f / (60*rounded_frame_rate) % 60; + secs = f / rounded_frame_rate % 60; + frames = f % rounded_frame_rate; return QString("%1%2:%3:%4%5%6").arg(prefix, padded(hours, 2), @@ -101,6 +130,108 @@ QString olive::timestamp_to_timecode(const int64_t ×tamp, return QString(); } +int64_t olive::timecode_to_timestamp(const QString &timecode, const rational &timebase, const olive::TimecodeDisplay &display, bool* ok) +{ + double timebase_dbl = timebase.toDouble(); + + if (timecode.isEmpty()) { + goto err_fatal; + } + + switch (display) { + case kTimecodeNonDropFrame: + case kTimecodeDropFrame: + case kTimecodeSeconds: + { + const int kTimecodeElementCount = 4; + QStringList timecode_split = timecode.split(QRegExp("(:)|(;)|(\\.)")); + + bool valid; + + // We only deal with HH, MM, SS, and FF. Any values after that are ignored. + while (timecode_split.size() > kTimecodeElementCount) { + timecode_split.removeLast(); + } + + // Convert values to integers + QList timecode_numbers; + + foreach (const QString& element, timecode_split) { + valid = true; + + timecode_numbers.append((element.isEmpty()) ? 0 : element.toLong(&valid)); + + // If element cannot be converted to a number, + if (!valid) { + goto err_fatal; + } + } + + // Ensure value size is always 4 + while (timecode_numbers.size() < 4) { + timecode_numbers.prepend(0); + } + + double frame_rate = timebase.flipped().toDouble(); + int rounded_frame_rate = qRound(frame_rate); + + int64_t hours = timecode_numbers.at(0); + int64_t mins = timecode_numbers.at(1); + int64_t secs = timecode_numbers.at(2); + int64_t frames = timecode_numbers.at(3); + + int64_t sec_count = (hours*3600 + mins*60 + secs); + int64_t timestamp = sec_count*rounded_frame_rate + frames; + + if (display == kTimecodeDropFrame && timebase.numerator() == 1001) { + + // Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate + int64_t dropFrames = qRound64(frame_rate * (2.0/30.0)); + + // d and m need to be calculated from + int64_t real_fr_ts = qRound64(static_cast(sec_count)*frame_rate) + frames; + + int64_t framesPer10Minutes = qRound(frame_rate * 600); + int64_t d = real_fr_ts / framesPer10Minutes; + int64_t m = real_fr_ts % framesPer10Minutes; + + if (m > dropFrames) { + timestamp -= dropFrames * ((m - dropFrames) / (qRound(frame_rate)*60 - dropFrames)); + } + timestamp -= dropFrames*9*d; + } + + if (ok) *ok = true; + return timestamp; + } + case kMilliseconds: + { + bool valid; + double timecode_secs = timecode.toDouble(&valid); + + if (valid) { + // Convert milliseconds to seconds + timecode_secs *= 0.001; + + // Convert seconds to frames + timecode_secs /= timebase_dbl; + + if (ok) *ok = true; + return qRound(timecode_secs); + } else { + goto err_fatal; + } + } + case kFrames: + if (ok) *ok = true; + return timecode.toLong(ok); + } + +err_fatal: + if (ok) *ok = false; + return 0; +} + rational olive::timestamp_to_time(const int64_t ×tamp, const rational &timebase) { return rational(timestamp) * timebase; @@ -108,7 +239,12 @@ rational olive::timestamp_to_time(const int64_t ×tamp, const rational &time int64_t olive::time_to_timestamp(const rational &time, const rational &timebase) { - return qRound64(time.toDouble() * timebase.flipped().toDouble()); + return time_to_timestamp(time.toDouble(), timebase); +} + +int64_t olive::time_to_timestamp(const double &time, const rational &timebase) +{ + return qRound64(time * timebase.flipped().toDouble()); } olive::TimecodeDisplay olive::CurrentTimecodeDisplay() diff --git a/app/common/timecodefunctions.h b/app/common/timecodefunctions.h index 3bd06a72e..393168a84 100644 --- a/app/common/timecodefunctions.h +++ b/app/common/timecodefunctions.h @@ -42,7 +42,10 @@ TimecodeDisplay CurrentTimecodeDisplay(); */ QString timestamp_to_timecode(const int64_t ×tamp, const rational& timebase, const TimecodeDisplay& display, bool show_plus_if_positive = false); +int64_t timecode_to_timestamp(const QString& timecode, const rational& timebase, const TimecodeDisplay& display, bool *ok = nullptr); + int64_t time_to_timestamp(const rational& time, const rational& timebase); +int64_t time_to_timestamp(const double& time, const rational& timebase); rational timestamp_to_time(const int64_t& timestamp, const rational& timebase); From f2013a6f3d7d8e3d4297414800fb8ff18732e081 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 00:11:47 +1100 Subject: [PATCH 03/10] created time slider UI widget Based on the integer slider, this can be used to interactively control time. --- .../nodeparamviewwidgetbridge.cpp | 2 +- app/widget/slider/CMakeLists.txt | 2 + app/widget/slider/floatslider.cpp | 11 ++++ app/widget/slider/floatslider.h | 5 ++ app/widget/slider/integerslider.cpp | 25 +++++++- app/widget/slider/integerslider.h | 11 ++-- app/widget/slider/sliderbase.cpp | 60 +++++++------------ app/widget/slider/sliderbase.h | 8 +++ app/widget/slider/stringslider.cpp | 6 ++ app/widget/slider/stringslider.h | 3 + app/widget/slider/timeslider.cpp | 31 ++++++++++ app/widget/slider/timeslider.h | 24 ++++++++ 12 files changed, 140 insertions(+), 48 deletions(-) create mode 100644 app/widget/slider/timeslider.cpp create mode 100644 app/widget/slider/timeslider.h diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 311a4b5ed..99bd91444 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -58,7 +58,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() { IntegerSlider* slider = new IntegerSlider(); widgets_.append(slider); - connect(slider, SIGNAL(ValueChanged(int)), this, SLOT(WidgetCallback())); + connect(slider, SIGNAL(ValueChanged(int64_t)), this, SLOT(WidgetCallback())); break; } case NodeParam::kFloat: diff --git a/app/widget/slider/CMakeLists.txt b/app/widget/slider/CMakeLists.txt index b58325bb3..37bd57931 100644 --- a/app/widget/slider/CMakeLists.txt +++ b/app/widget/slider/CMakeLists.txt @@ -28,5 +28,7 @@ set(OLIVE_SOURCES widget/slider/sliderlineedit.cpp widget/slider/stringslider.h widget/slider/stringslider.cpp + widget/slider/timeslider.h + widget/slider/timeslider.cpp PARENT_SCOPE ) diff --git a/app/widget/slider/floatslider.cpp b/app/widget/slider/floatslider.cpp index f351da7d5..7d1dad694 100644 --- a/app/widget/slider/floatslider.cpp +++ b/app/widget/slider/floatslider.cpp @@ -53,6 +53,17 @@ void FloatSlider::SetDecimalPlaces(int i) UpdateLabel(Value()); } +QString FloatSlider::ValueToString(const QVariant &v) +{ + return QString::number(v.toDouble(), 'f', decimal_places_); +} + +QVariant FloatSlider::StringToValue(const QString &s, bool *ok) +{ + // Allow both floats and integers for either modes + return s.toDouble(ok); +} + void FloatSlider::ConvertValue(QVariant v) { emit ValueChanged(v.toDouble()); diff --git a/app/widget/slider/floatslider.h b/app/widget/slider/floatslider.h index 1f9c4b6f2..337c62310 100644 --- a/app/widget/slider/floatslider.h +++ b/app/widget/slider/floatslider.h @@ -39,6 +39,11 @@ public: void SetDecimalPlaces(int i); +protected: + virtual QString ValueToString(const QVariant& v) override; + + virtual QVariant StringToValue(const QString& s, bool* ok) override; + signals: void ValueChanged(double); diff --git a/app/widget/slider/integerslider.cpp b/app/widget/slider/integerslider.cpp index 511f2b581..d5dd940c1 100644 --- a/app/widget/slider/integerslider.cpp +++ b/app/widget/slider/integerslider.cpp @@ -31,21 +31,40 @@ int IntegerSlider::GetValue() return Value().toInt(); } -void IntegerSlider::SetValue(const int &v) +void IntegerSlider::SetValue(const int64_t &v) { SliderBase::SetValue(v); } -void IntegerSlider::SetMinimum(const int &d) +void IntegerSlider::SetMinimum(const int64_t &d) { SetMinimumInternal(d); } -void IntegerSlider::SetMaximum(const int &d) +void IntegerSlider::SetMaximum(const int64_t &d) { SetMaximumInternal(d); } +QVariant IntegerSlider::StringToValue(const QString &s, bool *ok) +{ + bool valid; + + // Allow both floats and integers for either modes + double decimal_val = s.toDouble(&valid); + + if (ok) { + *ok = valid; + } + + if (valid) { + // But for an integer, we round it + return qRound(decimal_val); + } + + return QVariant(); +} + void IntegerSlider::ConvertValue(QVariant v) { emit ValueChanged(v.toInt()); diff --git a/app/widget/slider/integerslider.h b/app/widget/slider/integerslider.h index f5f78116c..f57fd7883 100644 --- a/app/widget/slider/integerslider.h +++ b/app/widget/slider/integerslider.h @@ -31,14 +31,17 @@ public: int GetValue(); - void SetValue(const int& v); + void SetValue(const int64_t& v); - void SetMinimum(const int& d); + void SetMinimum(const int64_t& d); - void SetMaximum(const int& d); + void SetMaximum(const int64_t& d); + +protected: + virtual QVariant StringToValue(const QString& s, bool* ok) override; signals: - void ValueChanged(int); + void ValueChanged(int64_t); private slots: void ConvertValue(QVariant v); diff --git a/app/widget/slider/sliderbase.cpp b/app/widget/slider/sliderbase.cpp index 53b5442ed..f20d884d6 100644 --- a/app/widget/slider/sliderbase.cpp +++ b/app/widget/slider/sliderbase.cpp @@ -31,7 +31,8 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : has_min_(false), has_max_(false), mode_(mode), - dragged_(false) + dragged_(false), + require_valid_input_(true) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); @@ -66,6 +67,11 @@ void SliderBase::SetDragMultiplier(const double &d) drag_multiplier_ = d; } +void SliderBase::SetRequireValidInput(bool e) +{ + require_valid_input_ = e; +} + const QVariant &SliderBase::Value() { if (dragged_) { @@ -127,26 +133,18 @@ const QVariant &SliderBase::ClampValue(const QVariant &v) void SliderBase::UpdateLabel(const QVariant &v) { - switch (mode_) { - case kString: - { - QString vstr = v.toString(); + label_->setText(ValueToString(v)); +} - if (vstr.isEmpty()) { - label_->setText(tr("(none)")); - } else { - label_->setText(vstr); - } - break; - } - case kInteger: - label_->setText(v.toString()); - break; - case kFloat: - // For floats, we show a limited number of decimal places - label_->setText(QString::number(v.toDouble(), 'f', decimal_places_)); - break; - } +QString SliderBase::ValueToString(const QVariant &v) +{ + return v.toString(); +} + +QVariant SliderBase::StringToValue(const QString &s, bool *ok) +{ + *ok = true; + return s; } void SliderBase::LabelPressed() @@ -224,25 +222,7 @@ void SliderBase::LabelDragged(int i) void SliderBase::LineEditConfirmed() { bool is_valid = true; - QVariant test_val; - - // Check whether the entered value is valid for this mode - switch (mode_) { - case kString: - // Anything goes for a string - test_val = editor_->text(); - break; - case kInteger: - case kFloat: - // Allow both floats and integers for either modes - test_val = editor_->text().toDouble(&is_valid); - - if (is_valid && mode_ == kInteger) { - // But for an integer, we round it - test_val = qRound(test_val.toDouble()); - } - break; - } + QVariant test_val = StringToValue(editor_->text(), &is_valid); // Ensure editor doesn't signal that the focus is lost editor_->blockSignals(true); @@ -253,7 +233,7 @@ void SliderBase::LineEditConfirmed() setCurrentWidget(label_); emit ValueChanged(value_); - } else { + } else if (require_valid_input_) { QMessageBox::critical(this, tr("Invalid Value"), tr("The entered value is not valid for this field."), diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/sliderbase.h index 15964315c..d79dde4e5 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/sliderbase.h @@ -40,6 +40,8 @@ public: void SetDragMultiplier(const double& d); + void SetRequireValidInput(bool e); + signals: void ValueChanged(QVariant v); @@ -54,6 +56,10 @@ protected: void UpdateLabel(const QVariant& v); + virtual QString ValueToString(const QVariant &v); + + virtual QVariant StringToValue(const QString& s, bool* ok); + virtual void changeEvent(QEvent* e) override; int decimal_places_; @@ -83,6 +89,8 @@ private: QVariant temp_dragged_value_; + bool require_valid_input_; + private slots: void LabelPressed(); diff --git a/app/widget/slider/stringslider.cpp b/app/widget/slider/stringslider.cpp index 23d01262f..5f52f2fee 100644 --- a/app/widget/slider/stringslider.cpp +++ b/app/widget/slider/stringslider.cpp @@ -36,6 +36,12 @@ void StringSlider::SetValue(const QString &v) SliderBase::SetValue(v); } +QString StringSlider::ValueToString(const QVariant &v) +{ + QString vstr = v.toString(); + return (vstr.isEmpty()) ? tr("(none)") : vstr; +} + void StringSlider::ConvertValue(QVariant v) { emit ValueChanged(v.toString()); diff --git a/app/widget/slider/stringslider.h b/app/widget/slider/stringslider.h index 020a46f9e..f086d334a 100644 --- a/app/widget/slider/stringslider.h +++ b/app/widget/slider/stringslider.h @@ -35,6 +35,9 @@ public: void SetValue(const QString& v); +protected: + virtual QString ValueToString(const QVariant& value) override; + signals: void ValueChanged(QString); diff --git a/app/widget/slider/timeslider.cpp b/app/widget/slider/timeslider.cpp new file mode 100644 index 000000000..7f91aecc9 --- /dev/null +++ b/app/widget/slider/timeslider.cpp @@ -0,0 +1,31 @@ +#include "timeslider.h" + +#include "common/timecodefunctions.h" + +TimeSlider::TimeSlider(QWidget *parent) : + IntegerSlider(parent) +{ + SetMinimum(0); +} + +void TimeSlider::SetTimebase(const rational &timebase) +{ + timebase_ = timebase; +} + +QString TimeSlider::ValueToString(const QVariant &v) +{ + if (timebase_.isNull()) { + // We can't generate a timecode without a timebase, so we just return the number + return IntegerSlider::ValueToString(v); + } + + return olive::timestamp_to_timecode(v.toLongLong(), + timebase_, + olive::CurrentTimecodeDisplay()); +} + +QVariant TimeSlider::StringToValue(const QString &s, bool *ok) +{ + return olive::timecode_to_timestamp(s, timebase_, olive::CurrentTimecodeDisplay(), ok); +} diff --git a/app/widget/slider/timeslider.h b/app/widget/slider/timeslider.h new file mode 100644 index 000000000..331572d96 --- /dev/null +++ b/app/widget/slider/timeslider.h @@ -0,0 +1,24 @@ +#ifndef TIMESLIDER_H +#define TIMESLIDER_H + +#include "common/rational.h" +#include "integerslider.h" + +class TimeSlider : public IntegerSlider +{ +public: + TimeSlider(QWidget* parent = nullptr); + + void SetTimebase(const rational& timebase); + +protected: + virtual QString ValueToString(const QVariant& v) override; + + virtual QVariant StringToValue(const QString& s, bool* ok) override; + +private: + rational timebase_; + +}; + +#endif // TIMESLIDER_H From 0080e01af67a058127188c0dc9e78d46efc4f88e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 00:12:19 +1100 Subject: [PATCH 04/10] use drop-frame timecode by default --- app/config/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/config.cpp b/app/config/config.cpp index 443a4e752..a852d3ae0 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -49,7 +49,7 @@ Config &Config::Current() void Config::SetDefaults() { config_map_.clear(); - config_map_["TimecodeDisplay"] = olive::kTimecodeNonDropFrame; + config_map_["TimecodeDisplay"] = olive::kTimecodeDropFrame; config_map_["DefaultStillLength"] = QVariant::fromValue(rational(2)); config_map_["HoverFocus"] = false; config_map_["AudioScrubbing"] = true; From d6fe49aad3e473c47f1780ee362ac9a62a8a6d6a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 00:12:46 +1100 Subject: [PATCH 05/10] started trackview widgets for timeline view --- app/widget/timelinewidget/CMakeLists.txt | 3 + .../timelinewidget/timelineandtrackview.cpp | 36 ++++++ .../timelinewidget/timelineandtrackview.h | 32 +++++ app/widget/timelinewidget/timelinewidget.cpp | 110 +++++++++++++----- app/widget/timelinewidget/timelinewidget.h | 13 ++- .../timelinewidget/trackview/CMakeLists.txt | 22 ++++ .../timelinewidget/trackview/trackview.cpp | 29 +++++ .../timelinewidget/trackview/trackview.h | 15 +++ 8 files changed, 225 insertions(+), 35 deletions(-) create mode 100644 app/widget/timelinewidget/timelineandtrackview.cpp create mode 100644 app/widget/timelinewidget/timelineandtrackview.h create mode 100644 app/widget/timelinewidget/trackview/CMakeLists.txt create mode 100644 app/widget/timelinewidget/trackview/trackview.cpp create mode 100644 app/widget/timelinewidget/trackview/trackview.h diff --git a/app/widget/timelinewidget/CMakeLists.txt b/app/widget/timelinewidget/CMakeLists.txt index a5a54425c..1f29b4d15 100644 --- a/app/widget/timelinewidget/CMakeLists.txt +++ b/app/widget/timelinewidget/CMakeLists.txt @@ -14,12 +14,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(trackview) add_subdirectory(tool) add_subdirectory(undo) add_subdirectory(view) set(OLIVE_SOURCES ${OLIVE_SOURCES} + widget/timelinewidget/timelineandtrackview.h + widget/timelinewidget/timelineandtrackview.cpp widget/timelinewidget/timelinescaledobject.h widget/timelinewidget/timelinescaledobject.cpp widget/timelinewidget/timelinewidget.h diff --git a/app/widget/timelinewidget/timelineandtrackview.cpp b/app/widget/timelinewidget/timelineandtrackview.cpp new file mode 100644 index 000000000..7995d007e --- /dev/null +++ b/app/widget/timelinewidget/timelineandtrackview.cpp @@ -0,0 +1,36 @@ +#include "timelineandtrackview.h" + +#include + +TimelineAndTrackView::TimelineAndTrackView(const TrackType &type, Qt::Alignment vertical_alignment, QWidget *parent) : + QWidget(parent) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setSpacing(0); + layout->setMargin(0); + + splitter_ = new QSplitter(Qt::Horizontal); + splitter_->setChildrenCollapsible(false); + layout->addWidget(splitter_); + + track_view_ = new TrackView(); + splitter_->addWidget(track_view_); + + view_ = new TimelineView(type, vertical_alignment); + splitter_->addWidget(view_); +} + +QSplitter *TimelineAndTrackView::splitter() const +{ + return splitter_; +} + +TimelineView *TimelineAndTrackView::view() const +{ + return view_; +} + +TrackView *TimelineAndTrackView::track_view() const +{ + return track_view_; +} diff --git a/app/widget/timelinewidget/timelineandtrackview.h b/app/widget/timelinewidget/timelineandtrackview.h new file mode 100644 index 000000000..c9af4144f --- /dev/null +++ b/app/widget/timelinewidget/timelineandtrackview.h @@ -0,0 +1,32 @@ +#ifndef TIMELINEANDTRACKVIEW_H +#define TIMELINEANDTRACKVIEW_H + +#include +#include + +#include "view/timelineview.h" +#include "trackview/trackview.h" + +class TimelineAndTrackView : public QWidget +{ +public: + TimelineAndTrackView(const TrackType& type, + Qt::Alignment vertical_alignment = Qt::AlignTop, + QWidget* parent = nullptr); + + QSplitter* splitter() const; + + TimelineView* view() const; + + TrackView* track_view() const; + +private: + QSplitter* splitter_; + + TimelineView* view_; + + TrackView* track_view_; + +}; + +#endif // TIMELINEANDTRACKVIEW_H diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index ecf24e6e8..2460cedf1 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -7,6 +7,7 @@ #include "core.h" #include "common/timecodefunctions.h" #include "tool/tool.h" +#include "trackview/trackview.h" TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent), @@ -15,26 +16,34 @@ TimelineWidget::TimelineWidget(QWidget *parent) : timeline_node_(nullptr), playhead_(0) { - QVBoxLayout* layout = new QVBoxLayout(this); - layout->setSpacing(0); - layout->setMargin(0); + QVBoxLayout* vert_layout = new QVBoxLayout(this); + vert_layout->setSpacing(0); + vert_layout->setMargin(0); + + QHBoxLayout* ruler_and_time_layout = new QHBoxLayout(); + vert_layout->addLayout(ruler_and_time_layout); + + timecode_label_ = new TimeSlider(); + connect(timecode_label_, SIGNAL(ValueChanged(int64_t)), this, SIGNAL(TimeChanged(const int64_t&))); + connect(timecode_label_, SIGNAL(ValueChanged(int64_t)), this, SLOT(UpdateInternalTime(const int64_t&))); + ruler_and_time_layout->addWidget(timecode_label_); ruler_ = new TimeRuler(true); connect(ruler_, SIGNAL(TimeChanged(const int64_t&)), this, SIGNAL(TimeChanged(const int64_t&))); connect(ruler_, SIGNAL(TimeChanged(const int64_t&)), this, SLOT(UpdateInternalTime(const int64_t&))); - layout->addWidget(ruler_); + ruler_and_time_layout->addWidget(ruler_); // Create list of TimelineViews - these MUST correspond to the ViewType enum QSplitter* view_splitter = new QSplitter(Qt::Vertical); view_splitter->setChildrenCollapsible(false); - layout->addWidget(view_splitter); + vert_layout->addWidget(view_splitter); // Video view - views_.append(new TimelineView(kTrackTypeVideo, Qt::AlignBottom)); + views_.append(new TimelineAndTrackView(kTrackTypeVideo, Qt::AlignBottom)); // Audio view - views_.append(new TimelineView(kTrackTypeAudio, Qt::AlignTop)); + views_.append(new TimelineAndTrackView(kTrackTypeAudio, Qt::AlignTop)); // Create tools tools_.resize(olive::tool::kCount); @@ -58,14 +67,16 @@ TimelineWidget::TimelineWidget(QWidget *parent) : // Global scrollbar horizontal_scroll_ = new QScrollBar(Qt::Horizontal); connect(horizontal_scroll_, SIGNAL(valueChanged(int)), ruler_, SLOT(SetScroll(int))); - connect(views_.first()->horizontalScrollBar(), SIGNAL(rangeChanged(int, int)), horizontal_scroll_, SLOT(setRange(int, int))); - layout->addWidget(horizontal_scroll_); + connect(views_.first()->view()->horizontalScrollBar(), SIGNAL(rangeChanged(int, int)), horizontal_scroll_, SLOT(setRange(int, int))); + vert_layout->addWidget(horizontal_scroll_); + + foreach (TimelineAndTrackView* tview, views_) { + TimelineView* view = tview->view(); - foreach (TimelineView* view, views_) { view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - view_splitter->addWidget(view); + view_splitter->addWidget(tview); connect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)), ruler_, SLOT(SetScroll(int))); connect(view, SIGNAL(ScaleChanged(double)), this, SLOT(SetScale(double))); @@ -84,8 +95,12 @@ TimelineWidget::TimelineWidget(QWidget *parent) : connect(view, SIGNAL(DragLeft(QDragLeaveEvent*)), this, SLOT(ViewDragLeft(QDragLeaveEvent*))); connect(view, SIGNAL(DragDropped(TimelineViewMouseEvent*)), this, SLOT(ViewDragDropped(TimelineViewMouseEvent*))); + connect(tview->splitter(), SIGNAL(splitterMoved(int, int)), this, SLOT(UpdateHorizontalSplitters())); + // Connect each view's scroll to each other - foreach (TimelineView* other_view, views_) { + foreach (TimelineAndTrackView* other_tview, views_) { + TimelineView* other_view = other_tview->view(); + if (view != other_view) { connect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)), other_view->horizontalScrollBar(), SLOT(setValue(int))); } @@ -121,9 +136,10 @@ void TimelineWidget::SetTimebase(const rational &timebase) SetTimebaseInternal(timebase); ruler_->SetTimebase(timebase); + timecode_label_->SetTimebase(timebase); - foreach (TimelineView* view, views_) { - view->SetTimebase(timebase); + foreach (TimelineAndTrackView* view, views_) { + view->view()->SetTimebase(timebase); } } @@ -131,15 +147,19 @@ void TimelineWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); + // Update horizontal scrollbar's page step to the width of the panel horizontal_scroll_->setPageStep(horizontal_scroll_->width()); + + // Update timecode label size + UpdateTimecodeWidthFromSplitters(views_.first()->splitter()); } -void TimelineWidget::SetTime(const int64_t ×tamp) +void TimelineWidget::SetTime(int64_t timestamp) { ruler_->SetTime(timestamp); - foreach (TimelineView* view, views_) { - view->SetTime(timestamp); + foreach (TimelineAndTrackView* view, views_) { + view->view()->SetTime(timestamp); } UpdateInternalTime(timestamp); @@ -175,7 +195,7 @@ void TimelineWidget::ConnectTimelineNode(TimelineOutput *node) for (int i=0;i(i); - TimelineView* view = views_.at(i); + TimelineView* view = views_.at(i)->view(); view->SetEndTime(timeline_node_->timeline_length()); @@ -204,15 +224,15 @@ void TimelineWidget::ZoomOut() void TimelineWidget::SelectAll() { - foreach (TimelineView* view, views_) { - view->SelectAll(); + foreach (TimelineAndTrackView* view, views_) { + view->view()->SelectAll(); } } void TimelineWidget::DeselectAll() { - foreach (TimelineView* view, views_) { - view->DeselectAll(); + foreach (TimelineAndTrackView* view, views_) { + view->view()->DeselectAll(); } } @@ -440,12 +460,12 @@ TrackOutput *TimelineWidget::GetTrackFromReference(const TrackReference &ref) int TimelineWidget::GetTrackY(const TrackReference &ref) { - return views_.at(ref.type())->GetTrackY(ref.index()); + return views_.at(ref.type())->view()->GetTrackY(ref.index()); } int TimelineWidget::GetTrackHeight(const TrackReference &ref) { - return views_.at(ref.type())->GetTrackHeight(ref.index()); + return views_.at(ref.type())->view()->GetTrackHeight(ref.index()); } void TimelineWidget::CenterOn(qreal scene_pos) @@ -473,8 +493,8 @@ void TimelineWidget::SetScale(double scale) ghost->SetScale(scale_); } - foreach (TimelineView* view, views_) { - view->SetScale(scale_); + foreach (TimelineAndTrackView* view, views_) { + view->view()->SetScale(scale_); } } @@ -497,12 +517,13 @@ bool TimelineWidget::HasGhosts() void TimelineWidget::UpdateInternalTime(const int64_t ×tamp) { playhead_ = timestamp; + timecode_label_->SetValue(timestamp); } void TimelineWidget::UpdateTimelineLength(const rational &length) { - foreach (TimelineView* view, views_) { - view->SetEndTime(length); + foreach (TimelineAndTrackView* view, views_) { + view->view()->SetEndTime(length); } } @@ -587,7 +608,7 @@ void TimelineWidget::AddBlock(Block *block, TrackReference track) block_items_.insert(block, item); // Add item to graphics scene - views_.at(track.type())->scene()->addItem(item); + views_.at(track.type())->view()->scene()->addItem(item); connect(block, SIGNAL(Refreshed()), this, SLOT(BlockChanged())); break; @@ -628,11 +649,33 @@ void TimelineWidget::BlockChanged() } } +void TimelineWidget::UpdateHorizontalSplitters() +{ + QSplitter* sender_splitter = static_cast(sender()); + + foreach (TimelineAndTrackView* tview, views_) { + QSplitter* recv_splitter = tview->splitter(); + + if (recv_splitter != sender_splitter) { + recv_splitter->blockSignals(true); + recv_splitter->setSizes(sender_splitter->sizes()); + recv_splitter->blockSignals(false); + } + } + + UpdateTimecodeWidthFromSplitters(sender_splitter); +} + +void TimelineWidget::UpdateTimecodeWidthFromSplitters(QSplitter* s) +{ + timecode_label_->setFixedWidth(s->sizes().first() + s->handleWidth()); +} + void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) { ghost->SetScale(scale_); ghost_items_.append(ghost); - views_.at(ghost->Track().type())->scene()->addItem(ghost); + views_.at(ghost->Track().type())->view()->scene()->addItem(ghost); } void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected) @@ -662,8 +705,9 @@ void TimelineWidget::MoveRubberBandSelect(bool select_links) QList new_selected_list; - foreach (TimelineView* view, views_) { + foreach (TimelineAndTrackView* tview, views_) { // Map global mouse coordinates to viewport + TimelineView* view = tview->view(); QRect mapped_rect(view->viewport()->mapFromGlobal(drag_origin_), view->viewport()->mapFromGlobal(rubberband_now)); @@ -711,7 +755,9 @@ void TimelineWidget::EndRubberBandSelect(bool select_links) void TimelineWidget::StartHandDrag() { // Determine which view to hand drag by which is under the cursor now - foreach (TimelineView* view, views_) { + foreach (TimelineAndTrackView* tview, views_) { + TimelineView* view = tview->view(); + if (view->underMouse()) { hand_drag_view_ = view; hand_drag_view_origin_ = view->GetScrollCoordinates(); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index dd4b6a6fc..74faebc1e 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -5,8 +5,9 @@ #include #include +#include "timelineandtrackview.h" +#include "widget/slider/timeslider.h" #include "widget/timelinewidget/timelinescaledobject.h" -#include "widget/timelinewidget/view/timelineview.h" #include "widget/timeruler/timeruler.h" /** @@ -22,7 +23,7 @@ public: void Clear(); - void SetTime(const int64_t& timestamp); + void SetTime(int64_t timestamp); void ConnectTimelineNode(TimelineOutput* node); @@ -324,7 +325,7 @@ private: TrackOutput* GetTrackFromReference(const TrackReference& ref); - QList views_; + QList views_; TimeRuler* ruler_; @@ -334,6 +335,8 @@ private: QScrollBar* horizontal_scroll_; + TimeSlider* timecode_label_; + int GetTrackY(const TrackReference& ref); int GetTrackHeight(const TrackReference& ref); @@ -372,6 +375,10 @@ private slots: */ void BlockChanged(); + void UpdateHorizontalSplitters(); + + void UpdateTimecodeWidthFromSplitters(QSplitter *s); + }; #endif // TIMELINEWIDGET_H diff --git a/app/widget/timelinewidget/trackview/CMakeLists.txt b/app/widget/timelinewidget/trackview/CMakeLists.txt new file mode 100644 index 000000000..bbd5bc605 --- /dev/null +++ b/app/widget/timelinewidget/trackview/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/timelinewidget/trackview/trackview.h + widget/timelinewidget/trackview/trackview.cpp + PARENT_SCOPE +) diff --git a/app/widget/timelinewidget/trackview/trackview.cpp b/app/widget/timelinewidget/trackview/trackview.cpp new file mode 100644 index 000000000..712360b55 --- /dev/null +++ b/app/widget/timelinewidget/trackview/trackview.cpp @@ -0,0 +1,29 @@ +#include "trackview.h" + +#include +#include + +TrackView::TrackView(QWidget *parent) : + QWidget(parent) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setMargin(0); + + QSplitter* splitter = new QSplitter(Qt::Vertical); + splitter->setChildrenCollapsible(false); + splitter->setHandleWidth(1); + layout->addWidget(splitter); + + QWidget* test1 = new QWidget(); + test1->setStyleSheet("background: red;"); + splitter->addWidget(test1); + + QWidget* test3 = new QWidget(); + test3->setStyleSheet("background: yellow;"); + splitter->addWidget(test3); + + QWidget* test2 = new QWidget(); + test2->setStyleSheet("background: blue;"); + splitter->addWidget(test2); +} diff --git a/app/widget/timelinewidget/trackview/trackview.h b/app/widget/timelinewidget/trackview/trackview.h new file mode 100644 index 000000000..6277a4c2b --- /dev/null +++ b/app/widget/timelinewidget/trackview/trackview.h @@ -0,0 +1,15 @@ +#ifndef TRACKVIEW_H +#define TRACKVIEW_H + +#include + +class TrackView : public QWidget +{ +public: + TrackView(QWidget* parent = nullptr); + +private: + +}; + +#endif // TRACKVIEW_H From 00c06e3e6b36d167905762923c4482b5eb9b3a81 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 00:14:45 +1100 Subject: [PATCH 06/10] update timeslider's label automatically when the timebase changes --- app/widget/slider/timeslider.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/widget/slider/timeslider.cpp b/app/widget/slider/timeslider.cpp index 7f91aecc9..9510e34bc 100644 --- a/app/widget/slider/timeslider.cpp +++ b/app/widget/slider/timeslider.cpp @@ -11,6 +11,9 @@ TimeSlider::TimeSlider(QWidget *parent) : void TimeSlider::SetTimebase(const rational &timebase) { timebase_ = timebase; + + // Refresh label since we have a new timebase to generate a timecode with + UpdateLabel(Value()); } QString TimeSlider::ValueToString(const QVariant &v) From dd8d1e84b0b9832aa33e45632763b90ceb12c7e6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 00:24:50 +1100 Subject: [PATCH 07/10] small visual improvements to the timeline's timecode label --- app/widget/slider/sliderbase.cpp | 6 ++++++ app/widget/slider/sliderbase.h | 2 ++ app/widget/timelinewidget/timelinewidget.cpp | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/widget/slider/sliderbase.cpp b/app/widget/slider/sliderbase.cpp index f20d884d6..06002b406 100644 --- a/app/widget/slider/sliderbase.cpp +++ b/app/widget/slider/sliderbase.cpp @@ -37,6 +37,7 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); label_ = new SliderLabel(this); + addWidget(label_); editor_ = new SliderLineEdit(this); @@ -72,6 +73,11 @@ void SliderBase::SetRequireValidInput(bool e) require_valid_input_ = e; } +void SliderBase::SetAlignment(Qt::Alignment alignment) +{ + label_->setAlignment(alignment); +} + const QVariant &SliderBase::Value() { if (dragged_) { diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/sliderbase.h index d79dde4e5..1c50aee4b 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/sliderbase.h @@ -42,6 +42,8 @@ public: void SetRequireValidInput(bool e); + void SetAlignment(Qt::Alignment alignment); + signals: void ValueChanged(QVariant v); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 2460cedf1..b5cc509ec 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -24,6 +24,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) : vert_layout->addLayout(ruler_and_time_layout); timecode_label_ = new TimeSlider(); + timecode_label_->SetAlignment(Qt::AlignCenter); + timecode_label_->setVisible(false); connect(timecode_label_, SIGNAL(ValueChanged(int64_t)), this, SIGNAL(TimeChanged(const int64_t&))); connect(timecode_label_, SIGNAL(ValueChanged(int64_t)), this, SLOT(UpdateInternalTime(const int64_t&))); ruler_and_time_layout->addWidget(timecode_label_); @@ -138,6 +140,8 @@ void TimelineWidget::SetTimebase(const rational &timebase) ruler_->SetTimebase(timebase); timecode_label_->SetTimebase(timebase); + timecode_label_->setVisible(!timebase.isNull()); + foreach (TimelineAndTrackView* view, views_) { view->view()->SetTimebase(timebase); } From 50f4e8767d39bba962e2a4353102633885a776fc Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 05:13:43 +1100 Subject: [PATCH 08/10] wrangled QWidgets to allow for resizable tracks with track headers --- .../timelinewidget/timelineandtrackview.cpp | 2 +- .../timelinewidget/trackview/CMakeLists.txt | 4 + .../timelinewidget/trackview/trackview.cpp | 57 ++++++--- .../timelinewidget/trackview/trackview.h | 24 +++- .../trackview/trackviewitem.cpp | 44 +++++++ .../timelinewidget/trackview/trackviewitem.h | 28 +++++ .../trackview/trackviewsplitter.cpp | 109 ++++++++++++++++++ .../trackview/trackviewsplitter.h | 39 +++++++ 8 files changed, 286 insertions(+), 21 deletions(-) create mode 100644 app/widget/timelinewidget/trackview/trackviewitem.cpp create mode 100644 app/widget/timelinewidget/trackview/trackviewitem.h create mode 100644 app/widget/timelinewidget/trackview/trackviewsplitter.cpp create mode 100644 app/widget/timelinewidget/trackview/trackviewsplitter.h diff --git a/app/widget/timelinewidget/timelineandtrackview.cpp b/app/widget/timelinewidget/timelineandtrackview.cpp index 7995d007e..33d43556f 100644 --- a/app/widget/timelinewidget/timelineandtrackview.cpp +++ b/app/widget/timelinewidget/timelineandtrackview.cpp @@ -13,7 +13,7 @@ TimelineAndTrackView::TimelineAndTrackView(const TrackType &type, Qt::Alignment splitter_->setChildrenCollapsible(false); layout->addWidget(splitter_); - track_view_ = new TrackView(); + track_view_ = new TrackView(vertical_alignment); splitter_->addWidget(track_view_); view_ = new TimelineView(type, vertical_alignment); diff --git a/app/widget/timelinewidget/trackview/CMakeLists.txt b/app/widget/timelinewidget/trackview/CMakeLists.txt index bbd5bc605..a569b27c9 100644 --- a/app/widget/timelinewidget/trackview/CMakeLists.txt +++ b/app/widget/timelinewidget/trackview/CMakeLists.txt @@ -18,5 +18,9 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/timelinewidget/trackview/trackview.h widget/timelinewidget/trackview/trackview.cpp + widget/timelinewidget/trackview/trackviewitem.h + widget/timelinewidget/trackview/trackviewitem.cpp + widget/timelinewidget/trackview/trackviewsplitter.h + widget/timelinewidget/trackview/trackviewsplitter.cpp PARENT_SCOPE ) diff --git a/app/widget/timelinewidget/trackview/trackview.cpp b/app/widget/timelinewidget/trackview/trackview.cpp index 712360b55..fed6d7526 100644 --- a/app/widget/timelinewidget/trackview/trackview.cpp +++ b/app/widget/timelinewidget/trackview/trackview.cpp @@ -1,29 +1,52 @@ #include "trackview.h" +#include +#include +#include #include #include -TrackView::TrackView(QWidget *parent) : - QWidget(parent) +#include "trackviewitem.h" + +TrackView::TrackView(Qt::Alignment vertical_alignment, QWidget *parent) : + QScrollArea(parent), + alignment_(vertical_alignment) { - QVBoxLayout* layout = new QVBoxLayout(this); - layout->setSpacing(0); + QWidget* central = new QWidget(); + setWidget(central); + setWidgetResizable(true); + + QVBoxLayout* layout = new QVBoxLayout(central); layout->setMargin(0); + layout->setSpacing(0); - QSplitter* splitter = new QSplitter(Qt::Vertical); - splitter->setChildrenCollapsible(false); - splitter->setHandleWidth(1); - layout->addWidget(splitter); + if (alignment_ == Qt::AlignBottom) { + layout->addStretch(); - QWidget* test1 = new QWidget(); - test1->setStyleSheet("background: red;"); - splitter->addWidget(test1); + connect(verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(ScrollbarRangeChanged(int, int))); + last_scrollbar_max_ = verticalScrollBar()->maximum(); + } - QWidget* test3 = new QWidget(); - test3->setStyleSheet("background: yellow;"); - splitter->addWidget(test3); + splitter_ = new TrackViewSplitter(alignment_); + splitter_->setChildrenCollapsible(false); + layout->addWidget(splitter_); +} - QWidget* test2 = new QWidget(); - test2->setStyleSheet("background: blue;"); - splitter->addWidget(test2); +void TrackView::resizeEvent(QResizeEvent *event) +{ + QScrollArea::resizeEvent(event); + + //splitter_->setFixedWidth(viewport()->width()); +} + +void TrackView::ScrollbarRangeChanged(int, int max) +{ + if (max != last_scrollbar_max_) { + int ba_val = last_scrollbar_max_ - verticalScrollBar()->value(); + int new_val = max - ba_val; + + verticalScrollBar()->setValue(new_val); + + last_scrollbar_max_ = max; + } } diff --git a/app/widget/timelinewidget/trackview/trackview.h b/app/widget/timelinewidget/trackview/trackview.h index 6277a4c2b..e6309ccc9 100644 --- a/app/widget/timelinewidget/trackview/trackview.h +++ b/app/widget/timelinewidget/trackview/trackview.h @@ -1,14 +1,32 @@ #ifndef TRACKVIEW_H #define TRACKVIEW_H -#include +#include +#include -class TrackView : public QWidget +#include "trackviewsplitter.h" + +class TrackView : public QScrollArea { + Q_OBJECT public: - TrackView(QWidget* parent = nullptr); + TrackView(Qt::Alignment vertical_alignment = Qt::AlignTop, + QWidget* parent = nullptr); + +protected: + virtual void resizeEvent(QResizeEvent* event) override; private: + TrackViewSplitter* splitter_; + + Qt::Alignment alignment_; + + int last_scrollbar_max_; + + QWidget* top_spacer_; + +private slots: + void ScrollbarRangeChanged(int min, int max); }; diff --git a/app/widget/timelinewidget/trackview/trackviewitem.cpp b/app/widget/timelinewidget/trackview/trackviewitem.cpp new file mode 100644 index 000000000..e61b79201 --- /dev/null +++ b/app/widget/timelinewidget/trackview/trackviewitem.cpp @@ -0,0 +1,44 @@ +#include "trackviewitem.h" + +#include +#include +#include +#include +#include + +TrackViewItem::TrackViewItem(Qt::Alignment alignment, QWidget *parent) : + QWidget(parent), + alignment_(alignment) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setSpacing(0); + layout->setMargin(0); + + label_ = new QLabel(tr("Track")); + layout->addWidget(label_); + + mute_button_ = CreateMSLButton(tr("M"), Qt::red); + layout->addWidget(mute_button_); + + solo_button_ = CreateMSLButton(tr("S"), Qt::yellow); + layout->addWidget(solo_button_); + + lock_button_ = CreateMSLButton(tr("L"), Qt::gray); + layout->addWidget(lock_button_); + + setMinimumHeight(mute_button_->height()); +} + +QPushButton *TrackViewItem::CreateMSLButton(const QString& text, const QColor& checked_color) const +{ + QPushButton* button = new QPushButton(text); + button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + button->setCheckable(true); + button->setStyleSheet(QStringLiteral("QPushButton::checked { background: %1; }").arg(checked_color.name())); + + int size = button->sizeHint().height(); + size = qRound(size * 0.75); + button->setFixedSize(size, size); + + return button; +} diff --git a/app/widget/timelinewidget/trackview/trackviewitem.h b/app/widget/timelinewidget/trackview/trackviewitem.h new file mode 100644 index 000000000..2d5b6a61f --- /dev/null +++ b/app/widget/timelinewidget/trackview/trackviewitem.h @@ -0,0 +1,28 @@ +#ifndef TRACKVIEWITEM_H +#define TRACKVIEWITEM_H + +#include +#include +#include + +class TrackViewItem : public QWidget +{ + Q_OBJECT +public: + TrackViewItem(Qt::Alignment alignment = Qt::AlignTop, + QWidget* parent = nullptr); + +private: + QPushButton* CreateMSLButton(const QString &text, const QColor &checked_color) const; + + Qt::Alignment alignment_; + + QLabel* label_; + + QPushButton* mute_button_; + QPushButton* solo_button_; + QPushButton* lock_button_; + +}; + +#endif // TRACKVIEWITEM_H diff --git a/app/widget/timelinewidget/trackview/trackviewsplitter.cpp b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp new file mode 100644 index 000000000..1d939c580 --- /dev/null +++ b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp @@ -0,0 +1,109 @@ +#include "trackviewsplitter.h" + +#include +#include + +#include "trackviewitem.h" + +TrackViewSplitter::TrackViewSplitter(Qt::Alignment vertical_alignment, QWidget* parent) : + QSplitter(Qt::Vertical, parent), + alignment_(vertical_alignment) +{ + setHandleWidth(1); + + int initial_height = 0; + + if (alignment_ == Qt::AlignBottom) { + // Add empty spacer so we get a splitter handle after the last element + addWidget(new QWidget()); + } + + for (int i=0;i<3;i++) { + TrackViewItem* w = new TrackViewItem(); + addWidget(w); + + initial_height += w->sizeHint().height() + handleWidth(); + } + + if (alignment_ == Qt::AlignTop) { + // Add empty spacer so we get a splitter handle after the last element + addWidget(new QWidget()); + } + + setFixedHeight(initial_height); +} + +void TrackViewSplitter::HandleReceiver(TrackViewSplitterHandle *h, int diff) +{ + int ele_id = -1; + + for (int i=0;i element_sizes = sizes(); + + int old_ele_sz = element_sizes.at(ele_id); + + // Transform element size by diff + int new_ele_sz = old_ele_sz + diff; + + // Validate it with the widget's minimum size + new_ele_sz = qMax(new_ele_sz, widget(ele_id)->minimumHeight()); + + // Correct diff + diff = new_ele_sz - old_ele_sz; + + // Set new size on element + element_sizes.replace(ele_id, new_ele_sz); + setSizes(element_sizes); + + // Increase height by the difference + setFixedHeight(height() + diff); +} + +QSplitterHandle *TrackViewSplitter::createHandle() +{ + return new TrackViewSplitterHandle(orientation(), this); +} + +TrackViewSplitterHandle::TrackViewSplitterHandle(Qt::Orientation orientation, QSplitter *parent) : + QSplitterHandle(orientation, parent), + dragging_(false) +{ +} + +void TrackViewSplitterHandle::mousePressEvent(QMouseEvent *) +{ +} + +void TrackViewSplitterHandle::mouseMoveEvent(QMouseEvent *) +{ + if (dragging_) { + static_cast(parent())->HandleReceiver(this, QCursor::pos().y() - drag_y_); + } + + drag_y_ = QCursor::pos().y(); + dragging_ = true; +} + +void TrackViewSplitterHandle::mouseReleaseEvent(QMouseEvent *) +{ + dragging_ = false; +} + +void TrackViewSplitterHandle::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.fillRect(rect(), palette().base()); +} diff --git a/app/widget/timelinewidget/trackview/trackviewsplitter.h b/app/widget/timelinewidget/trackview/trackviewsplitter.h new file mode 100644 index 000000000..803d88bda --- /dev/null +++ b/app/widget/timelinewidget/trackview/trackviewsplitter.h @@ -0,0 +1,39 @@ +#ifndef TRACKVIEWSPLITTER_H +#define TRACKVIEWSPLITTER_H + +#include + +class TrackViewSplitterHandle : public QSplitterHandle +{ + Q_OBJECT +public: + TrackViewSplitterHandle(Qt::Orientation orientation, QSplitter *parent); + +protected: + virtual void mousePressEvent(QMouseEvent *e) override; + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mouseReleaseEvent(QMouseEvent *e) override; + + virtual void paintEvent(QPaintEvent *e) override; + +private: + int drag_y_; + + bool dragging_; +}; + +class TrackViewSplitter : public QSplitter +{ +public: + TrackViewSplitter(Qt::Alignment vertical_alignment, QWidget* parent = nullptr); + + void HandleReceiver(TrackViewSplitterHandle* h, int diff); + +protected: + virtual QSplitterHandle *createHandle() override; + +private: + Qt::Alignment alignment_; +}; + +#endif // TRACKVIEWSPLITTER_H From 2207aa3fd539c2f3333b4e98d4f03031c7de2813 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 15:13:55 +1100 Subject: [PATCH 09/10] moved slider's focusable line edit to a separate distinct reusable widget Some of the widgets designed for Slider have uses elsewhere so now they have been moved to allow more generic usage. --- app/widget/CMakeLists.txt | 2 ++ app/widget/clickablelabel/CMakeLists.txt | 22 ++++++++++++++++++ app/widget/clickablelabel/clickablelabel.cpp | 23 +++++++++++++++++++ app/widget/clickablelabel/clickablelabel.h | 23 +++++++++++++++++++ app/widget/focusablelineedit/CMakeLists.txt | 22 ++++++++++++++++++ .../focusablelineedit.cpp} | 8 +++---- .../focusablelineedit.h} | 4 ++-- app/widget/slider/CMakeLists.txt | 2 -- app/widget/slider/sliderbase.cpp | 2 +- app/widget/slider/sliderbase.h | 4 ++-- 10 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 app/widget/clickablelabel/CMakeLists.txt create mode 100644 app/widget/clickablelabel/clickablelabel.cpp create mode 100644 app/widget/clickablelabel/clickablelabel.h create mode 100644 app/widget/focusablelineedit/CMakeLists.txt rename app/widget/{slider/sliderlineedit.cpp => focusablelineedit/focusablelineedit.cpp} (84%) rename app/widget/{slider/sliderlineedit.h => focusablelineedit/focusablelineedit.h} (91%) diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index eeb3575e8..da75b0919 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -15,8 +15,10 @@ # along with this program. If not, see . add_subdirectory(audiomonitor) +add_subdirectory(clickablelabel) add_subdirectory(columnedgridlayout) add_subdirectory(flowlayout) +add_subdirectory(focusablelineedit) add_subdirectory(footagecombobox) add_subdirectory(menu) add_subdirectory(nodeview) diff --git a/app/widget/clickablelabel/CMakeLists.txt b/app/widget/clickablelabel/CMakeLists.txt new file mode 100644 index 000000000..95a0c9d19 --- /dev/null +++ b/app/widget/clickablelabel/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/clickablelabel/clickablelabel.h + widget/clickablelabel/clickablelabel.cpp + PARENT_SCOPE +) diff --git a/app/widget/clickablelabel/clickablelabel.cpp b/app/widget/clickablelabel/clickablelabel.cpp new file mode 100644 index 000000000..8c0b6258a --- /dev/null +++ b/app/widget/clickablelabel/clickablelabel.cpp @@ -0,0 +1,23 @@ +#include "clickablelabel.h" + +ClickableLabel::ClickableLabel(const QString &text, QWidget *parent) : + QLabel(text, parent) +{ +} + +ClickableLabel::ClickableLabel(QWidget *parent) : + QLabel(parent) +{ +} + +void ClickableLabel::mouseReleaseEvent(QMouseEvent *) +{ + if (underMouse()) { + emit MouseClicked(); + } +} + +void ClickableLabel::mouseDoubleClickEvent(QMouseEvent *) +{ + emit MouseDoubleClicked(); +} diff --git a/app/widget/clickablelabel/clickablelabel.h b/app/widget/clickablelabel/clickablelabel.h new file mode 100644 index 000000000..cc11f11a7 --- /dev/null +++ b/app/widget/clickablelabel/clickablelabel.h @@ -0,0 +1,23 @@ +#ifndef CLICKABLELABEL_H +#define CLICKABLELABEL_H + +#include + +class ClickableLabel : public QLabel +{ + Q_OBJECT +public: + ClickableLabel(const QString& text, QWidget* parent = nullptr); + ClickableLabel(QWidget* parent = nullptr); + +protected: + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseDoubleClickEvent(QMouseEvent* event) override; + +signals: + void MouseClicked(); + void MouseDoubleClicked(); + +}; + +#endif // CLICKABLELABEL_H diff --git a/app/widget/focusablelineedit/CMakeLists.txt b/app/widget/focusablelineedit/CMakeLists.txt new file mode 100644 index 000000000..7ca836d05 --- /dev/null +++ b/app/widget/focusablelineedit/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/focusablelineedit/focusablelineedit.h + widget/focusablelineedit/focusablelineedit.cpp + PARENT_SCOPE +) diff --git a/app/widget/slider/sliderlineedit.cpp b/app/widget/focusablelineedit/focusablelineedit.cpp similarity index 84% rename from app/widget/slider/sliderlineedit.cpp rename to app/widget/focusablelineedit/focusablelineedit.cpp index 99ef499da..82ef516f5 100644 --- a/app/widget/slider/sliderlineedit.cpp +++ b/app/widget/focusablelineedit/focusablelineedit.cpp @@ -18,17 +18,17 @@ ***/ -#include "sliderlineedit.h" +#include "focusablelineedit.h" #include -SliderLineEdit::SliderLineEdit(QWidget *parent) : +FocusableLineEdit::FocusableLineEdit(QWidget *parent) : QLineEdit(parent) { } -void SliderLineEdit::keyPressEvent(QKeyEvent *e) +void FocusableLineEdit::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Return: @@ -43,7 +43,7 @@ void SliderLineEdit::keyPressEvent(QKeyEvent *e) } } -void SliderLineEdit::focusOutEvent(QFocusEvent *e) +void FocusableLineEdit::focusOutEvent(QFocusEvent *e) { QLineEdit::focusOutEvent(e); diff --git a/app/widget/slider/sliderlineedit.h b/app/widget/focusablelineedit/focusablelineedit.h similarity index 91% rename from app/widget/slider/sliderlineedit.h rename to app/widget/focusablelineedit/focusablelineedit.h index f2c0ed57c..08064d55a 100644 --- a/app/widget/slider/sliderlineedit.h +++ b/app/widget/focusablelineedit/focusablelineedit.h @@ -23,11 +23,11 @@ #include -class SliderLineEdit : public QLineEdit +class FocusableLineEdit : public QLineEdit { Q_OBJECT public: - SliderLineEdit(QWidget* parent); + FocusableLineEdit(QWidget* parent = nullptr); signals: void Confirmed(); diff --git a/app/widget/slider/CMakeLists.txt b/app/widget/slider/CMakeLists.txt index 37bd57931..c1835634b 100644 --- a/app/widget/slider/CMakeLists.txt +++ b/app/widget/slider/CMakeLists.txt @@ -24,8 +24,6 @@ set(OLIVE_SOURCES widget/slider/sliderbase.cpp widget/slider/sliderlabel.h widget/slider/sliderlabel.cpp - widget/slider/sliderlineedit.h - widget/slider/sliderlineedit.cpp widget/slider/stringslider.h widget/slider/stringslider.cpp widget/slider/timeslider.h diff --git a/app/widget/slider/sliderbase.cpp b/app/widget/slider/sliderbase.cpp index 06002b406..700c9d172 100644 --- a/app/widget/slider/sliderbase.cpp +++ b/app/widget/slider/sliderbase.cpp @@ -40,7 +40,7 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : addWidget(label_); - editor_ = new SliderLineEdit(this); + editor_ = new FocusableLineEdit(this); addWidget(editor_); connect(label_, SIGNAL(drag_start()), this, SLOT(LabelPressed())); diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/sliderbase.h index 1c50aee4b..25ec8570a 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/sliderbase.h @@ -24,7 +24,7 @@ #include #include "sliderlabel.h" -#include "sliderlineedit.h" +#include "widget/focusablelineedit/focusablelineedit.h" class SliderBase : public QStackedWidget { @@ -73,7 +73,7 @@ private: SliderLabel* label_; - SliderLineEdit* editor_; + FocusableLineEdit* editor_; QVariant value_; From 58bcbca4c59e93de83fa66a3c1c0e6380bd95ed2 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Dec 2019 15:14:43 +1100 Subject: [PATCH 10/10] wrote mechanisms that allow the tracks to be resized from the trackview --- app/node/output/timeline/timeline.cpp | 6 ++ app/node/output/timeline/timeline.h | 4 + app/node/output/timeline/tracklist.cpp | 32 +++++--- app/node/output/timeline/tracklist.h | 21 +++-- app/node/output/track/track.cpp | 52 ++++++++++++ app/node/output/track/track.h | 21 +++++ app/widget/timelinewidget/timelinewidget.cpp | 38 +++++++-- app/widget/timelinewidget/timelinewidget.h | 2 + .../timelinewidget/trackview/trackview.cpp | 49 +++++++++++- .../timelinewidget/trackview/trackview.h | 16 +++- .../trackview/trackviewitem.cpp | 46 ++++++++++- .../timelinewidget/trackview/trackviewitem.h | 23 +++++- .../trackview/trackviewsplitter.cpp | 79 ++++++++++++++----- .../trackview/trackviewsplitter.h | 12 +++ .../timelinewidget/view/timelineview.cpp | 19 ++++- app/widget/timelinewidget/view/timelineview.h | 14 ++-- 16 files changed, 375 insertions(+), 59 deletions(-) diff --git a/app/node/output/timeline/timeline.cpp b/app/node/output/timeline/timeline.cpp index 91e4204c0..03603c403 100644 --- a/app/node/output/timeline/timeline.cpp +++ b/app/node/output/timeline/timeline.cpp @@ -46,6 +46,7 @@ TimelineOutput::TimelineOutput() connect(list, SIGNAL(BlockRemoved(Block*)), this, SIGNAL(BlockRemoved(Block*))); connect(list, SIGNAL(TrackAdded(TrackOutput*)), this, SLOT(TrackListAddedTrack(TrackOutput*))); connect(list, SIGNAL(TrackRemoved(TrackOutput*)), this, SIGNAL(TrackRemoved(TrackOutput*))); + connect(list, SIGNAL(TrackHeightChanged(int, int)), this, SLOT(TrackHeightChangedSlot(int, int))); } } @@ -196,3 +197,8 @@ void TimelineOutput::TrackListAddedTrack(TrackOutput *track) TrackType type = static_cast(sender())->TrackType(); emit TrackAdded(track, type); } + +void TimelineOutput::TrackHeightChangedSlot(int index, int height) +{ + emit TrackHeightChanged(static_cast(sender())->type(), index, height); +} diff --git a/app/node/output/timeline/timeline.h b/app/node/output/timeline/timeline.h index 4b69397f2..8958fc46f 100644 --- a/app/node/output/timeline/timeline.h +++ b/app/node/output/timeline/timeline.h @@ -70,6 +70,8 @@ signals: void TrackAdded(TrackOutput* track, TrackType type); void TrackRemoved(TrackOutput* track); + void TrackHeightChanged(TrackType type, int index, int height); + protected: virtual NodeValueTable Value(const NodeValueDatabase& value) const override; @@ -93,6 +95,8 @@ private slots: void TrackListAddedTrack(TrackOutput* track); + void TrackHeightChangedSlot(int index, int height); + }; #endif // TIMELINEOUTPUT_H diff --git a/app/node/output/timeline/tracklist.cpp b/app/node/output/timeline/tracklist.cpp index 2dee5c2b7..839b9f2d1 100644 --- a/app/node/output/timeline/tracklist.cpp +++ b/app/node/output/timeline/tracklist.cpp @@ -33,6 +33,11 @@ TrackList::TrackList(TimelineOutput* parent, const enum TrackType &type, NodeInp connect(track_input, SIGNAL(SizeChanged(int)), this, SLOT(TrackListSizeChanged(int))); } +const TrackType &TrackList::type() const +{ + return type_; +} + void TrackList::TrackAddedBlock(Block *block) { emit BlockAdded(block, static_cast(sender())->Index()); @@ -55,12 +60,12 @@ void TrackList::TrackListSizeChanged(int size) } } -const QVector &TrackList::Tracks() +const QVector &TrackList::Tracks() const { return track_cache_; } -TrackOutput *TrackList::TrackAt(int index) +TrackOutput *TrackList::TrackAt(int index) const { if (index < 0 || index >= track_cache_.size()) { return nullptr; @@ -69,16 +74,21 @@ TrackOutput *TrackList::TrackAt(int index) return track_cache_.at(index); } -const rational &TrackList::TrackLength() +const rational &TrackList::TrackLength() const { return total_length_; } -const enum TrackType &TrackList::TrackType() +const enum TrackType &TrackList::TrackType() const { return type_; } +int TrackList::TrackCount() const +{ + return track_cache_.size(); +} + TrackOutput* TrackList::AddTrack() { TrackOutput* track = new TrackOutput(); @@ -104,9 +114,6 @@ TrackOutput* TrackList::AddTrack() }*/ // End test code - // Connect this track to the current last track - NodeParam::ConnectEdge(track->output(), assoc_input); - return track; } @@ -132,13 +139,14 @@ void TrackList::TrackConnected(NodeEdgePtr edge) Node* connected_node = edge->output()->parentNode(); if (connected_node->IsTrack()) { - TrackOutput* connected_track = static_cast(connected_node);// Traverse through Tracks caching and connecting them + TrackOutput* connected_track = static_cast(connected_node); track_cache_.replace(track_index, connected_track); connect(connected_track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); connect(connected_track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); connect(connected_track, SIGNAL(TrackLengthChanged()), this, SLOT(UpdateTotalLength())); + connect(connected_track, SIGNAL(TrackHeightChanged(int)), this, SLOT(TrackHeightChangedSlot(int))); connected_track->SetIndex(track_index); connected_track->set_track_type(type_); @@ -175,6 +183,7 @@ void TrackList::TrackDisconnected(NodeEdgePtr edge) disconnect(track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*))); disconnect(track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*))); disconnect(track, SIGNAL(TrackLengthChanged()), this, SLOT(UpdateTotalLength())); + disconnect(track, SIGNAL(TrackHeightChanged(int)), this, SLOT(TrackHeightChangedSlot(int))); emit TrackListChanged(); @@ -182,7 +191,7 @@ void TrackList::TrackDisconnected(NodeEdgePtr edge) } } -NodeGraph *TrackList::GetParentGraph() +NodeGraph *TrackList::GetParentGraph() const { return static_cast(parent()->parent()); } @@ -199,3 +208,8 @@ void TrackList::UpdateTotalLength() emit LengthChanged(total_length_); } + +void TrackList::TrackHeightChangedSlot(int height) +{ + emit TrackHeightChanged(static_cast(sender())->Index(), height); +} diff --git a/app/node/output/timeline/tracklist.h b/app/node/output/timeline/tracklist.h index ec3fed10c..1036864cf 100644 --- a/app/node/output/timeline/tracklist.h +++ b/app/node/output/timeline/tracklist.h @@ -34,17 +34,21 @@ class TrackList : public QObject { public: TrackList(TimelineOutput *parent, const enum TrackType& type, NodeInputArray* track_input); - const QVector& Tracks(); + const enum TrackType& type() const; - TrackOutput* TrackAt(int index); + const QVector& Tracks() const; + + TrackOutput* TrackAt(int index) const; TrackOutput *AddTrack(); void RemoveTrack(); - const rational& TrackLength(); + const rational& TrackLength() const; - const enum TrackType& TrackType(); + const enum TrackType& TrackType() const; + + int TrackCount() const; signals: void BlockAdded(Block* block, int index); @@ -59,8 +63,10 @@ signals: void LengthChanged(const rational &length); + void TrackHeightChanged(int index, int height); + private: - NodeGraph* GetParentGraph(); + NodeGraph* GetParentGraph() const; /** * @brief A cache of connected Tracks @@ -104,6 +110,11 @@ private slots: */ void UpdateTotalLength(); + /** + * @brief Slot when a track height changes, transforms to the TrackHeightChanged signal which includes a track index + */ + void TrackHeightChangedSlot(int height); + }; #endif // TRACKLIST_H diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 5c44649ed..f4d50713b 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -20,7 +20,9 @@ #include "track.h" +#include #include +#include #include "node/block/gap/gap.h" #include "node/graph.h" @@ -35,6 +37,9 @@ TrackOutput::TrackOutput() : connect(block_input_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(BlockConnected(NodeEdgePtr))); connect(block_input_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(BlockDisconnected(NodeEdgePtr))); connect(block_input_, SIGNAL(SizeChanged(int)), this, SLOT(BlockListSizeChanged(int))); + + // Set default height + track_height_ = GetDefaultTrackHeight(); } void TrackOutput::set_track_type(const TrackType &track_type) @@ -78,6 +83,26 @@ QString TrackOutput::Description() const "a Sequence."); } +QString TrackOutput::GetTrackName() +{ + if (track_name_.isEmpty()) { + return GetDefaultTrackName(track_type_, index_); + } + + return track_name_; +} + +const int &TrackOutput::GetTrackHeight() const +{ + return track_height_; +} + +void TrackOutput::SetTrackHeight(const int &height) +{ + track_height_ = height; + emit TrackHeightChanged(track_height_); +} + void TrackOutput::Retranslate() { block_input_->set_name(tr("Blocks")); @@ -310,6 +335,33 @@ bool TrackOutput::IsTrack() const return true; } +int TrackOutput::GetDefaultTrackHeight() +{ + return qApp->fontMetrics().height() * 3; +} + +QString TrackOutput::GetDefaultTrackName(TrackType type, int index) +{ + // Starts tracks at 1 rather than 0 + int user_friendly_index = index+1; + + switch (type) { + case kTrackTypeVideo: return tr("Video %1").arg(user_friendly_index); + case kTrackTypeAudio: return tr("Audio %1").arg(user_friendly_index); + case kTrackTypeSubtitle: return tr("Subtitle %1").arg(user_friendly_index); + case kTrackTypeNone: + case kTrackTypeCount: + break; + } + + return tr("Track %1").arg(user_friendly_index); +} + +void TrackOutput::SetTrackName(const QString &name) +{ + track_name_ = name; +} + void TrackOutput::UpdateInOutFrom(int index) { Q_ASSERT(index >= 0); diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index d60e8a02c..b2e94c306 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -45,6 +45,11 @@ public: virtual QString Category() const override; virtual QString Description() const override; + QString GetTrackName(); + + const int& GetTrackHeight() const; + void SetTrackHeight(const int& height); + virtual void Retranslate() override; const int& Index(); @@ -127,6 +132,13 @@ public: virtual bool IsTrack() const override; + static int GetDefaultTrackHeight(); + + static QString GetDefaultTrackName(TrackType type, int index); + +public slots: + void SetTrackName(const QString& name); + signals: /** * @brief Signal emitted when a Block is added to this Track @@ -143,6 +155,11 @@ signals: */ void TrackLengthChanged(); + /** + * @brief Signal emitted when the height of the track has changed + */ + void TrackHeightChanged(int height); + protected: private: @@ -158,6 +175,10 @@ private: rational track_length_; + int track_height_; + + QString track_name_; + int block_invalidate_cache_stack_; int index_; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index b5cc509ec..c59d4a8f9 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -52,7 +52,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) : tools_.fill(nullptr); tools_.replace(olive::tool::kPointer, std::make_shared(this)); - // tools_.replace(olive::tool::kEdit, new PointerTool(this)); FIXME: Implement + // tools_.replace(olive::tool::kEdit, new PointerTool(this)); FIXME: Implement tools_.replace(olive::tool::kRipple, std::make_shared(this)); tools_.replace(olive::tool::kRolling, std::make_shared(this)); tools_.replace(olive::tool::kRazor, std::make_shared(this)); @@ -60,9 +60,9 @@ TimelineWidget::TimelineWidget(QWidget *parent) : tools_.replace(olive::tool::kSlide, std::make_shared(this)); tools_.replace(olive::tool::kHand, std::make_shared(this)); tools_.replace(olive::tool::kZoom, std::make_shared(this)); - //tools_.replace(olive::tool::kTransition, new (this)); FIXME: Implement - //tools_.replace(olive::tool::kRecord, new PointerTool(this)); FIXME: Implement - //tools_.replace(olive::tool::kAdd, new PointerTool(this)); FIXME: Implement + //tools_.replace(olive::tool::kTransition, new (this)); FIXME: Implement + //tools_.replace(olive::tool::kRecord, new PointerTool(this)); FIXME: Implement + //tools_.replace(olive::tool::kAdd, new PointerTool(this)); FIXME: Implement import_tool_ = std::make_shared(this); @@ -178,10 +178,16 @@ void TimelineWidget::ConnectTimelineNode(TimelineOutput *node) disconnect(timeline_node_, SIGNAL(TrackAdded(TrackOutput*, TrackType)), this, SLOT(AddTrack(TrackOutput*, TrackType))); disconnect(timeline_node_, SIGNAL(TrackRemoved(TrackOutput*)), this, SLOT(RemoveTrack(TrackOutput*))); disconnect(timeline_node_, SIGNAL(TimebaseChanged(const rational&)), this, SLOT(SetTimebase(const rational&))); + disconnect(timeline_node_, SIGNAL(TrackHeightChanged(TrackType, int, int)), this, SLOT(TrackHeightChanged(TrackType, int, int))); SetTimebase(0); Clear(); + + for (int i=0;itrack_view(); + track_view->DisconnectTrackList(); + } } timeline_node_ = node; @@ -193,14 +199,18 @@ void TimelineWidget::ConnectTimelineNode(TimelineOutput *node) connect(timeline_node_, SIGNAL(TrackAdded(TrackOutput*, TrackType)), this, SLOT(AddTrack(TrackOutput*, TrackType))); connect(timeline_node_, SIGNAL(TrackRemoved(TrackOutput*)), this, SLOT(RemoveTrack(TrackOutput*))); connect(timeline_node_, SIGNAL(TimebaseChanged(const rational&)), this, SLOT(SetTimebase(const rational&))); + connect(timeline_node_, SIGNAL(TrackHeightChanged(TrackType, int, int)), this, SLOT(TrackHeightChanged(TrackType, int, int))); SetTimebase(timeline_node_->timebase()); for (int i=0;i(i); - TimelineView* view = views_.at(i)->view(); + TrackList* track_list = timeline_node_->track_list(track_type); + TrackView* track_view = views_.at(i)->track_view(); + track_view->ConnectTrackList(track_list); + view->ConnectTrackList(track_list); view->SetEndTime(timeline_node_->timeline_length()); // Defer to the track to make all the block UI items necessary @@ -675,6 +685,24 @@ void TimelineWidget::UpdateTimecodeWidthFromSplitters(QSplitter* s) timecode_label_->setFixedWidth(s->sizes().first() + s->handleWidth()); } +void TimelineWidget::TrackHeightChanged(TrackType type, int index, int height) +{ + Q_UNUSED(index) + Q_UNUSED(height) + + QMap::const_iterator iterator; + TimelineView* view = views_.at(type)->view(); + + for (iterator=block_items_.begin();iterator!=block_items_.end();iterator++) { + TimelineViewBlockItem* block_item = iterator.value(); + + if (block_item->Track().type() == type) { + block_item->SetYCoords(view->GetTrackY(block_item->Track().index()), + view->GetTrackHeight(block_item->Track().index())); + } + } +} + void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) { ghost->SetScale(scale_); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 74faebc1e..c9c4e7823 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -379,6 +379,8 @@ private slots: void UpdateTimecodeWidthFromSplitters(QSplitter *s); + void TrackHeightChanged(TrackType type, int index, int height); + }; #endif // TIMELINEWIDGET_H diff --git a/app/widget/timelinewidget/trackview/trackview.cpp b/app/widget/timelinewidget/trackview/trackview.cpp index fed6d7526..94a85b476 100644 --- a/app/widget/timelinewidget/trackview/trackview.cpp +++ b/app/widget/timelinewidget/trackview/trackview.cpp @@ -10,6 +10,7 @@ TrackView::TrackView(Qt::Alignment vertical_alignment, QWidget *parent) : QScrollArea(parent), + list_(nullptr), alignment_(vertical_alignment) { QWidget* central = new QWidget(); @@ -30,13 +31,40 @@ TrackView::TrackView(Qt::Alignment vertical_alignment, QWidget *parent) : splitter_ = new TrackViewSplitter(alignment_); splitter_->setChildrenCollapsible(false); layout->addWidget(splitter_); + connect(splitter_, SIGNAL(TrackHeightChanged(int, int)), this, SLOT(TrackHeightChanged(int, int))); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } -void TrackView::resizeEvent(QResizeEvent *event) +void TrackView::ConnectTrackList(TrackList *list) { - QScrollArea::resizeEvent(event); + if (list_ != nullptr) { + foreach (TrackViewItem* item, items_) { + delete item; + } + items_.clear(); - //splitter_->setFixedWidth(viewport()->width()); + disconnect(list_, SIGNAL(TrackHeightChanged(int, int)), splitter_, SLOT(SetTrackHeight(int, int))); + disconnect(list_, SIGNAL(TrackAdded(TrackOutput*)), this, SLOT(InsertTrack(TrackOutput*))); + disconnect(list_, SIGNAL(TrackRemoved(TrackOutput*)), this, SLOT(RemoveTrack(TrackOutput*))); + } + + list_ = list; + + if (list_ != nullptr) { + foreach (TrackOutput* track, list_->Tracks()) { + splitter_->Insert(track->Index(), track->GetTrackHeight(), new TrackViewItem(track->GetTrackName())); + } + + connect(list_, SIGNAL(TrackHeightChanged(int, int)), splitter_, SLOT(SetTrackHeight(int, int))); + connect(list_, SIGNAL(TrackAdded(TrackOutput*)), this, SLOT(InsertTrack(TrackOutput*))); + connect(list_, SIGNAL(TrackRemoved(TrackOutput*)), this, SLOT(RemoveTrack(TrackOutput*))); + } +} + +void TrackView::DisconnectTrackList() +{ + ConnectTrackList(nullptr); } void TrackView::ScrollbarRangeChanged(int, int max) @@ -50,3 +78,18 @@ void TrackView::ScrollbarRangeChanged(int, int max) last_scrollbar_max_ = max; } } + +void TrackView::TrackHeightChanged(int index, int height) +{ + list_->TrackAt(index)->SetTrackHeight(height); +} + +void TrackView::InsertTrack(TrackOutput *track) +{ + splitter_->Insert(track->Index(), track->GetTrackHeight(), new TrackViewItem(track->GetTrackName())); +} + +void TrackView::RemoveTrack(TrackOutput *track) +{ + splitter_->Remove(track->Index()); +} diff --git a/app/widget/timelinewidget/trackview/trackview.h b/app/widget/timelinewidget/trackview/trackview.h index e6309ccc9..89b3c75c3 100644 --- a/app/widget/timelinewidget/trackview/trackview.h +++ b/app/widget/timelinewidget/trackview/trackview.h @@ -4,6 +4,8 @@ #include #include +#include "node/output/timeline/tracklist.h" +#include "trackviewitem.h" #include "trackviewsplitter.h" class TrackView : public QScrollArea @@ -13,10 +15,14 @@ public: TrackView(Qt::Alignment vertical_alignment = Qt::AlignTop, QWidget* parent = nullptr); -protected: - virtual void resizeEvent(QResizeEvent* event) override; + void ConnectTrackList(TrackList* list); + void DisconnectTrackList(); private: + QList items_; + + TrackList* list_; + TrackViewSplitter* splitter_; Qt::Alignment alignment_; @@ -28,6 +34,12 @@ private: private slots: void ScrollbarRangeChanged(int min, int max); + void TrackHeightChanged(int index, int height); + + void InsertTrack(TrackOutput* track); + + void RemoveTrack(TrackOutput* track); + }; #endif // TRACKVIEW_H diff --git a/app/widget/timelinewidget/trackview/trackviewitem.cpp b/app/widget/timelinewidget/trackview/trackviewitem.cpp index e61b79201..f20dfb65f 100644 --- a/app/widget/timelinewidget/trackview/trackviewitem.cpp +++ b/app/widget/timelinewidget/trackview/trackviewitem.cpp @@ -6,7 +6,7 @@ #include #include -TrackViewItem::TrackViewItem(Qt::Alignment alignment, QWidget *parent) : +TrackViewItem::TrackViewItem(const QString& name, Qt::Alignment alignment, QWidget *parent) : QWidget(parent), alignment_(alignment) { @@ -14,8 +14,17 @@ TrackViewItem::TrackViewItem(Qt::Alignment alignment, QWidget *parent) : layout->setSpacing(0); layout->setMargin(0); - label_ = new QLabel(tr("Track")); - layout->addWidget(label_); + stack_ = new QStackedWidget(); + layout->addWidget(stack_); + + label_ = new ClickableLabel(name); + connect(label_, SIGNAL(MouseDoubleClicked()), this, SLOT(LabelClicked())); + stack_->addWidget(label_); + + line_edit_ = new FocusableLineEdit(); + connect(line_edit_, SIGNAL(Confirmed()), this, SLOT(LineEditConfirmed())); + connect(line_edit_, SIGNAL(Cancelled()), this, SLOT(LineEditCancelled())); + stack_->addWidget(line_edit_); mute_button_ = CreateMSLButton(tr("M"), Qt::red); layout->addWidget(mute_button_); @@ -42,3 +51,34 @@ QPushButton *TrackViewItem::CreateMSLButton(const QString& text, const QColor& c return button; } + +void TrackViewItem::LabelClicked() +{ + stack_->setCurrentWidget(line_edit_); + line_edit_->setFocus(); + line_edit_->selectAll(); +} + +void TrackViewItem::LineEditConfirmed() +{ + line_edit_->blockSignals(true); + + QString line_edit_str = line_edit_->text(); + if (!line_edit_str.isEmpty()) { + label_->setText(line_edit_str); + emit NameChanged(line_edit_str); + } + + stack_->setCurrentWidget(label_); + + line_edit_->blockSignals(false); +} + +void TrackViewItem::LineEditCancelled() +{ + line_edit_->blockSignals(true); + + stack_->setCurrentWidget(label_); + + line_edit_->blockSignals(false); +} diff --git a/app/widget/timelinewidget/trackview/trackviewitem.h b/app/widget/timelinewidget/trackview/trackviewitem.h index 2d5b6a61f..ccc6bc8a4 100644 --- a/app/widget/timelinewidget/trackview/trackviewitem.h +++ b/app/widget/timelinewidget/trackview/trackviewitem.h @@ -1,28 +1,45 @@ #ifndef TRACKVIEWITEM_H #define TRACKVIEWITEM_H -#include #include +#include #include +#include "widget/clickablelabel/clickablelabel.h" +#include "widget/focusablelineedit/focusablelineedit.h" + class TrackViewItem : public QWidget { Q_OBJECT public: - TrackViewItem(Qt::Alignment alignment = Qt::AlignTop, + TrackViewItem(const QString& name, + Qt::Alignment alignment = Qt::AlignTop, QWidget* parent = nullptr); +signals: + void NameChanged(const QString& name); + private: QPushButton* CreateMSLButton(const QString &text, const QColor &checked_color) const; Qt::Alignment alignment_; - QLabel* label_; + QStackedWidget* stack_; + + ClickableLabel* label_; + FocusableLineEdit* line_edit_; QPushButton* mute_button_; QPushButton* solo_button_; QPushButton* lock_button_; +private slots: + void LabelClicked(); + + void LineEditConfirmed(); + + void LineEditCancelled(); + }; #endif // TRACKVIEWITEM_H diff --git a/app/widget/timelinewidget/trackview/trackviewsplitter.cpp b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp index 1d939c580..5883a07c9 100644 --- a/app/widget/timelinewidget/trackview/trackviewsplitter.cpp +++ b/app/widget/timelinewidget/trackview/trackviewsplitter.cpp @@ -3,8 +3,6 @@ #include #include -#include "trackviewitem.h" - TrackViewSplitter::TrackViewSplitter(Qt::Alignment vertical_alignment, QWidget* parent) : QSplitter(Qt::Vertical, parent), alignment_(vertical_alignment) @@ -13,22 +11,8 @@ TrackViewSplitter::TrackViewSplitter(Qt::Alignment vertical_alignment, QWidget* int initial_height = 0; - if (alignment_ == Qt::AlignBottom) { - // Add empty spacer so we get a splitter handle after the last element - addWidget(new QWidget()); - } - - for (int i=0;i<3;i++) { - TrackViewItem* w = new TrackViewItem(); - addWidget(w); - - initial_height += w->sizeHint().height() + handleWidth(); - } - - if (alignment_ == Qt::AlignTop) { - // Add empty spacer so we get a splitter handle after the last element - addWidget(new QWidget()); - } + // Add empty spacer so we get a splitter handle after the last element + addWidget(new QWidget()); setFixedHeight(initial_height); } @@ -64,14 +48,71 @@ void TrackViewSplitter::HandleReceiver(TrackViewSplitterHandle *h, int diff) // Correct diff diff = new_ele_sz - old_ele_sz; + SetTrackHeight(ele_id, new_ele_sz); + + if (alignment_ == Qt::AlignBottom) { + ele_id = count() - ele_id - 1; + } + + emit TrackHeightChanged(ele_id, new_ele_sz); +} + +void TrackViewSplitter::SetTrackHeight(int index, int h) +{ + QList element_sizes = sizes(); + + int old_ele_sz = element_sizes.at(index); + + int diff = h - old_ele_sz; + // Set new size on element - element_sizes.replace(ele_id, new_ele_sz); + element_sizes.replace(index, h); setSizes(element_sizes); // Increase height by the difference setFixedHeight(height() + diff); } +void TrackViewSplitter::SetHeightWithSizes(const QList &sizes) +{ + int start_height = 0; + + foreach (int s, sizes) { + start_height += s + handleWidth(); + } + + setFixedHeight(start_height); + setSizes(sizes); +} + +void TrackViewSplitter::Insert(int index, int height, QWidget *item) +{ + QList sz = sizes(); + + if (alignment_ == Qt::AlignBottom) { + index = count() - index; + } + + sz.insert(index, height); + insertWidget(index, item); + + SetHeightWithSizes(sz); +} + +void TrackViewSplitter::Remove(int index) +{ + QList sz = sizes(); + + if (alignment_ == Qt::AlignBottom) { + index = count() - index; + } + + sz.removeAt(index); + delete widget(index); + + SetHeightWithSizes(sz); +} + QSplitterHandle *TrackViewSplitter::createHandle() { return new TrackViewSplitterHandle(orientation(), this); diff --git a/app/widget/timelinewidget/trackview/trackviewsplitter.h b/app/widget/timelinewidget/trackview/trackviewsplitter.h index 803d88bda..f6bb30ae8 100644 --- a/app/widget/timelinewidget/trackview/trackviewsplitter.h +++ b/app/widget/timelinewidget/trackview/trackviewsplitter.h @@ -24,11 +24,23 @@ private: class TrackViewSplitter : public QSplitter { + Q_OBJECT public: TrackViewSplitter(Qt::Alignment vertical_alignment, QWidget* parent = nullptr); void HandleReceiver(TrackViewSplitterHandle* h, int diff); + void SetHeightWithSizes(const QList& sizes); + + void Insert(int index, int height, QWidget* item); + void Remove(int index); + +public slots: + void SetTrackHeight(int index, int h); + +signals: + void TrackHeightChanged(int index, int height); + protected: virtual QSplitterHandle *createHandle() override; diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 2e039d0d9..fdef54aef 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -35,6 +35,7 @@ TimelineView::TimelineView(const TrackType &type, Qt::Alignment vertical_alignment, QWidget *parent) : QGraphicsView(parent), + connected_track_list_(nullptr), playhead_(0), type_(type) { @@ -230,12 +231,16 @@ int TimelineView::GetTrackY(int track_index) { int y = 0; + if (alignment() & Qt::AlignBottom) { + track_index++; + } + for (int i=0;i= connected_track_list_->TrackCount()) { + return TrackOutput::GetDefaultTrackHeight(); + } - return fontMetrics().height() * 3; + return connected_track_list_->TrackAt(track_index)->GetTrackHeight(); } QPoint TimelineView::GetScrollCoordinates() @@ -260,6 +266,11 @@ void TimelineView::SetScrollCoordinates(const QPoint &pt) verticalScrollBar()->setValue(pt.y()); } +void TimelineView::ConnectTrackList(TrackList *list) +{ + connected_track_list_ = list; +} + int TimelineView::SceneToTrack(double y) { int track = -1; diff --git a/app/widget/timelinewidget/view/timelineview.h b/app/widget/timelinewidget/view/timelineview.h index 2f334b96f..649508532 100644 --- a/app/widget/timelinewidget/view/timelineview.h +++ b/app/widget/timelinewidget/view/timelineview.h @@ -64,6 +64,8 @@ public: QPoint GetScrollCoordinates(); void SetScrollCoordinates(const QPoint& pt); + void ConnectTrackList(TrackList* list); + public slots: void SetTimebase(const rational& timebase); @@ -110,20 +112,20 @@ private: void UserSetTime(const int64_t& time); + rational GetPlayheadTime(); + + void UpdatePlayheadRect(); + + TrackList* connected_track_list_; + QGraphicsScene scene_; int64_t playhead_; - QVector track_heights_; - TimelineViewEndItem* end_item_; TimelinePlayhead playhead_style_; - rational GetPlayheadTime(); - - void UpdatePlayheadRect(); - QRect playhead_rect_; TrackType type_;