diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index ba8fedb90..ad08202bc 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -43,6 +43,7 @@ Block::Block() : out_transition_(nullptr) { AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + SetInputProperty(kLengthInput, "min", QVariant::fromValue(rational(0, 1))); IgnoreInvalidationsFrom(kLengthInput); IgnoreHashingFrom(kLengthInput); diff --git a/app/panel/timebased/timebased.cpp b/app/panel/timebased/timebased.cpp index f569fc2ac..cc31d9fe2 100644 --- a/app/panel/timebased/timebased.cpp +++ b/app/panel/timebased/timebased.cpp @@ -34,6 +34,11 @@ rational TimeBasedPanel::GetTime() return widget_->GetTime(); } +const rational& TimeBasedPanel::timebase() +{ + return widget_->timebase(); +} + void TimeBasedPanel::GoToStart() { widget_->GoToStart(); diff --git a/app/panel/timebased/timebased.h b/app/panel/timebased/timebased.h index 30b5e208b..984e2ba6e 100644 --- a/app/panel/timebased/timebased.h +++ b/app/panel/timebased/timebased.h @@ -41,6 +41,9 @@ public: rational GetTime(); + // Get the timebase of this panels widget + const rational& timebase(); + ViewerOutput *GetConnectedViewer() const { return widget_->GetConnectedNode(); diff --git a/app/widget/menu/menushared.cpp b/app/widget/menu/menushared.cpp index 1ccf5f37d..51260c9b0 100644 --- a/app/widget/menu/menushared.cpp +++ b/app/widget/menu/menushared.cpp @@ -21,6 +21,7 @@ #include "menushared.h" #include "core.h" +#include "common/timecodefunctions.h" #include "panel/panelmanager.h" #include "panel/timeline/timeline.h" @@ -159,10 +160,25 @@ void MenuShared::AddItemsForClipEditMenu(Menu *m) m->addAction(clip_nest_item_); } -void MenuShared::AddItemsForTimeRulerMenu(Menu *m) +void MenuShared::AddItemsForTimeRulerMenu(Menu *m, const rational& timebase) { - m->addAction(view_timecode_view_dropframe_item_); - m->addAction(view_timecode_view_nondropframe_item_); + // If a menu is already created (such as the view menu) we need to remove the instance + // of dropframe or non-dropframe timecode that is already there to avoid double displays + + if (m->actions().contains(view_timecode_view_dropframe_item_)) { + m->removeAction(view_timecode_view_dropframe_item_); + } + + if (m->actions().contains(view_timecode_view_nondropframe_item_)) { + m->removeAction(view_timecode_view_nondropframe_item_); + } + + if (Timecode::TimebaseIsDropFrame(timebase)) { + m->addAction(view_timecode_view_dropframe_item_); + m->addAction(view_timecode_view_nondropframe_item_); + } else { + m->addAction(view_timecode_view_nondropframe_item_); + } m->addAction(view_timecode_view_seconds_item_); m->addAction(view_timecode_view_frames_item_); m->addAction(view_timecode_view_milliseconds_item_); diff --git a/app/widget/menu/menushared.h b/app/widget/menu/menushared.h index ae0707833..5d1cb7a1e 100644 --- a/app/widget/menu/menushared.h +++ b/app/widget/menu/menushared.h @@ -21,6 +21,7 @@ #ifndef MENUSHARED_H #define MENUSHARED_H +#include "common/rational.h" #include "widget/colorlabelmenu/colorlabelmenu.h" #include "widget/menu/menu.h" @@ -45,7 +46,7 @@ public: void AddItemsForInOutMenu(Menu* m); void AddColorCodingMenu(Menu* m); void AddItemsForClipEditMenu(Menu* m); - void AddItemsForTimeRulerMenu(Menu* m); + void AddItemsForTimeRulerMenu(Menu* m, const rational& timebase); void AboutToShowTimeRulerActions(); diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index f05c4996a..eb37ef242 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -214,6 +214,10 @@ void NodeParamView::TimebaseChangedEvent(const rational &timebase) keyframe_view_->SetTimebase(timebase); + foreach (NodeParamViewItem* item, items_) { + item->SetTimebase(timebase); + } + UpdateItemTime(GetTimestamp()); } @@ -312,6 +316,9 @@ void NodeParamView::AddNode(Node *n) // Set time target item->SetTimeTarget(GetTimeTarget()); + // Set the timebase + item->SetTimebase(timebase()); + items_.insert(n, item); param_widget_area_->addDockWidget(Qt::LeftDockWidgetArea, item); diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 2a59ab5a3..aa8508ffd 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -86,6 +86,11 @@ void NodeParamViewItem::SetTime(const rational &time) body_->SetTime(time_); } +void NodeParamViewItem::SetTimebase(const rational& timebase) +{ + body_->SetTimebase(timebase); +} + Node *NodeParamViewItem::GetNode() const { return node_; @@ -532,6 +537,13 @@ void NodeParamViewItemBody::ToggleArrayExpanded() } } +void NodeParamViewItemBody::SetTimebase(const rational& timebase) +{ + foreach (const InputUI& ui_obj, input_ui_map_) { + ui_obj.widget_bridge->SetTimebase(timebase); + } +} + void NodeParamViewItemBody::ReplaceWidgets(const NodeInput &input) { InputUI ui = input_ui_map_.value(input); diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 3351682dd..12899525f 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -85,6 +85,9 @@ public: int GetElementY(NodeInput c) const; + // Set the timebase of any timebased widgets contained here + void SetTimebase(const rational& timebase); + signals: void RequestSetTime(const rational& time); @@ -167,6 +170,9 @@ public: void SetTime(const rational& time); + // Set the timebase of the NodeParamViewItemBody + void SetTimebase(const rational& timebase); + Node* GetNode() const; bool IsExpanded() const; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 54c5b2285..e49096321 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -37,6 +37,7 @@ #include "widget/filefield/filefield.h" #include "widget/slider/floatslider.h" #include "widget/slider/integerslider.h" +#include "widget/slider/rationalslider.h" #include "widget/videoparamedit/videoparamedit.h" namespace olive { @@ -77,7 +78,6 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeValue::kNone: case NodeValue::kTexture: case NodeValue::kMatrix: - case NodeValue::kRational: case NodeValue::kSamples: case NodeValue::kFootageJob: case NodeValue::kShaderJob: @@ -94,6 +94,11 @@ void NodeParamViewWidgetBridge::CreateWidgets() CreateSliders(1); break; } + case NodeValue::kRational: + { + CreateSliders(1); + break; + } case NodeValue::kVec2: { CreateSliders(2); @@ -270,7 +275,6 @@ void NodeParamViewWidgetBridge::WidgetCallback() case NodeValue::kTexture: case NodeValue::kMatrix: case NodeValue::kSamples: - case NodeValue::kRational: case NodeValue::kFootageJob: case NodeValue::kShaderJob: case NodeValue::kSampleJob: @@ -292,6 +296,13 @@ void NodeParamViewWidgetBridge::WidgetCallback() ProcessSlider(slider, slider->GetValue()); break; } + case NodeValue::kRational: + { + // Widget is a RationalSlider + RationalSlider* slider = static_cast(sender()); + ProcessSlider(slider, QVariant::fromValue(slider->GetValue())); + break; + } case NodeValue::kVec2: { // Widget is a FloatSlider @@ -416,7 +427,6 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() case NodeValue::kNone: case NodeValue::kTexture: case NodeValue::kMatrix: - case NodeValue::kRational: case NodeValue::kSamples: case NodeValue::kFootageJob: case NodeValue::kShaderJob: @@ -433,6 +443,11 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() static_cast(widgets_.first())->SetValue(input_.GetValueAtTime(node_time).toDouble()); break; } + case NodeValue::kRational: + { + static_cast(widgets_.first())->SetValue(input_.GetValueAtTime(node_time).value()); + break; + } case NodeValue::kVec2: { QVector2D vec2 = input_.GetValueAtTime(node_time).value(); @@ -523,6 +538,13 @@ rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const return GetAdjustedTime(GetTimeTarget(), input_.node(), time_, true); } +void NodeParamViewWidgetBridge::SetTimebase(const rational& timebase) +{ + if (input_.GetDataType() == NodeValue::kRational) { + static_cast(widgets_.first())->SetTimebase(timebase); + } +} + void NodeParamViewWidgetBridge::InputValueChanged(const NodeInput &input, const TimeRange &range) { if (input_ == input @@ -572,7 +594,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr static_cast(widgets_.first())->SetMinimum(value.toDouble()); break; case NodeValue::kRational: - // FIXME: Rational doesn't have a UI implementation yet + static_cast(widgets_.first())->SetMinimum(value.value()); break; case NodeValue::kVec2: { @@ -610,7 +632,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr static_cast(widgets_.first())->SetMaximum(value.toDouble()); break; case NodeValue::kRational: - // FIXME: Rational doesn't have a UI implementation yet + static_cast(widgets_.first())->SetMaximum(value.value()); break; case NodeValue::kVec2: { @@ -648,7 +670,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr static_cast(widgets_.first())->SetOffset(value); break; case NodeValue::kRational: - // FIXME: Rational doesn't have a UI implementation yet + static_cast(widgets_.first())->SetOffset(value); break; case NodeValue::kVec2: { diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index ee288f35f..e12892702 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -49,6 +49,9 @@ public: return widgets_; } + // Set the timebase of certain Timebased widgets + void SetTimebase(const rational& timebase); + signals: void ArrayWidgetDoubleClicked(); diff --git a/app/widget/slider/CMakeLists.txt b/app/widget/slider/CMakeLists.txt index ef2b3443c..0d9d22944 100644 --- a/app/widget/slider/CMakeLists.txt +++ b/app/widget/slider/CMakeLists.txt @@ -20,6 +20,8 @@ set(OLIVE_SOURCES widget/slider/floatslider.cpp widget/slider/integerslider.h widget/slider/integerslider.cpp + widget/slider/rationalslider.h + widget/slider/rationalslider.cpp widget/slider/sliderbase.h widget/slider/sliderbase.cpp widget/slider/sliderlabel.h diff --git a/app/widget/slider/rationalslider.cpp b/app/widget/slider/rationalslider.cpp new file mode 100644 index 000000000..d9b659d37 --- /dev/null +++ b/app/widget/slider/rationalslider.cpp @@ -0,0 +1,197 @@ +/*** + Olive - Non-Linear Video Editor + Copyright (C) 2021 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 . +***/ + +#include "rationalslider.h" + +#include "common/timecodefunctions.h" +#include "core.h" +#include "widget/menu/menu.h" +#include "widget/menu/menushared.h" + +namespace olive { + +RationalSlider::RationalSlider(QWidget *parent) : + SliderBase(SliderBase::kRational, parent), + display_type_(kTimecode), + decimal_places_(2), + autotrim_decimal_places_(false), + lock_display_type_(false) +{ + connect(this, SIGNAL(ValueChanged(QVariant)), this, SLOT(ConvertValue(QVariant))); + connect(Core::instance(), &Core::TimecodeDisplayChanged, this, &RationalSlider::ChangeTimecodeDisplayType); + connect(SliderBase::label(), &SliderLabel::customContextMenuRequested, this, &RationalSlider::changeDisplayType); + + SetDisplayType(display_type_); + + SetValue(rational(0, 0)); +} + +rational RationalSlider::GetValue() +{ + return Value().value(); +} + +void RationalSlider::SetValue(const rational &d) +{ + SliderBase::SetValue(QVariant::fromValue(d)); +} + +void RationalSlider::SetDefaultValue(const rational &r) +{ + SliderBase::SetDefaultValue(QVariant::fromValue(r)); +} + +void RationalSlider::SetDefaultValue(const QVariant &v) { + rational r = v.value(); + SetDefaultValue(r); +} + +void RationalSlider::SetMinimum(const rational &d) +{ + SetMinimumInternal(QVariant::fromValue(d)); +} + +void RationalSlider::SetMaximum(const rational &d) +{ + SetMaximumInternal(QVariant::fromValue(d)); +} + +void RationalSlider::SetDecimalPlaces(int i) +{ + decimal_places_ = i; + + ForceLabelUpdate(); +} + +void RationalSlider::SetTimebase(const rational &timebase) +{ + timebase_ = timebase; + + // Refresh label since we have a new timebase to generate a timecode with + UpdateLabel(Value()); +} + +void RationalSlider::SetAutoTrimDecimalPlaces(bool e) { + autotrim_decimal_places_ = e; + + ForceLabelUpdate(); +} + +void RationalSlider::SetDisplayType(const RationalSlider::DisplayType &type) +{ + display_type_ = type; + + switch (display_type_) { + case kTimecode: + ClearFormat(); + break; + case kTimestamp: + SetFormat("%1 Frames"); + break; + case kFloat: + SetFormat("%1 s"); + break; + } +} + +void RationalSlider::SetLockDisplayType(bool e) +{ + lock_display_type_ = e; +} + +bool RationalSlider::LockDisplayType() +{ + return lock_display_type_; +} + +QString RationalSlider::ValueToString(const QVariant &v) +{ + double time = v.value().toDouble() + GetOffset().value().toDouble(); + + switch (display_type_) { + case kTimecode: + return Timecode::time_to_timecode(v.value(), timebase_, Core::instance()->GetTimecodeDisplay()); + case kTimestamp: + return QString::number(Timecode::time_to_timestamp(time, timebase_)); + case kFloat: + { + QString s = QString::number(time, 'f', decimal_places_); + + if (autotrim_decimal_places_) { + while (s.endsWith('0') && s.at(s.size() - 2).isDigit()) { + s = s.left(s.size() - 1); + } + } + return s; + } + } + return v.toString(); +} + +QVariant RationalSlider::StringToValue(const QString &s, bool *ok) +{ + rational r; + *ok = false; + + switch (display_type_) { + case kTimecode: + { + int t = Timecode::timecode_to_timestamp(s, timebase_, Core::instance()->GetTimecodeDisplay(), ok); + r = rational(t, timebase_.denominator()); + break; + } + case kTimestamp: + r = rational(s.toInt(ok), timebase_.denominator()); + break; + case kFloat: + r = rational::fromDouble(s.toDouble(ok)); + if (!r.isNull()) { + *ok = true; + } + break; + } + + return QVariant::fromValue(r - GetOffset().value()); +} + +double RationalSlider::AdjustDragDistanceInternal(const double &start, const double &drag) +{ + // Assume we want smallest increment to be timebase or 1 frame + return start + drag*timebase_.toDouble(); +} + +void RationalSlider::ConvertValue(QVariant v) +{ + emit ValueChanged(v.value()); +} + +void RationalSlider::changeDisplayType() +{ + if (!LockDisplayType()) { + Menu m(this); + MenuShared::instance()->AddItemsForTimeRulerMenu(&m, timebase_); + MenuShared::instance()->AboutToShowTimeRulerActions(); + + m.exec(QCursor::pos()); + ForceLabelUpdate(); + } +} + +void RationalSlider::ChangeTimecodeDisplayType() +{ + ForceLabelUpdate(); +} + +} diff --git a/app/widget/slider/rationalslider.h b/app/widget/slider/rationalslider.h new file mode 100644 index 000000000..5a077c76c --- /dev/null +++ b/app/widget/slider/rationalslider.h @@ -0,0 +1,139 @@ +/*** + Olive - Non-Linear Video Editor + Copyright (C) 2021 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 . +***/ + +#ifndef RATIONALSLIDER_H +#define RATIONALSLIDER_H + +#include "sliderbase.h" + +#include + +#include "common/rational.h" + +namespace olive { + + /** + * @brief A olive::rational based slider + * + * A slider that can display rationals as either timecode (drop or non-drop), a timestamp (frames), + * or a float (seconds). + */ +class RationalSlider : public SliderBase +{ + Q_OBJECT +public: + /** + * @brief enum containing the possibly display types + */ + enum DisplayType { + kTimecode, + kTimestamp, + kFloat + }; + + RationalSlider(QWidget* parent = nullptr); + + /** + * @brief Returns the sliders value as a rational + */ + rational GetValue(); + + /** + * @brief Sets the sliders value + */ + void SetValue(const rational& d); + + /** + * @brief Sets the sliders default value + */ + void SetDefaultValue(const rational& r); + + /** + * @brief Sets the sliders default value + */ + void SetDefaultValue(const QVariant& v); + + /** + * @brief Sets the sliders minimum value + */ + void SetMinimum(const rational& d); + + /** + * @brief Sets the sliders maximum value + */ + void SetMaximum(const rational& d); + + /** + * @brief Sets the number of decimal places the slider shows when displaying a float + */ + void SetDecimalPlaces(int i); + + /** + * @brief Sets the sliders timebase which is also the minimum increment of the slider + */ + void SetTimebase(const rational& timebase); + + /** + * @brief Sets the display type of the slider + */ + void SetDisplayType(const DisplayType& type); + + /** + * @brief Set whether the user can change the display type or not + */ + void SetLockDisplayType(bool e); + + /** + * @brief Get whether the user can change the display type or not + */ + bool LockDisplayType(); + + void SetAutoTrimDecimalPlaces(bool e); + +protected: + virtual QString ValueToString(const QVariant& v) override; + + virtual QVariant StringToValue(const QString& s, bool* ok) override; + + virtual double AdjustDragDistanceInternal(const double& start, const double& drag) override; + +signals: + void ValueChanged(rational); + +private slots: + void ConvertValue(QVariant v); + + void changeDisplayType(); + + /** + * @brief Changes the timecodes display type (e.g. drop frome to none drop frame) + */ + void ChangeTimecodeDisplayType(); + +private: + DisplayType display_type_; + + int decimal_places_; + + bool autotrim_decimal_places_; + + rational timebase_; + + bool lock_display_type_; +}; + +} + +#endif // RATIONALSLIDER_H diff --git a/app/widget/slider/sliderbase.cpp b/app/widget/slider/sliderbase.cpp index 447fd57f4..b1490ed12 100644 --- a/app/widget/slider/sliderbase.cpp +++ b/app/widget/slider/sliderbase.cpp @@ -66,6 +66,7 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : break; case kInteger: case kFloat: + case kRational: setCursor(Qt::SizeHorCursor); break; } @@ -166,8 +167,14 @@ void SliderBase::SetMinimumInternal(const QVariant &v) has_min_ = true; // Limit value by this new minimum value - if (value_.toDouble() < min_value_.toDouble()) { - SetValue(min_value_); + if (mode_ == kRational) { + if (value_.value().toDouble() < min_value_.value().toDouble()) { + SetValue(min_value_); + } + } else { + if (value_.toDouble() < min_value_.toDouble()) { + SetValue(min_value_); + } } } @@ -177,8 +184,14 @@ void SliderBase::SetMaximumInternal(const QVariant &v) has_max_ = true; // Limit value by this new maximum value - if (value_.toDouble() > max_value_.toDouble()) { - SetValue(max_value_); + if (mode_ == kRational) { + if (value_.value().toDouble() > max_value_.value().toDouble()) { + SetValue(max_value_); + } + } else { + if (value_.toDouble() > max_value_.toDouble()) { + SetValue(max_value_); + } } } @@ -192,11 +205,21 @@ void SliderBase::changeEvent(QEvent *e) const QVariant &SliderBase::ClampValue(const QVariant &v) { - if (has_min_ && v.toDouble() < min_value_.toDouble()) { - return min_value_; + double value, min, max; + + if (mode_ == kRational) { + value = v.value().toDouble(); + min = min_value_.value().toDouble(); + max = max_value_.value().toDouble(); + } else { + value = v.toDouble(); + min = min_value_.toDouble(); + max = max_value_.toDouble(); } - - if (has_max_ && v.toDouble() > max_value_.toDouble()) { + + if (has_min_ && value < min) { + return min_value_; + }else if (has_max_ && value > max) { return max_value_; } @@ -266,8 +289,13 @@ void SliderBase::LabelPressed() break; case kInteger: case kFloat: + case kRational: { - drag_ladder_ = new SliderLadder(drag_multiplier_, ladder_element_count_); + if (mode_ == kRational) { + drag_ladder_ = new SliderLadder(drag_multiplier_, ladder_element_count_, "00:00:00:00"); + } else { + drag_ladder_ = new SliderLadder(drag_multiplier_, ladder_element_count_, "00000000"); + } drag_ladder_->SetValue(ValueToString(value_)); drag_ladder_->show(); @@ -315,6 +343,28 @@ void SliderBase::LadderDragged(int value, double multiplier) emit ValueChanged(clamped_temp_dragged_value_); break; } + + case kRational: + { + dragged_diff_ += value * drag_multiplier_ * multiplier; + double drag_val = AdjustDragDistanceInternal(value_.value().toDouble(), dragged_diff_); + rational d_v; + d_v = rational::fromDouble(drag_val); + temp_dragged_value_.setValue(d_v); + + clamped_temp_dragged_value_ = ClampValue(temp_dragged_value_); + + UpdateLabel(temp_dragged_value_); + + drag_ladder_->SetValue(ValueToString(clamped_temp_dragged_value_)); + + if (!Config::Current()[QStringLiteral("UseSliderLadders")].toBool()) { + RepositionLadder(); + } + + emit ValueChanged(clamped_temp_dragged_value_); + break; + } } } @@ -336,6 +386,8 @@ void SliderBase::LadderReleased() case kFloat: SetValue(clamped_temp_dragged_value_.toDouble()); break; + case kRational: + SetValue(temp_dragged_value_); } emit ValueChanged(value_); diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/sliderbase.h index d0987794e..720647721 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/sliderbase.h @@ -35,7 +35,8 @@ public: enum Mode { kString, kInteger, - kFloat + kFloat, + kRational }; SliderBase(Mode mode, QWidget* parent = nullptr); @@ -84,6 +85,8 @@ protected: void UpdateLabel(const QVariant& v); + QLabel* label() { return label_; } + virtual double AdjustDragDistanceInternal(const double& start, const double& drag); virtual QString ValueToString(const QVariant &v) = 0; @@ -155,7 +158,6 @@ private slots: void ResetValue(); void RepositionLadder(); - }; } diff --git a/app/widget/slider/sliderlabel.cpp b/app/widget/slider/sliderlabel.cpp index 11048658f..3aa37ecca 100644 --- a/app/widget/slider/sliderlabel.cpp +++ b/app/widget/slider/sliderlabel.cpp @@ -47,6 +47,9 @@ SliderLabel::SliderLabel(QWidget *parent) : // Allow users to tab to this widget setFocusPolicy(Qt::TabFocus); + + // Add ccustom context menu + setContextMenuPolicy(Qt::CustomContextMenu); } void SliderLabel::mousePressEvent(QMouseEvent *e) diff --git a/app/widget/slider/sliderlabel.h b/app/widget/slider/sliderlabel.h index 2f698eb2c..533208a3b 100644 --- a/app/widget/slider/sliderlabel.h +++ b/app/widget/slider/sliderlabel.h @@ -45,6 +45,8 @@ signals: void RequestReset(); + void ChangeSliderType(); + }; } diff --git a/app/widget/slider/sliderladder.cpp b/app/widget/slider/sliderladder.cpp index f25f55217..2d9977be2 100644 --- a/app/widget/slider/sliderladder.cpp +++ b/app/widget/slider/sliderladder.cpp @@ -37,8 +37,9 @@ namespace olive { -SliderLadder::SliderLadder(double drag_multiplier, int nb_outer_values, QWidget* parent) : - QFrame(parent, Qt::Popup) +SliderLadder::SliderLadder(double drag_multiplier, int nb_outer_values, QString width_hint, QWidget* parent) : + QFrame(parent, Qt::Popup), + width_hint_(width_hint) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); @@ -52,17 +53,17 @@ SliderLadder::SliderLadder(double drag_multiplier, int nb_outer_values, QWidget* } for (int i=nb_outer_values-1;i>=0;i--) { - elements_.append(new SliderLadderElement(qPow(10, i + 1) * drag_multiplier)); + elements_.append(new SliderLadderElement(qPow(10, i + 1) * drag_multiplier, width_hint_)); } // Create center entry - SliderLadderElement* start_element = new SliderLadderElement(drag_multiplier); + SliderLadderElement* start_element = new SliderLadderElement(drag_multiplier, width_hint_); active_element_ = elements_.size(); start_element->SetHighlighted(true); elements_.append(start_element); for (int i=0;i 1; } -SliderLadderElement::SliderLadderElement(const double &multiplier, QWidget *parent) : + SliderLadderElement::SliderLadderElement(const double &multiplier, QString width_hint, QWidget *parent) : QWidget(parent), multiplier_(multiplier), highlighted_(false), @@ -241,7 +242,7 @@ SliderLadderElement::SliderLadderElement(const double &multiplier, QWidget *pare label_ = new QLabel(); label_->setAlignment(Qt::AlignCenter); - label_->setFixedWidth(QtUtils::QFontMetricsWidth(label_->fontMetrics(), QStringLiteral("0000000"))); + label_->setFixedWidth(QtUtils::QFontMetricsWidth(label_->fontMetrics(), width_hint)); layout->addWidget(label_); QPalette p = palette(); diff --git a/app/widget/slider/sliderladder.h b/app/widget/slider/sliderladder.h index ffd4a6ced..2f2ac7eff 100644 --- a/app/widget/slider/sliderladder.h +++ b/app/widget/slider/sliderladder.h @@ -33,7 +33,7 @@ class SliderLadderElement : public QWidget { Q_OBJECT public: - SliderLadderElement(const double& multiplier, QWidget* parent = nullptr); + SliderLadderElement(const double& multiplier, QString width_hint, QWidget* parent = nullptr); void SetHighlighted(bool e); @@ -64,7 +64,7 @@ class SliderLadder : public QFrame { Q_OBJECT public: - SliderLadder(double drag_multiplier, int nb_outer_values, QWidget* parent = nullptr); + SliderLadder(double drag_multiplier, int nb_outer_values, QString width_hint, QWidget* parent = nullptr); virtual ~SliderLadder() override; @@ -94,6 +94,8 @@ private: QTimer drag_timer_; + QString width_hint_; + private slots: void TimerUpdate(); diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 3b3f92635..18e1948f6 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -306,7 +306,7 @@ void TimeRuler::ShowContextMenu() { Menu m(this); - MenuShared::instance()->AddItemsForTimeRulerMenu(&m); + MenuShared::instance()->AddItemsForTimeRulerMenu(&m, timebase()); MenuShared::instance()->AboutToShowTimeRulerActions(); m.exec(QCursor::pos()); diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index f207adf6f..c471c2ea2 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -112,9 +112,6 @@ MainMenu::MainMenu(MainWindow *parent) : view_decrease_track_height_item_ = view_menu_->AddItem("vzoomout", this, &MainMenu::DecreaseTrackHeightTriggered, "Ctrl+-"); view_show_all_item_ = view_menu_->AddItem("showall", this, &MainMenu::ToggleShowAllTriggered, "\\"); view_show_all_item_->setCheckable(true); - view_menu_->addSeparator(); - - MenuShared::instance()->AddItemsForTimeRulerMenu(view_menu_); view_menu_->addSeparator(); @@ -306,6 +303,15 @@ void MainMenu::ViewMenuAboutToShow() // Parent is QMainWindow view_full_screen_item_->setChecked(parentWidget()->isFullScreen()); + // Make sure we're displaying the correct options for the timebase + TimeBasedPanel* p = PanelManager::instance()->MostRecentlyFocused(); + if (p) { + if (p->timebase().denominator() != 0) { + view_menu_->addSeparator(); + MenuShared::instance()->AddItemsForTimeRulerMenu(view_menu_, p->timebase()); + } + } + // Ensure checked timecode display mode is correct MenuShared::instance()->AboutToShowTimeRulerActions(); }