File indexing completed on 2024-05-19 05:35:25

0001 // krazy:excludeall=qclasses
0002 
0003 //////////////////////////////////////////////////////////////////////////////
0004 // oxygenlineeditdata.cpp
0005 // data container for QLineEdit transition
0006 // -------------------
0007 //
0008 // SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0009 //
0010 // SPDX-License-Identifier: MIT
0011 //////////////////////////////////////////////////////////////////////////////
0012 
0013 #include "oxygenlineeditdata.h"
0014 
0015 #include <QDateTimeEdit>
0016 #include <QDoubleSpinBox>
0017 #include <QEvent>
0018 #include <QPainter>
0019 #include <QSpinBox>
0020 #include <QStyle>
0021 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0022 #include <QStyleOptionFrameV2>
0023 #else
0024 #include <QStyleOptionFrame>
0025 #endif
0026 
0027 namespace Oxygen
0028 {
0029 // use 20 milliseconds for animation lock
0030 const int LineEditData::_lockTime = 20;
0031 
0032 //______________________________________________________
0033 LineEditData::LineEditData(QObject *parent, QLineEdit *target, int duration)
0034     : TransitionData(parent, target, duration)
0035     , _target(target)
0036     , _hasClearButton(false)
0037     , _edited(false)
0038 {
0039     _target.data()->installEventFilter(this);
0040 
0041     checkClearButton();
0042 
0043     connect(_target.data(), SIGNAL(destroyed()), SLOT(targetDestroyed()));
0044     connect(_target.data(), SIGNAL(textEdited(QString)), SLOT(textEdited()));
0045     connect(_target.data(), SIGNAL(textChanged(QString)), SLOT(textChanged()));
0046 
0047     /*
0048     Additional signal/slot connections depending on widget's parent.
0049     This is needed because parents sometime disable the textChanged signal of the embedded
0050     QLineEdit
0051     */
0052     if (auto spinbox = qobject_cast<QSpinBox *>(_target.data()->parentWidget())) {
0053         connect(spinbox, &QSpinBox::textChanged, this, &LineEditData::textChanged);
0054     } else if (auto spinbox = qobject_cast<QDoubleSpinBox *>(_target.data()->parentWidget())) {
0055         connect(spinbox, &QDoubleSpinBox::textChanged, this, &LineEditData::textChanged);
0056     } else if (qobject_cast<QDateTimeEdit *>(_target.data()->parentWidget())) {
0057         connect(_target.data()->parentWidget(), SIGNAL(dateTimeChanged(QDateTime)), SLOT(textChanged()));
0058     }
0059 
0060     // update cached pixmap on selection change
0061     connect(_target.data(), SIGNAL(selectionChanged()), SLOT(selectionChanged()));
0062 }
0063 
0064 //___________________________________________________________________
0065 bool LineEditData::eventFilter(QObject *object, QEvent *event)
0066 {
0067     if (!(enabled() && object && object == _target.data())) {
0068         return TransitionData::eventFilter(object, event);
0069     }
0070 
0071     switch (event->type()) {
0072     case QEvent::Show:
0073     case QEvent::Resize:
0074     case QEvent::Move:
0075         transition().data()->setEndPixmap(QPixmap());
0076         break;
0077 
0078     default:
0079         break;
0080     }
0081 
0082     return TransitionData::eventFilter(object, event);
0083 }
0084 
0085 //___________________________________________________________________
0086 void LineEditData::timerEvent(QTimerEvent *event)
0087 {
0088     if (event->timerId() == _timer.timerId()) {
0089         _timer.stop();
0090         checkClearButton();
0091         if (enabled() && transition() && _target && _target.data()->isVisible()) {
0092             setRecursiveCheck(true);
0093             transition().data()->setEndPixmap(transition().data()->grab(_target.data(), targetRect()));
0094             setRecursiveCheck(false);
0095         }
0096 
0097     } else if (event->timerId() == _animationLockTimer.timerId()) {
0098         unlockAnimations();
0099 
0100     } else
0101         return TransitionData::timerEvent(event);
0102 }
0103 
0104 //___________________________________________________________________
0105 void LineEditData::checkClearButton(void)
0106 {
0107     if (!_target)
0108         return;
0109     const QObjectList children = _target.data()->children();
0110     _hasClearButton = false;
0111     for (QObject *child : children) {
0112         if (child->inherits("KLineEditButton")) {
0113             _hasClearButton = true;
0114             _clearButtonRect = static_cast<QWidget *>(child)->geometry();
0115             break;
0116         }
0117     }
0118 
0119     return;
0120 }
0121 
0122 //___________________________________________________________________
0123 void LineEditData::textEdited(void)
0124 {
0125     _edited = true;
0126     if (!recursiveCheck()) {
0127         _timer.start(0, this);
0128     }
0129 }
0130 
0131 //___________________________________________________________________
0132 void LineEditData::selectionChanged(void)
0133 {
0134     if (!recursiveCheck()) {
0135         _timer.start(0, this);
0136     }
0137 }
0138 
0139 //___________________________________________________________________
0140 void LineEditData::textChanged(void)
0141 {
0142     // check whether text change was triggered manually
0143     // in which case do not start transition
0144     if (_edited) {
0145         _edited = false;
0146         return;
0147     }
0148 
0149     if (transition().data()->isAnimated()) {
0150         transition().data()->endAnimation();
0151     }
0152 
0153     if (isLocked()) {
0154         // if locked one do not start the new animation, to prevent flicker
0155         // instead, one hides the transition pixmap, trigger an update, and return.
0156         // animations are re-locked.
0157         transition().data()->hide();
0158         lockAnimations();
0159         _timer.start(0, this);
0160         return;
0161     }
0162 
0163     if (initializeAnimation()) {
0164         lockAnimations();
0165         animate();
0166 
0167     } else {
0168         transition().data()->hide();
0169     }
0170 }
0171 
0172 //___________________________________________________________________
0173 bool LineEditData::initializeAnimation(void)
0174 {
0175     if (!(enabled() && _target && _target.data()->isVisible()))
0176         return false;
0177 
0178     if (recursiveCheck())
0179         return false;
0180 
0181     QRect current(targetRect());
0182 
0183     transition().data()->setOpacity(0);
0184     transition().data()->setGeometry(current);
0185 
0186     if (_widgetRect.isValid() && !transition().data()->currentPixmap().isNull() && _widgetRect != current) {
0187         // if label geometry has changed since last animation
0188         // one must clone the pixmap to make it match the right
0189         // geometry before starting the animation.
0190         QPixmap pixmap(current.size());
0191         pixmap.fill(Qt::transparent);
0192         QPainter p(&pixmap);
0193         p.drawPixmap(_widgetRect.topLeft() - current.topLeft(), transition().data()->currentPixmap());
0194         p.end();
0195         transition().data()->setStartPixmap(pixmap);
0196 
0197     } else {
0198         transition().data()->setStartPixmap(transition().data()->currentPixmap());
0199     }
0200 
0201     bool valid(!transition().data()->startPixmap().isNull());
0202     if (valid) {
0203         transition().data()->show();
0204         transition().data()->raise();
0205     }
0206 
0207     setRecursiveCheck(true);
0208     transition().data()->setEndPixmap(transition().data()->grab(_target.data(), targetRect()));
0209     setRecursiveCheck(false);
0210 
0211     return valid;
0212 }
0213 
0214 //___________________________________________________________________
0215 bool LineEditData::animate(void)
0216 {
0217     transition().data()->animate();
0218     return true;
0219 }
0220 
0221 //___________________________________________________________________
0222 void LineEditData::targetDestroyed(void)
0223 {
0224     setEnabled(false);
0225     _target.clear();
0226 }
0227 }