diff --git a/app/dialog/richtext/richtext.cpp b/app/dialog/richtext/richtext.cpp index 79c28a355..5b4303530 100644 --- a/app/dialog/richtext/richtext.cpp +++ b/app/dialog/richtext/richtext.cpp @@ -50,6 +50,8 @@ RichTextDialog::RichTextDialog(const QString &start, QWidget* parent) : toolbar_layout->addWidget(font_combo_); size_slider_ = new FloatSlider(); size_slider_->SetMinimum(0.1); + size_slider_->SetLadderEnabled(true); + size_slider_->SetLadderElementCount(1); size_slider_->setToolTip(tr("Font Size")); toolbar_layout->addWidget(size_slider_); diff --git a/app/widget/colorwheel/colorvalueswidget.cpp b/app/widget/colorwheel/colorvalueswidget.cpp index 5d5dad26c..1b7fd76fc 100644 --- a/app/widget/colorwheel/colorvalueswidget.cpp +++ b/app/widget/colorwheel/colorvalueswidget.cpp @@ -203,6 +203,8 @@ FloatSlider *ColorValuesTab::CreateColorSlider() FloatSlider* fs = new FloatSlider(); fs->SetDragMultiplier(0.01); fs->SetDecimalPlaces(5); + fs->SetLadderEnabled(true); + fs->SetLadderElementCount(1); connect(fs, &FloatSlider::ValueChanged, this, &ColorValuesTab::SliderChanged); return fs; } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 34fd5f1f7..dc2638ccd 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -96,6 +96,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() { IntegerSlider* slider = new IntegerSlider(); slider->SetDefaultValue(input_->GetDefaultValue()); + slider->SetLadderEnabled(true); widgets_.append(slider); connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; @@ -372,6 +373,7 @@ void NodeParamViewWidgetBridge::CreateSliders(int count) for (int i=0;iSetDefaultValue(input_->GetDefaultValueForTrack(i)); + fs->SetLadderEnabled(true); widgets_.append(fs); connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); } diff --git a/app/widget/slider/CMakeLists.txt b/app/widget/slider/CMakeLists.txt index c1835634b..3a14e174f 100644 --- a/app/widget/slider/CMakeLists.txt +++ b/app/widget/slider/CMakeLists.txt @@ -24,6 +24,8 @@ set(OLIVE_SOURCES 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/sliderbase.cpp b/app/widget/slider/sliderbase.cpp index 487533bc0..676176a28 100644 --- a/app/widget/slider/sliderbase.cpp +++ b/app/widget/slider/sliderbase.cpp @@ -24,6 +24,10 @@ #include #include +#include "common/qtutils.h" +#include "core.h" +#include "window/mainwindow/mainwindow.h" + OLIVE_NAMESPACE_ENTER SliderBase::SliderBase(Mode mode, QWidget *parent) : @@ -32,9 +36,11 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : has_min_(false), has_max_(false), mode_(mode), - dragged_(false), require_valid_input_(true), - tristate_(false) + tristate_(false), + drag_ladder_(nullptr), + enable_ladder_(false), + ladder_element_count_(2) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); @@ -45,9 +51,8 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : editor_ = new FocusableLineEdit(this); addWidget(editor_); - connect(label_, &SliderLabel::drag_start, this, &SliderBase::LabelPressed); - connect(label_, &SliderLabel::dragged, this, &SliderBase::LabelDragged); - connect(label_, &SliderLabel::drag_stop, this, &SliderBase::LabelClicked); + connect(label_, &SliderLabel::LabelMoved, this, &SliderBase::LabelDragged); + connect(label_, &SliderLabel::LabelReleased, this, &SliderBase::LabelClicked); connect(label_, &SliderLabel::focused, this, &SliderBase::LabelClicked); connect(label_, &SliderLabel::RequestReset, this, &SliderBase::ResetValue); connect(editor_, &FocusableLineEdit::Confirmed, this, &SliderBase::LineEditConfirmed); @@ -95,7 +100,7 @@ void SliderBase::SetTristate() bool SliderBase::IsDragging() const { - return dragged_; + return drag_ladder_; } void SliderBase::SetFormat(const QString &s) @@ -115,9 +120,9 @@ void SliderBase::ForceLabelUpdate() UpdateLabel(Value()); } -const QVariant &SliderBase::Value() +const QVariant &SliderBase::Value() const { - if (dragged_) { + if (IsDragging()) { return temp_dragged_value_; } @@ -220,32 +225,9 @@ QVariant SliderBase::StringToValue(const QString &s, bool *ok) return s; } -void SliderBase::LabelPressed() -{ - dragged_ = false; - dragged_diff_ = 0; -} - void SliderBase::LabelClicked() { - if (dragged_) { - dragged_ = false; - - // This was a drag - switch (mode_) { - case kString: - // No-op - break; - case kInteger: - SetValue(temp_dragged_value_.toInt()); - break; - case kFloat: - SetValue(temp_dragged_value_.toDouble()); - break; - } - - emit ValueChanged(value_); - } else { + if (!drag_ladder_) { // This was a simple click // Load label's text into editor editor_->setText(ValueToString(value_)); @@ -259,10 +241,29 @@ void SliderBase::LabelClicked() } } -void SliderBase::LabelDragged(int i) +void SliderBase::LabelDragged() { - dragged_ = true; + switch (mode_) { + case kString: + // No dragging supported for strings + break; + case kInteger: + case kFloat: + drag_ladder_ = new SliderLadder(value_.toDouble(), drag_multiplier_, enable_ladder_ ? ladder_element_count_ : 0); + drag_ladder_->show(); + QPoint label_global_pos = label_->mapToGlobal(label_->pos()); + drag_ladder_->move(label_global_pos.x() + QFontMetricsWidth(label_->fontMetrics(), label_->text()) / 2 - drag_ladder_->width() / 2, + label_global_pos.y() + label_->height() / 2 - drag_ladder_->height() / 2); + + connect(drag_ladder_, &SliderLadder::DraggedByValue, this, &SliderBase::LadderDragged); + connect(drag_ladder_, &SliderLadder::Released, this, &SliderBase::LadderReleased); + break; + } +} + +void SliderBase::LadderDragged(int value, double multiplier) +{ switch (mode_) { case kString: // No dragging supported for strings @@ -270,7 +271,7 @@ void SliderBase::LabelDragged(int i) case kInteger: case kFloat: { - dragged_diff_ += static_cast(i) * drag_multiplier_; + dragged_diff_ += static_cast(value) * drag_multiplier_ * multiplier; double drag_val = AdjustDragDistanceInternal(value_.toDouble(), dragged_diff_); @@ -281,15 +282,43 @@ void SliderBase::LabelDragged(int i) temp_dragged_value_ = drag_val; } - temp_dragged_value_ = ClampValue(temp_dragged_value_); + QVariant clamped = ClampValue(temp_dragged_value_); + + if (clamped != temp_dragged_value_) { + temp_dragged_value_ = clamped; + dragged_diff_ = temp_dragged_value_.toDouble() - value_.toDouble(); + } UpdateLabel(temp_dragged_value_); + drag_ladder_->SetValue(temp_dragged_value_.toDouble()); emit ValueChanged(temp_dragged_value_); break; } } } +void SliderBase::LadderReleased() +{ + drag_ladder_->deleteLater(); + drag_ladder_ = nullptr; + dragged_diff_ = 0; + + // This was a drag + switch (mode_) { + case kString: + // No-op + break; + case kInteger: + SetValue(temp_dragged_value_.toInt()); + break; + case kFloat: + SetValue(temp_dragged_value_.toDouble()); + break; + } + + emit ValueChanged(value_); +} + void SliderBase::LineEditConfirmed() { bool is_valid = true; diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/sliderbase.h index 316e82f37..fa956db22 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/sliderbase.h @@ -24,6 +24,7 @@ #include #include "sliderlabel.h" +#include "sliderladder.h" #include "widget/focusablelineedit/focusablelineedit.h" OLIVE_NAMESPACE_ENTER @@ -55,11 +56,21 @@ public: void SetFormat(const QString& s); void ClearFormat(); + void SetLadderEnabled(bool e) + { + enable_ladder_ = e; + } + + void SetLadderElementCount(int b) + { + ladder_element_count_ = b; + } + signals: void ValueChanged(QVariant v); protected: - const QVariant& Value(); + const QVariant& Value() const; void SetValue(const QVariant& v); @@ -101,8 +112,6 @@ private: Mode mode_; - bool dragged_; - double dragged_diff_; QVariant temp_dragged_value_; @@ -113,12 +122,20 @@ private: QString custom_format_; -private slots: - void LabelPressed(); + SliderLadder* drag_ladder_; + bool enable_ladder_; + + int ladder_element_count_; + +private slots: void LabelClicked(); - void LabelDragged(int i); + void LabelDragged(); + + void LadderDragged(int value, double multiplier); + + void LadderReleased(); void LineEditConfirmed(); diff --git a/app/widget/slider/sliderlabel.cpp b/app/widget/slider/sliderlabel.cpp index 79f077757..ab80988da 100644 --- a/app/widget/slider/sliderlabel.cpp +++ b/app/widget/slider/sliderlabel.cpp @@ -57,66 +57,25 @@ SliderLabel::SliderLabel(QWidget *parent) : void SliderLabel::mousePressEvent(QMouseEvent *e) { if (e->modifiers() & Qt::AltModifier) { - emit RequestReset(); - } else { - -#if defined(Q_OS_MAC) - CGAssociateMouseAndMouseCursorPosition(false); - CGDisplayHideCursor(kCGDirectMainDisplay); - CGGetLastMouseDelta(nullptr, nullptr); -#else - drag_start_ = QCursor::pos(); - - static_cast(QApplication::instance())->setOverrideCursor(Qt::BlankCursor); -#endif - - emit drag_start(); - dragging_ = true; - + emit LabelPressed(); } } void SliderLabel::mouseMoveEvent(QMouseEvent *) { if (dragging_) { - - int32_t x_mvmt, y_mvmt; - - // Keep cursor in the same position -#if defined(Q_OS_MAC) - CGGetLastMouseDelta(&x_mvmt, &y_mvmt); -#else - QPoint current_pos = QCursor::pos(); - - x_mvmt = current_pos.x() - drag_start_.x(); - y_mvmt = drag_start_.y() - current_pos.y(); - - QCursor::setPos(drag_start_); -#endif - - emit dragged(x_mvmt + y_mvmt); - + emit LabelMoved(); } } void SliderLabel::mouseReleaseEvent(QMouseEvent *) { if (dragging_) { - -#if defined(Q_OS_MAC) - CGAssociateMouseAndMouseCursorPosition(true); - CGDisplayShowCursor(kCGDirectMainDisplay); -#else - static_cast(QApplication::instance())->restoreOverrideCursor(); -#endif - - emit drag_stop(); - + emit LabelReleased(); dragging_ = false; - } } diff --git a/app/widget/slider/sliderlabel.h b/app/widget/slider/sliderlabel.h index 641caeac4..081c5eafa 100644 --- a/app/widget/slider/sliderlabel.h +++ b/app/widget/slider/sliderlabel.h @@ -43,19 +43,17 @@ protected: virtual void focusInEvent(QFocusEvent *event) override; signals: - void dragged(int x); + void LabelPressed(); - void drag_start(); + void LabelMoved(); - void drag_stop(); + void LabelReleased(); void focused(); void RequestReset(); private: - QPoint drag_start_; - bool dragging_; }; diff --git a/app/widget/slider/sliderladder.cpp b/app/widget/slider/sliderladder.cpp new file mode 100644 index 000000000..33768b58e --- /dev/null +++ b/app/widget/slider/sliderladder.cpp @@ -0,0 +1,230 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "sliderladder.h" + +#include +#include +#include +#include +#include + +#include "common/clamp.h" + +OLIVE_NAMESPACE_ENTER + +SliderLadder::SliderLadder(double start_val, double drag_multiplier, int nb_outer_values, QWidget* parent) : + QFrame(parent, Qt::Popup), + start_val_(start_val), + active_element_(nullptr), + relative_y_(-1) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + + setFrameShape(QFrame::Box); + setLineWidth(1); + + for (int i=nb_outer_values-1;i>=0;i--) { + elements_.append(new SliderLadderElement(qPow(10, i + 1) * drag_multiplier)); + } + + elements_.append(new SliderLadderElement(drag_multiplier)); + + for (int i=0;iSetValue(start_val_); + layout->addWidget(e); + } + + if (elements_.size() == 1) { + elements_.first()->SetMultiplierVisible(false); + } + +#if defined(Q_OS_MAC) + CGAssociateMouseAndMouseCursorPosition(false); + CGDisplayHideCursor(kCGDirectMainDisplay); + CGGetLastMouseDelta(nullptr, nullptr); +#else + drag_start_ = QCursor::pos(); + + static_cast(QApplication::instance())->setOverrideCursor(Qt::BlankCursor); +#endif +} + +SliderLadder::~SliderLadder() +{ +#if defined(Q_OS_MAC) + CGAssociateMouseAndMouseCursorPosition(true); + CGDisplayShowCursor(kCGDirectMainDisplay); +#else + static_cast(QApplication::instance())->restoreOverrideCursor(); +#endif +} + +void SliderLadder::SetValue(double val) +{ + foreach (SliderLadderElement* e, elements_) { + e->SetValue(val); + } +} + +void SliderLadder::mouseMoveEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + + int32_t x_mvmt, y_mvmt; + + // Keep cursor in the same position +#if defined(Q_OS_MAC) + CGGetLastMouseDelta(&x_mvmt, &y_mvmt); +#else + QPoint current_pos = QCursor::pos(); + + x_mvmt = current_pos.x() - drag_start_.x(); + y_mvmt = current_pos.y() - drag_start_.y(); + + QCursor::setPos(drag_start_); +#endif + + // Determine which element we're in + relative_y_ = clamp(relative_y_ + y_mvmt, + elements_.first()->y(), + elements_.last()->y() + elements_.last()->height() - 1); + + SetActiveElement(); + + emit DraggedByValue(x_mvmt, active_element_->GetMultiplier()); +} + +void SliderLadder::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event) + + emit Released(); +} + +void SliderLadder::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + + QMetaObject::invokeMethod(this, "InitRelativeY", Qt::QueuedConnection); +} + +void SliderLadder::SetActiveElement() +{ + if (!active_element_ + || relative_y_ < active_element_->y() + || relative_y_ >= active_element_->y() + active_element_->height()) { + if (active_element_) { + // Un-highlight active element if one is set + active_element_->SetHighlighted(false); + } + + // Find new active element + foreach (SliderLadderElement* ele, elements_) { + if (relative_y_ >= ele->y() && relative_y_ < ele->y() + ele->height()) { + // This is the element! + active_element_ = ele; + active_element_->SetHighlighted(true); + break; + } + } + } +} + +void SliderLadder::InitRelativeY() +{ + relative_y_ = QCursor::pos().y() - this->y(); + + SetActiveElement(); +} + +SliderLadderElement::SliderLadderElement(const double &multiplier, QWidget *parent) : + QWidget(parent), + multiplier_(multiplier), + highlighted_(false), + multiplier_visible_(true) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + + label_ = new QLabel(); + label_->setAlignment(Qt::AlignCenter); + layout->addWidget(label_); + + QPalette p = palette(); + QColor highlight_color = palette().text().color(); + highlight_color.setAlpha(64); + p.setColor(QPalette::Highlight, highlight_color); + setPalette(p); + + setAutoFillBackground(true); + + UpdateLabel(); +} + +void SliderLadderElement::SetHighlighted(bool e) +{ + highlighted_ = e; + + if (highlighted_) { + setBackgroundRole(QPalette::Highlight); + } else { + setBackgroundRole(QPalette::Window); + } + + UpdateLabel(); +} + +void SliderLadderElement::SetValue(double value) +{ + value_ = value; + + UpdateLabel(); +} + +void SliderLadderElement::SetMultiplierVisible(bool e) +{ + multiplier_visible_ = e; + + UpdateLabel(); +} + +void SliderLadderElement::UpdateLabel() +{ + if (multiplier_visible_) { + QString val_text; + + if (highlighted_) { + val_text = QString::number(value_); + } + + label_->setText(QStringLiteral("%1\n%2").arg(QString::number(multiplier_), + val_text)); + } else { + label_->setText(QString::number(value_)); + } +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/widget/slider/sliderladder.h b/app/widget/slider/sliderladder.h new file mode 100644 index 000000000..8c518ae05 --- /dev/null +++ b/app/widget/slider/sliderladder.h @@ -0,0 +1,104 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SLIDERLADDER_H +#define SLIDERLADDER_H + +#include +#include + +#include "common/define.h" + +OLIVE_NAMESPACE_ENTER + +class SliderLadderElement : public QWidget +{ + Q_OBJECT +public: + SliderLadderElement(const double& multiplier, QWidget* parent = nullptr); + + void SetHighlighted(bool e); + + void SetValue(double value); + + void SetMultiplierVisible(bool e); + + double GetMultiplier() const + { + return multiplier_; + } + +private: + void UpdateLabel(); + + QLabel* label_; + + double multiplier_; + double value_; + + bool highlighted_; + + bool multiplier_visible_; + +}; + +class SliderLadder : public QFrame +{ + Q_OBJECT +public: + SliderLadder(double start_val, double drag_multiplier, int nb_outer_values, QWidget* parent = nullptr); + + virtual ~SliderLadder() override; + + void SetValue(double val); + +protected: + virtual void mouseMoveEvent(QMouseEvent *event) override; + + virtual void mouseReleaseEvent(QMouseEvent *event) override; + + virtual void showEvent(QShowEvent *event) override; + +signals: + void DraggedByValue(int value, double multiplier); + + void Released(); + +private: + void SetActiveElement(); + + QPoint drag_start_; + + double start_val_; + + QList elements_; + + SliderLadderElement* active_element_; + + int relative_y_; + +private slots: + void InitRelativeY(); + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // SLIDERLADDER_H