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