]> git.lyx.org Git - features.git/blob - src/frontends/qt/FancyLineEdit.cpp
FancyLineEdit: use QLineEdit's own clear button with Qt >= 5.2
[features.git] / src / frontends / qt / FancyLineEdit.cpp
1 /**
2  * \file fancylineedit.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Nokia Corporation (qt-info@nokia.com)
7  *
8  * Full author contact details are available in file CREDITS.
9  *
10  */
11
12 // Code taken from the Qt Creator project and customized a little
13
14 #include <config.h>
15
16 #include "FancyLineEdit.h"
17
18 #if QT_VERSION < 0x050200
19 #include "GuiApplication.h"
20 #endif
21
22 #if QT_VERSION >= 0x040600
23
24 #include <QEvent>
25 #include <QDebug>
26 #include <QString>
27 #include <QPropertyAnimation>
28 #include <QApplication>
29 #include <QMenu>
30 #include <QMouseEvent>
31 #include <QLabel>
32 #include <QAbstractButton>
33 #include <QPainter>
34 #include <QStyle>
35 #include <QPaintEvent>
36
37 enum { margin = 6 };
38
39 #define ICONBUTTON_HEIGHT 18
40 #define FADE_TIME 160
41
42
43 namespace lyx {
44 namespace frontend {
45
46 ////////////////////////////////////////////////////////////////////////
47 //
48 // FancyLineEditPrivate
49 //
50 ////////////////////////////////////////////////////////////////////////
51
52 class FancyLineEditPrivate : public QObject {
53 public:
54         explicit FancyLineEditPrivate(FancyLineEdit *parent);
55
56         bool eventFilter(QObject *obj, QEvent *event) override;
57
58         FancyLineEdit  *m_lineEdit;
59         QPixmap m_pixmap[2];
60         QMenu *m_menu[2];
61         bool m_menuTabFocusTrigger[2];
62         IconButton *m_iconbutton[2];
63         bool m_iconEnabled[2];
64 };
65
66
67 FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent)
68         : QObject(parent), m_lineEdit(parent)
69 {
70         for (int i = 0; i < 2; ++i) {
71                 m_menu[i] = nullptr;
72                 m_menuTabFocusTrigger[i] = false;
73                 m_iconbutton[i] = new IconButton(parent);
74                 m_iconbutton[i]->installEventFilter(this);
75                 m_iconbutton[i]->hide();
76                 m_iconbutton[i]->setAutoHide(false);
77                 m_iconEnabled[i] = false;
78         }
79 }
80
81
82 bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
83 {
84         int buttonIndex = -1;
85         for (int i = 0; i < 2; ++i) {
86                 if (obj == m_iconbutton[i]) {
87                         buttonIndex = i;
88                         break;
89                 }
90         }
91         if (buttonIndex == -1)
92                 return QObject::eventFilter(obj, event);
93         switch (event->type()) {
94         case QEvent::FocusIn:
95                 if (m_menuTabFocusTrigger[buttonIndex] && m_menu[buttonIndex]) {
96                         m_lineEdit->setFocus();
97                         m_menu[buttonIndex]->exec(m_iconbutton[buttonIndex]->mapToGlobal(
98                                                           m_iconbutton[buttonIndex]->rect().center()));
99                         return true;
100                 }
101         default:
102                 break;
103         }
104         return QObject::eventFilter(obj, event);
105 }
106
107
108 ////////////////////////////////////////////////////////////////////////
109 //
110 // FancyLineEdit
111 //
112 ////////////////////////////////////////////////////////////////////////
113
114 FancyLineEdit::FancyLineEdit(QWidget *parent) :
115     QLineEdit(parent),
116     m_d(new FancyLineEditPrivate(this))
117 {
118         ensurePolished();
119         updateMargins();
120         
121         connect(this, SIGNAL(textChanged(QString)),
122                 this, SLOT(checkButtons(QString)));
123         connect(m_d->m_iconbutton[Left], SIGNAL(clicked()),
124                 this, SLOT(iconClicked()));
125         connect(m_d->m_iconbutton[Right], SIGNAL(clicked()),
126                 this, SLOT(iconClicked()));
127 }
128
129
130 void FancyLineEdit::checkButtons(const QString &text)
131 {
132         if (m_oldText.isEmpty() || text.isEmpty()) {
133                 for (int i = 0; i < 2; ++i) {
134                         if (m_d->m_iconbutton[i]->hasAutoHide())
135                                 m_d->m_iconbutton[i]->animateShow(!text.isEmpty());
136                 }
137                 m_oldText = text;
138         }
139 }
140
141
142 void FancyLineEdit::setClearButton(bool visible)
143 {
144 // QLineEdit::setClearButtonEnabled() has been implemented in Qt 5.2.
145 // In earlier Qt versions, we roll our own button
146 #if QT_VERSION < 0x050200
147         setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
148         setButtonVisible(FancyLineEdit::Right, visible);
149         setAutoHideButton(FancyLineEdit::Right, true);
150 #else
151         setClearButtonEnabled(visible);
152 #endif
153 }
154
155
156 void FancyLineEdit::setButtonVisible(Side side, bool visible)
157 {
158         m_d->m_iconbutton[side]->setVisible(visible);
159         m_d->m_iconEnabled[side] = visible;
160         updateMargins();
161 }
162
163
164 bool FancyLineEdit::isButtonVisible(Side side) const
165 {
166         return m_d->m_iconEnabled[side];
167 }
168
169
170 void FancyLineEdit::iconClicked()
171 {
172         IconButton *button = qobject_cast<IconButton *>(sender());
173         int index = -1;
174         for (int i = 0; i < 2; ++i)
175                 if (m_d->m_iconbutton[i] == button)
176                         index = i;
177         if (index == -1)
178                 return;
179         if (m_d->m_menu[index]) {
180                 m_d->m_menu[index]->exec(QCursor::pos());
181         } else {
182                 buttonClicked((Side)index);
183                 if (index == Left)
184                         leftButtonClicked();
185                 else if (index == Right)
186                         rightButtonClicked();
187         }
188 }
189
190
191 void FancyLineEdit::updateMargins()
192 {
193         bool leftToRight = (layoutDirection() == Qt::LeftToRight);
194         Side realLeft = (leftToRight ? Left : Right);
195         Side realRight = (leftToRight ? Right : Left);
196
197         int leftMargin = m_d->m_iconbutton[realLeft]->pixmap().width() + 8;
198         int rightMargin = m_d->m_iconbutton[realRight]->pixmap().width() + 8;
199         // Note KDE does not reserve space for the highlight color
200         if (style()->inherits("OxygenStyle")) {
201                 leftMargin = qMax(24, leftMargin);
202                 rightMargin = qMax(24, rightMargin);
203         }
204
205         QMargins margins((m_d->m_iconEnabled[realLeft] ? leftMargin : 0), 0,
206                          (m_d->m_iconEnabled[realRight] ? rightMargin : 0), 0);
207
208         setTextMargins(margins);
209 }
210
211
212 void FancyLineEdit::updateButtonPositions()
213 {
214         QRect contentRect = rect();
215         for (int i = 0; i < 2; ++i) {
216                 Side iconpos = (Side)i;
217                 if (layoutDirection() == Qt::RightToLeft)
218                         iconpos = (iconpos == Left ? Right : Left);
219                 
220                 if (iconpos == FancyLineEdit::Right) {
221                         const int iconoffset = textMargins().right() + 4;
222                         m_d->m_iconbutton[i]->setGeometry(
223                                                 contentRect.adjusted(width() - iconoffset,
224                                                                      0, 0, 0));
225                 } else {
226                         const int iconoffset = textMargins().left() + 4;
227                         m_d->m_iconbutton[i]->setGeometry(
228                                                 contentRect.adjusted(0, 0,
229                                                                      -width() + iconoffset, 0));
230                 }
231         }
232 }
233
234
235 void FancyLineEdit::resizeEvent(QResizeEvent *)
236 {
237         updateButtonPositions();
238 }
239
240
241 void FancyLineEdit::keyPressEvent(QKeyEvent * e)
242 {
243         if (e->type() == QEvent::KeyPress && e->key() == Qt::Key_Down)
244                 Q_EMIT downPressed();
245         else
246                 QLineEdit::keyPressEvent(e);
247 }
248
249
250 void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap)
251 {
252         m_d->m_iconbutton[side]->setPixmap(buttonPixmap);
253         updateMargins();
254         updateButtonPositions();
255         update();
256 }
257
258
259 QPixmap FancyLineEdit::buttonPixmap(Side side) const
260 {
261         return m_d->m_pixmap[side];
262 }
263
264
265 void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu)
266 {
267         m_d->m_menu[side] = buttonMenu;
268         m_d->m_iconbutton[side]->setIconOpacity(1.0);
269 }
270
271 QMenu *FancyLineEdit::buttonMenu(Side side) const
272 {
273         return  m_d->m_menu[side];
274 }
275
276
277 bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const
278 {
279         return m_d->m_menuTabFocusTrigger[side];
280 }
281
282
283 void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v)
284 {
285         if (m_d->m_menuTabFocusTrigger[side] == v)
286                 return;
287
288         m_d->m_menuTabFocusTrigger[side] = v;
289         m_d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);
290 }
291
292
293 bool FancyLineEdit::hasAutoHideButton(Side side) const
294 {
295         return m_d->m_iconbutton[side]->hasAutoHide();
296 }
297
298
299 void FancyLineEdit::setAutoHideButton(Side side, bool h)
300 {
301         m_d->m_iconbutton[side]->setAutoHide(h);
302         if (h)
303                 m_d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ?  0.0 : 1.0);
304         else
305                 m_d->m_iconbutton[side]->setIconOpacity(1.0);
306 }
307
308
309 void FancyLineEdit::setButtonToolTip(Side side, const QString &tip)
310 {
311         m_d->m_iconbutton[side]->setToolTip(tip);
312 }
313
314
315 void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy)
316 {
317         m_d->m_iconbutton[side]->setFocusPolicy(policy);
318 }
319
320
321 ////////////////////////////////////////////////////////////////////////
322 //
323 // IconButton - helper class to represent a clickable icon
324 //
325 ////////////////////////////////////////////////////////////////////////
326
327 IconButton::IconButton(QWidget *parent)
328         : QAbstractButton(parent), m_iconOpacity(0.0), m_autoHide(false)
329 {
330         setCursor(Qt::ArrowCursor);
331         setFocusPolicy(Qt::NoFocus);
332 }
333
334
335 void IconButton::paintEvent(QPaintEvent *)
336 {
337         QPainter painter(this);
338         QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
339         pixmapRect.moveCenter(rect().center());
340
341         if (m_autoHide)
342                 painter.setOpacity(m_iconOpacity);
343
344         painter.drawPixmap(pixmapRect, m_pixmap);
345 }
346
347
348 void IconButton::animateShow(bool visible)
349 {
350         if (visible) {
351                 QPropertyAnimation *animation =
352                         new QPropertyAnimation(this, "iconOpacity");
353                 animation->setDuration(FADE_TIME);
354                 animation->setEndValue(1.0);
355                 animation->start(QAbstractAnimation::DeleteWhenStopped);
356         } else {
357                 QPropertyAnimation *animation =
358                         new QPropertyAnimation(this, "iconOpacity");
359                 animation->setDuration(FADE_TIME);
360                 animation->setEndValue(0.0);
361                 animation->start(QAbstractAnimation::DeleteWhenStopped);
362         }
363 }
364
365 } // namespace frontend
366
367 } // namespace lyx
368
369 #endif // QT_VERSION >= 0x040600
370
371 #include "moc_FancyLineEdit.cpp"