diff --git a/src/widget/wpushbutton.cpp b/src/widget/wpushbutton.cpp index 5da332b5626..1d4d03d5cd6 100644 --- a/src/widget/wpushbutton.cpp +++ b/src/widget/wpushbutton.cpp @@ -155,9 +155,13 @@ void WPushButton::setup(const QDomNode& node, const SkinContext& context) { switch (m_leftButtonMode) { case ControlPushButton::PUSH: case ControlPushButton::POWERWINDOW: + leftConnection->setEmitOption( + ControlParameterWidgetConnection::EMIT_ON_PRESS_AND_RELEASE); + break; case ControlPushButton::LONGPRESSLATCHING: leftConnection->setEmitOption( ControlParameterWidgetConnection::EMIT_ON_PRESS_AND_RELEASE); + m_pLongPressLatching = std::make_unique(this); break; case ControlPushButton::TOGGLE: case ControlPushButton::TRIGGER: @@ -302,17 +306,21 @@ void WPushButton::onConnectedControlChanged(double dParameter, double dValue) { void WPushButton::paintEvent(QPaintEvent* e) { Q_UNUSED(e); + paintOnDevice(nullptr); +} + +void WPushButton::paintOnDevice(QPaintDevice* pd) { QStyleOption option; option.initFrom(this); - QStylePainter p(this); - p.drawPrimitive(QStyle::PE_Widget, option); + std::unique_ptr p(pd ? new QStylePainter(pd, this) : new QStylePainter(this)); + p->drawPrimitive(QStyle::PE_Widget, option); if (m_iNoStates == 0) { return; } if (m_pPixmapBack) { - m_pPixmapBack->draw(rect(), &p); + m_pPixmapBack->draw(rect(), p.get()); } const QVector& pixmaps = m_bPressed ? @@ -335,7 +343,7 @@ void WPushButton::paintEvent(QPaintEvent* e) { PaintablePointer pPixmap = pixmaps.at(idx); if (!pPixmap.isNull() && !pPixmap->isNull()) { - pPixmap->draw(rect(), &p); + pPixmap->draw(rect(), p.get()); } QString text = m_text.at(idx); @@ -348,7 +356,11 @@ void WPushButton::paintEvent(QPaintEvent* e) { // int textWidth = width() - lPad - rPad; // QRect textRect = rect().adjust(x1, y1, x2, y2); QString elidedText = metrics.elidedText(text, m_elideMode, width()); - p.drawText(rect(), m_align.at(idx), elidedText); + p->drawText(rect(), m_align.at(idx), elidedText); + } + + if (pd == nullptr && m_pLongPressLatching) { + m_pLongPressLatching->paint(p.get(), m_clickTimer.remainingTime()); } } @@ -395,12 +407,16 @@ void WPushButton::mousePressEvent(QMouseEvent * e) { } else { // Toggle through the states emitValue = getControlParameterLeft(); + const auto oldValue = emitValue; if (!util_isnan(emitValue) && m_iNoStates > 0) { emitValue = static_cast(emitValue + 1.0) % m_iNoStates; } if (m_leftButtonMode == ControlPushButton::LONGPRESSLATCHING) { m_clickTimer.setSingleShot(true); m_clickTimer.start(ControlPushButtonBehavior::kLongPressLatchingTimeMillis); + if (oldValue == 0.0 && m_pLongPressLatching) { + m_pLongPressLatching->start(); + } } } setControlParameterLeftDown(emitValue); @@ -455,6 +471,10 @@ void WPushButton::mouseReleaseEvent(QMouseEvent * e) { const bool leftClick = e->button() == Qt::LeftButton; const bool rightClick = e->button() == Qt::RightButton; + if (m_pLongPressLatching) { + m_pLongPressLatching->stop(); + } + if (m_leftButtonMode == ControlPushButton::POWERWINDOW && m_iNoStates == 2) { if (leftClick) { @@ -520,3 +540,52 @@ void WPushButton::fillDebugTooltip(QStringList* debug) { << QString("RightButtonMode: %1") .arg(ControlPushButton::buttonModeToString(m_rightButtonMode)); } + +void WPushButton::updateSlot() { + update(); +} + +WPushButton::LongPressLatching::LongPressLatching(WPushButton* pButton) + : m_pButton(pButton) { + // To animate the long press latching + connect(&m_animTimer, &QTimer::timeout, m_pButton, &WPushButton::updateSlot); +} + +void WPushButton::LongPressLatching::paint(QPainter* p, int remainingTime) { + if (m_animTimer.isActive()) { + // Animate the long press latching by capturing the off state in a pixmap + // and gradually draw less of it over the duration of the long press latching. + + remainingTime = std::max(0, remainingTime); + + if (remainingTime == 0) { + m_animTimer.stop(); + } else { + qreal x = m_pButton->width() * static_cast(remainingTime) / + static_cast(ControlPushButtonBehavior:: + kLongPressLatchingTimeMillis); + p->drawPixmap(QPointF(m_pButton->width() - x, 0), + m_preLongPressPixmap, + QRectF((m_pButton->width() - x) * m_pButton->devicePixelRatio(), + 0, + x * m_pButton->devicePixelRatio(), + m_pButton->height() * m_pButton->devicePixelRatio())); + } + } +} + +void WPushButton::LongPressLatching::start() { + // Capture pixmap with the off state ... + m_preLongPressPixmap = QPixmap(static_cast(m_pButton->width() * + m_pButton->devicePixelRatio()), + static_cast( + m_pButton->height() * m_pButton->devicePixelRatio())); + m_preLongPressPixmap.setDevicePixelRatio(m_pButton->devicePixelRatio()); + m_pButton->paintOnDevice(&m_preLongPressPixmap); + // ... and start the long press latching animation + m_animTimer.start(1000 / 60); +} + +void WPushButton::LongPressLatching::stop() { + m_animTimer.stop(); +} diff --git a/src/widget/wpushbutton.h b/src/widget/wpushbutton.h index 0e0b3557d73..fbda3083954 100644 --- a/src/widget/wpushbutton.h +++ b/src/widget/wpushbutton.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "control/controlpushbutton.h" #include "util/fpclassify.h" @@ -61,6 +62,9 @@ class WPushButton : public WWidget { public slots: void onConnectedControlChanged(double dParameter, double dValue) override; + private slots: + void updateSlot(); + protected: bool event(QEvent* e) override; void paintEvent(QPaintEvent* e) override; @@ -86,6 +90,8 @@ class WPushButton : public WWidget { Paintable::DrawMode mode, double scaleFactor); + void paintOnDevice(QPaintDevice* pd); + // True, if the button is currently pressed bool m_bPressed; // True, if the button is pointer is above button @@ -106,4 +112,25 @@ class WPushButton : public WWidget { ControlPushButton::ButtonMode m_rightButtonMode; QTimer m_clickTimer; QVector m_align; + + // Animates long press latching by storing the off state of the + // WPushButton in a pixmap and gradually (from left to right) + // drawing less of the off state on top of the on state, to + // give a visual indication that the long press latching is in + // progress. + class LongPressLatching { + public: + LongPressLatching(WPushButton* pButton); + void paint(QPainter* p, int remainingTime); + void start(); + void stop(); + + private: + WPushButton* m_pButton; + QPixmap m_preLongPressPixmap; + QTimer m_animTimer; + }; + + // Only assigned for WPushButtons that use long press latching + std::unique_ptr m_pLongPressLatching; };