QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsfilterlineedit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfilterlineedit.cpp
3  ------------------------
4  begin : October 27, 2012
5  copyright : (C) 2012 by Alexander Bruy
6  email : alexander dot bruy at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsfilterlineedit.h"
19 #include "qgsapplication.h"
20 #include "qgsanimatedicon.h"
21 
22 #include <QAction>
23 #include <QToolButton>
24 #include <QStyle>
25 #include <QFocusEvent>
26 #include <QPainter>
27 
28 QgsFilterLineEdit::QgsFilterLineEdit( QWidget *parent, const QString &nullValue )
29  : QLineEdit( parent )
30  , mNullValue( nullValue )
31 {
32  // icon size is about 2/3 height of text, but minimum size of 16
33  int iconSize = std::floor( std::max( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.75, 16.0 ) );
34 
35  mClearIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconClearText.svg" ).pixmap( QSize( iconSize, iconSize ) ), QIcon::Normal, QIcon::On );
36  mClearIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconClearTextHover.svg" ).pixmap( QSize( iconSize, iconSize ) ), QIcon::Selected, QIcon::On );
37 
38  connect( this, &QLineEdit::textChanged, this,
39  &QgsFilterLineEdit::onTextChanged );
40 }
41 
43 {
44  mClearButtonVisible = visible;
45  updateClearIcon();
46 }
47 
49 {
50  if ( visible && !mSearchAction )
51  {
52  QIcon searchIcon = QgsApplication::getThemeIcon( "/search.svg" );
53  mSearchAction = new QAction( searchIcon, QString(), this );
54  mSearchAction->setCheckable( false );
55  addAction( mSearchAction, QLineEdit::LeadingPosition );
56  }
57  else if ( !visible && mSearchAction )
58  {
59  mSearchAction->deleteLater();
60  mSearchAction = nullptr;
61  }
62 }
63 
64 void QgsFilterLineEdit::updateClearIcon()
65 {
66  bool showClear = shouldShowClear();
67  if ( showClear && !mClearAction )
68  {
69  mClearAction = new QAction( mClearIcon, QString(), this );
70  mClearAction->setCheckable( false );
71  addAction( mClearAction, QLineEdit::TrailingPosition );
72  connect( mClearAction, &QAction::triggered, this, &QgsFilterLineEdit::clearValue );
73  }
74  else if ( !showClear && mClearAction )
75  {
76  // pretty freakin weird... seems the deleteLater call on the mClearAction
77  // isn't sufficient to actually remove the action from the line edit, and
78  // a kind of "ghost" action gets left behind... resulting in duplicate
79  // clear actions appearing if later we re-create the action.
80  // in summary: don't remove this "removeAction" call!
81  removeAction( mClearAction );
82  mClearAction->deleteLater();
83  mClearAction = nullptr;
84  }
85 }
86 
87 void QgsFilterLineEdit::focusInEvent( QFocusEvent *e )
88 {
89  QLineEdit::focusInEvent( e );
90  if ( e->reason() == Qt::MouseFocusReason && ( isNull() || mSelectOnFocus ) )
91  {
92  mWaitingForMouseRelease = true;
93  }
94 }
95 
97 {
98  QLineEdit::mouseReleaseEvent( e );
99  if ( mWaitingForMouseRelease )
100  {
101  mWaitingForMouseRelease = false;
102  selectAll();
103  }
104 }
105 
107 {
108  switch ( mClearMode )
109  {
110  case ClearToNull:
111  setText( mNullValue );
112  selectAll();
113  break;
114 
115  case ClearToDefault:
116  setText( mDefaultValue );
117  break;
118  }
119 
120  setModified( true );
121  emit cleared();
122 }
123 
124 void QgsFilterLineEdit::onTextChanged( const QString &text )
125 {
126  updateClearIcon();
127 
128  if ( isNull() )
129  {
130  setStyleSheet( QStringLiteral( "QLineEdit { font: italic; color: gray; } %1" ).arg( mStyleSheet ) );
131  emit valueChanged( QString() );
132  }
133  else
134  {
135  setStyleSheet( mStyleSheet );
136  emit valueChanged( text );
137  }
138 }
139 
140 void QgsFilterLineEdit::updateBusySpinner()
141 {
142  if ( !mBusySpinnerAction )
143  {
144  mBusySpinnerAction = addAction( mBusySpinnerAnimatedIcon->icon(), QLineEdit::TrailingPosition );
145  }
146  mBusySpinnerAction->setIcon( mBusySpinnerAnimatedIcon->icon() );
147 }
148 
150 {
151  return mSelectOnFocus;
152 }
153 
155 {
156  if ( mSelectOnFocus == selectOnFocus )
157  return;
158 
159  mSelectOnFocus = selectOnFocus;
160  emit selectOnFocusChanged();
161 }
162 
164 {
165  return mShowSpinner;
166 }
167 
169 {
170 
171  if ( showSpinner == mShowSpinner )
172  return;
173 
174  if ( showSpinner )
175  {
176  if ( !mBusySpinnerAnimatedIcon )
177  mBusySpinnerAnimatedIcon = new QgsAnimatedIcon( QgsApplication::iconPath( QStringLiteral( "/mIconLoading.gif" ) ), this );
178 
179  mBusySpinnerAnimatedIcon->connectFrameChanged( this, &QgsFilterLineEdit::updateBusySpinner );
180  }
181  else
182  {
183  mBusySpinnerAnimatedIcon->disconnectFrameChanged( this, &QgsFilterLineEdit::updateBusySpinner );
184  removeAction( mBusySpinnerAction );
185  mBusySpinnerAction = nullptr;
186  }
187 
188  mShowSpinner = showSpinner;
189  emit showSpinnerChanged();
190 }
191 
192 bool QgsFilterLineEdit::shouldShowClear() const
193 {
194  if ( !isEnabled() || isReadOnly() || !mClearButtonVisible )
195  return false;
196 
197  switch ( mClearMode )
198  {
199  case ClearToNull:
200  return !isNull();
201 
202  case ClearToDefault:
203  return value() != mDefaultValue;
204  }
205  return false; //avoid warnings
206 }
207 
209 {
210  if ( event->type() == QEvent::ReadOnlyChange || event->type() == QEvent::EnabledChange )
211  updateClearIcon();
212 
213  return QLineEdit::event( event );;
214 }
215 
217 void QgsSpinBoxLineEdit::focusInEvent( QFocusEvent *e )
218 {
219  QLineEdit::focusInEvent( e );
220  if ( isNull() )
221  {
222  clear();
223  }
224 }
void valueChanged(const QString &value)
Same as textChanged() but with support for null values.
bool disconnectFrameChanged(const typename QtPrivate::FunctionPointer< Func1 >::Object *receiver, Func1 slot)
Convenience function to disconnect the same style that the frame change connection was established...
void setSelectOnFocus(bool selectOnFocus)
Will select all text when this widget receives the focus.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:152
QIcon icon() const
Gets the icons representation in the current frame.
void setShowSpinner(bool showSpinner)
Show a spinner icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
void showSpinnerChanged()
Show a spinner icon.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
bool showSpinner() const
Show a spinner icon.
void selectOnFocusChanged()
Will select all text when this widget receives the focus.
void setShowSearchIcon(bool visible)
Define if a search icon shall be shown on the left of the image when no text is entered.
QgsFilterLineEdit(QWidget *parent=nullptr, const QString &nullValue=QString())
Constructor for QgsFilterLineEdit.
QString value() const
Returns the text of this edit with support for handling null values.
Reset value to default value (see defaultValue() )
virtual void clearValue()
Clears the widget and resets it to the null value.
bool event(QEvent *event) override
Reimplemented to enable/disable the clear action depending on read-only status.
bool isNull() const
Determine if the current text represents null.
void focusInEvent(QFocusEvent *e) override
void mouseReleaseEvent(QMouseEvent *e) override
void cleared()
Emitted when the widget is cleared.
Animated icon is keeping an animation running if there are listeners connected to frameChanged...
bool selectOnFocus() const
Will select all text when this widget receives the focus.
void setShowClearButton(bool visible)
Sets whether the widget&#39;s clear button is visible.
bool connectFrameChanged(const typename QtPrivate::FunctionPointer< Func1 >::Object *receiver, Func1 slot)
Connect a slot that will be notified repeatedly whenever a frame changes and which should request the...