1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** 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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "filterwidget_p.h"
43 #include "iconloader_p.h"
44 
45 #include <QtGui/QVBoxLayout>
46 #include <QtGui/QHBoxLayout>
47 #include <QtGui/QLineEdit>
48 #include <QtGui/QFocusEvent>
49 #include <QtGui/QPalette>
50 #include <QtGui/QCursor>
51 #include <QtGui/QToolButton>
52 #include <QtGui/QPainter>
53 #include <QtGui/QStyle>
54 #include <QtGui/QStyleOption>
55 
56 #include <QtCore/QDebug>
57 #include <QtCore/QPropertyAnimation>
58 
59 enum { debugFilter = 0 };
60 
61 QT_BEGIN_NAMESPACE
62 
63 namespace qdesigner_internal {
64 
HintLineEdit(QWidget * parent)65 HintLineEdit::HintLineEdit(QWidget *parent) :
66     QLineEdit(parent),
67     m_defaultFocusPolicy(focusPolicy()),
68     m_refuseFocus(false)
69 {
70 }
71 
IconButton(QWidget * parent)72 IconButton::IconButton(QWidget *parent)
73     : QToolButton(parent)
74 {
75 #ifndef QT_NO_CURSOR
76     setCursor(Qt::ArrowCursor);
77 #endif
78 }
79 
paintEvent(QPaintEvent *)80 void IconButton::paintEvent(QPaintEvent *)
81 {
82     QPainter painter(this);
83     // Note isDown should really use the active state but in most styles
84     // this has no proper feedback
85     QIcon::Mode state = QIcon::Disabled;
86     if (isEnabled())
87         state = isDown() ? QIcon::Selected : QIcon::Normal;
88     QPixmap iconpixmap = icon().pixmap(QSize(ICONBUTTON_SIZE, ICONBUTTON_SIZE),
89                                        state, QIcon::Off);
90     QRect pixmapRect = QRect(0, 0, iconpixmap.width(), iconpixmap.height());
91     pixmapRect.moveCenter(rect().center());
92     painter.setOpacity(m_fader);
93     painter.drawPixmap(pixmapRect, iconpixmap);
94 }
95 
animateShow(bool visible)96 void IconButton::animateShow(bool visible)
97 {
98     if (visible) {
99         QPropertyAnimation *animation = new QPropertyAnimation(this, "fader");
100         animation->setDuration(160);
101         animation->setEndValue(1.0);
102         animation->start(QAbstractAnimation::DeleteWhenStopped);
103     } else {
104         QPropertyAnimation *animation = new QPropertyAnimation(this, "fader");
105         animation->setDuration(160);
106         animation->setEndValue(0.0);
107         animation->start(QAbstractAnimation::DeleteWhenStopped);
108     }
109 }
110 
refuseFocus() const111 bool HintLineEdit::refuseFocus() const
112 {
113     return m_refuseFocus;
114 }
115 
setRefuseFocus(bool v)116 void HintLineEdit::setRefuseFocus(bool v)
117 {
118     if (v == m_refuseFocus)
119         return;
120     m_refuseFocus = v;
121     setFocusPolicy(m_refuseFocus ? Qt::NoFocus : m_defaultFocusPolicy);
122 }
123 
mousePressEvent(QMouseEvent * e)124 void HintLineEdit::mousePressEvent(QMouseEvent *e)
125 {
126     if (debugFilter)
127         qDebug() << Q_FUNC_INFO;
128     // Explicitly focus on click.
129     if (m_refuseFocus && !hasFocus())
130         setFocus(Qt::OtherFocusReason);
131     QLineEdit::mousePressEvent(e);
132 }
133 
focusInEvent(QFocusEvent * e)134 void HintLineEdit::focusInEvent(QFocusEvent *e)
135 {
136     if (debugFilter)
137         qDebug() << Q_FUNC_INFO;
138     if (m_refuseFocus) {
139         // Refuse the focus if the mouse it outside. In addition to the mouse
140         // press logic, this prevents a re-focussing which occurs once
141         // we actually had focus
142         const Qt::FocusReason reason = e->reason();
143         if (reason == Qt::ActiveWindowFocusReason || reason == Qt::PopupFocusReason) {
144             const QPoint mousePos = mapFromGlobal(QCursor::pos());
145             const bool refuse = !geometry().contains(mousePos);
146             if (debugFilter)
147                 qDebug() << Q_FUNC_INFO << refuse ;
148             if (refuse) {
149                 e->ignore();
150                 return;
151             }
152         }
153     }
154 
155     QLineEdit::focusInEvent(e);
156 }
157 
158 // ------------------- FilterWidget
FilterWidget(QWidget * parent,LayoutMode lm)159 FilterWidget::FilterWidget(QWidget *parent, LayoutMode lm)  :
160     QWidget(parent),
161     m_editor(new HintLineEdit(this)),
162     m_button(new IconButton(m_editor)),
163     m_buttonwidth(0)
164 {
165     m_editor->setPlaceholderText(tr("Filter"));
166 
167     // Let the style determine minimum height for our widget
168     QSize size(ICONBUTTON_SIZE + 6, ICONBUTTON_SIZE + 2);
169 
170     // Note KDE does not reserve space for the highlight color
171     if (style()->inherits("OxygenStyle")) {
172         size = size.expandedTo(QSize(24, 0));
173     }
174 
175     // Make room for clear icon
176     QMargins margins = m_editor->textMargins();
177     if (layoutDirection() == Qt::LeftToRight)
178         margins.setRight(size.width());
179     else
180         margins.setLeft(size.width());
181 
182     m_editor->setTextMargins(margins);
183 
184     QHBoxLayout *l = new QHBoxLayout(this);
185     l->setMargin(0);
186     l->setSpacing(0);
187     if (lm == LayoutAlignRight)
188         l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
189 
190     l->addWidget(m_editor);
191 
192     // KDE has custom icons for this. Notice that icon namings are counter intuitive
193     // If these icons are not avaiable we use the freedesktop standard name before
194     // falling back to a bundled resource
195     QIcon icon = QIcon::fromTheme(layoutDirection() == Qt::LeftToRight ?
196                      QLatin1String("edit-clear-locationbar-rtl") :
197                      QLatin1String("edit-clear-locationbar-ltr"),
198                      QIcon::fromTheme("edit-clear", createIconSet(QLatin1String("cleartext.png"))));
199 
200     m_button->setIcon(icon);
201     m_button->setToolTip(tr("Clear text"));
202     connect(m_button, SIGNAL(clicked()), this, SLOT(reset()));
203     connect(m_editor, SIGNAL(textChanged(QString)), this, SLOT(checkButton(QString)));
204     connect(m_editor, SIGNAL(textEdited(QString)), this, SIGNAL(filterChanged(QString)));
205 }
206 
text() const207 QString FilterWidget::text() const
208 {
209     return m_editor->text();
210 }
211 
checkButton(const QString & text)212 void FilterWidget::checkButton(const QString &text)
213 {
214     if (m_oldText.isEmpty() || text.isEmpty())
215         m_button->animateShow(!m_editor->text().isEmpty());
216     m_oldText = text;
217 }
218 
reset()219 void FilterWidget::reset()
220 {
221     if (debugFilter)
222         qDebug() << Q_FUNC_INFO;
223 
224     if (!m_editor->text().isEmpty()) {
225         // Editor has lost focus once this is pressed
226         m_editor->clear();
227         emit filterChanged(QString());
228     }
229 }
230 
resizeEvent(QResizeEvent *)231 void FilterWidget::resizeEvent(QResizeEvent *)
232 {
233     QRect contentRect = m_editor->rect();
234     if (layoutDirection() == Qt::LeftToRight) {
235         const int iconoffset = m_editor->textMargins().right() + 4;
236         m_button->setGeometry(contentRect.adjusted(m_editor->width() - iconoffset, 0, 0, 0));
237     } else {
238         const int iconoffset = m_editor->textMargins().left() + 4;
239         m_button->setGeometry(contentRect.adjusted(0, 0, -m_editor->width() + iconoffset, 0));
240     }
241 }
242 
refuseFocus() const243 bool FilterWidget::refuseFocus() const
244 {
245     return m_editor->refuseFocus();
246 }
247 
setRefuseFocus(bool v)248 void FilterWidget::setRefuseFocus(bool v)
249 {
250     m_editor->setRefuseFocus(v);
251 }
252 } // namespace qdesigner_internal
253 
254 QT_END_NAMESPACE
255