diff options
Diffstat (limited to 'src/libs/installer')
-rw-r--r-- | src/libs/installer/componentmodel.cpp | 9 | ||||
-rw-r--r-- | src/libs/installer/componentselectionpage_p.cpp | 91 | ||||
-rw-r--r-- | src/libs/installer/componentselectionpage_p.h | 6 | ||||
-rw-r--r-- | src/libs/installer/componentsortfilterproxymodel.cpp | 152 | ||||
-rw-r--r-- | src/libs/installer/componentsortfilterproxymodel.h | 63 | ||||
-rw-r--r-- | src/libs/installer/installer.pro | 2 |
6 files changed, 303 insertions, 20 deletions
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp index aeaa0b2fc..56ee57fc6 100644 --- a/src/libs/installer/componentmodel.cpp +++ b/src/libs/installer/componentmodel.cpp @@ -511,15 +511,6 @@ void ComponentModel::updateAndEmitModelState() } emit checkStateChanged(m_modelState); - - foreach (const Component *component, m_rootComponentList) { - emit dataChanged(indexFromComponentName(component->treeName()), - indexFromComponentName(component->treeName())); - QList<Component *> children = component->childItems(); - foreach (const Component *child, children) - emit dataChanged(indexFromComponentName(child->treeName()), - indexFromComponentName(child->treeName())); - } } void ComponentModel::collectComponents(Component *const component, const QModelIndex &parent) const diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp index a180888d1..7717f4cf3 100644 --- a/src/libs/installer/componentselectionpage_p.cpp +++ b/src/libs/installer/componentselectionpage_p.cpp @@ -50,6 +50,7 @@ #include <QStackedLayout> #include <QStackedWidget> #include <QToolBox> +#include <QLineEdit> namespace QInstaller { @@ -71,8 +72,11 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP , m_descriptionBaseWidget(nullptr) , m_categoryWidget(Q_NULLPTR) , m_categoryLayoutVisible(false) + , m_proxyModel(new ComponentSortFilterProxyModel(q)) { m_treeView->setObjectName(QLatin1String("ComponentsTreeView")); + m_proxyModel->setRecursiveFilteringEnabled(true); + m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_descriptionBaseWidget = new QWidget(q); m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget")); @@ -155,9 +159,18 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP metaLayout->addWidget(m_progressBar); metaLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); + m_searchLineEdit = new QLineEdit(q); + m_searchLineEdit->setObjectName(QLatin1String("SearchLineEdit")); + m_searchLineEdit->setPlaceholderText(ComponentSelectionPage::tr("Search")); + m_searchLineEdit->setClearButtonEnabled(true); + connect(m_searchLineEdit, &QLineEdit::textChanged, + this, &ComponentSelectionPagePrivate::setSearchPattern); + connect(q, &ComponentSelectionPage::entered, m_searchLineEdit, &QLineEdit::clear); + QVBoxLayout *treeViewVLayout = new QVBoxLayout; treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout")); treeViewVLayout->addWidget(m_treeView, 3); + treeViewVLayout->addWidget(m_searchLineEdit); QWidget *mainStackedWidget = new QWidget(); m_mainGLayout = new QGridLayout(mainStackedWidget); @@ -292,15 +305,11 @@ void ComponentSelectionPagePrivate::updateTreeView() this, &ComponentSelectionPagePrivate::currentSelectedChanged); } + m_searchLineEdit->setVisible(!m_core->isUpdater()); m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel; - m_treeView->setModel(m_currentModel); - m_treeView->setExpanded(m_currentModel->index(0, 0), true); - foreach (Component *component, m_core->components(PackageManagerCore::ComponentType::All)) { - if (component->isExpandedByDefault()) { - const QModelIndex index = m_currentModel->indexFromComponentName(component->treeName()); - m_treeView->setExpanded(index, true); - } - } + m_proxyModel->setSourceModel(m_currentModel); + m_treeView->setModel(m_proxyModel); + expandDefault(); const bool installActionColumnVisible = m_core->settings().installActionColumnVisible(); if (!installActionColumnVisible) @@ -341,7 +350,44 @@ void ComponentSelectionPagePrivate::updateTreeView() connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ComponentSelectionPagePrivate::currentSelectedChanged); - m_treeView->setCurrentIndex(m_currentModel->index(0, 0)); + m_treeView->setCurrentIndex(m_proxyModel->index(0, 0)); +} + +/*! + Expands components that should be expanded by default. +*/ +void ComponentSelectionPagePrivate::expandDefault() +{ + m_treeView->setExpanded(m_proxyModel->index(0, 0), true); + foreach (auto *component, m_core->components(PackageManagerCore::ComponentType::All)) { + if (component->isExpandedByDefault()) { + const QModelIndex index = m_proxyModel->mapFromSource( + m_currentModel->indexFromComponentName(component->treeName())); + m_treeView->setExpanded(index, true); + } + } +} + +/*! + Expands components that were accepted by proxy models filter. +*/ +void ComponentSelectionPagePrivate::expandSearchResults() +{ + // Expand parents of root indexes accepted by filter + const QVector<QModelIndex> acceptedIndexes = m_proxyModel->directlyAcceptedIndexes(); + for (auto proxyModelIndex : acceptedIndexes) { + if (!proxyModelIndex.isValid()) + continue; + + QModelIndex index = proxyModelIndex.parent(); + while (index.isValid()) { + if (m_treeView->isExpanded(index)) + break; // Multiple direct matches in a branch, can be skipped + + m_treeView->expand(index); + index = index.parent(); + } + } } void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex ¤t) @@ -351,12 +397,12 @@ void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &cu m_sizeLabel->setText(QString()); - QString description = m_currentModel->data(m_currentModel->index(current.row(), + QString description = m_proxyModel->data(m_proxyModel->index(current.row(), ComponentModelHelper::NameColumn, current.parent()), Qt::ToolTipRole).toString(); m_descriptionLabel->setText(description); - Component *component = m_currentModel->componentFromIndex(current); + Component *component = m_currentModel->componentFromIndex(m_proxyModel->mapToSource(current)); if ((m_core->isUninstaller()) || (!component)) return; @@ -429,6 +475,7 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories() QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error()); } updateWidgetVisibility(false); + m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults(); } void ComponentSelectionPagePrivate::customButtonClicked(int which) @@ -507,4 +554,26 @@ void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentMod currentSelectedChanged(m_treeView->selectionModel()->currentIndex()); } +/*! + Sets the new filter pattern to \a text and expands the tree nodes. +*/ +void ComponentSelectionPagePrivate::setSearchPattern(const QString &text) +{ + m_proxyModel->setFilterWildcard(text); + + m_treeView->collapseAll(); + if (text.isEmpty()) { + // Expand user selection and default expanded, ensure selected is visible + QModelIndex index = m_treeView->selectionModel()->currentIndex(); + while (index.isValid()) { + m_treeView->expand(index); + index = index.parent(); + } + expandDefault(); + m_treeView->scrollTo(m_treeView->selectionModel()->currentIndex()); + } else { + expandSearchResults(); + } +} + } // namespace QInstaller diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h index d2d30e3f0..fc37ebdaa 100644 --- a/src/libs/installer/componentselectionpage_p.h +++ b/src/libs/installer/componentselectionpage_p.h @@ -34,6 +34,7 @@ #include "componentmodel.h" #include "packagemanagergui.h" +#include "componentsortfilterproxymodel.h" class QTreeView; class QLabel; @@ -70,6 +71,8 @@ public: void setupCategoryLayout(); void showCategoryLayout(bool show); void updateTreeView(); + void expandDefault(); + void expandSearchResults(); public slots: void currentSelectedChanged(const QModelIndex ¤t); @@ -84,6 +87,7 @@ public slots: void setTotalProgress(int totalProgress); void selectDefault(); void onModelStateChanged(QInstaller::ComponentModel::ModelState state); + void setSearchPattern(const QString &text); private: ComponentSelectionPage *q; @@ -107,6 +111,8 @@ private: ComponentModel *m_updaterModel; ComponentModel *m_currentModel; QStackedLayout *m_stackedLayout; + ComponentSortFilterProxyModel *m_proxyModel; + QLineEdit *m_searchLineEdit; }; } // namespace QInstaller diff --git a/src/libs/installer/componentsortfilterproxymodel.cpp b/src/libs/installer/componentsortfilterproxymodel.cpp new file mode 100644 index 000000000..ab736a7c9 --- /dev/null +++ b/src/libs/installer/componentsortfilterproxymodel.cpp @@ -0,0 +1,152 @@ +/************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "componentsortfilterproxymodel.h" + +namespace QInstaller { + +/*! + \class QInstaller::ComponentSortFilterProxyModel + \inmodule QtInstallerFramework + \brief The ComponentSortFilterProxyModel provides support for sorting and + filtering data passed between another model and a view. + + The class subclasses QSortFilterProxyModel. Compared to the base class, + filters affect also child indexes in the base model, meaning if a + certain row has a parent that is accepted by filter, it is also accepted. + A distinction is made betweed directly and indirectly accepted indexes. +*/ + +/*! + \enum ComponentSortFilterProxyModel::AcceptType + + This enum holds the possible values for filter acception type for model indexes. + + \value Direct + Index was accepted directly by filter. + \value Descendant + Index is a descendant of an accepted index. + \value Reject + Index was not accepted by filter. +*/ + +/*! + Constructs object with \a parent. +*/ +ComponentSortFilterProxyModel::ComponentSortFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +/*! + Returns a list of source model indexes that were accepted directly by the filter. +*/ +QVector<QModelIndex> ComponentSortFilterProxyModel::directlyAcceptedIndexes() const +{ + QVector<QModelIndex> indexes; + for (int i = 0; i < rowCount(); i++) { + QModelIndex childIndex = index(i, 0, QModelIndex()); + findDirectlyAcceptedIndexes(childIndex, indexes); + } + return indexes; +} + +/*! + Returns \c true if the item in the row indicated by the given \a sourceRow and + \a sourceParent should be included in the model; otherwise returns \c false. +*/ +bool ComponentSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + return acceptsRow(sourceRow, sourceParent); +} + +/*! + Returns \c true if the item in the row indicated by the given \a sourceRow and + \a sourceParent should be included in the model; otherwise returns \c false. The + acception type can be retrieved with \a type. +*/ +bool ComponentSortFilterProxyModel::acceptsRow(int sourceRow, const QModelIndex &sourceParent, + AcceptType *type) const +{ + if (type) + *type = AcceptType::Rejected; + + if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) { + if (type) + *type = AcceptType::Direct; + return true; + } + + if (!isRecursiveFilteringEnabled()) // filter not applied for children + return false; + + if (!sourceParent.isValid()) // already root + return false; + + int currentRow = sourceParent.row(); + QModelIndex currentParent = sourceParent.parent(); + // Ascend and check if any parent is accepted + forever { + if (QSortFilterProxyModel::filterAcceptsRow(currentRow, currentParent)) { + if (type) + *type = AcceptType::Descendant; + return true; + } + if (!currentParent.isValid()) // we hit a root node + break; + + currentRow = currentParent.row(); + currentParent = currentParent.parent(); + } + return false; +} + +/*! + Finds directly accepted child \a indexes for parent index \a in. Returns \c true + if at least one accepted index was found, \c false otherwise. +*/ +bool ComponentSortFilterProxyModel::findDirectlyAcceptedIndexes(const QModelIndex &in, QVector<QModelIndex> &indexes) const +{ + bool found = false; + for (int i = 0; i < rowCount(in); i++) { + if (findDirectlyAcceptedIndexes(index(i, 0, in), indexes)) + found = true; + } + if (!hasChildren(in) || !found) { // No need to check current if any child matched + AcceptType acceptType; + const QModelIndex sourceIndex = mapToSource(in); + acceptsRow(sourceIndex.row(), sourceIndex.parent(), &acceptType); + if (acceptType == AcceptType::Direct) { + indexes.append(in); + found = true; + } + } + return found; +} + +} // namespace QInstaller diff --git a/src/libs/installer/componentsortfilterproxymodel.h b/src/libs/installer/componentsortfilterproxymodel.h new file mode 100644 index 000000000..a6167d17b --- /dev/null +++ b/src/libs/installer/componentsortfilterproxymodel.h @@ -0,0 +1,63 @@ +/************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef COMPONENTSORTFILTERPROXYMODEL_H +#define COMPONENTSORTFILTERPROXYMODEL_H + +#include "installer_global.h" + +#include <QSortFilterProxyModel> + +namespace QInstaller { + +class INSTALLER_EXPORT ComponentSortFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + enum AcceptType { + Direct, + Descendant, + Rejected + }; + + explicit ComponentSortFilterProxyModel(QObject *parent = nullptr); + + QVector<QModelIndex> directlyAcceptedIndexes() const; + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE; + +private: + bool acceptsRow(int sourceRow, const QModelIndex &sourceParent, AcceptType *type = nullptr) const; + bool findDirectlyAcceptedIndexes(const QModelIndex &in, QVector<QModelIndex> &indexes) const; +}; + +} // namespace QInstaller + +#endif // COMPONENTSORTFILTERPROXYMODEL_H diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index 4721bb089..ed7d739ed 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -44,6 +44,7 @@ win32:QT += winextras HEADERS += packagemanagercore.h \ aspectratiolabel.h \ + componentsortfilterproxymodel.h \ loggingutils.h \ packagemanagercore_p.h \ packagemanagergui.h \ @@ -150,6 +151,7 @@ SOURCES += packagemanagercore.cpp \ aspectratiolabel.cpp \ directoryguard.cpp \ lib7zarchive.cpp \ + componentsortfilterproxymodel.cpp \ loggingutils.cpp \ packagemanagercore_p.cpp \ packagemanagergui.cpp \ |