QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 #include "qgis.h"
22 
23 #include <QAction>
24 #include <QToolButton>
25 #include <QStyle>
26 #include <QFocusEvent>
27 #include <QPainter>
28 
29 QgsFilterLineEdit::QgsFilterLineEdit( QWidget *parent, const QString &nullValue )
30  : QLineEdit( parent )
31  , mNullValue( nullValue )
32 {
33  // icon size is about 2/3 height of text, but minimum size of 16
34  int iconSize = std::floor( std::max( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 0.75, 16.0 ) );
35 
36  mClearIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconClearText.svg" ).pixmap( QSize( iconSize, iconSize ) ), QIcon::Normal, QIcon::On );
37  mClearIcon.addPixmap( QgsApplication::getThemeIcon( "/mIconClearTextHover.svg" ).pixmap( QSize( iconSize, iconSize ) ), QIcon::Selected, QIcon::On );
38 
39  connect( this, &QLineEdit::textChanged, this,
40  &QgsFilterLineEdit::onTextChanged );
41 }
42 
44 {
45  mClearButtonVisible = visible;
46  updateClearIcon();
47 }
48 
50 {
51  if ( visible && !mSearchAction )
52  {
53  QIcon searchIcon = QgsApplication::getThemeIcon( "/search.svg" );
54  mSearchAction = new QAction( searchIcon, QString(), this );
55  mSearchAction->setCheckable( false );
56  addAction( mSearchAction, QLineEdit::LeadingPosition );
57  }
58  else if ( !visible && mSearchAction )
59  {
60  mSearchAction->deleteLater();
61  mSearchAction = nullptr;
62  }
63 }
64 
65 void QgsFilterLineEdit::updateClearIcon()
66 {
67  bool showClear = shouldShowClear();
68  if ( showClear && !mClearAction )
69  {
70  mClearAction = new QAction( mClearIcon, QString(), this );
71  mClearAction->setCheckable( false );
72  addAction( mClearAction, QLineEdit::TrailingPosition );
73  connect( mClearAction, &QAction::triggered, this, &QgsFilterLineEdit::clearValue );
74  }
75  else if ( !showClear && mClearAction )
76  {
77  // pretty freakin weird... seems the deleteLater call on the mClearAction
78  // isn't sufficient to actually remove the action from the line edit, and
79  // a kind of "ghost" action gets left behind... resulting in duplicate
80  // clear actions appearing if later we re-create the action.
81  // in summary: don't remove this "removeAction" call!
82  removeAction( mClearAction );
83  mClearAction->deleteLater();
84  mClearAction = nullptr;
85  }
86 }
87 
88 void QgsFilterLineEdit::focusInEvent( QFocusEvent *e )
89 {
90  QLineEdit::focusInEvent( e );
91  if ( e->reason() == Qt::MouseFocusReason && ( isNull() || mSelectOnFocus ) )
92  {
93  mWaitingForMouseRelease = true;
94  }
95 }
96 
98 {
99  QLineEdit::mouseReleaseEvent( e );
100  if ( mWaitingForMouseRelease )
101  {
102  mWaitingForMouseRelease = false;
103  selectAll();
104  }
105 }
106 
108 {
109  switch ( mClearMode )
110  {
111  case ClearToNull:
112  setText( mNullValue );
113  selectAll();
114  break;
115 
116  case ClearToDefault:
117  setText( mDefaultValue );
118  break;
119  }
120 
121  setModified( true );
122  emit cleared();
123 }
124 
125 void QgsFilterLineEdit::onTextChanged( const QString &text )
126 {
127  updateClearIcon();
128 
129  if ( isNull() )
130  {
131  setStyleSheet( QStringLiteral( "QLineEdit { font: italic; color: gray; } %1" ).arg( mStyleSheet ) );
132  emit valueChanged( QString() );
133  }
134  else
135  {
136  setStyleSheet( mStyleSheet );
137  emit valueChanged( text );
138  }
139 }
140 
141 void QgsFilterLineEdit::updateBusySpinner()
142 {
143  if ( !mBusySpinnerAction )
144  {
145  mBusySpinnerAction = addAction( mBusySpinnerAnimatedIcon->icon(), QLineEdit::TrailingPosition );
146  }
147  mBusySpinnerAction->setIcon( mBusySpinnerAnimatedIcon->icon() );
148 }
149 
151 {
152  return mSelectOnFocus;
153 }
154 
155 void QgsFilterLineEdit::setSelectOnFocus( bool selectOnFocus )
156 {
157  if ( mSelectOnFocus == selectOnFocus )
158  return;
159 
160  mSelectOnFocus = selectOnFocus;
161  emit selectOnFocusChanged();
162 }
163 
165 {
166  return mShowSpinner;
167 }
168 
169 void QgsFilterLineEdit::setShowSpinner( bool showSpinner )
170 {
171 
172  if ( showSpinner == mShowSpinner )
173  return;
174 
175  if ( showSpinner )
176  {
177  if ( !mBusySpinnerAnimatedIcon )
178  mBusySpinnerAnimatedIcon = new QgsAnimatedIcon( QgsApplication::iconPath( QStringLiteral( "/mIconLoading.gif" ) ), this );
179 
180  mBusySpinnerAnimatedIcon->connectFrameChanged( this, &QgsFilterLineEdit::updateBusySpinner );
181  }
182  else
183  {
184  mBusySpinnerAnimatedIcon->disconnectFrameChanged( this, &QgsFilterLineEdit::updateBusySpinner );
185  removeAction( mBusySpinnerAction );
186  mBusySpinnerAction = nullptr;
187  }
188 
189  mShowSpinner = showSpinner;
190  emit showSpinnerChanged();
191 }
192 
193 bool QgsFilterLineEdit::shouldShowClear() const
194 {
195  if ( !isEnabled() || isReadOnly() || !mClearButtonVisible )
196  return false;
197 
198  switch ( mClearMode )
199  {
200  case ClearToNull:
201  return !isNull();
202 
203  case ClearToDefault:
204  return value() != mDefaultValue;
205  }
206  return false; //avoid warnings
207 }
208 
209 bool QgsFilterLineEdit::event( QEvent *event )
210 {
211  if ( event->type() == QEvent::ReadOnlyChange || event->type() == QEvent::EnabledChange )
212  updateClearIcon();
213 
214  return QLineEdit::event( event );
215 }
216 
218 {
219  mLineEditState.text = text();
220  mLineEditState.selectionStart = selectionStart();
221  mLineEditState.selectionLength = selectedText().length();
222  mLineEditState.cursorPosition = cursorPosition();
223  mLineEditState.hasStateStored = true;
224 }
225 
227 {
228  setText( mLineEditState.text );
229  setCursorPosition( mLineEditState.cursorPosition );
230  if ( mLineEditState.selectionStart > -1 )
231  setSelection( mLineEditState.selectionStart, mLineEditState.selectionLength );
232  mLineEditState.hasStateStored = false;
233 }
234 
236 void QgsSpinBoxLineEdit::focusInEvent( QFocusEvent *e )
237 {
239  if ( isNull() )
240  {
241  clear();
242  }
243 }
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:416
Animated icon is keeping an animation running if there are listeners connected to frameChanged.
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.
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...
QIcon icon() const
Gets the icons representation in the current frame.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
void focusInEvent(QFocusEvent *e) override
void restoreState()
Restores the current state of the line edit (selection and cursor position)
void storeState()
Stores the current state of the line edit (selection and cursor position)
void selectOnFocusChanged()
Will select all text when this widget receives the focus.
void showSpinnerChanged()
Show a spinner icon.
virtual void clearValue()
Clears the widget and resets it to the null value.
@ ClearToNull
Reset value to null.
@ ClearToDefault
Reset value to default value (see defaultValue() )
bool isNull() const
Determine if the current text represents null.
bool selectOnFocus() const
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.
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
QgsFilterLineEdit(QWidget *parent=nullptr, const QString &nullValue=QString())
Constructor for QgsFilterLineEdit.
void setSelectOnFocus(bool selectOnFocus)
Will select all text when this widget receives the focus.
void mouseReleaseEvent(QMouseEvent *e) override
void cleared()
Emitted when the widget is cleared.
void setShowSpinner(bool showSpinner)
Show a spinner icon.
bool event(QEvent *event) override
Reimplemented to enable/disable the clear action depending on read-only status.
void valueChanged(const QString &value)
Same as textChanged() but with support for null values.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.