1 /*
2 Copyright (C) 2011 by Mike McQuaid
3 Copyright (C) 2018-2021 by Jonas Kvinge <jonas@jkvinge.net>
4 
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 THE SOFTWARE.
22 */
23 
24 #include "qsearchfield.h"
25 
26 #include <QtGlobal>
27 #include <QObject>
28 #include <QWidget>
29 #include <QApplication>
30 #include <QString>
31 #include <QIcon>
32 #include <QPointer>
33 #include <QLineEdit>
34 #include <QToolButton>
35 #include <QStyle>
36 #include <QSize>
37 #include <QBoxLayout>
38 #include <QEvent>
39 #include <QResizeEvent>
40 
41 #include "core/iconloader.h"
42 
43 class QSearchFieldPrivate : public QObject {  // clazy:exclude=missing-qobject-macro
44 
45  public:
QSearchFieldPrivate(QSearchField * searchField,QLineEdit * lineedit,QToolButton * clearbutton)46   QSearchFieldPrivate(QSearchField *searchField, QLineEdit *lineedit, QToolButton *clearbutton)
47       : QObject(searchField), lineedit_(lineedit), clearbutton_(clearbutton) {}
48 
lineEditFrameWidth() const49   int lineEditFrameWidth() const {
50     return lineedit_->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
51   }
52 
clearButtonPaddedWidth() const53   int clearButtonPaddedWidth() const {
54     return clearbutton_->width() + lineEditFrameWidth() * 2;
55   }
56 
clearButtonPaddedHeight() const57   int clearButtonPaddedHeight() const {
58     return clearbutton_->height() + lineEditFrameWidth() * 2;
59   }
60 
61   QPointer<QLineEdit> lineedit_;
62   QPointer<QToolButton> clearbutton_;
63 
64 };
65 
QSearchField(QWidget * parent)66 QSearchField::QSearchField(QWidget *parent) : QWidget(parent) {
67 
68   QLineEdit *lineEdit = new QLineEdit(this);
69   QObject::connect(lineEdit, &QLineEdit::textChanged, this, &QSearchField::textChanged);
70   QObject::connect(lineEdit, &QLineEdit::editingFinished, this, &QSearchField::editingFinished);
71   QObject::connect(lineEdit, &QLineEdit::returnPressed, this, &QSearchField::returnPressed);
72   QObject::connect(lineEdit, &QLineEdit::textChanged, this, &QSearchField::setText);
73 
74   QToolButton *clearbutton = new QToolButton(this);
75   QIcon clearIcon(IconLoader::Load("edit-clear-locationbar-ltr"));
76 
77   clearbutton->setIcon(clearIcon);
78   clearbutton->setIconSize(QSize(20, 20));
79   clearbutton->setStyleSheet("border: none; padding: 0px;");
80   clearbutton->resize(clearbutton->sizeHint());
81 
82   QObject::connect(clearbutton, &QToolButton::clicked, this, &QSearchField::clear);
83 
84   pimpl = new QSearchFieldPrivate(this, lineEdit, clearbutton);
85 
86   const int frame_width = lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
87 
88   lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(clearbutton->width()));
89   const int width = frame_width + qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
90   const int height = frame_width + qMax(lineEdit->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
91   lineEdit->setMinimumSize(width, height);
92 
93   QVBoxLayout *layout = new QVBoxLayout(this);
94   layout->setContentsMargins(0, 0, 0, 0);
95   layout->addWidget(lineEdit);
96 
97   lineEdit->installEventFilter(this);
98 
99 }
100 
setIconSize(const int iconsize)101 void QSearchField::setIconSize(const int iconsize) {
102 
103   pimpl->clearbutton_->setIconSize(QSize(iconsize, iconsize));
104   pimpl->clearbutton_->resize(pimpl->clearbutton_->sizeHint());
105 
106   pimpl->lineedit_->setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(pimpl->clearbutton_->width()));
107   const int frame_width = pimpl->lineedit_->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
108   const int width = frame_width + qMax(pimpl->lineedit_->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
109   const int height = frame_width + qMax(pimpl->lineedit_->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
110   pimpl->lineedit_->setMinimumSize(width, height);
111 
112 }
113 
setText(const QString & new_text)114 void QSearchField::setText(const QString &new_text) {
115 
116   Q_ASSERT(pimpl && pimpl->clearbutton_ && pimpl->lineedit_);
117   if (!(pimpl && pimpl->clearbutton_ && pimpl->lineedit_)) return;
118   if (new_text != text()) pimpl->lineedit_->setText(new_text);
119 
120 }
121 
setPlaceholderText(const QString & text)122 void QSearchField::setPlaceholderText(const QString &text) {
123 
124   Q_ASSERT(pimpl && pimpl->lineedit_);
125   if (!(pimpl && pimpl->lineedit_)) return;
126   pimpl->lineedit_->setPlaceholderText(text);
127 
128 }
129 
placeholderText() const130 QString QSearchField::placeholderText() const {
131   return pimpl->lineedit_->placeholderText();
132 }
133 
hasFocus() const134 bool QSearchField::hasFocus() const {
135   Q_ASSERT(pimpl && pimpl->lineedit_);
136   return pimpl && pimpl->lineedit_ && pimpl->lineedit_->hasFocus();
137 }
138 
setFocus(Qt::FocusReason reason)139 void QSearchField::setFocus(Qt::FocusReason reason) {
140   Q_ASSERT(pimpl && pimpl->lineedit_);
141   if (pimpl && pimpl->lineedit_) pimpl->lineedit_->setFocus(reason);
142 }
143 
setFocus()144 void QSearchField::setFocus() {
145   setFocus(Qt::OtherFocusReason);
146 }
147 
clear()148 void QSearchField::clear() {
149 
150   Q_ASSERT(pimpl && pimpl->lineedit_);
151 
152   if (!(pimpl && pimpl->lineedit_)) return;
153   pimpl->lineedit_->clear();
154 
155 }
156 
selectAll()157 void QSearchField::selectAll() {
158 
159   Q_ASSERT(pimpl && pimpl->lineedit_);
160 
161   if (!(pimpl && pimpl->lineedit_)) return;
162   pimpl->lineedit_->selectAll();
163 
164 }
165 
text() const166 QString QSearchField::text() const {
167 
168   Q_ASSERT(pimpl && pimpl->lineedit_);
169 
170   if (!(pimpl && pimpl->lineedit_)) return QString();
171   return pimpl->lineedit_->text();
172 
173 }
174 
showEvent(QShowEvent * e)175 void QSearchField::showEvent(QShowEvent *e) {
176 
177   QWidget::showEvent(e);
178 
179 }
180 
resizeEvent(QResizeEvent * resizeEvent)181 void QSearchField::resizeEvent(QResizeEvent *resizeEvent) {
182 
183   Q_ASSERT(pimpl && pimpl->clearbutton_ && pimpl->lineedit_);
184   if (!(pimpl && pimpl->clearbutton_ && pimpl->lineedit_)) return;
185 
186   QWidget::resizeEvent(resizeEvent);
187   const int x = pimpl->lineEditFrameWidth();
188   const int y = (height() - pimpl->clearbutton_->height()) / 2;
189   pimpl->clearbutton_->move(x, y);
190 
191 }
192 
eventFilter(QObject * o,QEvent * e)193 bool QSearchField::eventFilter(QObject *o, QEvent *e) {
194 
195   if (pimpl && pimpl->lineedit_ && o == pimpl->lineedit_) {
196     // Forward some lineEdit events to QSearchField (only those we need for
197     // now, but some might be added later if needed)
198     switch (e->type()) {
199       case QEvent::FocusIn:
200       case QEvent::FocusOut:
201         QApplication::sendEvent(this, e);
202         break;
203       default:
204         break;
205     }
206   }
207   return QWidget::eventFilter(o, e);
208 
209 }
210