#ifndef FANCYLINEEDIT_H#define FANCYLINEEDIT_H#include <QtCore/QMetaType>#include <QtGui/QLineEdit>class FancyLineEditPrivate;/* A line edit with an embedded pixmap on one side that is connected to * a menu. Additionally, it can display a grayed hintText (like "Type Here to") * when not focussed 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). */class FancyLineEdit : public QLineEdit{ Q_DISABLE_COPY(FancyLineEdit) Q_OBJECT Q_ENUMS(Side) Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap DESIGNABLE true) Q_PROPERTY(Side side READ side WRITE setSide DESIGNABLE isSideStored STORED isSideStored) Q_PROPERTY(bool useLayoutDirection READ useLayoutDirection WRITE setUseLayoutDirection DESIGNABLE true) Q_PROPERTY(bool menuTabFocusTrigger READ hasMenuTabFocusTrigger WRITE setMenuTabFocusTrigger DESIGNABLE true) Q_PROPERTY(QString hintText READ hintText WRITE setHintText DESIGNABLE true)public: enum Side {Left, Right}; explicit FancyLineEdit(QWidget *parent = 0); ~FancyLineEdit(); QPixmap pixmap() const; void setMenu(QMenu *menu); QMenu *menu() const; void setSide(Side side); Side side() const; bool useLayoutDirection() const; void setUseLayoutDirection(bool v); // Set whether tabbing in will trigger the menu. bool hasMenuTabFocusTrigger() const; void setMenuTabFocusTrigger(bool v); // Hint text that is displayed when no focus is set. QString hintText() const; bool isShowingHintText() const; // Convenience for accessing the text that returns "" in case of isShowingHintText(). QString typedText() const;public slots: void setPixmap(const QPixmap &pixmap); void setHintText(const QString &ht); void showHintText(); void hideHintText();protected: virtual void resizeEvent(QResizeEvent *e); virtual void focusInEvent(QFocusEvent *e); virtual void focusOutEvent(QFocusEvent *e);private: bool isSideStored() const; void updateMenuLabel(); void positionMenuLabel(); void updateStyleSheet(Side side); FancyLineEditPrivate *m_d;};#endif // FANCYLINEEDIT_H
#include "fancylineedit.h"#include <QtCore/QEvent>#include <QtCore/QDebug>#include <QtCore/QString>#include <QtGui/QApplication>#include <QtGui/QMenu>#include <QtGui/QMouseEvent>#include <QtGui/QLabel>enum { margin = 6 };static inline QString sideToStyleSheetString(FancyLineEdit::Side side){ return side == FancyLineEdit::Left ? QLatin1String("left") : QLatin1String("right");}// Format style sheet for the label containing the pixmap. It has a margin on// the outer side of the whole FancyLineEdit.static QString labelStyleSheet(FancyLineEdit::Side side){ QString rc = QLatin1String("QLabel { margin-"); rc += sideToStyleSheetString(side); rc += QLatin1String(": "); rc += QString::number(margin); rc += QLatin1Char('}'); return rc;}// --------- FancyLineEditPrivate as QObject with label// event filterclass FancyLineEditPrivate : public QObject {public: explicit FancyLineEditPrivate(QLineEdit *parent); virtual bool eventFilter(QObject *obj, QEvent *event); const QString m_leftLabelStyleSheet; const QString m_rightLabelStyleSheet; QLineEdit *m_lineEdit; QPixmap m_pixmap; QMenu *m_menu; QLabel *m_menuLabel; FancyLineEdit::Side m_side; bool m_useLayoutDirection; bool m_menuTabFocusTrigger; QString m_hintText; bool m_showingHintText;};FancyLineEditPrivate::FancyLineEditPrivate(QLineEdit *parent) : QObject(parent), m_leftLabelStyleSheet(labelStyleSheet(FancyLineEdit::Left)), m_rightLabelStyleSheet(labelStyleSheet(FancyLineEdit::Right)), m_lineEdit(parent), m_menu(0), m_menuLabel(0), m_side(FancyLineEdit::Left), m_useLayoutDirection(false), m_menuTabFocusTrigger(false), m_showingHintText(false){}bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event){ if (!m_menu || obj != m_menuLabel) return QObject::eventFilter(obj, event); switch (event->type()) { case QEvent::MouseButtonPress: { const QMouseEvent *me = static_cast<QMouseEvent *>(event); m_menu->exec(me->globalPos()); return true; } case QEvent::FocusIn: if (m_menuTabFocusTrigger) { m_lineEdit->setFocus(); m_menu->exec(m_menuLabel->mapToGlobal(m_menuLabel->rect().center())); return true; } default: break; } return QObject::eventFilter(obj, event);}// --------- FancyLineEditFancyLineEdit::FancyLineEdit(QWidget *parent) : QLineEdit(parent), m_d(new FancyLineEditPrivate(this)){ m_d->m_menuLabel = new QLabel(this); m_d->m_menuLabel->installEventFilter(m_d); updateMenuLabel(); showHintText();}FancyLineEdit::~FancyLineEdit(){}// Position the menu label left or right according to size.// Called when switching side and from resizeEvent.void FancyLineEdit::positionMenuLabel(){ switch (side()) { case Left: m_d->m_menuLabel->setGeometry(0, 0, m_d->m_pixmap.width()+margin, height()); break; case Right: m_d->m_menuLabel->setGeometry(width() - m_d->m_pixmap.width() - margin, 0, m_d->m_pixmap.width()+margin, height()); break; }}void FancyLineEdit::updateStyleSheet(Side side){ // Udate the LineEdit style sheet. Make room for the label on the // respective side and set color according to whether we are showing the // hint text QString sheet = QLatin1String("QLineEdit{ padding-"); sheet += sideToStyleSheetString(side); sheet += QLatin1String(": "); sheet += QString::number(m_d->m_pixmap.width() + margin); sheet += QLatin1Char(';'); if (m_d->m_showingHintText) sheet += QLatin1String(" color: #BBBBBB;"); sheet += QLatin1Char('}'); setStyleSheet(sheet);}void FancyLineEdit::updateMenuLabel(){ m_d->m_menuLabel->setPixmap(m_d->m_pixmap); const Side s = side(); switch (s) { case Left: m_d->m_menuLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); m_d->m_menuLabel->setStyleSheet(m_d->m_leftLabelStyleSheet); break; case Right: m_d->m_menuLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); m_d->m_menuLabel->setStyleSheet(m_d->m_rightLabelStyleSheet); break; } updateStyleSheet(s); positionMenuLabel();}void FancyLineEdit::setSide(Side side){ m_d->m_side = side; updateMenuLabel();}FancyLineEdit::Side FancyLineEdit::side() const{ if (m_d->m_useLayoutDirection) return qApp->layoutDirection() == Qt::LeftToRight ? Left : Right; return m_d->m_side;}void FancyLineEdit::resizeEvent(QResizeEvent *){ positionMenuLabel();}void FancyLineEdit::setPixmap(const QPixmap &pixmap){ m_d->m_pixmap = pixmap; updateMenuLabel();}QPixmap FancyLineEdit::pixmap() const{ return m_d->m_pixmap;}void FancyLineEdit::setMenu(QMenu *menu){ m_d->m_menu = menu;}QMenu *FancyLineEdit::menu() const{ return m_d->m_menu;}bool FancyLineEdit::useLayoutDirection() const{ return m_d->m_useLayoutDirection;}void FancyLineEdit::setUseLayoutDirection(bool v){ m_d->m_useLayoutDirection = v;}bool FancyLineEdit::isSideStored() const{ return !m_d->m_useLayoutDirection;}bool FancyLineEdit::hasMenuTabFocusTrigger() const{ return m_d->m_menuTabFocusTrigger;}void FancyLineEdit::setMenuTabFocusTrigger(bool v){ if (m_d->m_menuTabFocusTrigger == v) return; m_d->m_menuTabFocusTrigger = v; m_d->m_menuLabel->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);}QString FancyLineEdit::hintText() const{ return m_d->m_hintText;}void FancyLineEdit::setHintText(const QString &ht){ // Updating magic to make the property work in Designer. if (ht == m_d->m_hintText) return; hideHintText(); m_d->m_hintText = ht; if (!hasFocus() && !ht.isEmpty()) showHintText();}void FancyLineEdit::showHintText(){ if (!m_d->m_showingHintText && text().isEmpty() && !m_d->m_hintText.isEmpty()) { m_d->m_showingHintText = true; setText(m_d->m_hintText); updateStyleSheet(side()); }}void FancyLineEdit::hideHintText(){ if (m_d->m_showingHintText && !m_d->m_hintText.isEmpty()) { m_d->m_showingHintText = false; setText(QString()); updateStyleSheet(side()); }}void FancyLineEdit::focusInEvent(QFocusEvent *e){ hideHintText(); QLineEdit::focusInEvent(e);}void FancyLineEdit::focusOutEvent(QFocusEvent *e){ // Focus out: Switch to displaying the hint text unless // there is user input showHintText(); QLineEdit::focusOutEvent(e);}bool FancyLineEdit::isShowingHintText() const{ return m_d->m_showingHintText;}QString FancyLineEdit::typedText() const{ return m_d->m_showingHintText ? QString() : text();}