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