1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
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.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "fancylineedit.h"
36 #include <QtCore/QEvent>
37 #include <QtCore/QDebug>
38 #include <QtCore/QString>
39 #include <QtCore/QPropertyAnimation>
40 #include <QtGui/QApplication>
41 #include <QtGui/QMenu>
42 #include <QtGui/QMouseEvent>
43 #include <QtGui/QLabel>
44 #include <QtGui/QAbstractButton>
45 #include <QtGui/QPainter>
46 #include <QtGui/QStyle>
47 #include <QtGui/QPaintEvent>
50 \class Utils::FancyLineEdit
52 \brief A line edit with an embedded pixmap on one side that is connected to
55 Additionally, it can display a grayed hintText (like "Type Here to")
56 when not focused and empty. When connecting to the changed signals and
57 querying text, one has to be aware that the text is set to that hint
58 text if isShowingHintText() returns true (that is, does not contain
64 #define ICONBUTTON_HEIGHT 18
69 // --------- FancyLineEditPrivate
70 class FancyLineEditPrivate : public QObject {
72 explicit FancyLineEditPrivate(FancyLineEdit *parent);
74 virtual bool eventFilter(QObject *obj, QEvent *event);
76 FancyLineEdit *m_lineEdit;
79 bool m_menuTabFocusTrigger[2];
80 IconButton *m_iconbutton[2];
81 bool m_iconEnabled[2];
85 FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
89 for (int i = 0; i < 2; ++i) {
91 m_menuTabFocusTrigger[i] = false;
92 m_iconbutton[i] = new IconButton(parent);
93 m_iconbutton[i]->installEventFilter(this);
94 m_iconbutton[i]->hide();
95 m_iconbutton[i]->setAutoHide(false);
96 m_iconEnabled[i] = false;
100 bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
102 int buttonIndex = -1;
103 for (int i = 0; i < 2; ++i) {
104 if (obj == m_iconbutton[i]) {
109 if (buttonIndex == -1)
110 return QObject::eventFilter(obj, event);
111 switch (event->type()) {
112 case QEvent::FocusIn:
113 if (m_menuTabFocusTrigger[buttonIndex] && m_menu[buttonIndex]) {
114 m_lineEdit->setFocus();
115 m_menu[buttonIndex]->exec(m_iconbutton[buttonIndex]->mapToGlobal(
116 m_iconbutton[buttonIndex]->rect().center()));
122 return QObject::eventFilter(obj, event);
126 // --------- FancyLineEdit
127 FancyLineEdit::FancyLineEdit(QWidget *parent) :
129 m_d(new FancyLineEditPrivate(this))
134 connect(this, SIGNAL(textChanged(QString)), this, SLOT(checkButtons(QString)));
135 connect(m_d->m_iconbutton[Left], SIGNAL(clicked()), this, SLOT(iconClicked()));
136 connect(m_d->m_iconbutton[Right], SIGNAL(clicked()), this, SLOT(iconClicked()));
139 void FancyLineEdit::checkButtons(const QString &text)
141 if (m_oldText.isEmpty() || text.isEmpty()) {
142 for (int i = 0; i < 2; ++i) {
143 if (m_d->m_iconbutton[i]->hasAutoHide())
144 m_d->m_iconbutton[i]->animateShow(!text.isEmpty());
150 FancyLineEdit::~FancyLineEdit()
154 void FancyLineEdit::setButtonVisible(Side side, bool visible)
156 m_d->m_iconbutton[side]->setVisible(visible);
157 m_d->m_iconEnabled[side] = visible;
161 bool FancyLineEdit::isButtonVisible(Side side) const
163 return m_d->m_iconEnabled[side];
166 void FancyLineEdit::iconClicked()
168 IconButton *button = qobject_cast<IconButton *>(sender());
170 for (int i = 0; i < 2; ++i)
171 if (m_d->m_iconbutton[i] == button)
175 if (m_d->m_menu[index]) {
176 m_d->m_menu[index]->exec(QCursor::pos());
178 emit buttonClicked((Side)index);
180 emit leftButtonClicked();
181 else if (index == Right)
182 emit rightButtonClicked();
186 void FancyLineEdit::updateMargins()
188 bool leftToRight = (layoutDirection() == Qt::LeftToRight);
189 Side realLeft = (leftToRight ? Left : Right);
190 Side realRight = (leftToRight ? Right : Left);
192 int leftMargin = m_d->m_iconbutton[realLeft]->pixmap().width() + 8;
193 int rightMargin = m_d->m_iconbutton[realRight]->pixmap().width() + 8;
194 // Note KDE does not reserve space for the highlight color
195 if (style()->inherits("OxygenStyle")) {
196 leftMargin = qMax(24, leftMargin);
197 rightMargin = qMax(24, rightMargin);
200 QMargins margins((m_d->m_iconEnabled[realLeft] ? leftMargin : 0), 0,
201 (m_d->m_iconEnabled[realRight] ? rightMargin : 0), 0);
203 setTextMargins(margins);
206 void FancyLineEdit::updateButtonPositions()
208 QRect contentRect = rect();
209 for (int i = 0; i < 2; ++i) {
210 Side iconpos = (Side)i;
211 if (layoutDirection() == Qt::RightToLeft)
212 iconpos = (iconpos == Left ? Right : Left);
214 if (iconpos == FancyLineEdit::Right) {
215 const int iconoffset = textMargins().right() + 4;
216 m_d->m_iconbutton[i]->setGeometry(contentRect.adjusted(width() - iconoffset, 0, 0, 0));
218 const int iconoffset = textMargins().left() + 4;
219 m_d->m_iconbutton[i]->setGeometry(contentRect.adjusted(0, 0, -width() + iconoffset, 0));
224 void FancyLineEdit::resizeEvent(QResizeEvent *)
226 updateButtonPositions();
229 void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap)
231 m_d->m_iconbutton[side]->setPixmap(buttonPixmap);
233 updateButtonPositions();
237 QPixmap FancyLineEdit::buttonPixmap(Side side) const
239 return m_d->m_pixmap[side];
242 void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu)
244 m_d->m_menu[side] = buttonMenu;
245 m_d->m_iconbutton[side]->setIconOpacity(1.0);
248 QMenu *FancyLineEdit::buttonMenu(Side side) const
250 return m_d->m_menu[side];
253 bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const
255 return m_d->m_menuTabFocusTrigger[side];
258 void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v)
260 if (m_d->m_menuTabFocusTrigger[side] == v)
263 m_d->m_menuTabFocusTrigger[side] = v;
264 m_d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);
267 bool FancyLineEdit::hasAutoHideButton(Side side) const
269 return m_d->m_iconbutton[side]->hasAutoHide();
272 void FancyLineEdit::setAutoHideButton(Side side, bool h)
274 m_d->m_iconbutton[side]->setAutoHide(h);
276 m_d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ? 0.0 : 1.0);
278 m_d->m_iconbutton[side]->setIconOpacity(1.0);
281 void FancyLineEdit::setButtonToolTip(Side side, const QString &tip)
283 m_d->m_iconbutton[side]->setToolTip(tip);
286 void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy)
288 m_d->m_iconbutton[side]->setFocusPolicy(policy);
291 // IconButton - helper class to represent a clickable icon
293 IconButton::IconButton(QWidget *parent)
294 : QAbstractButton(parent), m_autoHide(false)
296 setCursor(Qt::ArrowCursor);
297 setFocusPolicy(Qt::NoFocus);
300 void IconButton::paintEvent(QPaintEvent *)
302 QPainter painter(this);
303 // Note isDown should really use the active state but in most styles
304 // this has no proper feedback
305 QIcon::Mode state = QIcon::Disabled;
307 state = isDown() ? QIcon::Selected : QIcon::Normal;
308 QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
309 pixmapRect.moveCenter(rect().center());
312 painter.setOpacity(m_iconOpacity);
314 painter.drawPixmap(pixmapRect, m_pixmap);
317 void IconButton::animateShow(bool visible)
320 QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
321 animation->setDuration(FADE_TIME);
322 animation->setEndValue(1.0);
323 animation->start(QAbstractAnimation::DeleteWhenStopped);
325 QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
326 animation->setDuration(FADE_TIME);
327 animation->setEndValue(0.0);
328 animation->start(QAbstractAnimation::DeleteWhenStopped);