From 9650a2bded4990108e96a6d0d7afe7d400cb928c Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 15 Mar 2023 12:54:51 +0100 Subject: [PATCH] Examples: Limit categories to single row and allow showing all Limit the items shown for categories to a single row (two rows for first, "featured" category), and add a "Show All" link/button in the heading, that shows all items in that category only (with a "Back" button). The "Search in Examples" input ignores categories. Change-Id: I7c8561a306ad86c3b537587621d3fd030cd08af8 Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/coreplugin/welcomepagehelper.cpp | 90 ++++++++++++++++++-- src/plugins/coreplugin/welcomepagehelper.h | 19 ++++- src/plugins/qtsupport/exampleslistmodel.cpp | 24 +++--- 3 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp index 0e8f1211bf2..bc3afe89ee1 100644 --- a/src/plugins/coreplugin/welcomepagehelper.cpp +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -25,9 +26,11 @@ #include +using namespace Utils; + namespace Core { -using namespace Utils; +using namespace WelcomePageHelpers; static QColor themeColor(Theme::Color role) { @@ -123,6 +126,17 @@ SectionGridView::SectionGridView(QWidget *parent) : GridView(parent) {} +void SectionGridView::setMaxRows(std::optional max) +{ + m_maxRows = max; + updateGeometry(); +} + +std::optional SectionGridView::maxRows() const +{ + return m_maxRows; +} + bool SectionGridView::hasHeightForWidth() const { return true; @@ -132,7 +146,16 @@ int SectionGridView::heightForWidth(int width) const { const int columnCount = width / Core::ListItemDelegate::GridItemWidth; const int rowCount = (model()->rowCount() + columnCount - 1) / columnCount; - return rowCount * Core::ListItemDelegate::GridItemHeight; + const int maxRowCount = m_maxRows ? std::min(*m_maxRows, rowCount) : rowCount; + return maxRowCount * Core::ListItemDelegate::GridItemHeight; +} + +void SectionGridView::wheelEvent(QWheelEvent *e) +{ + if (m_maxRows) // circumvent scrolling of the list view + QWidget::wheelEvent(e); + else + GridView::wheelEvent(e); } const QSize ListModel::defaultImageSize(214, 160); @@ -646,7 +669,7 @@ SectionedGridView::SectionedGridView(QWidget *parent) auto sectionedView = new QWidget; auto layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 0); - layout->addStretch(); + layout->addStretch(1); sectionedView->setLayout(layout); area->setWidget(sectionedView); @@ -678,8 +701,12 @@ void SectionedGridView::setPixmapFunction(const Core::ListModel::PixmapFunction void SectionedGridView::setSearchString(const QString &searchString) { if (searchString.isEmpty()) { - // back to sectioned view - setCurrentIndex(0); + // back to previous view + m_allItemsView.reset(); + if (m_zoomedInWidget) + setCurrentWidget(m_zoomedInWidget); + else + setCurrentIndex(0); return; } if (!m_allItemsView) { @@ -711,13 +738,19 @@ ListModel *SectionedGridView::addSection(const Section §ion, const QListsetVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); gridView->setModel(model); + gridView->setMaxRows(section.maxRows); m_sectionModels.insert(section, model); const auto it = m_gridViews.insert(section, gridView); - auto sectionLabel = new QLabel(section.name); + using namespace Layouting; + auto seeAllLink = new QLabel("" + Tr::tr("Show All") + " >", this); + seeAllLink->setVisible(gridView->maxRows().has_value()); + connect(seeAllLink, &QLabel::linkActivated, this, [this, section] { zoomInSection(section); }); + QWidget *sectionLabel = Column{hr, Row{section.name, st, seeAllLink, Space(HSpacing)}}.emerge( + Layouting::WithoutMargins); m_sectionLabels.append(sectionLabel); - sectionLabel->setContentsMargins(0, Core::WelcomePageHelpers::ItemGap, 0, 0); + sectionLabel->setContentsMargins(0, ItemGap, 0, 0); sectionLabel->setFont(Core::WelcomePageHelpers::brandFont()); auto scrollArea = qobject_cast(widget(0)); auto vbox = qobject_cast(scrollArea->widget()->layout()); @@ -753,4 +786,47 @@ void SectionedGridView::clear() m_allItemsView.reset(); } +void SectionedGridView::zoomInSection(const Section §ion) +{ + auto zoomedInWidget = new QWidget(this); + auto layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + zoomedInWidget->setLayout(layout); + + using namespace Layouting; + auto backLink = new QLabel("< " + Tr::tr("Back") + "", zoomedInWidget); + connect(backLink, &QLabel::linkActivated, this, [this, zoomedInWidget] { + removeWidget(zoomedInWidget); + delete zoomedInWidget; + setCurrentIndex(0); + }); + QWidget *sectionLabel = Column{hr, Row{section.name, st, backLink, Space(HSpacing)}}.emerge( + Layouting::WithoutMargins); + sectionLabel->setContentsMargins(0, ItemGap, 0, 0); + sectionLabel->setFont(Core::WelcomePageHelpers::brandFont()); + + auto gridView = new GridView(zoomedInWidget); + gridView->setItemDelegate(m_itemDelegate); + gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + gridView->setModel(m_sectionModels.value(section)); + + layout->addWidget(sectionLabel); + layout->addWidget(gridView); + + m_zoomedInWidget = zoomedInWidget; + addWidget(zoomedInWidget); + setCurrentWidget(zoomedInWidget); +} + +Section::Section(const QString &name, int priority) + : name(name) + , priority(priority) +{} + +Section::Section(const QString &name, int priority, std::optional maxRows) + : name(name) + , priority(priority) + , maxRows(maxRows) +{} + } // namespace Core diff --git a/src/plugins/coreplugin/welcomepagehelper.h b/src/plugins/coreplugin/welcomepagehelper.h index cf9f438aced..8308d65311b 100644 --- a/src/plugins/coreplugin/welcomepagehelper.h +++ b/src/plugins/coreplugin/welcomepagehelper.h @@ -51,8 +51,16 @@ class CORE_EXPORT SectionGridView : public GridView public: explicit SectionGridView(QWidget *parent); - bool hasHeightForWidth() const; - int heightForWidth(int width) const; + void setMaxRows(std::optional max); + std::optional maxRows() const; + + bool hasHeightForWidth() const override; + int heightForWidth(int width) const override; + + void wheelEvent(QWheelEvent *e) override; + +private: + std::optional m_maxRows; }; using OptModelIndex = std::optional; @@ -165,6 +173,9 @@ private: class CORE_EXPORT Section { public: + Section(const QString &name, int priority); + Section(const QString &name, int priority, std::optional maxRows); + friend bool operator<(const Section &lhs, const Section &rhs) { if (lhs.priority < rhs.priority) @@ -179,6 +190,7 @@ public: QString name; int priority; + std::optional maxRows; }; class CORE_EXPORT SectionedGridView : public QStackedWidget @@ -196,11 +208,14 @@ public: void clear(); private: + void zoomInSection(const Section §ion); + QMap m_sectionModels; QList m_sectionLabels; QMap m_gridViews; std::unique_ptr m_allItemsModel; std::unique_ptr m_allItemsView; + QPointer m_zoomedInWidget; Core::ListModel::PixmapFunction m_pixmapFunction; QAbstractItemDelegate *m_itemDelegate = nullptr; }; diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp index c567f992e41..33817e08c40 100644 --- a/src/plugins/qtsupport/exampleslistmodel.cpp +++ b/src/plugins/qtsupport/exampleslistmodel.cpp @@ -329,7 +329,7 @@ static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second) return first->name.compare(second->name, Qt::CaseInsensitive) < 0; } -static QList>> getCategories( +static QList>> getCategories( const QList &items) { static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples"); @@ -345,7 +345,7 @@ static QList>> getCategories( other.append(item); } } - QList>> categories; + QList>> categories; if (categoryMap.isEmpty()) { // The example set doesn't define categories. Consider the "highlighted" ones as "featured" QList featured; @@ -354,15 +354,19 @@ static QList>> getCategories( return i->isHighlighted; }); if (!featured.isEmpty()) - categories.append({Tr::tr("Featured", "Category for highlighted examples"), featured}); + categories.append( + {{Tr::tr("Featured", "Category for highlighted examples"), 0}, featured}); if (!allOther.isEmpty()) - categories.append({otherDisplayName, allOther}); + categories.append({{otherDisplayName, 1}, allOther}); } else { + int index = 0; const auto end = categoryMap.constKeyValueEnd(); - for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) - categories.append(*it); + for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) { + categories.append({{it->first, index, /*maxRows=*/index == 0 ? 2 : 1}, it->second}); + ++index; + } if (!other.isEmpty()) - categories.append({otherDisplayName, other}); + categories.append({{otherDisplayName, index, /*maxRows=*/1}, other}); } const auto end = categories.end(); for (auto it = categories.begin(); it != end; ++it) @@ -414,9 +418,9 @@ void ExamplesViewController::updateExamples() } } - const QList>> sections = getCategories(items); + const QList>> sections = getCategories(items); for (int i = 0; i < sections.size(); ++i) { - m_view->addSection({sections.at(i).first, i}, + m_view->addSection(sections.at(i).first, static_container_cast(sections.at(i).second)); } } @@ -519,7 +523,7 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QStrin const QStringList demosPattern(QLatin1String("demos-manifest.xml")); QFileInfoList fis; const QFileInfoList subDirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (QFileInfo subDir : subDirs) { + for (const QFileInfo &subDir : subDirs) { fis << QDir(subDir.absoluteFilePath()).entryInfoList(examplesPattern); fis << QDir(subDir.absoluteFilePath()).entryInfoList(demosPattern); }