File indexing completed on 2024-04-28 08:44:56
0001 /* 0002 SPDX-FileCopyrightText: 2011 Till Theato <root@ttill.de> 0003 SPDX-FileCopyrightText: 2011 Jean-Baptiste Mardelle <jb@kdenlive.org> 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "dragvalue.h" 0008 0009 #include "kdenlivesettings.h" 0010 0011 #include <cmath> 0012 0013 #include <QAction> 0014 #include <QApplication> 0015 #include <QFocusEvent> 0016 #include <QHBoxLayout> 0017 #include <QMenu> 0018 #include <QMouseEvent> 0019 #include <QWheelEvent> 0020 0021 #include <KLocalizedString> 0022 #include <QFontDatabase> 0023 #include <QStyle> 0024 #include <kwidgetsaddons_version.h> 0025 0026 DragValue::DragValue(const QString &label, double defaultValue, int decimals, double min, double max, int id, const QString &suffix, bool showSlider, 0027 bool oddOnly, QWidget *parent) 0028 : QWidget(parent) 0029 , m_maximum(max) 0030 , m_minimum(min) 0031 , m_decimals(decimals) 0032 , m_default(defaultValue) 0033 , m_id(id) 0034 , m_intEdit(nullptr) 0035 , m_doubleEdit(nullptr) 0036 { 0037 if (showSlider) { 0038 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0039 } else { 0040 setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); 0041 } 0042 setFocusPolicy(Qt::StrongFocus); 0043 setContextMenuPolicy(Qt::CustomContextMenu); 0044 setFocusPolicy(Qt::StrongFocus); 0045 0046 auto *l = new QHBoxLayout; 0047 l->setSpacing(0); 0048 l->setContentsMargins(0, 0, 0, 0); 0049 m_label = new CustomLabel(label, showSlider, m_maximum - m_minimum, this); 0050 m_label->setObjectName("draggLabel"); 0051 l->addWidget(m_label); 0052 setMinimumHeight(m_label->sizeHint().height()); 0053 if (decimals == 0) { 0054 m_label->setMaximum(max - min); 0055 m_label->setStep(oddOnly ? 2 : 1); 0056 m_intEdit = new QSpinBox(this); 0057 m_intEdit->setObjectName(QStringLiteral("dragBox")); 0058 m_intEdit->setFocusPolicy(Qt::StrongFocus); 0059 if (!suffix.isEmpty()) { 0060 m_intEdit->setSuffix(QLatin1Char(' ') + suffix); 0061 } 0062 m_intEdit->setKeyboardTracking(false); 0063 m_intEdit->setButtonSymbols(QAbstractSpinBox::NoButtons); 0064 m_intEdit->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 0065 m_intEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); 0066 m_intEdit->setRange((int)m_minimum, (int)m_maximum); 0067 m_intEdit->setValue((int)m_default); 0068 if (oddOnly) { 0069 m_intEdit->setSingleStep(2); 0070 } 0071 l->addWidget(m_intEdit); 0072 connect(m_intEdit, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0073 static_cast<void (DragValue::*)(int)>(&DragValue::slotSetValue)); 0074 connect(m_intEdit, &QAbstractSpinBox::editingFinished, this, &DragValue::slotEditingFinished); 0075 m_intEdit->installEventFilter(this); 0076 } else { 0077 m_doubleEdit = new QDoubleSpinBox(this); 0078 m_doubleEdit->setDecimals(decimals); 0079 m_doubleEdit->setFocusPolicy(Qt::StrongFocus); 0080 m_doubleEdit->setObjectName(QStringLiteral("dragBox")); 0081 if (!suffix.isEmpty()) { 0082 m_doubleEdit->setSuffix(QLatin1Char(' ') + suffix); 0083 } 0084 m_doubleEdit->setKeyboardTracking(false); 0085 m_doubleEdit->setButtonSymbols(QAbstractSpinBox::NoButtons); 0086 m_doubleEdit->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 0087 m_doubleEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); 0088 m_doubleEdit->setRange(m_minimum, m_maximum); 0089 double factor = 100; 0090 if (m_maximum - m_minimum > 10000) { 0091 factor = 1000; 0092 } 0093 double steps = (m_maximum - m_minimum) / factor; 0094 m_doubleEdit->setSingleStep(steps); 0095 m_label->setStep(steps); 0096 // m_label->setStep(1); 0097 l->addWidget(m_doubleEdit); 0098 m_doubleEdit->setValue(m_default); 0099 m_doubleEdit->installEventFilter(this); 0100 connect(m_doubleEdit, SIGNAL(valueChanged(double)), this, SLOT(slotSetValue(double))); 0101 connect(m_doubleEdit, &QAbstractSpinBox::editingFinished, this, &DragValue::slotEditingFinished); 0102 } 0103 connect(m_label, SIGNAL(valueChanged(double, bool)), this, SLOT(setValueFromProgress(double, bool))); 0104 connect(m_label, &CustomLabel::resetValue, this, &DragValue::slotReset); 0105 setLayout(l); 0106 if (m_intEdit) { 0107 m_label->setMaximumHeight(m_intEdit->sizeHint().height()); 0108 } else { 0109 m_label->setMaximumHeight(m_doubleEdit->sizeHint().height()); 0110 } 0111 0112 m_menu = new QMenu(this); 0113 0114 m_scale = new KSelectAction(i18n("Scaling"), this); 0115 m_scale->addAction(i18n("Normal scale")); 0116 m_scale->addAction(i18n("Pixel scale")); 0117 m_scale->addAction(i18n("Nonlinear scale")); 0118 m_scale->setCurrentItem(KdenliveSettings::dragvalue_mode()); 0119 m_menu->addAction(m_scale); 0120 0121 m_directUpdate = new QAction(i18n("Direct update"), this); 0122 m_directUpdate->setCheckable(true); 0123 m_directUpdate->setChecked(KdenliveSettings::dragvalue_directupdate()); 0124 m_menu->addAction(m_directUpdate); 0125 0126 QAction *reset = new QAction(QIcon::fromTheme(QStringLiteral("edit-undo")), i18n("Reset value"), this); 0127 connect(reset, &QAction::triggered, this, &DragValue::slotReset); 0128 m_menu->addAction(reset); 0129 0130 if (m_id > -1) { 0131 QAction *timeline = new QAction(QIcon::fromTheme(QStringLiteral("go-jump")), i18n("Show %1 in timeline", label), this); 0132 connect(timeline, &QAction::triggered, this, &DragValue::slotSetInTimeline); 0133 connect(m_label, &CustomLabel::setInTimeline, this, &DragValue::slotSetInTimeline); 0134 m_menu->addAction(timeline); 0135 } 0136 connect(this, &QWidget::customContextMenuRequested, this, &DragValue::slotShowContextMenu); 0137 connect(m_scale, &KSelectAction::indexTriggered, this, &DragValue::slotSetScaleMode); 0138 connect(m_directUpdate, &QAction::triggered, this, &DragValue::slotSetDirectUpdate); 0139 } 0140 0141 DragValue::~DragValue() 0142 { 0143 delete m_intEdit; 0144 delete m_doubleEdit; 0145 delete m_menu; 0146 delete m_label; 0147 // delete m_scale; 0148 // delete m_directUpdate; 0149 } 0150 0151 bool DragValue::eventFilter(QObject *watched, QEvent *event) 0152 { 0153 if (event->type() == QEvent::Wheel) { 0154 // Check if we should ignore the event 0155 bool useEvent = false; 0156 if (m_intEdit) { 0157 useEvent = m_intEdit->hasFocus(); 0158 } else if (m_doubleEdit) { 0159 useEvent = m_doubleEdit->hasFocus(); 0160 } 0161 if (!useEvent) { 0162 return true; 0163 } 0164 0165 auto *we = static_cast<QWheelEvent *>(event); 0166 if (we->angleDelta().y() > 0) { 0167 m_label->slotValueInc(); 0168 } else { 0169 m_label->slotValueDec(); 0170 } 0171 // Stop processing, event accepted 0172 event->accept(); 0173 return true; 0174 } 0175 return QObject::eventFilter(watched, event); 0176 } 0177 0178 bool DragValue::hasEditFocus() const 0179 { 0180 QWidget *fWidget = QApplication::focusWidget(); 0181 return ((fWidget != nullptr) && fWidget->parentWidget() == this); 0182 } 0183 0184 int DragValue::spinSize() 0185 { 0186 if (m_intEdit) { 0187 return m_intEdit->sizeHint().width(); 0188 } 0189 return m_doubleEdit->sizeHint().width(); 0190 } 0191 0192 void DragValue::setSpinSize(int width) 0193 { 0194 if (m_intEdit) { 0195 m_intEdit->setMinimumWidth(width); 0196 } else { 0197 m_doubleEdit->setMinimumWidth(width); 0198 } 0199 } 0200 0201 void DragValue::slotSetInTimeline() 0202 { 0203 Q_EMIT inTimeline(m_id); 0204 } 0205 0206 int DragValue::precision() const 0207 { 0208 return m_decimals; 0209 } 0210 0211 qreal DragValue::maximum() const 0212 { 0213 return m_maximum; 0214 } 0215 0216 qreal DragValue::minimum() const 0217 { 0218 return m_minimum; 0219 } 0220 0221 qreal DragValue::value() const 0222 { 0223 if (m_intEdit) { 0224 return m_intEdit->value(); 0225 } 0226 return m_doubleEdit->value(); 0227 } 0228 0229 void DragValue::setMaximum(qreal max) 0230 { 0231 if (!qFuzzyCompare(m_maximum, max)) { 0232 m_maximum = max; 0233 if (m_intEdit) { 0234 m_intEdit->setRange(m_minimum, m_maximum); 0235 } else { 0236 m_doubleEdit->setRange(m_minimum, m_maximum); 0237 } 0238 } 0239 } 0240 0241 void DragValue::setMinimum(qreal min) 0242 { 0243 if (!qFuzzyCompare(m_minimum, min)) { 0244 m_minimum = min; 0245 if (m_intEdit) { 0246 m_intEdit->setRange(m_minimum, m_maximum); 0247 } else { 0248 m_doubleEdit->setRange(m_minimum, m_maximum); 0249 } 0250 } 0251 } 0252 0253 void DragValue::setRange(qreal min, qreal max) 0254 { 0255 m_maximum = max; 0256 m_minimum = min; 0257 if (m_intEdit) { 0258 m_intEdit->setRange(m_minimum, m_maximum); 0259 } else { 0260 m_doubleEdit->setRange(m_minimum, m_maximum); 0261 } 0262 m_label->setMaximum(max - min); 0263 } 0264 0265 void DragValue::setStep(qreal step) 0266 { 0267 if (m_intEdit) { 0268 m_intEdit->setSingleStep(step); 0269 } else { 0270 m_doubleEdit->setSingleStep(step); 0271 } 0272 m_label->setStep(step); 0273 } 0274 0275 void DragValue::slotReset() 0276 { 0277 if (m_intEdit) { 0278 m_intEdit->blockSignals(true); 0279 m_intEdit->setValue(m_default); 0280 m_intEdit->blockSignals(false); 0281 Q_EMIT valueChanged((int)m_default, true); 0282 } else { 0283 m_doubleEdit->blockSignals(true); 0284 m_doubleEdit->setValue(m_default); 0285 m_doubleEdit->blockSignals(false); 0286 Q_EMIT valueChanged(m_default, true); 0287 } 0288 m_label->setProgressValue((m_default - m_minimum) / (m_maximum - m_minimum) * m_label->maximum()); 0289 } 0290 0291 void DragValue::slotSetValue(int value) 0292 { 0293 setValue(value, true); 0294 } 0295 0296 void DragValue::slotSetValue(double value) 0297 { 0298 setValue(value, true); 0299 } 0300 0301 void DragValue::setValueFromProgress(double value, bool final) 0302 { 0303 value = m_minimum + value * (m_maximum - m_minimum) / m_label->maximum(); 0304 if (m_decimals == 0) { 0305 setValue(qRound(value), final); 0306 } else { 0307 setValue(value, final); 0308 } 0309 } 0310 0311 void DragValue::setValue(double value, bool final) 0312 { 0313 value = qBound(m_minimum, value, m_maximum); 0314 if (m_intEdit && m_intEdit->singleStep() > 1) { 0315 int div = (value - m_minimum) / m_intEdit->singleStep(); 0316 value = m_minimum + (div * m_intEdit->singleStep()); 0317 } 0318 m_label->setProgressValue((value - m_minimum) / (m_maximum - m_minimum) * m_label->maximum()); 0319 if (m_intEdit) { 0320 m_intEdit->blockSignals(true); 0321 m_intEdit->setValue((int)value); 0322 m_intEdit->blockSignals(false); 0323 Q_EMIT valueChanged((int)value, final); 0324 } else { 0325 m_doubleEdit->blockSignals(true); 0326 m_doubleEdit->setValue(value); 0327 m_doubleEdit->blockSignals(false); 0328 Q_EMIT valueChanged(value, final); 0329 } 0330 } 0331 0332 void DragValue::slotEditingFinished() 0333 { 0334 qDebug() << "::: EDITING FINISHED..."; 0335 if (m_intEdit) { 0336 int newValue = m_intEdit->value(); 0337 m_intEdit->blockSignals(true); 0338 if (m_intEdit->singleStep() > 1) { 0339 int div = (newValue - m_minimum) / m_intEdit->singleStep(); 0340 newValue = m_minimum + (div * m_intEdit->singleStep()); 0341 m_intEdit->setValue(newValue); 0342 } 0343 m_intEdit->clearFocus(); 0344 m_intEdit->blockSignals(false); 0345 if (!KdenliveSettings::dragvalue_directupdate()) { 0346 Q_EMIT valueChanged((double)newValue, true); 0347 } 0348 } else { 0349 double value = m_doubleEdit->value(); 0350 m_doubleEdit->blockSignals(true); 0351 m_doubleEdit->clearFocus(); 0352 m_doubleEdit->blockSignals(false); 0353 if (!KdenliveSettings::dragvalue_directupdate()) { 0354 Q_EMIT valueChanged(value, true); 0355 } 0356 } 0357 } 0358 0359 void DragValue::slotShowContextMenu(const QPoint &pos) 0360 { 0361 // values might have been changed by another object of this class 0362 m_scale->setCurrentItem(KdenliveSettings::dragvalue_mode()); 0363 m_directUpdate->setChecked(KdenliveSettings::dragvalue_directupdate()); 0364 m_menu->exec(mapToGlobal(pos)); 0365 } 0366 0367 void DragValue::slotSetScaleMode(int mode) 0368 { 0369 KdenliveSettings::setDragvalue_mode(mode); 0370 } 0371 0372 void DragValue::slotSetDirectUpdate(bool directUpdate) 0373 { 0374 KdenliveSettings::setDragvalue_directupdate(directUpdate); 0375 } 0376 0377 void DragValue::setInTimelineProperty(bool intimeline) 0378 { 0379 if (m_label->property("inTimeline").toBool() == intimeline) { 0380 return; 0381 } 0382 m_label->setProperty("inTimeline", intimeline); 0383 style()->unpolish(m_label); 0384 style()->polish(m_label); 0385 m_label->update(); 0386 if (m_intEdit) { 0387 m_intEdit->setProperty("inTimeline", intimeline); 0388 style()->unpolish(m_intEdit); 0389 style()->polish(m_intEdit); 0390 m_intEdit->update(); 0391 } else { 0392 m_doubleEdit->setProperty("inTimeline", intimeline); 0393 style()->unpolish(m_doubleEdit); 0394 style()->polish(m_doubleEdit); 0395 m_doubleEdit->update(); 0396 } 0397 } 0398 0399 CustomLabel::CustomLabel(const QString &label, bool showSlider, int range, QWidget *parent) 0400 : QProgressBar(parent) 0401 , m_dragMode(false) 0402 , m_showSlider(showSlider) 0403 , m_step(10.0) 0404 , m_value(0.) 0405 // m_precision(pow(10, precision)), 0406 { 0407 setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); 0408 setFormat(QLatin1Char(' ') + label); 0409 setFocusPolicy(Qt::StrongFocus); 0410 setCursor(Qt::PointingHandCursor); 0411 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); 0412 if (m_showSlider) { 0413 setToolTip(xi18n("Shift + Drag to adjust value one by one.")); 0414 } 0415 if (showSlider) { 0416 setRange(0, 1000); 0417 } else { 0418 setRange(0, range); 0419 QSize sh; 0420 const QFontMetrics &fm = fontMetrics(); 0421 sh.setWidth(fm.horizontalAdvance(QLatin1Char(' ') + label + QLatin1Char(' '))); 0422 setMaximumWidth(sh.width()); 0423 setObjectName(QStringLiteral("dragOnly")); 0424 } 0425 setValue(0); 0426 } 0427 0428 void CustomLabel::mousePressEvent(QMouseEvent *e) 0429 { 0430 if (e->button() == Qt::LeftButton) { 0431 m_dragStartPosition = m_dragLastPosition = e->pos(); 0432 e->accept(); 0433 } else if (e->button() == Qt::MiddleButton) { 0434 Q_EMIT resetValue(); 0435 m_dragStartPosition = QPoint(-1, -1); 0436 } else { 0437 QWidget::mousePressEvent(e); 0438 } 0439 } 0440 0441 void CustomLabel::mouseMoveEvent(QMouseEvent *e) 0442 { 0443 if ((e->buttons() & Qt::LeftButton) && m_dragStartPosition != QPoint(-1, -1)) { 0444 if (!m_dragMode && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { 0445 m_dragMode = true; 0446 m_dragLastPosition = e->pos(); 0447 e->accept(); 0448 return; 0449 } 0450 if (m_dragMode) { 0451 if (KdenliveSettings::dragvalue_mode() > 0 || !m_showSlider) { 0452 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0453 int diff = e->pos().x() - m_dragLastPosition.x(); 0454 #else 0455 int diff = e->position().x() - m_dragLastPosition.x(); 0456 #endif 0457 if (qApp->isRightToLeft()) { 0458 diff = 0 - diff; 0459 } 0460 0461 if (e->modifiers() == Qt::ControlModifier) { 0462 diff *= 2; 0463 } else if (e->modifiers() == Qt::ShiftModifier) { 0464 diff /= 2; 0465 } 0466 if (KdenliveSettings::dragvalue_mode() == 2) { 0467 diff = (diff > 0 ? 1 : -1) * pow(diff, 2); 0468 } 0469 double nv = m_value + diff * m_step; 0470 if (!qFuzzyCompare(nv, m_value)) { 0471 setNewValue(nv, KdenliveSettings::dragvalue_directupdate()); 0472 } 0473 } else { 0474 double nv; 0475 if (qApp->isLeftToRight()) { 0476 nv = minimum() + ((double)maximum() - minimum()) / width() * e->pos().x(); 0477 } else { 0478 nv = maximum() - ((double)maximum() - minimum()) / width() * e->pos().x(); 0479 } 0480 if (!qFuzzyCompare(nv, value())) { 0481 if (m_step > 1) { 0482 int current = (int)value(); 0483 int diff = (nv - current) / m_step; 0484 setNewValue(current + diff * m_step, true); 0485 } else { 0486 if (e->modifiers() == Qt::ShiftModifier) { 0487 double current = value(); 0488 if (e->pos().x() > m_dragLastPosition.x()) { 0489 nv = qMin(current + 1, (double)maximum()); 0490 } else { 0491 nv = qMax((double)minimum(), current - 1); 0492 } 0493 } 0494 setNewValue(nv, KdenliveSettings::dragvalue_directupdate()); 0495 } 0496 } 0497 } 0498 m_dragLastPosition = e->pos(); 0499 e->accept(); 0500 } 0501 } else { 0502 QProgressBar::mouseMoveEvent(e); 0503 } 0504 } 0505 0506 void CustomLabel::mouseReleaseEvent(QMouseEvent *e) 0507 { 0508 if (e->button() == Qt::MiddleButton) { 0509 e->accept(); 0510 return; 0511 } 0512 if (e->modifiers() == Qt::ControlModifier) { 0513 Q_EMIT setInTimeline(); 0514 e->accept(); 0515 return; 0516 } 0517 if (m_dragMode) { 0518 setNewValue(m_value, true); 0519 m_dragLastPosition = m_dragStartPosition; 0520 e->accept(); 0521 } else if (m_showSlider) { 0522 int newVal = (double)maximum() * e->pos().x() / width(); 0523 if (qApp->isRightToLeft()) { 0524 newVal = maximum() - newVal; 0525 } 0526 if (m_step > 1) { 0527 int current = (int)value(); 0528 int diff = (newVal - current) / m_step; 0529 setNewValue(current + diff * m_step, true); 0530 } else { 0531 setNewValue(newVal, true); 0532 } 0533 m_dragLastPosition = m_dragStartPosition; 0534 e->accept(); 0535 } 0536 m_dragMode = false; 0537 } 0538 0539 void CustomLabel::wheelEvent(QWheelEvent *e) 0540 { 0541 qDebug()<<":::: GOT WHEEL DELTA: "<<e->angleDelta().y(); 0542 if (e->angleDelta().y() > 0) { 0543 if (e->modifiers() == Qt::ControlModifier) { 0544 slotValueInc(10); 0545 } else if (e->modifiers() == Qt::AltModifier) { 0546 slotValueInc(0.1); 0547 } else { 0548 slotValueInc(); 0549 } 0550 } else { 0551 if (e->modifiers() == Qt::ControlModifier) { 0552 slotValueDec(10); 0553 } else if (e->modifiers() == Qt::AltModifier) { 0554 slotValueDec(0.1); 0555 } else { 0556 slotValueDec(); 0557 } 0558 } 0559 e->accept(); 0560 } 0561 0562 void CustomLabel::slotValueInc(double factor) 0563 { 0564 setNewValue(m_value + m_step * factor, true); 0565 } 0566 0567 void CustomLabel::slotValueDec(double factor) 0568 { 0569 setNewValue(m_value - m_step * factor, true); 0570 } 0571 0572 void CustomLabel::setProgressValue(double value) 0573 { 0574 m_value = value; 0575 setValue(qRound(value)); 0576 } 0577 0578 void CustomLabel::setNewValue(double value, bool update) 0579 { 0580 m_value = value; 0581 setValue(qRound(value)); 0582 Q_EMIT valueChanged(value, update); 0583 } 0584 0585 void CustomLabel::setStep(double step) 0586 { 0587 m_step = step; 0588 }