1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "fancylineedit.h"
31
32 #include <QtCore/QEvent>
33 #include <QtCore/QDebug>
34 #include <QtCore/QString>
35 #include <QtCore/QPropertyAnimation>
36 #include <QApplication>
37 #include <QMenu>
38 #include <QMouseEvent>
39 #include <QLabel>
40 #include <QAbstractButton>
41 #ifndef QT_NO_PRINTER
42 #include <QPainter>
43 #endif
44 #include <QStyle>
45 #include <QPaintEvent>
46
47 enum { margin = 6 };
48
49 #define ICONBUTTON_HEIGHT 18
50 #define FADE_TIME 160
51
52 namespace Utils {
53
54 // --------- FancyLineEditPrivate
55 class FancyLineEditPrivate : public QObject {
56 public:
57 explicit FancyLineEditPrivate(FancyLineEdit *parent);
58
59 virtual bool eventFilter(QObject *obj, QEvent *event);
60
61 FancyLineEdit *m_lineEdit;
62 QPixmap m_pixmap[2];
63 QMenu *m_menu[2];
64 bool m_menuTabFocusTrigger[2];
65 IconButton *m_iconbutton[2];
66 bool m_iconEnabled[2];
67 };
68
69
FancyLineEditPrivate(FancyLineEdit * parent)70 FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
71 QObject(parent),
72 m_lineEdit(parent)
73 {
74 for (int i = 0; i < 2; ++i) {
75 m_menu[i] = 0;
76 m_menuTabFocusTrigger[i] = false;
77 m_iconbutton[i] = new IconButton(parent);
78 m_iconbutton[i]->installEventFilter(this);
79 m_iconbutton[i]->hide();
80 m_iconbutton[i]->setAutoHide(false);
81 m_iconEnabled[i] = false;
82 }
83 }
84
eventFilter(QObject * obj,QEvent * event)85 bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
86 {
87 int buttonIndex = -1;
88 for (int i = 0; i < 2; ++i) {
89 if (obj == m_iconbutton[i]) {
90 buttonIndex = i;
91 break;
92 }
93 }
94 if (buttonIndex == -1)
95 return QObject::eventFilter(obj, event);
96 switch (event->type()) {
97 case QEvent::FocusIn:
98 if (m_menuTabFocusTrigger[buttonIndex] && m_menu[buttonIndex]) {
99 m_lineEdit->setFocus();
100 m_menu[buttonIndex]->exec(m_iconbutton[buttonIndex]->mapToGlobal(
101 m_iconbutton[buttonIndex]->rect().center()));
102 return true;
103 }
104 default:
105 break;
106 }
107 return QObject::eventFilter(obj, event);
108 }
109
110
111 // --------- FancyLineEdit
FancyLineEdit(QWidget * parent)112 FancyLineEdit::FancyLineEdit(QWidget *parent) :
113 QLineEdit(parent),
114 m_d(new FancyLineEditPrivate(this))
115 {
116 ensurePolished();
117 updateMargins();
118
119 connect(this, SIGNAL(textChanged(QString)), this, SLOT(checkButtons(QString)));
120 connect(m_d->m_iconbutton[Left], SIGNAL(clicked()), this, SLOT(iconClicked()));
121 connect(m_d->m_iconbutton[Right], SIGNAL(clicked()), this, SLOT(iconClicked()));
122 }
123
checkButtons(const QString & text)124 void FancyLineEdit::checkButtons(const QString &text)
125 {
126 if (m_oldText.isEmpty() || text.isEmpty()) {
127 for (int i = 0; i < 2; ++i) {
128 if (m_d->m_iconbutton[i]->hasAutoHide())
129 m_d->m_iconbutton[i]->animateShow(!text.isEmpty());
130 }
131 m_oldText = text;
132 }
133 }
134
~FancyLineEdit()135 FancyLineEdit::~FancyLineEdit()
136 {
137 }
138
setButtonVisible(Side side,bool visible)139 void FancyLineEdit::setButtonVisible(Side side, bool visible)
140 {
141 m_d->m_iconbutton[side]->setVisible(visible);
142 m_d->m_iconEnabled[side] = visible;
143 updateMargins();
144 updateButtonPositions();
145 }
146
isButtonVisible(Side side) const147 bool FancyLineEdit::isButtonVisible(Side side) const
148 {
149 return m_d->m_iconEnabled[side];
150 }
151
iconClicked()152 void FancyLineEdit::iconClicked()
153 {
154 IconButton *button = qobject_cast<IconButton *>(sender());
155 int index = -1;
156 for (int i = 0; i < 2; ++i)
157 if (m_d->m_iconbutton[i] == button)
158 index = i;
159 if (index == -1)
160 return;
161 if (m_d->m_menu[index]) {
162 m_d->m_menu[index]->exec(QCursor::pos());
163 } else {
164 emit buttonClicked((Side)index);
165 if (index == Left)
166 emit leftButtonClicked();
167 else if (index == Right)
168 emit rightButtonClicked();
169 }
170 }
171
updateMargins()172 void FancyLineEdit::updateMargins()
173 {
174 bool leftToRight = (layoutDirection() == Qt::LeftToRight);
175 Side realLeft = (leftToRight ? Left : Right);
176 Side realRight = (leftToRight ? Right : Left);
177
178 int leftMargin = m_d->m_iconbutton[realLeft]->pixmap().width() + 8;
179 int rightMargin = m_d->m_iconbutton[realRight]->pixmap().width() + 8;
180 // Note KDE does not reserve space for the highlight color
181 if (style()->inherits("OxygenStyle")) {
182 leftMargin = qMax(24, leftMargin);
183 rightMargin = qMax(24, rightMargin);
184 }
185
186 QMargins margins((m_d->m_iconEnabled[realLeft] ? leftMargin : 0), 0,
187 (m_d->m_iconEnabled[realRight] ? rightMargin : 0), 0);
188
189 setTextMargins(margins);
190 }
191
updateButtonPositions()192 void FancyLineEdit::updateButtonPositions()
193 {
194 QRect contentRect = rect();
195 for (int i = 0; i < 2; ++i) {
196 Side iconpos = (Side)i;
197 if (layoutDirection() == Qt::RightToLeft)
198 iconpos = (iconpos == Left ? Right : Left);
199
200 if (iconpos == FancyLineEdit::Right) {
201 const int iconoffset = textMargins().right() + 4;
202 m_d->m_iconbutton[i]->setGeometry(contentRect.adjusted(width() - iconoffset, 0, 0, 0));
203 } else {
204 const int iconoffset = textMargins().left() + 4;
205 m_d->m_iconbutton[i]->setGeometry(contentRect.adjusted(0, 0, -width() + iconoffset, 0));
206 }
207 }
208 }
209
resizeEvent(QResizeEvent *)210 void FancyLineEdit::resizeEvent(QResizeEvent *)
211 {
212 updateButtonPositions();
213 }
214
setButtonPixmap(Side side,const QPixmap & buttonPixmap)215 void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap)
216 {
217 m_d->m_iconbutton[side]->setPixmap(buttonPixmap);
218 updateMargins();
219 updateButtonPositions();
220 update();
221 }
222
buttonPixmap(Side side) const223 QPixmap FancyLineEdit::buttonPixmap(Side side) const
224 {
225 return m_d->m_pixmap[side];
226 }
227
setButtonMenu(Side side,QMenu * buttonMenu)228 void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu)
229 {
230 m_d->m_menu[side] = buttonMenu;
231 m_d->m_iconbutton[side]->setIconOpacity(1.0);
232 }
233
buttonMenu(Side side) const234 QMenu *FancyLineEdit::buttonMenu(Side side) const
235 {
236 return m_d->m_menu[side];
237 }
238
hasMenuTabFocusTrigger(Side side) const239 bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const
240 {
241 return m_d->m_menuTabFocusTrigger[side];
242 }
243
setMenuTabFocusTrigger(Side side,bool v)244 void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v)
245 {
246 if (m_d->m_menuTabFocusTrigger[side] == v)
247 return;
248
249 m_d->m_menuTabFocusTrigger[side] = v;
250 m_d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);
251 }
252
hasAutoHideButton(Side side) const253 bool FancyLineEdit::hasAutoHideButton(Side side) const
254 {
255 return m_d->m_iconbutton[side]->hasAutoHide();
256 }
257
setAutoHideButton(Side side,bool h)258 void FancyLineEdit::setAutoHideButton(Side side, bool h)
259 {
260 m_d->m_iconbutton[side]->setAutoHide(h);
261 if (h)
262 m_d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ? 0.0 : 1.0);
263 else
264 m_d->m_iconbutton[side]->setIconOpacity(1.0);
265 }
266
setButtonToolTip(Side side,const QString & tip)267 void FancyLineEdit::setButtonToolTip(Side side, const QString &tip)
268 {
269 m_d->m_iconbutton[side]->setToolTip(tip);
270 }
271
setButtonFocusPolicy(Side side,Qt::FocusPolicy policy)272 void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy)
273 {
274 m_d->m_iconbutton[side]->setFocusPolicy(policy);
275 }
276
277 // IconButton - helper class to represent a clickable icon
278
IconButton(QWidget * parent)279 IconButton::IconButton(QWidget *parent)
280 : QAbstractButton(parent), m_autoHide(false)
281 {
282 setCursor(Qt::ArrowCursor);
283 setFocusPolicy(Qt::NoFocus);
284 }
285
paintEvent(QPaintEvent *)286 void IconButton::paintEvent(QPaintEvent *)
287 {
288 QPainter painter(this);
289 // Note isDown should really use the active state but in most styles
290 // this has no proper feedback
291 QIcon::Mode state = QIcon::Disabled;
292 if (isEnabled())
293 state = isDown() ? QIcon::Selected : QIcon::Normal;
294 QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
295 pixmapRect.moveCenter(rect().center());
296
297 if (m_autoHide)
298 painter.setOpacity(m_iconOpacity);
299
300 painter.drawPixmap(pixmapRect, m_pixmap);
301 }
302
animateShow(bool visible)303 void IconButton::animateShow(bool visible)
304 {
305 if (visible) {
306 QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
307 animation->setDuration(FADE_TIME);
308 animation->setEndValue(1.0);
309 animation->start(QAbstractAnimation::DeleteWhenStopped);
310 } else {
311 QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
312 animation->setDuration(FADE_TIME);
313 animation->setEndValue(0.0);
314 animation->start(QAbstractAnimation::DeleteWhenStopped);
315 }
316 }
317
318 } // namespace Utils
319