slider: implemented value ladder

Allows finer control over value changes.
This commit is contained in:
itsmattkc
2020-06-14 03:56:18 +10:00
parent 49cf9f4458
commit 4c548bc6a5
10 changed files with 436 additions and 91 deletions
+2
View File
@@ -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_);
@@ -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;
}
@@ -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;i<count;i++) {
FloatSlider* fs = new FloatSlider();
fs->SetDefaultValue(input_->GetDefaultValueForTrack(i));
fs->SetLadderEnabled(true);
widgets_.append(fs);
connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
}
+2
View File
@@ -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
+65 -36
View File
@@ -24,6 +24,10 @@
#include <QEvent>
#include <QMessageBox>
#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<double>(i) * drag_multiplier_;
dragged_diff_ += static_cast<double>(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;
+23 -6
View File
@@ -24,6 +24,7 @@
#include <QStackedWidget>
#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();
+3 -44
View File
@@ -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<QGuiApplication*>(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<QGuiApplication*>(QApplication::instance())->restoreOverrideCursor();
#endif
emit drag_stop();
emit LabelReleased();
dragging_ = false;
}
}
+3 -5
View File
@@ -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_;
};
+230
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#include "sliderladder.h"
#include <QApplication>
#include <QDateTime>
#include <QDebug>
#include <QtMath>
#include <QVBoxLayout>
#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;i<nb_outer_values;i++) {
elements_.append(new SliderLadderElement(qPow(10, - i - 1) * drag_multiplier));
}
foreach (SliderLadderElement* e, elements_) {
e->SetValue(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<QGuiApplication*>(QApplication::instance())->setOverrideCursor(Qt::BlankCursor);
#endif
}
SliderLadder::~SliderLadder()
{
#if defined(Q_OS_MAC)
CGAssociateMouseAndMouseCursorPosition(true);
CGDisplayShowCursor(kCGDirectMainDisplay);
#else
static_cast<QGuiApplication*>(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
+104
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#ifndef SLIDERLADDER_H
#define SLIDERLADDER_H
#include <QLabel>
#include <QWidget>
#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<SliderLadderElement*> elements_;
SliderLadderElement* active_element_;
int relative_y_;
private slots:
void InitRelativeY();
};
OLIVE_NAMESPACE_EXIT
#endif // SLIDERLADDER_H