OSDN Git Service

47e6eb033f1b65dfa89b78b6a250a84463b75fcb
[qt-creator-jp/qt-creator-jp.git] / src / libs / utils / fancylineedit.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
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 ** 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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "fancylineedit.h"
35
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>
48
49 /*!
50     \class Utils::FancyLineEdit
51
52     \brief A line edit with an embedded pixmap on one side that is connected to
53     a menu.
54
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
59     valid user input).
60  */
61
62 enum { margin = 6 };
63
64 #define ICONBUTTON_HEIGHT 18
65 #define FADE_TIME 160
66
67 namespace Utils {
68
69 // --------- FancyLineEditPrivate
70 class FancyLineEditPrivate : public QObject {
71 public:
72     explicit FancyLineEditPrivate(FancyLineEdit *parent);
73
74     virtual bool eventFilter(QObject *obj, QEvent *event);
75
76     FancyLineEdit  *m_lineEdit;
77     QPixmap m_pixmap[2];
78     QMenu *m_menu[2];
79     bool m_menuTabFocusTrigger[2];
80     IconButton *m_iconbutton[2];
81     bool m_iconEnabled[2];
82 };
83
84
85 FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
86     QObject(parent),
87     m_lineEdit(parent)
88 {
89     for (int i = 0; i < 2; ++i) {
90         m_menu[i] = 0;
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;
97     }
98 }
99
100 bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
101 {
102     int buttonIndex = -1;
103     for (int i = 0; i < 2; ++i) {
104         if (obj == m_iconbutton[i]) {
105             buttonIndex = i;
106             break;
107         }
108     }
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()));
117             return true;
118         }
119     default:
120         break;
121     }
122     return QObject::eventFilter(obj, event);
123 }
124
125
126 // --------- FancyLineEdit
127 FancyLineEdit::FancyLineEdit(QWidget *parent) :
128     QLineEdit(parent),
129     m_d(new FancyLineEditPrivate(this))
130 {
131     ensurePolished();
132     updateMargins();
133
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()));
137 }
138
139 void FancyLineEdit::checkButtons(const QString &text)
140 {
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());
145         }
146         m_oldText = text;
147     }
148 }
149
150 FancyLineEdit::~FancyLineEdit()
151 {
152 }
153
154 void FancyLineEdit::setButtonVisible(Side side, bool visible)
155 {
156     m_d->m_iconbutton[side]->setVisible(visible);
157     m_d->m_iconEnabled[side] = visible;
158     updateMargins();
159 }
160
161 bool FancyLineEdit::isButtonVisible(Side side) const
162 {
163     return m_d->m_iconEnabled[side];
164 }
165
166 void FancyLineEdit::iconClicked()
167 {
168     IconButton *button = qobject_cast<IconButton *>(sender());
169     int index = -1;
170     for (int i = 0; i < 2; ++i)
171         if (m_d->m_iconbutton[i] == button)
172             index = i;
173     if (index == -1)
174         return;
175     if (m_d->m_menu[index]) {
176         m_d->m_menu[index]->exec(QCursor::pos());
177     } else {
178         emit buttonClicked((Side)index);
179         if (index == Left)
180             emit leftButtonClicked();
181         else if (index == Right)
182             emit rightButtonClicked();
183     }
184 }
185
186 void FancyLineEdit::updateMargins()
187 {
188     bool leftToRight = (layoutDirection() == Qt::LeftToRight);
189     Side realLeft = (leftToRight ? Left : Right);
190     Side realRight = (leftToRight ? Right : Left);
191
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);
198     }
199
200     QMargins margins((m_d->m_iconEnabled[realLeft] ? leftMargin : 0), 0,
201                      (m_d->m_iconEnabled[realRight] ? rightMargin : 0), 0);
202
203     setTextMargins(margins);
204 }
205
206 void FancyLineEdit::updateButtonPositions()
207 {
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);
213
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));
217         } else {
218             const int iconoffset = textMargins().left() + 4;
219             m_d->m_iconbutton[i]->setGeometry(contentRect.adjusted(0, 0, -width() + iconoffset, 0));
220         }
221     }
222 }
223
224 void FancyLineEdit::resizeEvent(QResizeEvent *)
225 {
226     updateButtonPositions();
227 }
228
229 void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap)
230 {
231     m_d->m_iconbutton[side]->setPixmap(buttonPixmap);
232     updateMargins();
233     updateButtonPositions();
234     update();
235 }
236
237 QPixmap FancyLineEdit::buttonPixmap(Side side) const
238 {
239     return m_d->m_pixmap[side];
240 }
241
242 void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu)
243 {
244      m_d->m_menu[side] = buttonMenu;
245      m_d->m_iconbutton[side]->setIconOpacity(1.0);
246  }
247
248 QMenu *FancyLineEdit::buttonMenu(Side side) const
249 {
250     return  m_d->m_menu[side];
251 }
252
253 bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const
254 {
255     return m_d->m_menuTabFocusTrigger[side];
256 }
257
258 void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v)
259 {
260     if (m_d->m_menuTabFocusTrigger[side] == v)
261         return;
262
263     m_d->m_menuTabFocusTrigger[side] = v;
264     m_d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);
265 }
266
267 bool FancyLineEdit::hasAutoHideButton(Side side) const
268 {
269     return m_d->m_iconbutton[side]->hasAutoHide();
270 }
271
272 void FancyLineEdit::setAutoHideButton(Side side, bool h)
273 {
274     m_d->m_iconbutton[side]->setAutoHide(h);
275     if (h)
276         m_d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ?  0.0 : 1.0);
277     else
278         m_d->m_iconbutton[side]->setIconOpacity(1.0);
279 }
280
281 void FancyLineEdit::setButtonToolTip(Side side, const QString &tip)
282 {
283     m_d->m_iconbutton[side]->setToolTip(tip);
284 }
285
286 void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy)
287 {
288     m_d->m_iconbutton[side]->setFocusPolicy(policy);
289 }
290
291 // IconButton - helper class to represent a clickable icon
292
293 IconButton::IconButton(QWidget *parent)
294     : QAbstractButton(parent), m_autoHide(false)
295 {
296     setCursor(Qt::ArrowCursor);
297     setFocusPolicy(Qt::NoFocus);
298 }
299
300 void IconButton::paintEvent(QPaintEvent *)
301 {
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;
306     if (isEnabled())
307         state = isDown() ? QIcon::Selected : QIcon::Normal;
308     QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
309     pixmapRect.moveCenter(rect().center());
310
311     if (m_autoHide)
312         painter.setOpacity(m_iconOpacity);
313
314     painter.drawPixmap(pixmapRect, m_pixmap);
315 }
316
317 void IconButton::animateShow(bool visible)
318 {
319     if (visible) {
320         QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
321         animation->setDuration(FADE_TIME);
322         animation->setEndValue(1.0);
323         animation->start(QAbstractAnimation::DeleteWhenStopped);
324     } else {
325         QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
326         animation->setDuration(FADE_TIME);
327         animation->setEndValue(0.0);
328         animation->start(QAbstractAnimation::DeleteWhenStopped);
329     }
330 }
331
332 } // namespace Utils