1 // krazy:excludeall=qclasses 2 3 ////////////////////////////////////////////////////////////////////////////// 4 // oxygenlineeditdata.cpp 5 // data container for QLineEdit transition 6 // ------------------- 7 // 8 // SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr> 9 // 10 // SPDX-License-Identifier: MIT 11 ////////////////////////////////////////////////////////////////////////////// 12 13 #include "oxygenlineeditdata.h" 14 15 #include <QEvent> 16 #include <QDateTimeEdit> 17 #include <QDoubleSpinBox> 18 #include <QPainter> 19 #include <QSpinBox> 20 #include <QStyle> 21 #include <QStyleOptionFrameV2> 22 23 namespace Oxygen 24 { 25 26 // use 20 milliseconds for animation lock 27 const int LineEditData::_lockTime = 20; 28 29 //______________________________________________________ LineEditData(QObject * parent,QLineEdit * target,int duration)30 LineEditData::LineEditData( QObject* parent, QLineEdit* target, int duration ): 31 TransitionData( parent, target, duration ), 32 _target( target ), 33 _hasClearButton( false ), 34 _edited( false ) 35 { 36 _target.data()->installEventFilter( this ); 37 38 checkClearButton(); 39 40 connect( _target.data(), SIGNAL(destroyed()), SLOT(targetDestroyed()) ); 41 connect( _target.data(), SIGNAL(textEdited(QString)), SLOT(textEdited()) ); 42 connect( _target.data(), SIGNAL(textChanged(QString)), SLOT(textChanged()) ); 43 44 /* 45 Additional signal/slot connections depending on widget's parent. 46 This is needed because parents sometime disable the textChanged signal of the embedded 47 QLineEdit 48 */ 49 if( qobject_cast<QSpinBox*>( _target.data()->parentWidget() ) ||qobject_cast<QDoubleSpinBox*>( _target.data()->parentWidget() ) ) 50 { 51 52 connect( _target.data()->parentWidget(), SIGNAL(valueChanged(QString)), SLOT(textChanged()) ); 53 54 } else if( qobject_cast<QDateTimeEdit*>( _target.data()->parentWidget() ) ) { 55 56 connect( _target.data()->parentWidget(), SIGNAL(dateTimeChanged(QDateTime)), SLOT(textChanged()) ); 57 58 } 59 60 // update cached pixmap on selection change 61 connect( _target.data(), SIGNAL(selectionChanged()), SLOT(selectionChanged()) ); 62 63 } 64 65 //___________________________________________________________________ eventFilter(QObject * object,QEvent * event)66 bool LineEditData::eventFilter( QObject* object, QEvent* event ) 67 { 68 69 if( !( enabled() && object && object == _target.data() ) ) 70 { return TransitionData::eventFilter( object, event ); } 71 72 switch ( event->type() ) 73 { 74 case QEvent::Show: 75 case QEvent::Resize: 76 case QEvent::Move: 77 transition().data()->setEndPixmap( QPixmap() ); 78 break; 79 80 default: break; 81 } 82 83 return TransitionData::eventFilter( object, event ); 84 85 } 86 87 //___________________________________________________________________ timerEvent(QTimerEvent * event)88 void LineEditData::timerEvent( QTimerEvent* event ) 89 { 90 if( event->timerId() == _timer.timerId() ) 91 { 92 93 _timer.stop(); 94 checkClearButton(); 95 if( enabled() && transition() && _target && _target.data()->isVisible() ) 96 { 97 setRecursiveCheck( true ); 98 transition().data()->setEndPixmap( transition().data()->grab( _target.data(), targetRect() ) ); 99 setRecursiveCheck( false ); 100 } 101 102 } else if( event->timerId() == _animationLockTimer.timerId() ) { 103 104 unlockAnimations(); 105 106 } else return TransitionData::timerEvent( event ); 107 108 } 109 110 //___________________________________________________________________ checkClearButton(void)111 void LineEditData::checkClearButton( void ) 112 { 113 if( !_target ) return; 114 QObjectList children( _target.data()->children() ); 115 _hasClearButton = false; 116 foreach( QObject* child, children ) 117 { 118 if( child->inherits( "KLineEditButton" ) ) 119 { 120 _hasClearButton = true; 121 _clearButtonRect = static_cast<QWidget*>(child)->geometry(); 122 break; 123 } 124 } 125 126 return; 127 } 128 129 //___________________________________________________________________ textEdited(void)130 void LineEditData::textEdited( void ) 131 { 132 _edited = true; 133 if( !recursiveCheck() ) 134 { _timer.start( 0, this ); } 135 } 136 137 138 //___________________________________________________________________ selectionChanged(void)139 void LineEditData::selectionChanged( void ) 140 { 141 if( !recursiveCheck() ) 142 { _timer.start( 0, this ); } 143 } 144 145 //___________________________________________________________________ textChanged(void)146 void LineEditData::textChanged( void ) 147 { 148 149 // check whether text change was triggered manually 150 // in which case do not start transition 151 if( _edited ) 152 { 153 _edited = false; 154 return; 155 } 156 157 if( transition().data()->isAnimated() ) 158 { transition().data()->endAnimation(); } 159 160 if( isLocked() ) 161 { 162 // if locked one do not start the new animation, to prevent flicker 163 // instead, one hides the transition pixmap, trigger an update, and return. 164 // animations are re-locked. 165 transition().data()->hide(); 166 lockAnimations(); 167 _timer.start( 0, this ); 168 return; 169 } 170 171 if( initializeAnimation() ) 172 { 173 174 lockAnimations(); 175 animate(); 176 177 } else { 178 179 transition().data()->hide(); 180 181 } 182 } 183 184 //___________________________________________________________________ initializeAnimation(void)185 bool LineEditData::initializeAnimation( void ) 186 { 187 if( !( enabled() && _target && _target.data()->isVisible() ) ) return false; 188 189 if( recursiveCheck() ) return false; 190 191 QRect current( targetRect() ); 192 193 transition().data()->setOpacity(0); 194 transition().data()->setGeometry( current ); 195 196 if( _widgetRect.isValid() && 197 !transition().data()->currentPixmap().isNull() && 198 _widgetRect != current ) 199 { 200 201 // if label geometry has changed since last animation 202 // one must clone the pixmap to make it match the right 203 // geometry before starting the animation. 204 QPixmap pixmap( current.size() ); 205 pixmap.fill( Qt::transparent ); 206 QPainter p( &pixmap ); 207 p.drawPixmap( _widgetRect.topLeft() - current.topLeft(), transition().data()->currentPixmap() ); 208 p.end(); 209 transition().data()->setStartPixmap( pixmap ); 210 211 } else { 212 213 transition().data()->setStartPixmap( transition().data()->currentPixmap() ); 214 215 } 216 217 bool valid( !transition().data()->startPixmap().isNull() ); 218 if( valid ) 219 { 220 transition().data()->show(); 221 transition().data()->raise(); 222 } 223 224 setRecursiveCheck( true ); 225 transition().data()->setEndPixmap( transition().data()->grab( _target.data(), targetRect() ) ); 226 setRecursiveCheck( false ); 227 228 return valid; 229 230 } 231 232 //___________________________________________________________________ animate(void)233 bool LineEditData::animate( void ) 234 { 235 transition().data()->animate(); 236 return true; 237 } 238 239 //___________________________________________________________________ targetDestroyed(void)240 void LineEditData::targetDestroyed( void ) 241 { 242 setEnabled( false ); 243 _target.clear(); 244 } 245 246 } 247