From cc81d48b18aac52d5c668d825404c249f69f4c12 Mon Sep 17 00:00:00 2001 From: Vincent van Ravesteijn Date: Sun, 5 Jun 2011 17:51:02 +0000 Subject: [PATCH] Add a FancyLineEdit class. This code was taken from QtCreator and modified a little. Patch from venom00. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@38957 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/frontends/qt4/FancyLineEdit.cpp | 305 ++++++++++++++++++++++++++++ src/frontends/qt4/FancyLineEdit.h | 111 ++++++++++ src/frontends/qt4/Makefile.am | 2 + 3 files changed, 418 insertions(+) create mode 100644 src/frontends/qt4/FancyLineEdit.cpp create mode 100644 src/frontends/qt4/FancyLineEdit.h diff --git a/src/frontends/qt4/FancyLineEdit.cpp b/src/frontends/qt4/FancyLineEdit.cpp new file mode 100644 index 0000000000..5c48d9ff27 --- /dev/null +++ b/src/frontends/qt4/FancyLineEdit.cpp @@ -0,0 +1,305 @@ +/** + * \file fancylineedit.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Nokia Corporation (qt-info@nokia.com) + * + * Full author contact details are available in file CREDITS. + * + */ + +// Code taken from the Qt Creator project and customized a little + +#include "FancyLineEdit.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { margin = 6 }; + +#define ICONBUTTON_HEIGHT 18 +#define FADE_TIME 160 + + +namespace lyx { +namespace frontend { + +// --------- FancyLineEditPrivate +class FancyLineEditPrivate : public QObject { +public: + explicit FancyLineEditPrivate(FancyLineEdit *parent); + + virtual bool eventFilter(QObject *obj, QEvent *event); + + FancyLineEdit *m_lineEdit; + QPixmap m_pixmap[2]; + QMenu *m_menu[2]; + bool m_menuTabFocusTrigger[2]; + IconButton *m_iconbutton[2]; + bool m_iconEnabled[2]; +}; + + +FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) : + QObject(parent), + m_lineEdit(parent) +{ + for (int i = 0; i < 2; ++i) { + m_menu[i] = 0; + m_menuTabFocusTrigger[i] = false; + m_iconbutton[i] = new IconButton(parent); + m_iconbutton[i]->installEventFilter(this); + m_iconbutton[i]->hide(); + m_iconbutton[i]->setAutoHide(false); + m_iconEnabled[i] = false; + } +} + +bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event) +{ + int buttonIndex = -1; + for (int i = 0; i < 2; ++i) { + if (obj == m_iconbutton[i]) { + buttonIndex = i; + break; + } + } + if (buttonIndex == -1) + return QObject::eventFilter(obj, event); + switch (event->type()) { + case QEvent::FocusIn: + if (m_menuTabFocusTrigger[buttonIndex] && m_menu[buttonIndex]) { + m_lineEdit->setFocus(); + m_menu[buttonIndex]->exec(m_iconbutton[buttonIndex]->mapToGlobal( + m_iconbutton[buttonIndex]->rect().center())); + return true; + } + default: + break; + } + return QObject::eventFilter(obj, event); +} + + +// --------- FancyLineEdit +FancyLineEdit::FancyLineEdit(QWidget *parent) : + QLineEdit(parent), + m_d(new FancyLineEditPrivate(this)) +{ + ensurePolished(); + updateMargins(); + + connect(this, SIGNAL(textChanged(QString)), this, SLOT(checkButtons(QString))); + connect(m_d->m_iconbutton[Left], SIGNAL(clicked()), this, SLOT(iconClicked())); + connect(m_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 (m_d->m_iconbutton[i]->hasAutoHide()) + m_d->m_iconbutton[i]->animateShow(!text.isEmpty()); + } + m_oldText = text; + } +} + +FancyLineEdit::~FancyLineEdit() +{ +} + +void FancyLineEdit::setButtonVisible(Side side, bool visible) +{ + m_d->m_iconbutton[side]->setVisible(visible); + m_d->m_iconEnabled[side] = visible; + updateMargins(); +} + +bool FancyLineEdit::isButtonVisible(Side side) const +{ + return m_d->m_iconEnabled[side]; +} + +void FancyLineEdit::iconClicked() +{ + IconButton *button = qobject_cast(sender()); + int index = -1; + for (int i = 0; i < 2; ++i) + if (m_d->m_iconbutton[i] == button) + index = i; + if (index == -1) + return; + if (m_d->m_menu[index]) { + m_d->m_menu[index]->exec(QCursor::pos()); + } else { + buttonClicked((Side)index); + if (index == Left) + leftButtonClicked(); + else if (index == Right) + rightButtonClicked(); + } +} + +void FancyLineEdit::updateMargins() +{ + bool leftToRight = (layoutDirection() == Qt::LeftToRight); + Side realLeft = (leftToRight ? Left : Right); + Side realRight = (leftToRight ? Right : Left); + + int leftMargin = m_d->m_iconbutton[realLeft]->pixmap().width() + 8; + int rightMargin = m_d->m_iconbutton[realRight]->pixmap().width() + 8; + // Note KDE does not reserve space for the highlight color + if (style()->inherits("OxygenStyle")) { + leftMargin = qMax(24, leftMargin); + rightMargin = qMax(24, rightMargin); + } + + QMargins margins((m_d->m_iconEnabled[realLeft] ? leftMargin : 0), 0, + (m_d->m_iconEnabled[realRight] ? rightMargin : 0), 0); + + setTextMargins(margins); +} + +void FancyLineEdit::updateButtonPositions() +{ + QRect contentRect = rect(); + for (int i = 0; i < 2; ++i) { + Side iconpos = (Side)i; + if (layoutDirection() == Qt::RightToLeft) + iconpos = (iconpos == Left ? Right : Left); + + if (iconpos == FancyLineEdit::Right) { + const int iconoffset = textMargins().right() + 4; + m_d->m_iconbutton[i]->setGeometry(contentRect.adjusted(width() - iconoffset, 0, 0, 0)); + } else { + const int iconoffset = textMargins().left() + 4; + m_d->m_iconbutton[i]->setGeometry(contentRect.adjusted(0, 0, -width() + iconoffset, 0)); + } + } +} + +void FancyLineEdit::resizeEvent(QResizeEvent *) +{ + updateButtonPositions(); +} + +void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap) +{ + m_d->m_iconbutton[side]->setPixmap(buttonPixmap); + updateMargins(); + updateButtonPositions(); + update(); +} + +QPixmap FancyLineEdit::buttonPixmap(Side side) const +{ + return m_d->m_pixmap[side]; +} + +void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu) +{ + m_d->m_menu[side] = buttonMenu; + m_d->m_iconbutton[side]->setIconOpacity(1.0); + } + +QMenu *FancyLineEdit::buttonMenu(Side side) const +{ + return m_d->m_menu[side]; +} + +bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const +{ + return m_d->m_menuTabFocusTrigger[side]; +} + +void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v) +{ + if (m_d->m_menuTabFocusTrigger[side] == v) + return; + + m_d->m_menuTabFocusTrigger[side] = v; + m_d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus); +} + +bool FancyLineEdit::hasAutoHideButton(Side side) const +{ + return m_d->m_iconbutton[side]->hasAutoHide(); +} + +void FancyLineEdit::setAutoHideButton(Side side, bool h) +{ + m_d->m_iconbutton[side]->setAutoHide(h); + if (h) + m_d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ? 0.0 : 1.0); + else + m_d->m_iconbutton[side]->setIconOpacity(1.0); +} + +void FancyLineEdit::setButtonToolTip(Side side, const QString &tip) +{ + m_d->m_iconbutton[side]->setToolTip(tip); +} + +void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy) +{ + m_d->m_iconbutton[side]->setFocusPolicy(policy); +} + +// IconButton - helper class to represent a clickable icon + +IconButton::IconButton(QWidget *parent) + : QAbstractButton(parent), m_autoHide(false) +{ + setCursor(Qt::ArrowCursor); + setFocusPolicy(Qt::NoFocus); +} + +void IconButton::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + // Note isDown should really use the active state but in most styles + // this has no proper feedback + QIcon::Mode state = QIcon::Disabled; + if (isEnabled()) + state = isDown() ? QIcon::Selected : QIcon::Normal; + QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height()); + pixmapRect.moveCenter(rect().center()); + + if (m_autoHide) + painter.setOpacity(m_iconOpacity); + + painter.drawPixmap(pixmapRect, m_pixmap); +} + +void IconButton::animateShow(bool visible) +{ + if (visible) { + QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity"); + animation->setDuration(FADE_TIME); + animation->setEndValue(1.0); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } else { + QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity"); + animation->setDuration(FADE_TIME); + animation->setEndValue(0.0); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } +} + +} + +} + +#include "moc_FancyLineEdit.cpp" diff --git a/src/frontends/qt4/FancyLineEdit.h b/src/frontends/qt4/FancyLineEdit.h new file mode 100644 index 0000000000..dbf216fdf2 --- /dev/null +++ b/src/frontends/qt4/FancyLineEdit.h @@ -0,0 +1,111 @@ +/** + * \file fancylineedit.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Nokia Corporation (qt-info@nokia.com) + * + * Full author contact details are available in file CREDITS. + * + */ + +// Code taken from the Qt Creator project and customized a little + +#ifndef FANCYLINEEDIT_H +#define FANCYLINEEDIT_H + +#include +#include + +namespace lyx { +namespace frontend { + +class FancyLineEditPrivate; + +class IconButton: public QAbstractButton +{ + Q_OBJECT + Q_PROPERTY(float iconOpacity READ iconOpacity WRITE setIconOpacity) + Q_PROPERTY(bool autoHide READ hasAutoHide WRITE setAutoHide) + Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) +public: + explicit IconButton(QWidget *parent = 0); + void paintEvent(QPaintEvent *event); + void setPixmap(const QPixmap &pixmap) { m_pixmap = pixmap; update(); } + QPixmap pixmap() const { return m_pixmap; } + float iconOpacity() { return m_iconOpacity; } + void setIconOpacity(float value) { m_iconOpacity = value; update(); } + void animateShow(bool visible); + + void setAutoHide(bool hide) { m_autoHide = hide; } + bool hasAutoHide() const { return m_autoHide; } +private: + float m_iconOpacity; + bool m_autoHide; + QPixmap m_pixmap; +}; + + +/* 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 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). + */ +class FancyLineEdit : public QLineEdit +{ + Q_DISABLE_COPY(FancyLineEdit) + Q_OBJECT + Q_ENUMS(Side) + +public: + enum Side {Left = 0, Right = 1}; + + explicit FancyLineEdit(QWidget *parent = 0); + ~FancyLineEdit(); + + QPixmap buttonPixmap(Side side) const; + void setButtonPixmap(Side side, const QPixmap &pixmap); + + QMenu *buttonMenu(Side side) const; + void setButtonMenu(Side side, QMenu *menu); + + void setButtonVisible(Side side, bool visible); + bool isButtonVisible(Side side) const; + + void setButtonToolTip(Side side, const QString &); + void setButtonFocusPolicy(Side side, Qt::FocusPolicy policy); + + // Set whether tabbing in will trigger the menu. + void setMenuTabFocusTrigger(Side side, bool v); + bool hasMenuTabFocusTrigger(Side side) const; + + // Set if icon should be hidden when text is empty + void setAutoHideButton(Side side, bool h); + bool hasAutoHideButton(Side side) const; + +Q_SIGNALS: + void buttonClicked(Side side); + void leftButtonClicked(); + void rightButtonClicked(); + +private Q_SLOTS: + void checkButtons(const QString &); + void iconClicked(); + +protected: + virtual void resizeEvent(QResizeEvent *e); + +private: + void updateMargins(); + void updateButtonPositions(); + + FancyLineEditPrivate *m_d; + QString m_oldText; +}; + +} +} + +#endif // FANCYLINEEDIT_H diff --git a/src/frontends/qt4/Makefile.am b/src/frontends/qt4/Makefile.am index 299e3ca9e7..e112916270 100644 --- a/src/frontends/qt4/Makefile.am +++ b/src/frontends/qt4/Makefile.am @@ -58,6 +58,7 @@ SOURCEFILES = \ ColorCache.cpp \ CustomizedWidgets.cpp \ EmptyTable.cpp \ + FancyLineEdit.cpp \ FileDialog.cpp \ FindAndReplace.cpp \ FloatPlacement.cpp \ @@ -173,6 +174,7 @@ MOCHEADER = \ BulletsModule.h \ CustomizedWidgets.h \ EmptyTable.h \ + FancyLineEdit.h \ FindAndReplace.h \ FloatPlacement.h \ GuiAbout.h \