File indexing completed on 2024-04-21 03:49:43

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // The code in this file is largely based on KDE's KLineEdit class
0004 // as included in KDE 4.5. See there for its authors:
0005 // https://api.kde.org/4.x-api/kdelibs-apidocs/kdeui/html/klineedit_8cpp.html
0006 //
0007 // SPDX-FileCopyrightText: 2010, 2012 Dennis Nienhüser <nienhueser@kde.org>
0008 //
0009 
0010 #include "MarbleLineEdit.h"
0011 #include "MarbleGlobal.h"
0012 
0013 #include <QApplication>
0014 #include <QClipboard>
0015 #include <QLabel>
0016 #include <QStyle>
0017 #include <QMouseEvent>
0018 #include <QPainter>
0019 #include <QTimer>
0020 
0021 namespace Marble
0022 {
0023 
0024 class MarbleLineEditPrivate
0025 {
0026 public:
0027     QLabel* m_clearButton;
0028 
0029     QLabel* m_decoratorButton;
0030 
0031     QPixmap m_clearPixmap;
0032 
0033     QPixmap m_decoratorPixmap;
0034 
0035     QTimer m_progressTimer;
0036 
0037     QVector<QPixmap> m_progressAnimation;
0038 
0039     int m_currentFrame;
0040 
0041     int m_iconSize;
0042 
0043     explicit MarbleLineEditPrivate( MarbleLineEdit* parent );
0044 
0045     void createProgressAnimation();
0046 };
0047 
0048 MarbleLineEditPrivate::MarbleLineEditPrivate( MarbleLineEdit* parent ) :
0049     m_clearButton( new QLabel( parent ) ), m_decoratorButton( new QLabel( parent ) ),
0050     m_currentFrame( 0 ), m_iconSize( 16 )
0051 {
0052     m_clearButton->setCursor( Qt::ArrowCursor );
0053     m_clearButton->setToolTip( QObject::tr( "Clear" ) );
0054     m_decoratorButton->setCursor( Qt::ArrowCursor );
0055     createProgressAnimation();
0056     m_progressTimer.setInterval( 100 );
0057     if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) {
0058         m_iconSize = 32;
0059     }
0060 }
0061 
0062 void MarbleLineEditPrivate::createProgressAnimation()
0063 {
0064     // Size parameters
0065     qreal const h = m_iconSize / 2.0; // Half of the icon size
0066     qreal const q = h / 2.0; // Quarter of the icon size
0067     qreal const d = 7.5; // Circle diameter
0068     qreal const r = d / 2.0; // Circle radius
0069 
0070     // Canvas parameters
0071     QImage canvas( m_iconSize, m_iconSize, QImage::Format_ARGB32 );
0072     QPainter painter( &canvas );
0073     painter.setRenderHint( QPainter::Antialiasing, true );
0074     painter.setPen( QColor ( Qt::gray ) );
0075     painter.setBrush( QColor( Qt::white ) );
0076 
0077     // Create all frames
0078     for( double t = 0.0; t < 2 * M_PI; t += M_PI / 8.0 ) {
0079         canvas.fill( Qt::transparent );
0080         QRectF firstCircle( h - r + q * cos( t ), h - r + q * sin( t ), d, d );
0081         QRectF secondCircle( h - r + q * cos( t + M_PI ), h - r + q * sin( t + M_PI ), d, d );
0082         painter.drawEllipse( firstCircle );
0083         painter.drawEllipse( secondCircle );
0084         m_progressAnimation.push_back( QPixmap::fromImage( canvas ) );
0085     }
0086 }
0087 
0088 MarbleLineEdit::MarbleLineEdit( QWidget *parent ) :
0089         QLineEdit( parent ), d( new MarbleLineEditPrivate( this ) )
0090 {
0091     updateClearButtonIcon( text() );
0092     updateClearButton();
0093 
0094     setDecorator( d->m_decoratorPixmap );
0095     connect( this, SIGNAL(textChanged(QString)),
0096              SLOT(updateClearButtonIcon(QString)) );
0097     connect( &d->m_progressTimer, SIGNAL(timeout()),
0098              this, SLOT(updateProgress()) );
0099 }
0100 
0101 MarbleLineEdit::~MarbleLineEdit()
0102 {
0103     delete d;
0104 }
0105 
0106 void MarbleLineEdit::setDecorator(const QPixmap &decorator)
0107 {
0108     d->m_decoratorPixmap = decorator;
0109     d->m_decoratorButton->setPixmap( d->m_decoratorPixmap );
0110     int const padding = 2 + d->m_decoratorPixmap.width();
0111 
0112     QString const prefixDirection = layoutDirection() == Qt::LeftToRight ? "left" : "right";
0113     QString decoratorStyleSheet;
0114     if ( !d->m_decoratorPixmap.isNull() ) {
0115         decoratorStyleSheet = QString( "; padding-%1: %2" ).arg( prefixDirection ).arg( padding );
0116     }
0117     // Padding for clear button to avoid text underflow
0118     QString const postfixDirection  = layoutDirection() == Qt::LeftToRight ? "right" : "left";
0119     QString styleSheet = QString( ":enabled { padding-%1: %2; %3}").arg( postfixDirection ).arg( padding ).arg( decoratorStyleSheet );
0120 
0121     bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
0122     if ( !smallScreen ) {
0123         setStyleSheet( styleSheet );
0124     }
0125 }
0126 
0127 void MarbleLineEdit::setBusy(bool busy)
0128 {
0129     if ( busy ) {
0130         d->m_progressTimer.start();
0131     } else {
0132         d->m_progressTimer.stop();
0133         d->m_decoratorButton->setPixmap( d->m_decoratorPixmap );
0134     }
0135 }
0136 
0137 void MarbleLineEdit::updateClearButtonIcon( const QString& text )
0138 {
0139     d->m_clearButton->setVisible( text.length() > 0 );
0140     if ( d->m_clearButton->pixmap() && !d->m_clearButton->pixmap()->isNull() ) {
0141         return;
0142     }
0143 
0144     QString const direction = layoutDirection() == Qt::LeftToRight ? "rtl" : "ltr";
0145     int const size = (MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen) ? 32 : 16;
0146     QPixmap pixmap = QPixmap(QStringLiteral(":/icons/%1x%1/edit-clear-locationbar-%2.png").arg(size).arg(direction));
0147     d->m_clearButton->setPixmap( pixmap );
0148 }
0149 
0150 void MarbleLineEdit::updateClearButton()
0151 {
0152     const QSize geom = size();
0153     const int frameWidth = style()->pixelMetric( QStyle::PM_DefaultFrameWidth, nullptr, this );
0154     const int pixmapSize = d->m_clearButton->pixmap()->width() + 1;
0155     const int decoratorSize = d->m_decoratorPixmap.width() + 1;
0156 
0157     int y = ( geom.height() - pixmapSize ) / 2;
0158     if ( layoutDirection() == Qt::LeftToRight ) {
0159         d->m_clearButton->move( geom.width() - frameWidth - pixmapSize - decoratorSize, y );
0160         d->m_decoratorButton->move( frameWidth - decoratorSize + 1, y );
0161     } else {
0162         d->m_clearButton->move( frameWidth - decoratorSize + 1, y );
0163         d->m_decoratorButton->move( geom.width() - frameWidth - pixmapSize - decoratorSize, y );
0164     }
0165 }
0166 
0167 void MarbleLineEdit::updateProgress()
0168 {
0169     if ( !d->m_progressAnimation.isEmpty() ) {
0170         d->m_currentFrame = ( d->m_currentFrame + 1 ) % d->m_progressAnimation.size();
0171         QPixmap frame = d->m_progressAnimation[d->m_currentFrame];
0172         d->m_decoratorButton->setPixmap( frame );
0173     }
0174 }
0175 
0176 void MarbleLineEdit::mouseReleaseEvent( QMouseEvent* e )
0177 {
0178     if ( d->m_clearButton == childAt( e->pos() ) ) {
0179         QString newText;
0180         if ( e->button() == Qt::MiddleButton ) {
0181             newText = QApplication::clipboard()->text( QClipboard::Selection );
0182             setText( newText );
0183         } else {
0184             setSelection( 0, text().size() );
0185             del();
0186             emit clearButtonClicked();
0187         }
0188         emit textChanged( newText );
0189     }
0190 
0191     if ( d->m_decoratorButton == childAt( e->pos() ) ) {
0192         emit decoratorButtonClicked();
0193     }
0194 
0195     QLineEdit::mouseReleaseEvent( e );
0196 }
0197 
0198 void MarbleLineEdit::resizeEvent( QResizeEvent * event )
0199 {
0200     updateClearButton();
0201     QLineEdit::resizeEvent( event );
0202 }
0203 
0204 } // namespace Marble
0205 
0206 #include "moc_MarbleLineEdit.cpp"