X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FPanelStack.cpp;h=c61c8e02442f531fb3eae7ac2c0abde51a46f900;hb=c0a1893008bd13650d470afff16f56720b65c87c;hp=69158e1a8e98e9c2caffff3b0aa8fe30f4de9999;hpb=7c8a7ef67c54dc2920f24826012366c382f82229;p=lyx.git diff --git a/src/frontends/qt4/PanelStack.cpp b/src/frontends/qt4/PanelStack.cpp index 69158e1a8e..c61c8e0244 100644 --- a/src/frontends/qt4/PanelStack.cpp +++ b/src/frontends/qt4/PanelStack.cpp @@ -12,19 +12,30 @@ #include "PanelStack.h" +#include "GuiApplication.h" #include "qt_helpers.h" #include "support/debug.h" +#include "support/lassert.h" +#include +#include +#include #include -#include -#include +#include +#include +#include #include #include - -#include "support/lassert.h" - -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace std; @@ -35,25 +46,52 @@ namespace frontend { PanelStack::PanelStack(QWidget * parent) : QWidget(parent) { + delay_search_ = new QTimer(this); + search_ = new FancyLineEdit(this); list_ = new QTreeWidget(this); stack_ = new QStackedWidget(this); + // Configure the timer + delay_search_->setSingleShot(true); + connect(delay_search_, SIGNAL(timeout()), this, SLOT(search())); + + // Configure tree list_->setRootIsDecorated(false); list_->setColumnCount(1); - // Hide the pointless list header list_->header()->hide(); -// QStringList HeaderLabels; -// HeaderLabels << QString("Category"); -// list_->setHeaderLabels(HeaderLabels); + setSectionResizeMode(list_->header(), QHeaderView::ResizeToContents); + list_->header()->setStretchLastSection(false); + list_->setMinimumSize(list_->viewport()->size()); - connect(list_, SIGNAL(currentItemChanged (QTreeWidgetItem*, QTreeWidgetItem*)), - this, SLOT(switchPanel(QTreeWidgetItem *, QTreeWidgetItem*))); + connect(list_, SIGNAL(currentItemChanged(QTreeWidgetItem *, + QTreeWidgetItem *)), + this, SLOT(switchPanel(QTreeWidgetItem *, QTreeWidgetItem *))); 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 + search_->setPlaceholderText(qt_("Search")); + search_->setButtonPixmap(FancyLineEdit::Right, + getPixmap("images/", "editclear", "svgz,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))); + connect(search_, SIGNAL(downPressed()), + list_, SLOT(setFocus())); + + // 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); } @@ -67,13 +105,13 @@ void PanelStack::addCategory(QString const & name, QString const & parent) if (parent.isEmpty()) { item = new QTreeWidgetItem(list_); - item->setText(0, name); + item->setText(0, qt_(name)); } else { if (!panel_map_.contains(parent)) addCategory(parent); item = new QTreeWidgetItem(panel_map_.value(parent)); - item->setText(0, name); + item->setText(0, qt_(name)); depth = 2; list_->setRootIsDecorated(true); } @@ -81,17 +119,17 @@ void PanelStack::addCategory(QString const & name, QString const & parent) panel_map_[name] = item; QFontMetrics fm(list_->font()); - + // calculate the real size the current item needs in the listview - int itemsize = fm.width(name) + 10 - + list_->indentation() * depth; + int itemsize = fm.width(qt_(name)) + 10 + list_->indentation() * depth; // adjust the listview width to the max. itemsize if (itemsize > list_->minimumWidth()) list_->setMinimumWidth(itemsize); } -void PanelStack::addPanel(QWidget * panel, QString const & name, QString const & parent) +void PanelStack::addPanel(QWidget * panel, QString const & name, + QString const & parent) { addCategory(name, parent); QTreeWidgetItem * item = panel_map_.value(name); @@ -101,10 +139,19 @@ void PanelStack::addPanel(QWidget * panel, QString const & name, QString const & } +void PanelStack::showPanel(QString const & name, bool show) +{ + QTreeWidgetItem * item = panel_map_.value(name, 0); + LASSERT(item, return); + + item->setHidden(!show); +} + + void PanelStack::setCurrentPanel(QString const & name) { QTreeWidgetItem * item = panel_map_.value(name, 0); - LASSERT(item, /**/); + LASSERT(item, return); // force on first set if (list_->currentItem() == item) @@ -114,20 +161,186 @@ void PanelStack::setCurrentPanel(QString const & name) } +bool PanelStack::isCurrentPanel(QString const & name) const +{ + QTreeWidgetItem * item = panel_map_.value(name, 0); + LASSERT(item, return false); + + return (list_->currentItem() == item); +} + + void PanelStack::switchPanel(QTreeWidgetItem * item, QTreeWidgetItem * previous) { + // do nothing when clicked on whitespace (item=NULL) + 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 != item->child(0)) - list_->setCurrentItem(item->child(0)); + 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; + } + } + } } - if (QWidget * w = widget_map_.value(item, 0)) + else if (QWidget * w = widget_map_.value(item, 0)) { stack_->setCurrentWidget(w); + } } +static 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); +} + +static 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 + for (QTreeWidgetItem * tree_item : panel_map_) { + setTreeItemStatus(tree_item, enable_all); + } + + for (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(); + for (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(child_widget)) { + widget_matches = matches(button->text(), search); + + } else if (QGroupBox * group_box = + qobject_cast(child_widget)) { + widget_matches = matches(group_box->title(), search); + + } else if (QLabel * label = + qobject_cast(child_widget)) { + widget_matches = matches(label->text(), search); + + } else if (QLineEdit * line_edit = + qobject_cast(child_widget)) { + widget_matches = matches(line_edit->text(), search); + + } else if (QListWidget * list_widget = + qobject_cast(child_widget)) { + widget_matches = + list_widget->findItems(search, + Qt::MatchContains).count() > 0; + + } else if (QTreeWidget * tree_view = + qobject_cast(child_widget)) { + widget_matches = + tree_view->findItems(search, + Qt::MatchContains).count() > 0; + + } else if (QComboBox * combo_box = + qobject_cast(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) { @@ -146,4 +359,4 @@ QSize PanelStack::sizeHint() const } // namespace frontend } // namespace lyx -#include "PanelStack_moc.cpp" +#include "moc_PanelStack.cpp"