Commit 680209ae authored by hjk's avatar hjk

Utils: Merge BaseValidatingLineEdit into FancyLineEdit

Change-Id: Idb7a6f28ac41bacbfd2603feb8b786c31d3769e3
Reviewed-by: default avatarEike Ziller <eike.ziller@digia.com>
Reviewed-by: default avatarChristian Stenger <christian.stenger@digia.com>
parent a74de6af
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "basevalidatinglineedit.h"
#include <QDebug>
enum { debug = 0 };
/*!
\namespace Utils
General utility library namespace
*/
/*! \class Utils::BaseValidatingLineEdit
\brief The BaseValidatingLineEdit class is the base class for line edits
that perform validation.
Performs validation in a virtual validate() function to be implemented in
derived classes.
When invalid, the text color will turn red and a tooltip will
contain the error message. This approach is less intrusive than a
QValidator which will prevent the user from entering certain characters.
The widget has a concept of an "initialText" which can be something like
"<Enter name here>". This results in state 'DisplayingInitialText', which
is not valid, but is not marked red.
*/
namespace Utils {
struct BaseValidatingLineEditPrivate {
explicit BaseValidatingLineEditPrivate(const QWidget *w);
const QColor m_okTextColor;
QColor m_errorTextColor;
BaseValidatingLineEdit::State m_state;
QString m_errorMessage;
QString m_initialText;
bool m_firstChange;
};
BaseValidatingLineEditPrivate::BaseValidatingLineEditPrivate(const QWidget *w) :
m_okTextColor(BaseValidatingLineEdit::textColor(w)),
m_errorTextColor(Qt::red),
m_state(BaseValidatingLineEdit::Invalid),
m_firstChange(true)
{
}
BaseValidatingLineEdit::BaseValidatingLineEdit(QWidget *parent) :
FancyLineEdit(parent),
m_bd(new BaseValidatingLineEditPrivate(this))
{
// Note that textChanged() is also triggered automagically by
// QLineEdit::setText(), no need to trigger manually.
connect(this, SIGNAL(textChanged(QString)), this, SLOT(slotChanged(QString)));
}
BaseValidatingLineEdit::~BaseValidatingLineEdit()
{
delete m_bd;
}
QString BaseValidatingLineEdit::initialText() const
{
return m_bd->m_initialText;
}
void BaseValidatingLineEdit::setInitialText(const QString &t)
{
if (m_bd->m_initialText != t) {
m_bd->m_initialText = t;
m_bd->m_firstChange = true;
setText(t);
}
}
QColor BaseValidatingLineEdit::errorColor() const
{
return m_bd->m_errorTextColor;
}
void BaseValidatingLineEdit::setErrorColor(const QColor &c)
{
m_bd->m_errorTextColor = c;
}
QColor BaseValidatingLineEdit::textColor(const QWidget *w)
{
return w->palette().color(QPalette::Active, QPalette::Text);
}
void BaseValidatingLineEdit::setTextColor(QWidget *w, const QColor &c)
{
QPalette palette = w->palette();
palette.setColor(QPalette::Active, QPalette::Text, c);
w->setPalette(palette);
}
BaseValidatingLineEdit::State BaseValidatingLineEdit::state() const
{
return m_bd->m_state;
}
bool BaseValidatingLineEdit::isValid() const
{
return m_bd->m_state == Valid;
}
QString BaseValidatingLineEdit::errorMessage() const
{
return m_bd->m_errorMessage;
}
void BaseValidatingLineEdit::slotChanged(const QString &t)
{
m_bd->m_errorMessage.clear();
// Are we displaying the initial text?
const bool isDisplayingInitialText = !m_bd->m_initialText.isEmpty() && t == m_bd->m_initialText;
const State newState = isDisplayingInitialText ?
DisplayingInitialText :
(validate(t, &m_bd->m_errorMessage) ? Valid : Invalid);
setToolTip(m_bd->m_errorMessage);
if (debug)
qDebug() << Q_FUNC_INFO << t << "State" << m_bd->m_state << "->" << newState << m_bd->m_errorMessage;
// Changed..figure out if valid changed. DisplayingInitialText is not valid,
// but should not show error color. Also trigger on the first change.
if (newState != m_bd->m_state || m_bd->m_firstChange) {
const bool validHasChanged = (m_bd->m_state == Valid) != (newState == Valid);
m_bd->m_state = newState;
m_bd->m_firstChange = false;
setTextColor(this, newState == Invalid ? m_bd->m_errorTextColor : m_bd->m_okTextColor);
if (validHasChanged) {
emit validChanged(newState == Valid);
emit validChanged();
}
}
bool block = blockSignals(true);
const QString fixedString = fixInputString(t);
if (t != fixedString) {
const int cursorPos = cursorPosition();
setText(fixedString);
setCursorPosition(qMin(cursorPos, fixedString.length()));
}
blockSignals(block);
}
void BaseValidatingLineEdit::slotReturnPressed()
{
if (isValid())
emit validReturnPressed();
}
void BaseValidatingLineEdit::triggerChanged()
{
slotChanged(text());
}
QString BaseValidatingLineEdit::fixInputString(const QString &string)
{
return string;
}
} // namespace Utils
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef BASEVALIDATINGLINEEDIT_H
#define BASEVALIDATINGLINEEDIT_H
#include "fancylineedit.h"
namespace Utils {
struct BaseValidatingLineEditPrivate;
class QTCREATOR_UTILS_EXPORT BaseValidatingLineEdit : public FancyLineEdit
{
Q_OBJECT
Q_PROPERTY(QString initialText READ initialText WRITE setInitialText DESIGNABLE true)
Q_PROPERTY(QColor errorColor READ errorColor WRITE setErrorColor DESIGNABLE true)
public:
enum State { Invalid, DisplayingInitialText, Valid };
explicit BaseValidatingLineEdit(QWidget *parent = 0);
virtual ~BaseValidatingLineEdit();
State state() const;
bool isValid() const;
QString errorMessage() const;
QString initialText() const;
void setInitialText(const QString &);
QColor errorColor() const;
void setErrorColor(const QColor &);
// Trigger an update (after changing settings)
void triggerChanged();
static QColor textColor(const QWidget *w);
static void setTextColor(QWidget *w, const QColor &c);
signals:
void validChanged();
void validChanged(bool validState);
void validReturnPressed();
protected:
virtual bool validate(const QString &value, QString *errorMessage) const = 0;
virtual QString fixInputString(const QString &string);
protected slots:
// Custom behaviour can be added here. The base implementation must
// be called.
virtual void slotReturnPressed();
virtual void slotChanged(const QString &t);
private:
BaseValidatingLineEditPrivate *m_bd;
};
} // namespace Utils
#endif // BASEVALIDATINGLINEEDIT_H
......@@ -65,7 +65,7 @@ ClassNameValidatingLineEditPrivate:: ClassNameValidatingLineEditPrivate() :
// --------------------- ClassNameValidatingLineEdit
ClassNameValidatingLineEdit::ClassNameValidatingLineEdit(QWidget *parent) :
Utils::BaseValidatingLineEdit(parent),
Utils::FancyLineEdit(parent),
d(new ClassNameValidatingLineEditPrivate)
{
updateRegExp();
......@@ -123,9 +123,8 @@ bool ClassNameValidatingLineEdit::validate(const QString &value, QString *errorM
return true;
}
void ClassNameValidatingLineEdit::slotChanged(const QString &t)
void ClassNameValidatingLineEdit::handleChanged(const QString &t)
{
Utils::BaseValidatingLineEdit::slotChanged(t);
if (isValid()) {
// Suggest file names, strip namespaces
QString fileName = d->m_lowerCaseFileName ? t.toLower() : t;
......
......@@ -30,14 +30,14 @@
#ifndef CLASSNAMEVALIDATINGLINEEDIT_H
#define CLASSNAMEVALIDATINGLINEEDIT_H
#include "basevalidatinglineedit.h"
#include "fancylineedit.h"
namespace Utils {
struct ClassNameValidatingLineEditPrivate;
class QTCREATOR_UTILS_EXPORT ClassNameValidatingLineEdit
: public Utils::BaseValidatingLineEdit
: public Utils::FancyLineEdit
{
Q_OBJECT
Q_PROPERTY(bool namespacesEnabled READ namespacesEnabled WRITE setNamespacesEnabled DESIGNABLE true)
......@@ -68,9 +68,9 @@ signals:
void updateFileName(const QString &t);
protected:
virtual bool validate(const QString &value, QString *errorMessage) const;
virtual void slotChanged(const QString &t);
virtual QString fixInputString(const QString &string);
bool validate(const QString &value, QString *errorMessage) const;
void handleChanged(const QString &t);
QString fixInputString(const QString &string);
private:
void updateRegExp() const;
......
......@@ -43,15 +43,33 @@
/*!
\class Utils::FancyLineEdit
\brief The FancyLineEdit class is a line edit with an embedded pixmap on
one side that is connected to
a menu.
\brief The FancyLineEdit class is an enhanced line edit with several
opt-in features.
Additionally, it can display a grayed hintText (like "Type Here to")
A FancyLineEdit instance can have:
\list
\li An embedded pixmap on one side that is connected to a menu.
\li A grayed hintText (like "Type Here to")
when not focused and empty. When connecting to the changed signals and
querying text, one has to be aware that the text is set to that hint
text if isShowingHintText() returns true (that is, does not contain
valid user input).
\li A history completer.
\li The ability to validate the contents of the text field by overriding
virtual \c validate() function in derived clases.
\endlist
When invalid, the text color will turn red and a tooltip will
contain the error message. This approach is less intrusive than a
QValidator which will prevent the user from entering certain characters.
A visible hint text results validation to be in state 'DisplayingInitialText',
which is not valid, but is not marked red.
*/
enum { margin = 6 };
......@@ -69,7 +87,8 @@ public:
virtual bool eventFilter(QObject *obj, QEvent *event);
FancyLineEdit *m_lineEdit;
FancyLineEdit *m_lineEdit;
QString m_oldText;
QPixmap m_pixmap[2];
QMenu *m_menu[2];
bool m_menuTabFocusTrigger[2];
......@@ -80,11 +99,25 @@ public:
bool m_isFiltering;
QString m_lastFilterText;
const QColor m_okTextColor;
QColor m_errorTextColor;
FancyLineEdit::State m_state;
QString m_errorMessage;
QString m_initialText;
bool m_firstChange;
};
FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
QObject(parent), m_lineEdit(parent), m_historyCompleter(0), m_isFiltering(false)
QObject(parent),
m_lineEdit(parent),
m_historyCompleter(0),
m_isFiltering(false),
m_okTextColor(FancyLineEdit::textColor(parent)),
m_errorTextColor(Qt::red),
m_state(FancyLineEdit::Invalid),
m_firstChange(true)
{
for (int i = 0; i < 2; ++i) {
m_menu[i] = 0;
......@@ -130,20 +163,9 @@ FancyLineEdit::FancyLineEdit(QWidget *parent) :
ensurePolished();
updateMargins();
connect(this, SIGNAL(textChanged(QString)), this, SLOT(checkButtons(QString)));
connect(d->m_iconbutton[Left], SIGNAL(clicked()), this, SLOT(iconClicked()));
connect(d->m_iconbutton[Right], SIGNAL(clicked()), this, SLOT(iconClicked()));
}
void FancyLineEdit::checkButtons(const QString &text)
{
if (m_oldText.isEmpty() || text.isEmpty()) {
for (int i = 0; i < 2; ++i) {
if (d->m_iconbutton[i]->hasAutoHide())
d->m_iconbutton[i]->animateShow(!text.isEmpty());
}
m_oldText = text;
}
connect(this, SIGNAL(textChanged(QString)), this, SLOT(onTextChanged(QString)));
}
FancyLineEdit::~FancyLineEdit()
......@@ -322,24 +344,132 @@ void FancyLineEdit::setFiltering(bool on)
setButtonToolTip(Right, tr("Clear text"));
setAutoHideButton(Right, true);
connect(this, SIGNAL(rightButtonClicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged()));
} else {
disconnect(this, SIGNAL(rightButtonClicked()), this, SLOT(clear()));
disconnect(this, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged()));
}
}
void FancyLineEdit::slotTextChanged()
QString FancyLineEdit::initialText() const
{
return d->m_initialText;
}
void FancyLineEdit::setInitialText(const QString &t)
{
const QString newlyTypedText = text();
if (newlyTypedText != d->m_lastFilterText) {
d->m_lastFilterText = newlyTypedText;
emit filterChanged(d->m_lastFilterText);
if (d->m_initialText != t) {
d->m_initialText = t;
d->m_firstChange = true;
setText(t);
}
}
QColor FancyLineEdit::errorColor() const
{
return d->m_errorTextColor;
}
void FancyLineEdit::setErrorColor(const QColor &c)
{
d->m_errorTextColor = c;
}
QColor FancyLineEdit::textColor(const QWidget *w)
{
return w->palette().color(QPalette::Active, QPalette::Text);
}
void FancyLineEdit::setTextColor(QWidget *w, const QColor &c)
{
QPalette palette = w->palette();
palette.setColor(QPalette::Active, QPalette::Text, c);
w->setPalette(palette);
}
bool FancyLineEdit::validate(const QString &value, QString *errorMessage) const
{
Q_UNUSED(value);
Q_UNUSED(errorMessage);
return true;
}
FancyLineEdit::State FancyLineEdit::state() const
{
return d->m_state;
}
bool FancyLineEdit::isValid() const
{
return d->m_state == Valid;
}
QString FancyLineEdit::errorMessage() const
{
return d->m_errorMessage;
}
void FancyLineEdit::onTextChanged(const QString &t)
{
if (d->m_isFiltering){
if (t != d->m_lastFilterText) {
d->m_lastFilterText = t;
emit filterChanged(t);
}
}
d->m_errorMessage.clear();
// Are we displaying the initial text?
const bool isDisplayingInitialText = !d->m_initialText.isEmpty() && t == d->m_initialText;
const State newState = isDisplayingInitialText ?
DisplayingInitialText :
(validate(t, &d->m_errorMessage) ? Valid : Invalid);
setToolTip(d->m_errorMessage);
// Changed..figure out if valid changed. DisplayingInitialText is not valid,
// but should not show error color. Also trigger on the first change.
if (newState != d->m_state || d->m_firstChange) {
const bool validHasChanged = (d->m_state == Valid) != (newState == Valid);
d->m_state = newState;
d->m_firstChange = false;
setTextColor(this, newState == Invalid ? d->m_errorTextColor : d->m_okTextColor);
if (validHasChanged) {
emit validChanged(newState == Valid);
emit validChanged();
}
}
bool block = blockSignals(true);
const QString fixedString = fixInputString(t);
if (t != fixedString) {
const int cursorPos = cursorPosition();
setText(fixedString);
setCursorPosition(qMin(cursorPos, fixedString.length()));
}
blockSignals(block);
// Check buttons.
if (d->m_oldText.isEmpty() || t.isEmpty()) {
for (int i = 0; i < 2; ++i) {
if (d->m_iconbutton[i]->hasAutoHide())
d->m_iconbutton[i]->animateShow(!t.isEmpty());
}
d->m_oldText = t;
}
handleChanged(t);
}
void FancyLineEdit::triggerChanged()
{
onTextChanged(text());
}
QString FancyLineEdit::fixInputString(const QString &string)
{
return string;
}
//
// IconButton - helper class to represent a clickable icon
//
IconButton::IconButton(QWidget *parent)
: QAbstractButton(parent), m_autoHide(false)
......
......@@ -71,6 +71,10 @@ class QTCREATOR_UTILS_EXPORT FancyLineEdit : public CompletingLineEdit
Q_OBJECT
Q_ENUMS(Side)
// Validation.
Q_PROPERTY(QString initialText READ initialText WRITE setInitialText DESIGNABLE true)
Q_PROPERTY(QColor errorColor READ errorColor WRITE setErrorColor DESIGNABLE true)
public:
enum Side {Left = 0, Right = 1};
......@@ -97,29 +101,66 @@ public:
void setAutoHideButton(Side side, bool h);
bool hasAutoHideButton(Side side) const;
// Completion
// Enable a history completer with a history of entries.
void setHistoryCompleter(const QString &historyKey);
// Sets a completer that is not a history completer.
void setSpecialCompleter(QCompleter *completer);
// Filtering
// Enables fitering
void setFiltering(bool on);
// Validation
enum State { Invalid, DisplayingInitialText, Valid };
State state() const;
bool isValid() const;
QString errorMessage() const;
QString initialText() const;
void setInitialText(const QString &);
QColor errorColor() const;
void setErrorColor(const QColor &);
// Trigger an update (after changing settings)
void triggerChanged();
static QColor textColor(const QWidget *w);
static void setTextColor(QWidget *w, const QColor &c);
protected slots:
// Custom behaviour can be added here.
virtual void handleChanged(const QString &) {}
signals:
void buttonClicked(Utils::FancyLineEdit::Side side);
void leftButtonClicked();
void rightButtonClicked();
void filterChanged(const QString &);
void validChanged();
void validChanged(bool validState);
void validReturnPressed();
private slots:
void checkButtons(const QString &);
void iconClicked();
void slotTextChanged(); // For filtering.
void onTextChanged(const QString &);
protected:
void resizeEvent(QResizeEvent *e);
virtual bool validate(const QString &value, QString *errorMessage) const;
virtual QString fixInputString(const QString &string);
private:
// Unimplemented, to force the user to make a decision on
// whether to use setHistoryCompleter() or setSpecialCompleter().
......@@ -130,7 +171,6 @@ private:
friend class Utils::FancyLineEditPrivate;
FancyLineEditPrivate *d;
QString m_oldText;
};
} // namespace Utils
......
......@@ -66,7 +66,7 @@ static QRegExp &windowsDeviceSubDirPattern()
// ----------- FileNameValidatingLineEdit
FileNameValidatingLineEdit::FileNameValidatingLineEdit(QWidget *parent) :
BaseValidatingLineEdit(parent),
FancyLineEdit(parent),
m_allowDirectories(false),
m_forceFirstCapitalLetter(false)
{
......
......@@ -30,11 +30,11 @@
#ifndef FILENAMEVALIDATINGLINEEDIT_H
#define FILENAMEVALIDATINGLINEEDIT_H
#include "basevalidatinglineedit.h"
#include "<