> Style and form comments are always welcome but please give a > little attention to the content too: > - Alternative ideas to highlight matching widgets? Currently > they become red. > - Ideas on how to make the research faster? In the next patch > I'll try to add a little delay.
Here's the new patch. I've added a 300 ms delay, now it should be more responsive as the search it's not done for each charatecter in input. venom00 Index: src/frontends/qt4/GuiDocument.h =================================================================== --- src/frontends/qt4/GuiDocument.h (revisione 38474) +++ src/frontends/qt4/GuiDocument.h (copia locale) @@ -77,6 +77,7 @@ void updateDefaultFormat(); void updatePagestyle(std::string const &, std::string const &); bool isChildIncluded(std::string const &); + void hideView(); /// BufferParams const & params() const { return bp_; } Index: src/frontends/qt4/GuiDocument.cpp =================================================================== --- src/frontends/qt4/GuiDocument.cpp (revisione 38474) +++ src/frontends/qt4/GuiDocument.cpp (copia locale) @@ -2139,6 +2139,12 @@ includeonlys_.end(), child) != includeonlys_.end()); } +void GuiDocument::hideView() +{ + Dialog::hideView(); + // Reset the search box + this->docPS->resetSearch(); +} void GuiDocument::applyView() { Index: src/frontends/qt4/GuiPrefs.cpp =================================================================== --- src/frontends/qt4/GuiPrefs.cpp (revisione 38474) +++ src/frontends/qt4/GuiPrefs.cpp (copia locale) @@ -3201,6 +3201,12 @@ modules_[i]->update(rc); } +void GuiPreferences::hideView() +{ + Dialog::hideView(); + // Reset the search box + this->prefsPS->resetSearch(); +} void GuiPreferences::applyView() { Index: src/frontends/qt4/PanelStack.cpp =================================================================== --- src/frontends/qt4/PanelStack.cpp (revisione 38474) +++ src/frontends/qt4/PanelStack.cpp (copia locale) @@ -15,12 +15,23 @@ #include "qt_helpers.h" #include "support/debug.h" +#include "support/foreach.h" +#include <QApplication> +#include <QAbstractButton> +#include <QColorGroup> +#include <QComboBox> #include <QFontMetrics> +#include <QGroupBox> #include <QHBoxLayout> #include <QHeaderView> +#include <QLabel> +#include <QLineEdit> +#include <QListWidget> +#include <QPalette> #include <QStackedWidget> #include <QTreeWidget> +#include <QVBoxLayout> #include "support/lassert.h" @@ -33,9 +44,16 @@ PanelStack::PanelStack(QWidget * parent) : QWidget(parent) { + delay_search_ = new QTimer(this); list_ = new QTreeWidget(this); stack_ = new QStackedWidget(this); + search_ = new QLineEdit(this); + // Configure the timer + delay_search_->setSingleShot(true); + connect(delay_search_, SIGNAL(timeout()), this, SLOT(searchTimeout())); + + // Configure tree list_->setRootIsDecorated(false); list_->setColumnCount(1); list_->header()->hide(); @@ -48,9 +66,22 @@ 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 + + connect(search_, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + + // 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->addWidget(search_, 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 +163,138 @@ 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 & input, QString const & search) +{ + // Check if the input contains the search string + return input.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::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::searchTimeout() +{ + QString search = search_->text(); + bool enable_all = search.isEmpty(); + + // If the search string is empty we renable 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 @@ -162,6 +309,11 @@ qMax(list_->height(), stack_->height())); } +void PanelStack::resetSearch() +{ + search_->setText(QString()); +} + } // namespace frontend } // namespace lyx Index: src/frontends/qt4/PanelStack.h =================================================================== --- src/frontends/qt4/PanelStack.h (revisione 38474) +++ src/frontends/qt4/PanelStack.h (copia locale) @@ -13,12 +13,15 @@ #ifndef PANELSTACK_H #define PANELSTACK_H +#include <QHash> +#include <QTimer> #include <QWidget> -#include <QHash> +class QAbstractButton; +class QLineEdit; +class QStackedWidget; class QTreeWidget; class QTreeWidgetItem; -class QStackedWidget; namespace lyx { namespace frontend { @@ -44,8 +47,14 @@ bool isCurrentPanel(QString const & name) const; /// QSize sizeHint() const; + /// resets the search box + void resetSearch(); public Q_SLOTS: + /// the option filter changed + void filterChanged(QString const & search); + /// timeout for the search box + void searchTimeout(); /// set current panel from an item void switchPanel(QTreeWidgetItem * it, QTreeWidgetItem * previous = 0); /// click on the tree @@ -61,11 +70,17 @@ WidgetMap widget_map_; + /// contains the search box + QLineEdit * search_; + /// contains the items QTreeWidget * list_; /// contains the panes QStackedWidget * stack_; + + // timer to delay the search between options + QTimer * delay_search_; }; } // namespace frontend Index: src/frontends/qt4/Dialog.h =================================================================== --- src/frontends/qt4/Dialog.h (revisione 38474) +++ src/frontends/qt4/Dialog.h (copia locale) @@ -119,7 +119,7 @@ virtual void applyView() = 0; /// Hide the dialog from sight - void hideView(); + virtual void hideView(); /// Prepare dialog and display it. void showView(); Index: src/frontends/qt4/GuiPrefs.h =================================================================== --- src/frontends/qt4/GuiPrefs.h (revisione 38474) +++ src/frontends/qt4/GuiPrefs.h (copia locale) @@ -67,6 +67,7 @@ void apply(LyXRC & rc) const; void updateRc(LyXRC const & rc); + void hideView(); public Q_SLOTS: void change_adaptor();
Index: src/frontends/qt4/GuiDocument.h =================================================================== --- src/frontends/qt4/GuiDocument.h (revisione 38474) +++ src/frontends/qt4/GuiDocument.h (copia locale) @@ -77,6 +77,7 @@ void updateDefaultFormat(); void updatePagestyle(std::string const &, std::string const &); bool isChildIncluded(std::string const &); + void hideView(); /// BufferParams const & params() const { return bp_; } Index: src/frontends/qt4/GuiDocument.cpp =================================================================== --- src/frontends/qt4/GuiDocument.cpp (revisione 38474) +++ src/frontends/qt4/GuiDocument.cpp (copia locale) @@ -2139,6 +2139,12 @@ includeonlys_.end(), child) != includeonlys_.end()); } +void GuiDocument::hideView() +{ + Dialog::hideView(); + // Reset the search box + this->docPS->resetSearch(); +} void GuiDocument::applyView() { Index: src/frontends/qt4/GuiPrefs.cpp =================================================================== --- src/frontends/qt4/GuiPrefs.cpp (revisione 38474) +++ src/frontends/qt4/GuiPrefs.cpp (copia locale) @@ -3201,6 +3201,12 @@ modules_[i]->update(rc); } +void GuiPreferences::hideView() +{ + Dialog::hideView(); + // Reset the search box + this->prefsPS->resetSearch(); +} void GuiPreferences::applyView() { Index: src/frontends/qt4/PanelStack.cpp =================================================================== --- src/frontends/qt4/PanelStack.cpp (revisione 38474) +++ src/frontends/qt4/PanelStack.cpp (copia locale) @@ -15,12 +15,23 @@ #include "qt_helpers.h" #include "support/debug.h" +#include "support/foreach.h" +#include <QApplication> +#include <QAbstractButton> +#include <QColorGroup> +#include <QComboBox> #include <QFontMetrics> +#include <QGroupBox> #include <QHBoxLayout> #include <QHeaderView> +#include <QLabel> +#include <QLineEdit> +#include <QListWidget> +#include <QPalette> #include <QStackedWidget> #include <QTreeWidget> +#include <QVBoxLayout> #include "support/lassert.h" @@ -33,9 +44,16 @@ PanelStack::PanelStack(QWidget * parent) : QWidget(parent) { + delay_search_ = new QTimer(this); list_ = new QTreeWidget(this); stack_ = new QStackedWidget(this); + search_ = new QLineEdit(this); + // Configure the timer + delay_search_->setSingleShot(true); + connect(delay_search_, SIGNAL(timeout()), this, SLOT(searchTimeout())); + + // Configure tree list_->setRootIsDecorated(false); list_->setColumnCount(1); list_->header()->hide(); @@ -48,9 +66,22 @@ 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 + + connect(search_, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + + // 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->addWidget(search_, 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 +163,138 @@ 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 & input, QString const & search) +{ + // Check if the input contains the search string + return input.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::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::searchTimeout() +{ + QString search = search_->text(); + bool enable_all = search.isEmpty(); + + // If the search string is empty we renable 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 @@ -162,6 +309,11 @@ qMax(list_->height(), stack_->height())); } +void PanelStack::resetSearch() +{ + search_->setText(QString()); +} + } // namespace frontend } // namespace lyx Index: src/frontends/qt4/PanelStack.h =================================================================== --- src/frontends/qt4/PanelStack.h (revisione 38474) +++ src/frontends/qt4/PanelStack.h (copia locale) @@ -13,12 +13,15 @@ #ifndef PANELSTACK_H #define PANELSTACK_H +#include <QHash> +#include <QTimer> #include <QWidget> -#include <QHash> +class QAbstractButton; +class QLineEdit; +class QStackedWidget; class QTreeWidget; class QTreeWidgetItem; -class QStackedWidget; namespace lyx { namespace frontend { @@ -44,8 +47,14 @@ bool isCurrentPanel(QString const & name) const; /// QSize sizeHint() const; + /// resets the search box + void resetSearch(); public Q_SLOTS: + /// the option filter changed + void filterChanged(QString const & search); + /// timeout for the search box + void searchTimeout(); /// set current panel from an item void switchPanel(QTreeWidgetItem * it, QTreeWidgetItem * previous = 0); /// click on the tree @@ -61,11 +70,17 @@ WidgetMap widget_map_; + /// contains the search box + QLineEdit * search_; + /// contains the items QTreeWidget * list_; /// contains the panes QStackedWidget * stack_; + + // timer to delay the search between options + QTimer * delay_search_; }; } // namespace frontend Index: src/frontends/qt4/Dialog.h =================================================================== --- src/frontends/qt4/Dialog.h (revisione 38474) +++ src/frontends/qt4/Dialog.h (copia locale) @@ -119,7 +119,7 @@ virtual void applyView() = 0; /// Hide the dialog from sight - void hideView(); + virtual void hideView(); /// Prepare dialog and display it. void showView(); Index: src/frontends/qt4/GuiPrefs.h =================================================================== --- src/frontends/qt4/GuiPrefs.h (revisione 38474) +++ src/frontends/qt4/GuiPrefs.h (copia locale) @@ -67,6 +67,7 @@ void apply(LyXRC & rc) const; void updateRc(LyXRC const & rc); + void hideView(); public Q_SLOTS: void change_adaptor();