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