File indexing completed on 2023-12-03 04:55:09
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 int diff = e->x() - m_dragLastPosition.x(); 0453 if (qApp->isRightToLeft()) { 0454 diff = 0 - diff; 0455 } 0456 0457 if (e->modifiers() == Qt::ControlModifier) { 0458 diff *= 2; 0459 } else if (e->modifiers() == Qt::ShiftModifier) { 0460 diff /= 2; 0461 } 0462 if (KdenliveSettings::dragvalue_mode() == 2) { 0463 diff = (diff > 0 ? 1 : -1) * pow(diff, 2); 0464 } 0465 double nv = m_value + diff * m_step; 0466 if (!qFuzzyCompare(nv, m_value)) { 0467 setNewValue(nv, KdenliveSettings::dragvalue_directupdate()); 0468 } 0469 } else { 0470 double nv; 0471 if (qApp->isLeftToRight()) { 0472 nv = minimum() + ((double)maximum() - minimum()) / width() * e->pos().x(); 0473 } else { 0474 nv = maximum() - ((double)maximum() - minimum()) / width() * e->pos().x(); 0475 } 0476 if (!qFuzzyCompare(nv, value())) { 0477 if (m_step > 1) { 0478 int current = (int)value(); 0479 int diff = (nv - current) / m_step; 0480 setNewValue(current + diff * m_step, true); 0481 } else { 0482 if (e->modifiers() == Qt::ShiftModifier) { 0483 double current = value(); 0484 if (e->pos().x() > m_dragLastPosition.x()) { 0485 nv = qMin(current + 1, (double)maximum()); 0486 } else { 0487 nv = qMax((double)minimum(), current - 1); 0488 } 0489 } 0490 setNewValue(nv, KdenliveSettings::dragvalue_directupdate()); 0491 } 0492 } 0493 } 0494 m_dragLastPosition = e->pos(); 0495 e->accept(); 0496 } 0497 } else { 0498 QProgressBar::mouseMoveEvent(e); 0499 } 0500 } 0501 0502 void CustomLabel::mouseReleaseEvent(QMouseEvent *e) 0503 { 0504 if (e->button() == Qt::MiddleButton) { 0505 e->accept(); 0506 return; 0507 } 0508 if (e->modifiers() == Qt::ControlModifier) { 0509 Q_EMIT setInTimeline(); 0510 e->accept(); 0511 return; 0512 } 0513 if (m_dragMode) { 0514 setNewValue(m_value, true); 0515 m_dragLastPosition = m_dragStartPosition; 0516 e->accept(); 0517 } else if (m_showSlider) { 0518 int newVal = (double)maximum() * e->pos().x() / width(); 0519 if (qApp->isRightToLeft()) { 0520 newVal = maximum() - newVal; 0521 } 0522 if (m_step > 1) { 0523 int current = (int)value(); 0524 int diff = (newVal - current) / m_step; 0525 setNewValue(current + diff * m_step, true); 0526 } else { 0527 setNewValue(newVal, true); 0528 } 0529 m_dragLastPosition = m_dragStartPosition; 0530 e->accept(); 0531 } 0532 m_dragMode = false; 0533 } 0534 0535 void CustomLabel::wheelEvent(QWheelEvent *e) 0536 { 0537 qDebug()<<":::: GOT WHEEL DELTA: "<<e->angleDelta().y(); 0538 if (e->angleDelta().y() > 0) { 0539 if (e->modifiers() == Qt::ControlModifier) { 0540 slotValueInc(10); 0541 } else if (e->modifiers() == Qt::AltModifier) { 0542 slotValueInc(0.1); 0543 } else { 0544 slotValueInc(); 0545 } 0546 } else { 0547 if (e->modifiers() == Qt::ControlModifier) { 0548 slotValueDec(10); 0549 } else if (e->modifiers() == Qt::AltModifier) { 0550 slotValueDec(0.1); 0551 } else { 0552 slotValueDec(); 0553 } 0554 } 0555 e->accept(); 0556 } 0557 0558 void CustomLabel::slotValueInc(double factor) 0559 { 0560 setNewValue(m_value + m_step * factor, true); 0561 } 0562 0563 void CustomLabel::slotValueDec(double factor) 0564 { 0565 setNewValue(m_value - m_step * factor, true); 0566 } 0567 0568 void CustomLabel::setProgressValue(double value) 0569 { 0570 m_value = value; 0571 setValue(qRound(value)); 0572 } 0573 0574 void CustomLabel::setNewValue(double value, bool update) 0575 { 0576 m_value = value; 0577 setValue(qRound(value)); 0578 Q_EMIT valueChanged(value, update); 0579 } 0580 0581 void CustomLabel::setStep(double step) 0582 { 0583 m_step = step; 0584 }