> If this are all changes, I wouldn't touch it any more, maybe > we update it later, > and if we have changed it there would be too much noise in the diff.
You mean update from the original Qt Creator source? Yeah, that's a good idea. Then here's the patch with camel-cased filenames. venom00
Index: src/frontends/qt4/PanelStack.cpp =================================================================== --- src/frontends/qt4/PanelStack.cpp (revisione 38779) +++ src/frontends/qt4/PanelStack.cpp (copia locale) @@ -14,16 +14,28 @@ #include "qt_helpers.h" +#include "GuiApplication.h" #include "support/debug.h" +#include "support/foreach.h" +#include "support/lassert.h" +#include <QAbstractButton> +#include <QApplication> +#include <QColorGroup> +#include <QComboBox> #include <QFontMetrics> +#include <QGroupBox> #include <QHBoxLayout> #include <QHeaderView> +#include <QLabel> +#include <QLineEdit> +#include <QListWidget> +#include <QPalette> +#include <QPushButton> #include <QStackedWidget> #include <QTreeWidget> +#include <QVBoxLayout> -#include "support/lassert.h" - using namespace std; namespace lyx { @@ -33,9 +45,16 @@ PanelStack::PanelStack(QWidget * parent) : QWidget(parent) { + delay_search_ = new QTimer(this); list_ = new QTreeWidget(this); stack_ = new QStackedWidget(this); + search_ = new FancyLineEdit(this); + // Configure the timer + delay_search_->setSingleShot(true); + connect(delay_search_, SIGNAL(timeout()), this, SLOT(search())); + + // Configure tree list_->setRootIsDecorated(false); list_->setColumnCount(1); list_->header()->hide(); @@ -48,9 +67,31 @@ connect(list_, SIGNAL(itemClicked (QTreeWidgetItem*, int)), this, SLOT(itemSelected(QTreeWidgetItem *, int))); - QHBoxLayout * layout = new QHBoxLayout(this); - layout->addWidget(list_, 0); - layout->addWidget(stack_, 1); + // Configure the search box +#if QT_VERSION >= 0x040700 + search_->setPlaceholderText(qt_("Search")); +#endif + + search_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "png")); + search_->setButtonVisible(FancyLineEdit::Right, true); + search_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text")); + search_->setAutoHideButton(FancyLineEdit::Right, true); + connect(search_, SIGNAL(rightButtonClicked()), this, SLOT(resetSearch())); + connect(search_, SIGNAL(textEdited(QString)), this, SLOT(filterChanged(QString))); + + // Create a HBox for the search box and the reset button + QHBoxLayout * search_layout = new QHBoxLayout(); + search_layout->addWidget(search_, 0); + + // Create the output layout, horizontal plus a VBox on the left with the search + // box and the tree + QVBoxLayout * left_layout = new QVBoxLayout(); + left_layout->addLayout(search_layout, 0); + left_layout->addWidget(list_, 1); + + QHBoxLayout * main_layout = new QHBoxLayout(this); + main_layout->addLayout(left_layout, 0); + main_layout->addWidget(stack_, 1); } @@ -132,22 +173,156 @@ QTreeWidgetItem * previous) { // do nothing when clicked on whitespace (item=NULL) - if( !item ) + if (!item) return; // if we have a category, expand the tree and go to the - // first item + // first enabled item if (item->childCount() > 0) { item->setExpanded(true); - if (previous && previous->parent() != item) - switchPanel( item->child(0), previous ); + if (previous && previous->parent() != item) { + // Looks for a child not disabled + for (int i = 0; i < item->childCount(); ++i) { + if (item->child(i)->flags() & Qt::ItemIsEnabled) { + switchPanel(item->child(i), previous); + break; + } + } + } } else if (QWidget * w = widget_map_.value(item, 0)) { stack_->setCurrentWidget(w); } } +bool matches(QString const & input, QString const & search) +{ + QString text = input; + // Check if the input contains the search string + return text.remove('&').contains(search, Qt::CaseInsensitive); +} + +void setTreeItemStatus(QTreeWidgetItem * tree_item, bool enabled) +{ + // Enable/disable the item + tree_item->setDisabled(!enabled); + + // Change the color from black to gray or viceversa + QPalette::ColorGroup new_color = enabled ? QPalette::Active : QPalette::Disabled; + tree_item->setTextColor(0, QApplication::palette().color(new_color, QPalette::Text)); +} + +void PanelStack::hideEvent(QHideEvent * event) +{ + QWidget::hideEvent(event); + + // Programatically hidden (not simply minimized by the user) + if (!event->spontaneous()) { + resetSearch(); + } +} + +void PanelStack::resetSearch() +{ + search_->setText(QString()); + search(); +} + +void PanelStack::filterChanged(QString const & /*search*/) +{ + // The text in the search box is changed, reset the timer + // and then search in the widgets + delay_search_->start(300); +} + +void PanelStack::search() +{ + QString search = search_->text(); + bool enable_all = search.isEmpty(); + + // If the search string is empty we enable all the items + // otherwise we disable everything and then selectively + // re-enable matching items + foreach (QTreeWidgetItem * tree_item, panel_map_) { + setTreeItemStatus(tree_item, enable_all); + } + + foreach (QTreeWidgetItem * tree_item, panel_map_) { + + // Current widget + QWidget * pane_widget = widget_map_[tree_item]; + + // First of all we look in the pane name + bool pane_matches = tree_item->text(0).contains(search, Qt::CaseInsensitive); + + // If the tree item has an associated pane + if (pane_widget) { + + // Loops on the list of children widgets (recursive) + QWidgetList children = pane_widget->findChildren<QWidget *>(); + foreach (QWidget * child_widget, children) { + bool widget_matches = false; + + // Try to cast to the most common widgets and looks in it's content + // It's bad OOP, it would be nice to have a QWidget::toString() overloaded by + // each widget, but this would require to change Qt or subclass each widget. + // Note that we have to ignore the amperstand symbol + if (QAbstractButton * button = qobject_cast<QAbstractButton *>(child_widget)) { + widget_matches = matches(button->text(), search); + + } else if (QGroupBox * group_box = qobject_cast<QGroupBox *>(child_widget)) { + widget_matches = matches(group_box->title(), search); + + } else if (QLabel * label = qobject_cast<QLabel *>(child_widget)) { + widget_matches = matches(label->text(), search); + + } else if (QLineEdit * line_edit = qobject_cast<QLineEdit *>(child_widget)) { + widget_matches = matches(line_edit->text(), search); + + } else if (QListWidget * list_widget = qobject_cast<QListWidget *>(child_widget)) { + widget_matches = (list_widget->findItems(search, Qt::MatchContains)).count() > 0; + + } else if (QTreeWidget * tree_view = qobject_cast<QTreeWidget *>(child_widget)) { + widget_matches = (tree_view->findItems(search, Qt::MatchContains)).count() > 0; + + } else if (QComboBox * combo_box = qobject_cast<QComboBox *>(child_widget)) { + widget_matches = (combo_box->findText(search, Qt::MatchContains)) != -1; + + } else { + continue; + } + + // If this widget meets the search criteria + if (widget_matches && !enable_all) { + // The pane too meets the search criteria + pane_matches = true; + + // Highlight the widget + QPalette widget_palette = child_widget->palette(); + widget_palette.setColor(child_widget->foregroundRole(), Qt::red); + child_widget->setPalette(widget_palette); + } else { + // Reset the color of the widget + child_widget->setPalette(QApplication::palette(child_widget)); + } + } + + // If the pane meets the search criteria + if (pane_matches && !enable_all) { + // Expand and enable the pane and his ancestors (typically just the parent) + QTreeWidgetItem * item = tree_item; + do { + item->setExpanded(true); + setTreeItemStatus(item, true); + item = item->parent(); + } while (item); + } + } + + } +} + void PanelStack::itemSelected(QTreeWidgetItem * item, int) { // de-select the category if a child is selected Index: src/frontends/qt4/Makefile.am =================================================================== --- src/frontends/qt4/Makefile.am (revisione 38779) +++ src/frontends/qt4/Makefile.am (copia locale) @@ -58,6 +58,7 @@ ColorCache.cpp \ CustomizedWidgets.cpp \ EmptyTable.cpp \ + FancyLineEdit.cpp \ FileDialog.cpp \ FindAndReplace.cpp \ FloatPlacement.cpp \ @@ -173,6 +174,7 @@ BulletsModule.h \ CustomizedWidgets.h \ EmptyTable.h \ + FancyLineEdit.h \ FindAndReplace.h \ FloatPlacement.h \ GuiAbout.h \ Index: src/frontends/qt4/FancyLineEdit.cpp =================================================================== --- src/frontends/qt4/FancyLineEdit.cpp (revisione 0) +++ src/frontends/qt4/FancyLineEdit.cpp (revisione 0) @@ -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-i...@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 <QtCore/QEvent> +#include <QtCore/QDebug> +#include <QtCore/QString> +#include <QtCore/QPropertyAnimation> +#include <QtGui/QApplication> +#include <QtGui/QMenu> +#include <QtGui/QMouseEvent> +#include <QtGui/QLabel> +#include <QtGui/QAbstractButton> +#include <QtGui/QPainter> +#include <QtGui/QStyle> +#include <QtGui/QPaintEvent> + +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<IconButton *>(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" Index: src/frontends/qt4/FancyLineEdit.h =================================================================== --- src/frontends/qt4/FancyLineEdit.h (revisione 0) +++ src/frontends/qt4/FancyLineEdit.h (revisione 0) @@ -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-i...@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 <QtGui/QLineEdit> +#include <QtGui/QAbstractButton> + +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 Index: src/frontends/qt4/PanelStack.h =================================================================== --- src/frontends/qt4/PanelStack.h (revisione 38779) +++ src/frontends/qt4/PanelStack.h (copia locale) @@ -13,12 +13,19 @@ #ifndef PANELSTACK_H #define PANELSTACK_H +#include "FancyLineEdit.h" + +#include <QHash> +#include <QHideEvent> +#include <QTimer> #include <QWidget> -#include <QHash> +class QAbstractButton; +class QLineEdit; +class QPushButton; +class QStackedWidget; class QTreeWidget; class QTreeWidgetItem; -class QStackedWidget; namespace lyx { namespace frontend { @@ -46,11 +53,21 @@ QSize sizeHint() const; public Q_SLOTS: + /// the option filter changed + void filterChanged(QString const & search); + /// perform the search + void search(); + /// reset the search box + void resetSearch(); /// set current panel from an item void switchPanel(QTreeWidgetItem * it, QTreeWidgetItem * previous = 0); /// click on the tree void itemSelected(QTreeWidgetItem *, int); +protected: + /// widget hidden + void hideEvent(QHideEvent * event); + private: /// typedef QHash<QString, QTreeWidgetItem *> PanelMap; @@ -61,11 +78,18 @@ WidgetMap widget_map_; + /// contains the search box + FancyLineEdit * search_; + /// contains the items QTreeWidget * list_; /// contains the panes QStackedWidget * stack_; + + // timer to delay the search between options + QTimer * delay_search_; + }; } // namespace frontend