From 9328539373be9ffac635a95412163c8513c911ec Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 17 Apr 2021 12:49:21 +1000 Subject: [PATCH] slider: reworked sliderbase and derivatives Mostly cleaning up code, SliderBase was fairly messy. --- app/common/rational.cpp | 33 +- app/common/rational.h | 4 +- app/common/timecodefunctions.cpp | 6 + app/common/timecodefunctions.h | 1 + .../tabs/preferencesgeneraltab.cpp | 9 +- .../preferences/tabs/preferencesgeneraltab.h | 4 +- app/node/block/block.cpp | 7 +- app/widget/menu/menushared.cpp | 35 +- app/widget/menu/menushared.h | 4 +- .../nodeparamviewwidgetbridge.cpp | 20 +- .../nodeparamview/nodeparamviewwidgetbridge.h | 4 +- app/widget/slider/CMakeLists.txt | 8 +- app/widget/slider/base/CMakeLists.txt | 30 ++ app/widget/slider/base/decimalsliderbase.cpp | 63 +++ app/widget/slider/base/decimalsliderbase.h | 50 ++ app/widget/slider/base/numericsliderbase.cpp | 203 ++++++++ app/widget/slider/base/numericsliderbase.h | 103 ++++ app/widget/slider/base/sliderbase.cpp | 230 +++++++++ app/widget/slider/{ => base}/sliderbase.h | 117 ++--- app/widget/slider/{ => base}/sliderlabel.cpp | 9 + app/widget/slider/{ => base}/sliderlabel.h | 6 +- app/widget/slider/{ => base}/sliderladder.cpp | 11 +- app/widget/slider/{ => base}/sliderladder.h | 2 - app/widget/slider/floatslider.cpp | 64 +-- app/widget/slider/floatslider.h | 27 +- app/widget/slider/integerslider.cpp | 28 +- app/widget/slider/integerslider.h | 18 +- app/widget/slider/rationalslider.cpp | 190 +++---- app/widget/slider/rationalslider.h | 67 ++- app/widget/slider/sliderbase.cpp | 470 ------------------ app/widget/slider/stringslider.cpp | 29 +- app/widget/slider/stringslider.h | 18 +- app/widget/slider/timeslider.cpp | 20 +- app/widget/slider/timeslider.h | 7 +- app/widget/timeruler/timeruler.cpp | 4 +- app/widget/videoparamedit/videoparamedit.cpp | 14 +- app/widget/videoparamedit/videoparamedit.h | 4 +- app/window/mainwindow/mainmenu.cpp | 4 +- 38 files changed, 1068 insertions(+), 855 deletions(-) create mode 100644 app/widget/slider/base/CMakeLists.txt create mode 100644 app/widget/slider/base/decimalsliderbase.cpp create mode 100644 app/widget/slider/base/decimalsliderbase.h create mode 100644 app/widget/slider/base/numericsliderbase.cpp create mode 100644 app/widget/slider/base/numericsliderbase.h create mode 100644 app/widget/slider/base/sliderbase.cpp rename app/widget/slider/{ => base}/sliderbase.h (50%) rename app/widget/slider/{ => base}/sliderlabel.cpp (90%) rename app/widget/slider/{ => base}/sliderlabel.h (88%) rename app/widget/slider/{ => base}/sliderladder.cpp (96%) rename app/widget/slider/{ => base}/sliderladder.h (98%) delete mode 100644 app/widget/slider/sliderbase.cpp diff --git a/app/common/rational.cpp b/app/common/rational.cpp index 62c9af3e0..f8702acb6 100644 --- a/app/common/rational.cpp +++ b/app/common/rational.cpp @@ -5,23 +5,42 @@ namespace olive { -rational rational::fromDouble(const double &flt) +rational rational::fromDouble(const double &flt, bool* ok) { // Use FFmpeg function for the time being - return av_d2q(flt, INT_MAX); + AVRational r = av_d2q(flt, INT_MAX); + if (r.den == 0) { + // If den == 0, we were unable to convert to a rational + if (ok) { + *ok = false; + } + + return rational(); + } else { + // Otherwise, assume we received a real rational + if (ok) { + *ok = true; + } + + return r; + } + } -rational rational::fromString(const QString &str) +rational rational::fromString(const QString &str, bool* ok) { QStringList elements = str.split('/'); switch (elements.size()) { - case 0: - return rational(); case 1: - return rational(elements.first().toLongLong()); + return rational(elements.first().toLongLong(ok)); + case 2: + return rational(elements.at(0).toLongLong(ok), elements.at(1).toLongLong(ok)); default: - return rational(elements.at(0).toLongLong(), elements.at(1).toLongLong()); + if (ok) { + *ok = false; + } + return rational(); } } diff --git a/app/common/rational.h b/app/common/rational.h index 95c369d1b..8dd3327fa 100644 --- a/app/common/rational.h +++ b/app/common/rational.h @@ -61,9 +61,9 @@ public: reduce(); } - static rational fromDouble(const double& flt); + static rational fromDouble(const double& flt, bool *ok = nullptr); - static rational fromString(const QString& str); + static rational fromString(const QString& str, bool* ok = nullptr); //Assignment Operators const rational& operator=(const rational &rhs); diff --git a/app/common/timecodefunctions.cpp b/app/common/timecodefunctions.cpp index 491f76821..3c19fc807 100644 --- a/app/common/timecodefunctions.cpp +++ b/app/common/timecodefunctions.cpp @@ -233,6 +233,12 @@ err_fatal: return 0; } +rational Timecode::timecode_to_time(const QString &timecode, const rational &timebase, const Timecode::Display &display, bool *ok) +{ + int64_t timestamp = timecode_to_timestamp(timecode, timebase, display, ok); + return timestamp_to_time(timestamp, timebase); +} + rational Timecode::snap_time_to_timebase(const rational &time, const rational &timebase) { // Just convert to a timestamp in timebase units and back diff --git a/app/common/timecodefunctions.h b/app/common/timecodefunctions.h index 872b9f823..e72d9a138 100644 --- a/app/common/timecodefunctions.h +++ b/app/common/timecodefunctions.h @@ -53,6 +53,7 @@ public: static QString timestamp_to_timecode(const int64_t ×tamp, const rational& timebase, const Display &display, bool show_plus_if_positive = false); static int64_t timecode_to_timestamp(const QString& timecode, const rational& timebase, const Display& display, bool *ok = nullptr); + static rational timecode_to_time(const QString& timecode, const rational& timebase, const Display& display, bool *ok = nullptr); static rational snap_time_to_timebase(const rational& time, const rational& timebase); diff --git a/app/dialog/preferences/tabs/preferencesgeneraltab.cpp b/app/dialog/preferences/tabs/preferencesgeneraltab.cpp index 646d20642..5d4436937 100644 --- a/app/dialog/preferences/tabs/preferencesgeneraltab.cpp +++ b/app/dialog/preferences/tabs/preferencesgeneraltab.cpp @@ -101,10 +101,11 @@ PreferencesGeneralTab::PreferencesGeneralTab() timeline_layout->addWidget(new QLabel(tr("Default Still Image Length:")), row, 0); - default_still_length_ = new FloatSlider(); - default_still_length_->SetMinimum(0.1); + default_still_length_ = new RationalSlider(); + default_still_length_->SetMinimum(rational(100, 1000)); + default_still_length_->SetTimebase(rational(100, 1000)); default_still_length_->SetFormat(tr("%1 seconds")); - default_still_length_->SetValue(Config::Current()["DefaultStillLength"].value().toDouble()); + default_still_length_->SetValue(Config::Current()["DefaultStillLength"].value()); timeline_layout->addWidget(default_still_length_); row++; @@ -168,7 +169,7 @@ void PreferencesGeneralTab::Accept(MultiUndoCommand *command) Config::Current()[QStringLiteral("Autoscroll")] = autoscroll_method_->currentData(); - Config::Current()[QStringLiteral("DefaultStillLength")] = QVariant::fromValue(rational::fromDouble(default_still_length_->GetValue())); + Config::Current()[QStringLiteral("DefaultStillLength")] = QVariant::fromValue(default_still_length_->GetValue()); QString set_language = language_combobox_->currentData().toString(); if (QLocale::system().name() == set_language) { diff --git a/app/dialog/preferences/tabs/preferencesgeneraltab.h b/app/dialog/preferences/tabs/preferencesgeneraltab.h index f4f8ab8c8..b0d71275a 100644 --- a/app/dialog/preferences/tabs/preferencesgeneraltab.h +++ b/app/dialog/preferences/tabs/preferencesgeneraltab.h @@ -27,7 +27,7 @@ #include "dialog/configbase/configdialogbase.h" #include "node/project/sequence/sequence.h" -#include "widget/slider/floatslider.h" +#include "widget/slider/rationalslider.h" #include "widget/slider/integerslider.h" namespace olive { @@ -49,7 +49,7 @@ private: QCheckBox* rectified_waveforms_; - FloatSlider* default_still_length_; + RationalSlider* default_still_length_; QCheckBox* autorecovery_enabled_; diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index ad08202bc..a4f82f73d 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -25,6 +25,7 @@ #include "node/output/track/track.h" #include "transition/transition.h" #include "widget/slider/floatslider.h" +#include "widget/slider/rationalslider.h" namespace olive { @@ -43,11 +44,15 @@ Block::Block() : out_transition_(nullptr) { AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - SetInputProperty(kLengthInput, "min", QVariant::fromValue(rational(0, 1))); + SetInputProperty(kLengthInput, QStringLiteral("min"), QVariant::fromValue(rational(0, 1))); + SetInputProperty(kLengthInput, QStringLiteral("view"), RationalSlider::kTime); + SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true); IgnoreInvalidationsFrom(kLengthInput); IgnoreHashingFrom(kLengthInput); AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + SetInputProperty(kMediaInInput, QStringLiteral("view"), RationalSlider::kTime); + SetInputProperty(kMediaInInput, QStringLiteral("viewlock"), true); IgnoreHashingFrom(kMediaInInput); AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); diff --git a/app/widget/menu/menushared.cpp b/app/widget/menu/menushared.cpp index 51260c9b0..cc0a0df2d 100644 --- a/app/widget/menu/menushared.cpp +++ b/app/widget/menu/menushared.cpp @@ -160,35 +160,30 @@ void MenuShared::AddItemsForClipEditMenu(Menu *m) m->addAction(clip_nest_item_); } -void MenuShared::AddItemsForTimeRulerMenu(Menu *m, const rational& timebase) +void MenuShared::AddItemsForTimeRulerMenu(Menu *m) { - // 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_dropframe_item_); + 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_); } -void MenuShared::AboutToShowTimeRulerActions() +void MenuShared::AboutToShowTimeRulerActions(const rational& timebase) { QList timecode_display_actions = frame_view_mode_group_->actions(); + Timecode::Display current_timecode_display = Core::instance()->GetTimecodeDisplay(); + + // Only show the drop-frame option if the timebase is drop-frame + view_timecode_view_dropframe_item_->setVisible(!timebase.isNull() && Timecode::TimebaseIsDropFrame(timebase)); + + if (!view_timecode_view_dropframe_item_->isVisible() && current_timecode_display == Timecode::kTimecodeDropFrame) { + // If the current setting is drop-frame, correct to non-drop frame + current_timecode_display = Timecode::kTimecodeNonDropFrame; + } + foreach (QAction* a, timecode_display_actions) { - if (a->data() == Core::instance()->GetTimecodeDisplay()) { + if (a->data() == current_timecode_display) { a->setChecked(true); break; } diff --git a/app/widget/menu/menushared.h b/app/widget/menu/menushared.h index 5d1cb7a1e..561e57660 100644 --- a/app/widget/menu/menushared.h +++ b/app/widget/menu/menushared.h @@ -46,9 +46,9 @@ public: void AddItemsForInOutMenu(Menu* m); void AddColorCodingMenu(Menu* m); void AddItemsForClipEditMenu(Menu* m); - void AddItemsForTimeRulerMenu(Menu* m, const rational& timebase); + void AddItemsForTimeRulerMenu(Menu* m); - void AboutToShowTimeRulerActions(); + void AboutToShowTimeRulerActions(const rational& timebase); static MenuShared* instance(); diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index e49096321..f33b1068a 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -238,7 +238,7 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int } } -void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant &value) +void NodeParamViewWidgetBridge::ProcessSlider(NumericSliderBase *slider, const QVariant &value) { rational node_time = GetCurrentTimeAsNodeTime(); @@ -406,7 +406,7 @@ void NodeParamViewWidgetBridge::CreateSliders(int count) { for (int i=0;iSetDefaultValue(input_.GetSplitDefaultValueForTrack(i)); + fs->SliderBase::SetDefaultValue(input_.GetSplitDefaultValueForTrack(i)); fs->SetLadderElementCount(2); widgets_.append(fs); connect(fs, &T::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); @@ -758,6 +758,22 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr } } + if (data_type == NodeValue::kRational) { + if (key == QStringLiteral("view")) { + RationalSlider::DisplayType display_type = static_cast(value.toInt()); + + foreach (QWidget* w, widgets_) { + static_cast(w)->SetDisplayType(display_type); + } + } else if (key == QStringLiteral("viewlock")) { + bool locked = value.toBool(); + + foreach (QWidget* w, widgets_) { + static_cast(w)->SetLockDisplayType(locked); + } + } + } + // Parameters for files if (data_type == NodeValue::kFile) { FileField* ff = static_cast(widgets_.first()); diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index e12892702..0afde7e5d 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -24,7 +24,7 @@ #include #include "node/inputdragger.h" -#include "widget/slider/sliderbase.h" +#include "widget/slider/base/numericsliderbase.h" #include "widget/timetarget/timetarget.h" namespace olive { @@ -64,7 +64,7 @@ private: void SetInputValueInternal(const QVariant& value, int track, MultiUndoCommand *command); - void ProcessSlider(SliderBase* slider, const QVariant& value); + void ProcessSlider(NumericSliderBase* slider, const QVariant& value); template void CreateSliders(int count); diff --git a/app/widget/slider/CMakeLists.txt b/app/widget/slider/CMakeLists.txt index 0d9d22944..203639ef5 100644 --- a/app/widget/slider/CMakeLists.txt +++ b/app/widget/slider/CMakeLists.txt @@ -14,6 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(base) + set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/slider/floatslider.h @@ -22,12 +24,6 @@ set(OLIVE_SOURCES widget/slider/integerslider.cpp widget/slider/rationalslider.h widget/slider/rationalslider.cpp - widget/slider/sliderbase.h - widget/slider/sliderbase.cpp - widget/slider/sliderlabel.h - widget/slider/sliderlabel.cpp - widget/slider/sliderladder.h - widget/slider/sliderladder.cpp widget/slider/stringslider.h widget/slider/stringslider.cpp widget/slider/timeslider.h diff --git a/app/widget/slider/base/CMakeLists.txt b/app/widget/slider/base/CMakeLists.txt new file mode 100644 index 000000000..a02e2be2f --- /dev/null +++ b/app/widget/slider/base/CMakeLists.txt @@ -0,0 +1,30 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2020 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/slider/base/decimalsliderbase.h + widget/slider/base/decimalsliderbase.cpp + widget/slider/base/numericsliderbase.h + widget/slider/base/numericsliderbase.cpp + widget/slider/base/sliderbase.h + widget/slider/base/sliderbase.cpp + widget/slider/base/sliderlabel.h + widget/slider/base/sliderlabel.cpp + widget/slider/base/sliderladder.h + widget/slider/base/sliderladder.cpp + PARENT_SCOPE +) diff --git a/app/widget/slider/base/decimalsliderbase.cpp b/app/widget/slider/base/decimalsliderbase.cpp new file mode 100644 index 000000000..b7debe994 --- /dev/null +++ b/app/widget/slider/base/decimalsliderbase.cpp @@ -0,0 +1,63 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 "decimalsliderbase.h" + +namespace olive { + +#define super NumericSliderBase + +DecimalSliderBase::DecimalSliderBase(QWidget *parent) : + super(parent), + decimal_places_(2), + autotrim_decimal_places_(false) +{ + +} + +void DecimalSliderBase::SetAutoTrimDecimalPlaces(bool e) +{ + autotrim_decimal_places_ = e; + + UpdateLabel(); +} + +QString DecimalSliderBase::FloatToString(double val, int decimal_places, bool autotrim_decimal_places) +{ + QString s = QString::number(val, '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; +} + +void DecimalSliderBase::SetDecimalPlaces(int i) +{ + decimal_places_ = i; + + UpdateLabel(); +} + +} diff --git a/app/widget/slider/base/decimalsliderbase.h b/app/widget/slider/base/decimalsliderbase.h new file mode 100644 index 000000000..f13a7740d --- /dev/null +++ b/app/widget/slider/base/decimalsliderbase.h @@ -0,0 +1,50 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 DECIMALSLIDERBASE_H +#define DECIMALSLIDERBASE_H + +#include "numericsliderbase.h" + +namespace olive { + +class DecimalSliderBase : public NumericSliderBase +{ +public: + DecimalSliderBase(QWidget* parent = nullptr); + + int GetDecimalPlaces() const { return decimal_places_; } + void SetDecimalPlaces(int i); + + bool GetAutoTrimDecimalPlaces() const { return autotrim_decimal_places_; }; + void SetAutoTrimDecimalPlaces(bool e); + + static QString FloatToString(double val, int decimal_places, bool autotrim_decimal_places); + +private: + int decimal_places_; + + bool autotrim_decimal_places_; + +}; + +} + +#endif // DECIMALSLIDERBASE_H diff --git a/app/widget/slider/base/numericsliderbase.cpp b/app/widget/slider/base/numericsliderbase.cpp new file mode 100644 index 000000000..d2ada3f53 --- /dev/null +++ b/app/widget/slider/base/numericsliderbase.cpp @@ -0,0 +1,203 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 "numericsliderbase.h" + +#include "common/qtutils.h" +#include "config/config.h" + +namespace olive { + +NumericSliderBase::NumericSliderBase(QWidget *parent) : + SliderBase(parent), + drag_ladder_(nullptr), + ladder_element_count_(0), + dragged_(false), + has_min_(false), + has_max_(false), + dragged_diff_(0), + drag_multiplier_(1.0), + setting_drag_value_(false) +{ + // Numeric sliders are draggable, so we have a cursor that indicates that + setCursor(Qt::SizeHorCursor); + + connect(label(), &SliderLabel::LabelPressed, this, &NumericSliderBase::LabelPressed); +} + +void NumericSliderBase::SetDragMultiplier(const double &d) +{ + drag_multiplier_ = d; +} + +void NumericSliderBase::LabelPressed() +{ + // Generate width hint + drag_ladder_ = new SliderLadder(drag_multiplier_, ladder_element_count_, GetFormattedValueToString(99999999)); + drag_ladder_->SetValue(GetFormattedValueToString()); + drag_ladder_->show(); + + drag_start_value_ = GetValueInternal(); + + QMetaObject::invokeMethod(this, "RepositionLadder", Qt::QueuedConnection); + + connect(drag_ladder_, &SliderLadder::DraggedByValue, this, &NumericSliderBase::LadderDragged); + connect(drag_ladder_, &SliderLadder::Released, this, &NumericSliderBase::LadderReleased); +} + +void NumericSliderBase::LadderDragged(int value, double multiplier) +{ + dragged_ = true; + + dragged_diff_ += value * drag_multiplier_ * multiplier; + + // Store current value to try and prevent any unnecessary signalling if the value doesn't change + QVariant pre_set_value = GetValueInternal(); + + setting_drag_value_ = true; + SetValueInternal(AdjustDragDistanceInternal(drag_start_value_, dragged_diff_)); + setting_drag_value_ = false; + + if (GetValueInternal() != pre_set_value) { + // We retrieve the value instead of storing it ourselves because SetValueInternal may do extra + // processing (such as clamping). + drag_ladder_->SetValue(GetFormattedValueToString()); + + if (!UsingLadders()) { + RepositionLadder(); + } + + ValueSignalEvent(GetValueInternal()); + } +} + +void NumericSliderBase::LadderReleased() +{ + drag_ladder_->deleteLater(); + drag_ladder_ = nullptr; + dragged_diff_ = 0; + + if (dragged_) { + // This was a drag, send another value changed event + ValueSignalEvent(GetValueInternal()); + + dragged_ = false; + } else { + ShowEditor(); + } +} + +void NumericSliderBase::RepositionLadder() +{ + if (drag_ladder_) { + if (UsingLadders()) { + drag_ladder_->move(QCursor::pos() - QPoint(drag_ladder_->width()/2, drag_ladder_->height()/2)); + } else { + QPoint label_global_pos = label()->mapToGlobal(label()->pos()); + int text_width = QtUtils::QFontMetricsWidth(label()->fontMetrics(), label()->text()); + + if (label()->alignment() & Qt::AlignRight) { + label_global_pos.setX(label_global_pos.x() + label()->width() - text_width); + } else if (label()->alignment() & Qt::AlignHCenter) { + label_global_pos.setX(label_global_pos.x() + label()->width()/2 - text_width/2); + } + + int ladder_x = label_global_pos.x() + text_width / 2 - drag_ladder_->width() / 2; + int ladder_y = label_global_pos.y() + label()->height() / 2 - drag_ladder_->height() / 2; + + drag_ladder_->move(ladder_x, ladder_y); + } + + drag_ladder_->StartListeningToMouseInput(); + } +} + +bool NumericSliderBase::IsDragging() const +{ + return drag_ladder_; +} + +bool NumericSliderBase::UsingLadders() const +{ + return ladder_element_count_ > 0 && Config::Current()[QStringLiteral("UseSliderLadders")].toBool(); +} + +QVariant NumericSliderBase::AdjustValue(const QVariant &value) const +{ + // Clamps between min/max + if (has_min_ && ValueLessThan(value, min_value_)) { + return min_value_; + } else if (has_max_ && ValueGreaterThan(value, max_value_)) { + return max_value_; + } + + return value; +} + +void NumericSliderBase::SetOffset(const QVariant &v) +{ + offset_ = v; + + UpdateLabel(); +} + +QVariant NumericSliderBase::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const +{ + return start.toDouble() + drag; +} + +void NumericSliderBase::SetMinimumInternal(const QVariant &v) +{ + min_value_ = v; + has_min_ = true; + + // Limit value by this new minimum value + if (ValueLessThan(GetValueInternal(), min_value_)) { + SetValueInternal(min_value_); + } +} + +void NumericSliderBase::SetMaximumInternal(const QVariant &v) +{ + max_value_ = v; + has_max_ = true; + + // Limit value by this new maximum value + if (ValueGreaterThan(GetValueInternal(), max_value_)) { + SetValueInternal(max_value_); + } +} + +bool NumericSliderBase::ValueGreaterThan(const QVariant &lhs, const QVariant &rhs) const +{ + return lhs.toDouble() > rhs.toDouble(); +} + +bool NumericSliderBase::ValueLessThan(const QVariant &lhs, const QVariant &rhs) const +{ + return lhs.toDouble() < rhs.toDouble(); +} + +bool NumericSliderBase::CanSetValue() const +{ + return !IsDragging() || setting_drag_value_; +} + +} diff --git a/app/widget/slider/base/numericsliderbase.h b/app/widget/slider/base/numericsliderbase.h new file mode 100644 index 000000000..a1c0466ea --- /dev/null +++ b/app/widget/slider/base/numericsliderbase.h @@ -0,0 +1,103 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 NUMERICSLIDERBASE_H +#define NUMERICSLIDERBASE_H + +#include "sliderbase.h" + +namespace olive { + +class NumericSliderBase : public SliderBase +{ + Q_OBJECT +public: + NumericSliderBase(QWidget *parent = nullptr); + + void SetLadderElementCount(int b) + { + ladder_element_count_ = b; + } + + void SetDragMultiplier(const double& d); + + void SetOffset(const QVariant& v); + + bool IsDragging() const; + +protected: + const QVariant& GetOffset() const + { + return offset_; + } + + virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const; + + void SetMinimumInternal(const QVariant& v); + + void SetMaximumInternal(const QVariant& v); + + virtual bool ValueGreaterThan(const QVariant& lhs, const QVariant& rhs) const; + + virtual bool ValueLessThan(const QVariant& lhs, const QVariant& rhs) const; + + virtual bool CanSetValue() const override; + +private: + bool UsingLadders() const; + + virtual QVariant AdjustValue(const QVariant& value) const override; + + SliderLadder* drag_ladder_; + + int ladder_element_count_; + + bool dragged_; + + bool has_min_; + QVariant min_value_; + + bool has_max_; + QVariant max_value_; + + double dragged_diff_; + + QVariant drag_start_value_; + + QVariant offset_; + + double drag_multiplier_; + + bool setting_drag_value_; + +private slots: + void LabelPressed(); + + void RepositionLadder(); + + void LadderDragged(int value, double multiplier); + + void LadderReleased(); + +}; + +} + +#endif // NUMERICSLIDERBASE_H diff --git a/app/widget/slider/base/sliderbase.cpp b/app/widget/slider/base/sliderbase.cpp new file mode 100644 index 000000000..6946c1f99 --- /dev/null +++ b/app/widget/slider/base/sliderbase.cpp @@ -0,0 +1,230 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 "sliderbase.h" + +#include +#include +#include + +#include "common/qtutils.h" +#include "core.h" +#include "window/mainwindow/mainwindow.h" + +namespace olive { + +#define super QStackedWidget + +SliderBase::SliderBase(QWidget *parent) : + super(parent), + tristate_(false), + format_plural_(false) +{ + // Standard (non-numeric) sliders are not draggable, so we indicate as such + setCursor(Qt::PointingHandCursor); + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + label_ = new SliderLabel(this); + addWidget(label_); + + editor_ = new FocusableLineEdit(this); + addWidget(editor_); + + connect(label_, &SliderLabel::focused, this, &SliderBase::ShowEditor); + connect(label_, &SliderLabel::RequestReset, this, &SliderBase::ResetValue); + connect(editor_, &FocusableLineEdit::Confirmed, this, &SliderBase::LineEditConfirmed); + connect(editor_, &FocusableLineEdit::Cancelled, this, &SliderBase::LineEditCancelled); +} + +void SliderBase::SetAlignment(Qt::Alignment alignment) +{ + label_->setAlignment(alignment); +} + +bool SliderBase::IsTristate() const +{ + return tristate_; +} + +void SliderBase::SetTristate() +{ + tristate_ = true; + UpdateLabel(); +} + +const QVariant &SliderBase::GetValueInternal() const +{ + return value_; +} + +void SliderBase::SetValueInternal(const QVariant &v) +{ + if (!CanSetValue()) { + return; + } + + value_ = AdjustValue(v); + + // Disable tristate + tristate_ = false; + + UpdateLabel(); +} + +void SliderBase::SetDefaultValue(const QVariant &v) +{ + default_value_ = v; +} + +void SliderBase::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) { + UpdateLabel(); + } + super::changeEvent(e); +} + +void SliderBase::UpdateLabel() +{ + label_->setText(tristate_ ? tr("---") : GetFormattedValueToString()); +} + +QVariant SliderBase::AdjustValue(const QVariant &value) const +{ + return value; +} + +bool SliderBase::CanSetValue() const +{ + return true; +} + +void SliderBase::ValueSignalEvent(const QVariant &value) +{ + Q_UNUSED(value) +} + +void SliderBase::ShowEditor() +{ + // This was a simple click + // Load label's text into editor + editor_->setText(ValueToString(value_)); + + // Show editor + setCurrentWidget(editor_); + + // Select all text in the editor + editor_->setFocus(); + editor_->selectAll(); +} + +void SliderBase::LineEditConfirmed() +{ + bool is_valid = true; + QVariant test_val = StringToValue(editor_->text(), &is_valid); + + // Ensure editor doesn't signal that the focus is lost + editor_->blockSignals(true); + label_->blockSignals(true); + + if (is_valid) { + SetValueInternal(test_val); + + setCurrentWidget(label_); + + ValueSignalEvent(value_); + } else { + QMessageBox::critical(this, + tr("Invalid Value"), + tr("The entered value is not valid for this field."), + QMessageBox::Ok); + + // Refocus editor + editor_->setFocus(); + } + + editor_->blockSignals(false); + label_->blockSignals(false); +} + +void SliderBase::LineEditCancelled() +{ + // Ensure editor doesn't signal that the focus is lost + editor_->blockSignals(true); + label_->blockSignals(true); + + // Set widget back to label + setCurrentWidget(label_); + + editor_->blockSignals(false); + label_->blockSignals(false); +} + +void SliderBase::ResetValue() +{ + if (default_value_.isValid()) { + SetValueInternal(default_value_); + ValueSignalEvent(value_); + } +} + +void SliderBase::SetFormat(const QString &s, const bool plural) +{ + custom_format_ = s; + format_plural_ = plural; + UpdateLabel(); +} + +void SliderBase::ClearFormat() +{ + custom_format_.clear(); + UpdateLabel(); +} + +bool SliderBase::IsFormatPlural() const +{ + return format_plural_; +} + +QString SliderBase::GetFormat() const +{ + if (custom_format_.isEmpty()) { + return QStringLiteral("%1"); + } else { + return custom_format_; + } +} + +QString SliderBase::GetFormattedValueToString() const +{ + return GetFormattedValueToString(GetValueInternal()); +} + +QString SliderBase::GetFormattedValueToString(const QVariant &v) const +{ + if (format_plural_) { + return tr(GetFormat().toUtf8().constData(), nullptr, v.toInt()); + } else { + return GetFormat().arg(ValueToString(v)); + } +} + +} diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/base/sliderbase.h similarity index 50% rename from app/widget/slider/sliderbase.h rename to app/widget/slider/base/sliderbase.h index 720647721..c2f8da40c 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/base/sliderbase.h @@ -28,84 +28,57 @@ #include "widget/focusablelineedit/focusablelineedit.h" namespace olive { + class SliderBase : public QStackedWidget { Q_OBJECT public: - enum Mode { - kString, - kInteger, - kFloat, - kRational - }; - - SliderBase(Mode mode, QWidget* parent = nullptr); - - void SetDragMultiplier(const double& d); - - void SetRequireValidInput(bool e); + SliderBase(QWidget* parent = nullptr); void SetAlignment(Qt::Alignment alignment); - void SetDefaultValue(const QVariant& v); - - const QVariant& GetOffset() const - { - return offset_; - } - - void SetOffset(const QVariant& v); - bool IsTristate() const; void SetTristate(); - bool IsDragging() const; - - void SetFormat(const QString& s, const bool plural=false); + void SetFormat(const QString& s, const bool plural = false); void ClearFormat(); bool IsFormatPlural() const; - void SetLadderElementCount(int b) - { - ladder_element_count_ = b; - } + void SetDefaultValue(const QVariant& v); -signals: - void ValueChanged(QVariant v); + QString GetFormattedValueToString(const QVariant& v) const; + +public slots: + void ShowEditor(); + +protected slots: + void UpdateLabel(); protected: - const QVariant& Value() const; + const QVariant& GetValueInternal() const; - void SetValue(const QVariant& v); - - void SetMinimumInternal(const QVariant& v); - - void SetMaximumInternal(const QVariant& v); - - 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; - - virtual QVariant StringToValue(const QString& s, bool* ok) = 0; - - virtual void changeEvent(QEvent* e) override; - - void ForceLabelUpdate(); - - double drag_multiplier_; - -private: - const QVariant& ClampValue(const QVariant& v); + void SetValueInternal(const QVariant& v); QString GetFormat() const; - bool UsingLadders() const; + QString GetFormattedValueToString() const; + SliderLabel* label() { return label_; } + + virtual QString ValueToString(const QVariant &v) const = 0; + + virtual QVariant StringToValue(const QString& s, bool* ok) const = 0; + + virtual QVariant AdjustValue(const QVariant& value) const; + + virtual bool CanSetValue() const; + + virtual void ValueSignalEvent(const QVariant& value) = 0; + + virtual void changeEvent(QEvent* e) override; + +private: SliderLabel* label_; FocusableLineEdit* editor_; @@ -113,51 +86,19 @@ private: QVariant value_; QVariant default_value_; - bool has_min_; - QVariant min_value_; - - bool has_max_; - QVariant max_value_; - - Mode mode_; - - double dragged_diff_; - - QVariant temp_dragged_value_; - QVariant clamped_temp_dragged_value_; - - QVariant offset_; - - bool require_valid_input_; - bool tristate_; QString custom_format_; bool format_plural_; - SliderLadder* drag_ladder_; - - int ladder_element_count_; - - bool dragged_; - private slots: - void ShowEditor(); - - void LabelPressed(); - - void LadderDragged(int value, double multiplier); - - void LadderReleased(); - void LineEditConfirmed(); void LineEditCancelled(); void ResetValue(); - void RepositionLadder(); }; } diff --git a/app/widget/slider/sliderlabel.cpp b/app/widget/slider/base/sliderlabel.cpp similarity index 90% rename from app/widget/slider/sliderlabel.cpp rename to app/widget/slider/base/sliderlabel.cpp index 3aa37ecca..486088bb6 100644 --- a/app/widget/slider/sliderlabel.cpp +++ b/app/widget/slider/base/sliderlabel.cpp @@ -63,6 +63,15 @@ void SliderLabel::mousePressEvent(QMouseEvent *e) } } +void SliderLabel::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) { + if (!(e->modifiers() & Qt::AltModifier)) { + emit LabelReleased(); + } + } +} + void SliderLabel::focusInEvent(QFocusEvent *event) { QWidget::focusInEvent(event); diff --git a/app/widget/slider/sliderlabel.h b/app/widget/slider/base/sliderlabel.h similarity index 88% rename from app/widget/slider/sliderlabel.h rename to app/widget/slider/base/sliderlabel.h index 533208a3b..2af0626dd 100644 --- a/app/widget/slider/sliderlabel.h +++ b/app/widget/slider/base/sliderlabel.h @@ -34,13 +34,17 @@ public: SliderLabel(QWidget* parent); protected: - virtual void mousePressEvent(QMouseEvent *ev) override; + virtual void mousePressEvent(QMouseEvent *e) override; + + virtual void mouseReleaseEvent(QMouseEvent *e) override; virtual void focusInEvent(QFocusEvent *event) override; signals: void LabelPressed(); + void LabelReleased(); + void focused(); void RequestReset(); diff --git a/app/widget/slider/sliderladder.cpp b/app/widget/slider/base/sliderladder.cpp similarity index 96% rename from app/widget/slider/sliderladder.cpp rename to app/widget/slider/base/sliderladder.cpp index 2d9977be2..5cbe37ee7 100644 --- a/app/widget/slider/sliderladder.cpp +++ b/app/widget/slider/base/sliderladder.cpp @@ -38,8 +38,7 @@ namespace olive { SliderLadder::SliderLadder(double drag_multiplier, int nb_outer_values, QString width_hint, QWidget* parent) : - QFrame(parent, Qt::Popup), - width_hint_(width_hint) + QFrame(parent, Qt::Popup) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); @@ -53,17 +52,17 @@ SliderLadder::SliderLadder(double drag_multiplier, int nb_outer_values, QString } for (int i=nb_outer_values-1;i>=0;i--) { - elements_.append(new SliderLadderElement(qPow(10, i + 1) * drag_multiplier, width_hint_)); + elements_.append(new SliderLadderElement(qPow(10, i + 1) * drag_multiplier, width_hint)); } // Create center entry - SliderLadderElement* start_element = new SliderLadderElement(drag_multiplier, width_hint_); + 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, QString width_hint, QWidget *parent) : +SliderLadderElement::SliderLadderElement(const double &multiplier, QString width_hint, QWidget *parent) : QWidget(parent), multiplier_(multiplier), highlighted_(false), diff --git a/app/widget/slider/sliderladder.h b/app/widget/slider/base/sliderladder.h similarity index 98% rename from app/widget/slider/sliderladder.h rename to app/widget/slider/base/sliderladder.h index 2f2ac7eff..fa1ce3965 100644 --- a/app/widget/slider/sliderladder.h +++ b/app/widget/slider/base/sliderladder.h @@ -94,8 +94,6 @@ private: QTimer drag_timer_; - QString width_hint_; - private slots: void TimerUpdate(); diff --git a/app/widget/slider/floatslider.cpp b/app/widget/slider/floatslider.cpp index 181221670..6d5c6b00a 100644 --- a/app/widget/slider/floatslider.cpp +++ b/app/widget/slider/floatslider.cpp @@ -26,25 +26,28 @@ namespace olive { +#define super DecimalSliderBase + FloatSlider::FloatSlider(QWidget *parent) : - SliderBase(kFloat, parent), - display_type_(kNormal), - decimal_places_(1), - autotrim_decimal_places_(false) + super(parent), + display_type_(kNormal) { SetValue(0.0); - - connect(this, SIGNAL(ValueChanged(QVariant)), this, SLOT(ConvertValue(QVariant))); } -double FloatSlider::GetValue() +double FloatSlider::GetValue() const { - return Value().toDouble(); + return GetValueInternal().toDouble(); } void FloatSlider::SetValue(const double &d) { - SliderBase::SetValue(d); + SetValueInternal(d); +} + +void FloatSlider::SetDefaultValue(const double &d) +{ + super::SetDefaultValue(d); } void FloatSlider::SetMinimum(const double &d) @@ -57,13 +60,6 @@ void FloatSlider::SetMaximum(const double &d) SetMaximumInternal(d); } -void FloatSlider::SetDecimalPlaces(int i) -{ - decimal_places_ = i; - - ForceLabelUpdate(); -} - void FloatSlider::SetDisplayType(const FloatSlider::DisplayType &type) { display_type_ = type; @@ -81,13 +77,6 @@ void FloatSlider::SetDisplayType(const FloatSlider::DisplayType &type) } } -void FloatSlider::SetAutoTrimDecimalPlaces(bool e) -{ - autotrim_decimal_places_ = e; - - ForceLabelUpdate(); -} - QString FloatSlider::ValueToString(double val, FloatSlider::DisplayType display, int decimal_places, bool autotrim_decimal_places) { switch (display) { @@ -110,24 +99,15 @@ QString FloatSlider::ValueToString(double val, FloatSlider::DisplayType display, break; } - QString s = QString::number(val, '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 FloatToString(val, decimal_places, autotrim_decimal_places); } -QString FloatSlider::ValueToString(const QVariant &v) +QString FloatSlider::ValueToString(const QVariant &v) const { - return ValueToString(v.toDouble() + GetOffset().toDouble(), display_type_, decimal_places_, autotrim_decimal_places_); + return ValueToString(v.toDouble() + GetOffset().toDouble(), display_type_, GetDecimalPlaces(), GetAutoTrimDecimalPlaces()); } -QVariant FloatSlider::StringToValue(const QString &s, bool *ok) +QVariant FloatSlider::StringToValue(const QString &s, bool *ok) const { switch (display_type_) { case kNormal: @@ -171,7 +151,7 @@ QVariant FloatSlider::StringToValue(const QString &s, bool *ok) return s.toDouble(ok) - GetOffset().toDouble(); } -double FloatSlider::AdjustDragDistanceInternal(const double &start, const double &drag) +QVariant FloatSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const { switch (display_type_) { case kNormal: @@ -179,22 +159,22 @@ double FloatSlider::AdjustDragDistanceInternal(const double &start, const double break; case kDecibel: { - double current_db = LinearToDecibel(start); + double current_db = LinearToDecibel(start.toDouble()); current_db += drag; double adjusted_linear = DecibelToLinear(current_db); return adjusted_linear; } case kPercentage: - return SliderBase::AdjustDragDistanceInternal(start, drag * 0.01); + return super::AdjustDragDistanceInternal(start, drag * 0.01); } - return SliderBase::AdjustDragDistanceInternal(start, drag); + return super::AdjustDragDistanceInternal(start, drag); } -void FloatSlider::ConvertValue(QVariant v) +void FloatSlider::ValueSignalEvent(const QVariant &value) { - emit ValueChanged(v.toDouble()); + emit ValueChanged(value.toDouble()); } double FloatSlider::LinearToDecibel(double linear) diff --git a/app/widget/slider/floatslider.h b/app/widget/slider/floatslider.h index f7d71d6a9..225f17be6 100644 --- a/app/widget/slider/floatslider.h +++ b/app/widget/slider/floatslider.h @@ -21,11 +21,11 @@ #ifndef FLOATSLIDER_H #define FLOATSLIDER_H -#include "sliderbase.h" +#include "base/decimalsliderbase.h" namespace olive { -class FloatSlider : public SliderBase +class FloatSlider : public DecimalSliderBase { Q_OBJECT public: @@ -37,35 +37,32 @@ public: kPercentage }; - double GetValue(); + double GetValue() const; void SetValue(const double& d); + void SetDefaultValue(const double& d); + void SetMinimum(const double& d); void SetMaximum(const double& d); - void SetDecimalPlaces(int i); - void SetDisplayType(const DisplayType& type); - void SetAutoTrimDecimalPlaces(bool e); - static QString ValueToString(double val, DisplayType display, int decimal_places, bool autotrim_decimal_places); protected: - virtual QString ValueToString(const QVariant& v) override; + virtual QString ValueToString(const QVariant& v) const override; - virtual QVariant StringToValue(const QString& s, bool* ok) override; + virtual QVariant StringToValue(const QString& s, bool* ok) const override; - virtual double AdjustDragDistanceInternal(const double& start, const double& drag) override; + virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const override; + + virtual void ValueSignalEvent(const QVariant &value) override; signals: void ValueChanged(double); -private slots: - void ConvertValue(QVariant v); - private: static double LinearToDecibel(double linear); @@ -73,10 +70,6 @@ private: DisplayType display_type_; - int decimal_places_; - - bool autotrim_decimal_places_; - }; } diff --git a/app/widget/slider/integerslider.cpp b/app/widget/slider/integerslider.cpp index ce1bfea54..173d2d311 100644 --- a/app/widget/slider/integerslider.cpp +++ b/app/widget/slider/integerslider.cpp @@ -22,22 +22,22 @@ namespace olive { +#define super NumericSliderBase + IntegerSlider::IntegerSlider(QWidget* parent) : - SliderBase(kInteger, parent) + super(parent) { SetValue(0); - - connect(this, SIGNAL(ValueChanged(QVariant)), this, SLOT(ConvertValue(QVariant))); } int64_t IntegerSlider::GetValue() { - return Value().toLongLong(); + return GetValueInternal().toLongLong(); } void IntegerSlider::SetValue(const int64_t &v) { - SliderBase::SetValue(QVariant::fromValue(v)); + SetValueInternal(QVariant::fromValue(v)); } void IntegerSlider::SetMinimum(const int64_t &d) @@ -50,12 +50,17 @@ void IntegerSlider::SetMaximum(const int64_t &d) SetMaximumInternal(QVariant::fromValue(d)); } -QString IntegerSlider::ValueToString(const QVariant &v) +void IntegerSlider::SetDefaultValue(const int64_t &d) +{ + super::SetDefaultValue(QVariant::fromValue(d)); +} + +QString IntegerSlider::ValueToString(const QVariant &v) const { return QString::number(v.toLongLong() + GetOffset().toLongLong()); } -QVariant IntegerSlider::StringToValue(const QString &s, bool *ok) +QVariant IntegerSlider::StringToValue(const QString &s, bool *ok) const { bool valid; @@ -76,9 +81,14 @@ QVariant IntegerSlider::StringToValue(const QString &s, bool *ok) return QVariant(); } -void IntegerSlider::ConvertValue(QVariant v) +void IntegerSlider::ValueSignalEvent(const QVariant &value) { - emit ValueChanged(v.toInt()); + emit ValueChanged(value.toInt()); +} + +QVariant IntegerSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const +{ + return qRound64(super::AdjustDragDistanceInternal(start, drag).toDouble()); } } diff --git a/app/widget/slider/integerslider.h b/app/widget/slider/integerslider.h index 99128dfab..ba0fdd086 100644 --- a/app/widget/slider/integerslider.h +++ b/app/widget/slider/integerslider.h @@ -21,11 +21,11 @@ #ifndef INTEGERSLIDER_H #define INTEGERSLIDER_H -#include "sliderbase.h" +#include "base/numericsliderbase.h" namespace olive { -class IntegerSlider : public SliderBase +class IntegerSlider : public NumericSliderBase { Q_OBJECT public: @@ -39,16 +39,20 @@ public: void SetMaximum(const int64_t& d); -protected: - virtual QString ValueToString(const QVariant& v) override; + void SetDefaultValue(const int64_t& d); - virtual QVariant StringToValue(const QString& s, bool* ok) override; +protected: + virtual QString ValueToString(const QVariant& v) const override; + + virtual QVariant StringToValue(const QString& s, bool* ok) const override; + + virtual void ValueSignalEvent(const QVariant &value) override; + + virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const override; signals: void ValueChanged(int64_t); -private slots: - void ConvertValue(QVariant v); }; } diff --git a/app/widget/slider/rationalslider.cpp b/app/widget/slider/rationalslider.cpp index d9b659d37..0d6c53285 100644 --- a/app/widget/slider/rationalslider.cpp +++ b/app/widget/slider/rationalslider.cpp @@ -1,16 +1,21 @@ /*** + Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team + Copyright (C) 2020 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" @@ -22,40 +27,33 @@ namespace olive { +#define super DecimalSliderBase + RationalSlider::RationalSlider(QWidget *parent) : - SliderBase(SliderBase::kRational, parent), - display_type_(kTimecode), - decimal_places_(2), - autotrim_decimal_places_(false), + super(parent), 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); + connect(Core::instance(), &Core::TimecodeDisplayChanged, this, &RationalSlider::UpdateLabel); + connect(SliderBase::label(), &SliderLabel::customContextMenuRequested, this, &RationalSlider::ShowDisplayTypeMenu); - SetDisplayType(display_type_); + SetDisplayType(kFloat); SetValue(rational(0, 0)); } rational RationalSlider::GetValue() { - return Value().value(); + return GetValueInternal().value(); } void RationalSlider::SetValue(const rational &d) { - SliderBase::SetValue(QVariant::fromValue(d)); + SetValueInternal(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); + super::SetDefaultValue(QVariant::fromValue(r)); } void RationalSlider::SetMinimum(const rational &d) @@ -68,42 +66,19 @@ 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(); + UpdateLabel(); } 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; - } + UpdateLabel(); } void RationalSlider::SetLockDisplayType(bool e) @@ -111,87 +86,130 @@ void RationalSlider::SetLockDisplayType(bool e) lock_display_type_ = e; } -bool RationalSlider::LockDisplayType() +bool RationalSlider::GetLockDisplayType() { return lock_display_type_; } -QString RationalSlider::ValueToString(const QVariant &v) +void RationalSlider::DisableDisplayType(RationalSlider::DisplayType type) { - double time = v.value().toDouble() + GetOffset().value().toDouble(); + disabled_.append(type); +} + +QString RationalSlider::ValueToString(const QVariant &v) const +{ + double val = 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; - } + case kTime: + return Timecode::time_to_timecode(v.value(), timebase_, Core::instance()->GetTimecodeDisplay()); + case kFloat: + return FloatToString(val, GetDecimalPlaces(), GetAutoTrimDecimalPlaces()); + case kRational: + return v.value().toString(); } + return v.toString(); } -QVariant RationalSlider::StringToValue(const QString &s, bool *ok) +QVariant RationalSlider::StringToValue(const QString &s, bool *ok) const { 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()); + case kTime: + { + r = Timecode::timecode_to_time(s, timebase_, Core::instance()->GetTimecodeDisplay(), ok); + break; + } + case kFloat: + { + // First, convert to a double + double d = s.toDouble(ok); + if (!(*ok)) { 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; + + // If double conversion succeeded, convert to a rational + r = rational::fromDouble(d, ok); + break; + } + case kRational: + r = rational::fromString(s, ok); + break; } - return QVariant::fromValue(r - GetOffset().value()); + //return QVariant::fromValue(r - GetOffset().value()); + return QVariant::fromValue(r); } -double RationalSlider::AdjustDragDistanceInternal(const double &start, const double &drag) +QVariant RationalSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const { // Assume we want smallest increment to be timebase or 1 frame - return start + drag*timebase_.toDouble(); + return QVariant::fromValue(start.value() + rational::fromDouble(drag)*timebase_); } -void RationalSlider::ConvertValue(QVariant v) +void RationalSlider::ValueSignalEvent(const QVariant &v) { emit ValueChanged(v.value()); } -void RationalSlider::changeDisplayType() +bool RationalSlider::ValueGreaterThan(const QVariant &lhs, const QVariant &rhs) const { - if (!LockDisplayType()) { - Menu m(this); - MenuShared::instance()->AddItemsForTimeRulerMenu(&m, timebase_); - MenuShared::instance()->AboutToShowTimeRulerActions(); + return lhs.value() > rhs.value(); +} +bool RationalSlider::ValueLessThan(const QVariant &lhs, const QVariant &rhs) const +{ + return lhs.value() < rhs.value(); +} + +void RationalSlider::ShowDisplayTypeMenu() +{ + Menu m(this); + + if (!GetLockDisplayType()) { + if (!disabled_.contains(kFloat)) { + QAction* float_action = m.addAction(tr("Float")); + float_action->setData(kFloat); + connect(float_action, &QAction::triggered, this, &RationalSlider::SetDisplayTypeFromMenu); + } + + if (!disabled_.contains(kRational)) { + QAction* rational_action = m.addAction(tr("Rational")); + rational_action->setData(kRational); + connect(rational_action, &QAction::triggered, this, &RationalSlider::SetDisplayTypeFromMenu); + } + + if (!disabled_.contains(kTime)) { + QAction* time_action = m.addAction(tr("Time")); + time_action->setData(kTime); + connect(time_action, &QAction::triggered, this, &RationalSlider::SetDisplayTypeFromMenu); + } + } + + if (display_type_ == kTime) { + if (!m.actions().isEmpty()) { + m.addSeparator(); + } + MenuShared::instance()->AddItemsForTimeRulerMenu(&m); + MenuShared::instance()->AboutToShowTimeRulerActions(timebase_); + } + + if (!m.actions().isEmpty()) { m.exec(QCursor::pos()); - ForceLabelUpdate(); + UpdateLabel(); } } -void RationalSlider::ChangeTimecodeDisplayType() +void RationalSlider::SetDisplayTypeFromMenu() { - ForceLabelUpdate(); + QAction* action = static_cast(sender()); + + DisplayType type = static_cast(action->data().toInt()); + + SetDisplayType(type); } } diff --git a/app/widget/slider/rationalslider.h b/app/widget/slider/rationalslider.h index 5a077c76c..c58e422cd 100644 --- a/app/widget/slider/rationalslider.h +++ b/app/widget/slider/rationalslider.h @@ -1,36 +1,40 @@ /*** + Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team + Copyright (C) 2020 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 "base/decimalsliderbase.h" #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). + * or a float (seconds). */ -class RationalSlider : public SliderBase +class RationalSlider : public DecimalSliderBase { Q_OBJECT public: @@ -38,9 +42,9 @@ public: * @brief enum containing the possibly display types */ enum DisplayType { - kTimecode, - kTimestamp, - kFloat + kTime, + kFloat, + kRational }; RationalSlider(QWidget* parent = nullptr); @@ -60,11 +64,6 @@ public: */ void SetDefaultValue(const rational& r); - /** - * @brief Sets the sliders default value - */ - void SetDefaultValue(const QVariant& v); - /** * @brief Sets the sliders minimum value */ @@ -75,11 +74,6 @@ public: */ 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 */ @@ -98,40 +92,43 @@ public: /** * @brief Get whether the user can change the display type or not */ - bool LockDisplayType(); + bool GetLockDisplayType(); - void SetAutoTrimDecimalPlaces(bool e); + /** + * @brief Hide display type in menu + */ + void DisableDisplayType(DisplayType type); protected: - virtual QString ValueToString(const QVariant& v) override; + virtual QString ValueToString(const QVariant& v) const override; - virtual QVariant StringToValue(const QString& s, bool* ok) override; + virtual QVariant StringToValue(const QString& s, bool* ok) const override; - virtual double AdjustDragDistanceInternal(const double& start, const double& drag) override; + virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const override; + + virtual void ValueSignalEvent(const QVariant& v) override; + + virtual bool ValueGreaterThan(const QVariant& lhs, const QVariant& rhs) const override; + + virtual bool ValueLessThan(const QVariant& lhs, const QVariant& rhs) const override; signals: void ValueChanged(rational); private slots: - void ConvertValue(QVariant v); + void ShowDisplayTypeMenu(); - void changeDisplayType(); - - /** - * @brief Changes the timecodes display type (e.g. drop frome to none drop frame) - */ - void ChangeTimecodeDisplayType(); + void SetDisplayTypeFromMenu(); private: DisplayType display_type_; - int decimal_places_; - - bool autotrim_decimal_places_; - rational timebase_; bool lock_display_type_; + + QVector disabled_; + }; } diff --git a/app/widget/slider/sliderbase.cpp b/app/widget/slider/sliderbase.cpp deleted file mode 100644 index b1490ed12..000000000 --- a/app/widget/slider/sliderbase.cpp +++ /dev/null @@ -1,470 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "sliderbase.h" - -#include -#include -#include - -#include "common/qtutils.h" -#include "core.h" -#include "window/mainwindow/mainwindow.h" - -namespace olive { - -SliderBase::SliderBase(Mode mode, QWidget *parent) : - QStackedWidget(parent), - drag_multiplier_(1.0), - has_min_(false), - has_max_(false), - mode_(mode), - dragged_diff_(0), - require_valid_input_(true), - tristate_(false), - format_plural_(false), - drag_ladder_(nullptr), - ladder_element_count_(0), - dragged_(false) -{ - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - - label_ = new SliderLabel(this); - - addWidget(label_); - - editor_ = new FocusableLineEdit(this); - addWidget(editor_); - - connect(label_, &SliderLabel::LabelPressed, this, &SliderBase::LabelPressed); - connect(label_, &SliderLabel::focused, this, &SliderBase::ShowEditor); - connect(label_, &SliderLabel::RequestReset, this, &SliderBase::ResetValue); - connect(editor_, &FocusableLineEdit::Confirmed, this, &SliderBase::LineEditConfirmed); - connect(editor_, &FocusableLineEdit::Cancelled, this, &SliderBase::LineEditCancelled); - - // Set valid cursor based on mode - switch (mode_) { - case kString: - setCursor(Qt::PointingHandCursor); - break; - case kInteger: - case kFloat: - case kRational: - setCursor(Qt::SizeHorCursor); - break; - } -} - -void SliderBase::SetDragMultiplier(const double &d) -{ - drag_multiplier_ = d; -} - -void SliderBase::SetRequireValidInput(bool e) -{ - require_valid_input_ = e; -} - -void SliderBase::SetAlignment(Qt::Alignment alignment) -{ - label_->setAlignment(alignment); -} - -bool SliderBase::IsTristate() const -{ - return tristate_; -} - -void SliderBase::SetTristate() -{ - tristate_ = true; - UpdateLabel(0); -} - -bool SliderBase::IsDragging() const -{ - return drag_ladder_; -} - -void SliderBase::SetFormat(const QString &s, const bool plural) -{ - custom_format_ = s; - format_plural_ = plural; - ForceLabelUpdate(); -} - -void SliderBase::ClearFormat() -{ - custom_format_.clear(); - ForceLabelUpdate(); -} - -bool SliderBase::IsFormatPlural() const -{ - return format_plural_; -} - -void SliderBase::ForceLabelUpdate() -{ - UpdateLabel(Value()); -} - -const QVariant &SliderBase::Value() const -{ - if (IsDragging()) { - return clamped_temp_dragged_value_; - } - - return value_; -} - -void SliderBase::SetValue(const QVariant &v) -{ - if (IsDragging()) { - return; - } - - value_ = ClampValue(v); - - // Disable tristate - tristate_ = false; - - UpdateLabel(value_); -} - -void SliderBase::SetDefaultValue(const QVariant &v) -{ - default_value_ = v; -} - -void SliderBase::SetOffset(const QVariant &v) -{ - offset_ = v; - - UpdateLabel(value_); -} - -void SliderBase::SetMinimumInternal(const QVariant &v) -{ - min_value_ = v; - has_min_ = true; - - // Limit value by this new minimum 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_); - } - } -} - -void SliderBase::SetMaximumInternal(const QVariant &v) -{ - max_value_ = v; - has_max_ = true; - - // Limit value by this new maximum 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_); - } - } -} - -void SliderBase::changeEvent(QEvent *e) -{ - if (e->type() == QEvent::LanguageChange) { - UpdateLabel(value_); - } - QStackedWidget::changeEvent(e); -} - -const QVariant &SliderBase::ClampValue(const QVariant &v) -{ - 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_min_ && value < min) { - return min_value_; - }else if (has_max_ && value > max) { - return max_value_; - } - - return v; -} - -QString SliderBase::GetFormat() const -{ - if (custom_format_.isEmpty()) { - return QStringLiteral("%1"); - } else { - return custom_format_; - } -} - -bool SliderBase::UsingLadders() const -{ - return ladder_element_count_ > 0 && Config::Current()[QStringLiteral("UseSliderLadders")].toBool(); -} - -void SliderBase::UpdateLabel(const QVariant &v) -{ - if (tristate_) { - label_->setText("---"); - } else if (format_plural_) { - label_->setText(tr(GetFormat().toUtf8().constData(), nullptr, v.toInt())); - } else { - label_->setText(GetFormat().arg(ValueToString(v))); - } -} - -double SliderBase::AdjustDragDistanceInternal(const double &start, const double &drag) -{ - return start + drag; -} - -QString SliderBase::ValueToString(const QVariant &v) -{ - return v.toString(); -} - -QVariant SliderBase::StringToValue(const QString &s, bool *ok) -{ - *ok = true; - return s; -} - -void SliderBase::ShowEditor() -{ - // This was a simple click - // Load label's text into editor - editor_->setText(ValueToString(value_)); - - // Show editor - setCurrentWidget(editor_); - - // Select all text in the editor - editor_->setFocus(); - editor_->selectAll(); -} - -void SliderBase::LabelPressed() -{ - switch (mode_) { - case kString: - // No dragging supported for strings - break; - case kInteger: - case kFloat: - case kRational: - { - 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(); - - QMetaObject::invokeMethod(this, "RepositionLadder", Qt::QueuedConnection); - - connect(drag_ladder_, &SliderLadder::DraggedByValue, this, &SliderBase::LadderDragged); - connect(drag_ladder_, &SliderLadder::Released, this, &SliderBase::LadderReleased); - break; - } - } -} - -void SliderBase::LadderDragged(int value, double multiplier) -{ - dragged_ = true; - - switch (mode_) { - case kString: - // No dragging supported for strings - break; - case kInteger: - case kFloat: - { - dragged_diff_ += value * drag_multiplier_ * multiplier; - - double drag_val = AdjustDragDistanceInternal(value_.toDouble(), dragged_diff_); - - // Update temporary value - if (mode_ == kInteger) { - temp_dragged_value_ = qRound(drag_val); - } else { - temp_dragged_value_ = drag_val; - } - - clamped_temp_dragged_value_ = ClampValue(temp_dragged_value_); - - UpdateLabel(clamped_temp_dragged_value_); - - drag_ladder_->SetValue(ValueToString(clamped_temp_dragged_value_)); - - if (!UsingLadders()) { - RepositionLadder(); - } - - 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; - } - } -} - -void SliderBase::LadderReleased() -{ - drag_ladder_->deleteLater(); - drag_ladder_ = nullptr; - dragged_diff_ = 0; - - if (dragged_) { - // This was a drag - switch (mode_) { - case kString: - // No-op - break; - case kInteger: - SetValue(clamped_temp_dragged_value_.toInt()); - break; - case kFloat: - SetValue(clamped_temp_dragged_value_.toDouble()); - break; - case kRational: - SetValue(temp_dragged_value_); - } - - emit ValueChanged(value_); - - dragged_ = false; - } else { - ShowEditor(); - } -} - -void SliderBase::LineEditConfirmed() -{ - bool is_valid = true; - QVariant test_val = StringToValue(editor_->text(), &is_valid); - - // Ensure editor doesn't signal that the focus is lost - editor_->blockSignals(true); - label_->blockSignals(true); - - if (is_valid) { - SetValue(test_val); - - setCurrentWidget(label_); - - emit ValueChanged(value_); - } else if (require_valid_input_ && !IsTristate()) { - QMessageBox::critical(this, - tr("Invalid Value"), - tr("The entered value is not valid for this field."), - QMessageBox::Ok); - - // Refocus editor - editor_->setFocus(); - } - - editor_->blockSignals(false); - label_->blockSignals(false); -} - -void SliderBase::LineEditCancelled() -{ - // Ensure editor doesn't signal that the focus is lost - editor_->blockSignals(true); - label_->blockSignals(true); - - // Set widget back to label - setCurrentWidget(label_); - - editor_->blockSignals(false); - label_->blockSignals(false); -} - -void SliderBase::ResetValue() -{ - if (!default_value_.isNull()) { - SetValue(default_value_); - emit ValueChanged(value_); - } -} - -void SliderBase::RepositionLadder() -{ - if (drag_ladder_) { - if (UsingLadders()) { - drag_ladder_->move(QCursor::pos() - QPoint(drag_ladder_->width()/2, drag_ladder_->height()/2)); - } else { - QPoint label_global_pos = label_->mapToGlobal(label_->pos()); - int text_width = QtUtils::QFontMetricsWidth(label_->fontMetrics(), label_->text()); - - int ladder_x = label_global_pos.x() + text_width / 2 - drag_ladder_->width() / 2; - int ladder_y = label_global_pos.y() + label_->height() / 2 - drag_ladder_->height() / 2; - - drag_ladder_->move(ladder_x, ladder_y); - } - - drag_ladder_->StartListeningToMouseInput(); - } -} - -} diff --git a/app/widget/slider/stringslider.cpp b/app/widget/slider/stringslider.cpp index 4568e285e..98bd49bd9 100644 --- a/app/widget/slider/stringslider.cpp +++ b/app/widget/slider/stringslider.cpp @@ -22,33 +22,46 @@ namespace olive { +#define super SliderBase + StringSlider::StringSlider(QWidget* parent) : - SliderBase(kString, parent) + super(parent) { SetValue(QString()); - connect(this, SIGNAL(ValueChanged(QVariant)), this, SLOT(ConvertValue(QVariant))); + connect(label(), &SliderLabel::LabelReleased, this, &SliderBase::ShowEditor); } -QString StringSlider::GetValue() +QString StringSlider::GetValue() const { - return Value().toString(); + return GetValueInternal().toString(); } void StringSlider::SetValue(const QString &v) { - SliderBase::SetValue(v); + SetValueInternal(v); } -QString StringSlider::ValueToString(const QVariant &v) +void StringSlider::SetDefaultValue(const QString &v) +{ + super::SetDefaultValue(v); +} + +QString StringSlider::ValueToString(const QVariant &v) const { QString vstr = v.toString(); return (vstr.isEmpty()) ? tr("(none)") : vstr; } -void StringSlider::ConvertValue(QVariant v) +QVariant StringSlider::StringToValue(const QString &s, bool *ok) const { - emit ValueChanged(v.toString()); + *ok = true; + return s; +} + +void StringSlider::ValueSignalEvent(const QVariant &value) +{ + emit ValueChanged(value.toString()); } } diff --git a/app/widget/slider/stringslider.h b/app/widget/slider/stringslider.h index 232df2480..1e9ebeefa 100644 --- a/app/widget/slider/stringslider.h +++ b/app/widget/slider/stringslider.h @@ -21,7 +21,7 @@ #ifndef STRINGSLIDER_H #define STRINGSLIDER_H -#include "sliderbase.h" +#include "base/sliderbase.h" namespace olive { @@ -33,18 +33,22 @@ public: void SetDragMultiplier(const double& d) = delete; - QString GetValue(); + QString GetValue() const; void SetValue(const QString& v); -protected: - virtual QString ValueToString(const QVariant& value) override; + void SetDefaultValue(const QString& v); signals: - void ValueChanged(QString); + void ValueChanged(const QString& str); + +protected: + virtual QString ValueToString(const QVariant& value) const override; + + virtual QVariant StringToValue(const QString &s, bool *ok) const override; + + virtual void ValueSignalEvent(const QVariant &value) override; -private slots: - void ConvertValue(QVariant v); }; } diff --git a/app/widget/slider/timeslider.cpp b/app/widget/slider/timeslider.cpp index 9bee7ce61..10229d1c3 100644 --- a/app/widget/slider/timeslider.cpp +++ b/app/widget/slider/timeslider.cpp @@ -25,13 +25,14 @@ namespace olive { +#define super IntegerSlider + TimeSlider::TimeSlider(QWidget *parent) : - IntegerSlider(parent) + super(parent) { SetMinimum(0); - SetValue(0); - connect(Core::instance(), &Core::TimecodeDisplayChanged, this, &TimeSlider::TimecodeDisplayChanged); + connect(Core::instance(), &Core::TimecodeDisplayChanged, this, &TimeSlider::UpdateLabel); } void TimeSlider::SetTimebase(const rational &timebase) @@ -39,14 +40,14 @@ void TimeSlider::SetTimebase(const rational &timebase) timebase_ = timebase; // Refresh label since we have a new timebase to generate a timecode with - UpdateLabel(Value()); + UpdateLabel(); } -QString TimeSlider::ValueToString(const QVariant &v) +QString TimeSlider::ValueToString(const QVariant &v) const { if (timebase_.isNull()) { // We can't generate a timecode without a timebase, so we just return the number - return IntegerSlider::ValueToString(v); + return super::ValueToString(v); } return Timecode::timestamp_to_timecode(v.toLongLong() + GetOffset().toLongLong(), @@ -54,14 +55,9 @@ QString TimeSlider::ValueToString(const QVariant &v) Core::instance()->GetTimecodeDisplay()); } -QVariant TimeSlider::StringToValue(const QString &s, bool *ok) +QVariant TimeSlider::StringToValue(const QString &s, bool *ok) const { return QVariant::fromValue(Timecode::timecode_to_timestamp(s, timebase_, Core::instance()->GetTimecodeDisplay(), ok) - GetOffset().toLongLong()); } -void TimeSlider::TimecodeDisplayChanged() -{ - UpdateLabel(Value()); -} - } diff --git a/app/widget/slider/timeslider.h b/app/widget/slider/timeslider.h index a8e6d977c..b8e1d8e06 100644 --- a/app/widget/slider/timeslider.h +++ b/app/widget/slider/timeslider.h @@ -35,16 +35,13 @@ public: void SetTimebase(const rational& timebase); protected: - virtual QString ValueToString(const QVariant& v) override; + virtual QString ValueToString(const QVariant& v) const override; - virtual QVariant StringToValue(const QString& s, bool* ok) override; + virtual QVariant StringToValue(const QString& s, bool* ok) const override; private: rational timebase_; -private slots: - void TimecodeDisplayChanged(); - }; } diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 18e1948f6..59fe1b770 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -306,8 +306,8 @@ void TimeRuler::ShowContextMenu() { Menu m(this); - MenuShared::instance()->AddItemsForTimeRulerMenu(&m, timebase()); - MenuShared::instance()->AboutToShowTimeRulerActions(); + MenuShared::instance()->AddItemsForTimeRulerMenu(&m); + MenuShared::instance()->AboutToShowTimeRulerActions(timebase()); m.exec(QCursor::pos()); } diff --git a/app/widget/videoparamedit/videoparamedit.cpp b/app/widget/videoparamedit/videoparamedit.cpp index 63754ed5a..cf7f3880c 100644 --- a/app/widget/videoparamedit/videoparamedit.cpp +++ b/app/widget/videoparamedit/videoparamedit.cpp @@ -97,11 +97,13 @@ VideoParamEdit::VideoParamEdit(QWidget* parent) : connect(frame_rate_combobox_, static_cast(&FrameRateComboBox::currentIndexChanged), this, &VideoParamEdit::Changed); layout->addWidget(frame_rate_combobox_, row, 1); - // FIXME: Replace with rational slider - frame_rate_slider_ = new FloatSlider(); + frame_rate_slider_ = new RationalSlider(); frame_rate_slider_->SetMinimum(0); - frame_rate_slider_->SetDecimalPlaces(2); - connect(frame_rate_slider_, &FloatSlider::ValueChanged, this, &VideoParamEdit::Changed); + frame_rate_slider_->SetDecimalPlaces(3); + frame_rate_slider_->SetAutoTrimDecimalPlaces(true); + frame_rate_slider_->SetTimebase(rational(1, 1000)); // Drag interval + frame_rate_slider_->DisableDisplayType(RationalSlider::kTime); + connect(frame_rate_slider_, &RationalSlider::ValueChanged, this, &VideoParamEdit::Changed); layout->addWidget(frame_rate_slider_, row, 1); row++; @@ -277,7 +279,7 @@ VideoParams VideoParamEdit::GetVideoParams() const rational using_frame_rate; if (mask_ & kFrameRateIsArbitrary) { - using_frame_rate = rational::fromDouble(frame_rate_slider_->GetValue()); + using_frame_rate = frame_rate_slider_->GetValue(); } else { using_frame_rate = frame_rate_combobox_->GetFrameRate(); } @@ -316,7 +318,7 @@ void VideoParamEdit::SetVideoParams(const VideoParams &p) depth_slider_->SetValue(p.depth()); frame_rate_combobox_->SetFrameRate(p.frame_rate()); - frame_rate_slider_->SetValue(p.frame_rate().toDouble()); + frame_rate_slider_->SetValue(p.frame_rate()); timebase_temp_ = p.time_base(); pixel_aspect_combobox_->SetPixelAspectRatio(p.pixel_aspect_ratio()); diff --git a/app/widget/videoparamedit/videoparamedit.h b/app/widget/videoparamedit/videoparamedit.h index 95cf42395..333cf7f4c 100644 --- a/app/widget/videoparamedit/videoparamedit.h +++ b/app/widget/videoparamedit/videoparamedit.h @@ -27,8 +27,8 @@ #include "node/color/colormanager/colormanager.h" #include "render/videoparams.h" -#include "widget/slider/floatslider.h" #include "widget/slider/integerslider.h" +#include "widget/slider/rationalslider.h" #include "widget/standardcombos/frameratecombobox.h" #include "widget/standardcombos/interlacedcombobox.h" #include "widget/standardcombos/pixelaspectratiocombobox.h" @@ -145,7 +145,7 @@ private: IntegerSlider* depth_slider_; QLabel* frame_rate_lbl_; FrameRateComboBox* frame_rate_combobox_; - FloatSlider* frame_rate_slider_; + RationalSlider* frame_rate_slider_; QLabel* pixel_aspect_lbl_; PixelAspectRatioComboBox* pixel_aspect_combobox_; QLabel* interlaced_lbl_; diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index c471c2ea2..1637816ac 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -308,12 +308,12 @@ void MainMenu::ViewMenuAboutToShow() if (p) { if (p->timebase().denominator() != 0) { view_menu_->addSeparator(); - MenuShared::instance()->AddItemsForTimeRulerMenu(view_menu_, p->timebase()); + MenuShared::instance()->AddItemsForTimeRulerMenu(view_menu_); } } // Ensure checked timecode display mode is correct - MenuShared::instance()->AboutToShowTimeRulerActions(); + MenuShared::instance()->AboutToShowTimeRulerActions(p->timebase()); } void MainMenu::ToolsMenuAboutToShow()