forked from qt-creator/qt-creator
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: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/fancylineedit.h>
|
#include <utils/fancylineedit.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stylehelper.h>
|
#include <utils/stylehelper.h>
|
||||||
#include <utils/theme/theme.h>
|
#include <utils/theme/theme.h>
|
||||||
@@ -25,9 +26,11 @@
|
|||||||
|
|
||||||
#include <qdrawutil.h>
|
#include <qdrawutil.h>
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace WelcomePageHelpers;
|
||||||
|
|
||||||
static QColor themeColor(Theme::Color role)
|
static QColor themeColor(Theme::Color role)
|
||||||
{
|
{
|
||||||
@@ -123,6 +126,17 @@ SectionGridView::SectionGridView(QWidget *parent)
|
|||||||
: GridView(parent)
|
: GridView(parent)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
void SectionGridView::setMaxRows(std::optional<int> max)
|
||||||
|
{
|
||||||
|
m_maxRows = max;
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> SectionGridView::maxRows() const
|
||||||
|
{
|
||||||
|
return m_maxRows;
|
||||||
|
}
|
||||||
|
|
||||||
bool SectionGridView::hasHeightForWidth() const
|
bool SectionGridView::hasHeightForWidth() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -132,7 +146,16 @@ int SectionGridView::heightForWidth(int width) const
|
|||||||
{
|
{
|
||||||
const int columnCount = width / Core::ListItemDelegate::GridItemWidth;
|
const int columnCount = width / Core::ListItemDelegate::GridItemWidth;
|
||||||
const int rowCount = (model()->rowCount() + columnCount - 1) / columnCount;
|
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);
|
const QSize ListModel::defaultImageSize(214, 160);
|
||||||
@@ -646,7 +669,7 @@ SectionedGridView::SectionedGridView(QWidget *parent)
|
|||||||
auto sectionedView = new QWidget;
|
auto sectionedView = new QWidget;
|
||||||
auto layout = new QVBoxLayout;
|
auto layout = new QVBoxLayout;
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
layout->addStretch();
|
layout->addStretch(1);
|
||||||
sectionedView->setLayout(layout);
|
sectionedView->setLayout(layout);
|
||||||
area->setWidget(sectionedView);
|
area->setWidget(sectionedView);
|
||||||
|
|
||||||
@@ -678,8 +701,12 @@ void SectionedGridView::setPixmapFunction(const Core::ListModel::PixmapFunction
|
|||||||
void SectionedGridView::setSearchString(const QString &searchString)
|
void SectionedGridView::setSearchString(const QString &searchString)
|
||||||
{
|
{
|
||||||
if (searchString.isEmpty()) {
|
if (searchString.isEmpty()) {
|
||||||
// back to sectioned view
|
// back to previous view
|
||||||
setCurrentIndex(0);
|
m_allItemsView.reset();
|
||||||
|
if (m_zoomedInWidget)
|
||||||
|
setCurrentWidget(m_zoomedInWidget);
|
||||||
|
else
|
||||||
|
setCurrentIndex(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!m_allItemsView) {
|
if (!m_allItemsView) {
|
||||||
@@ -711,13 +738,19 @@ ListModel *SectionedGridView::addSection(const Section §ion, const QList<Lis
|
|||||||
gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
gridView->setModel(model);
|
gridView->setModel(model);
|
||||||
|
gridView->setMaxRows(section.maxRows);
|
||||||
|
|
||||||
m_sectionModels.insert(section, model);
|
m_sectionModels.insert(section, model);
|
||||||
const auto it = m_gridViews.insert(section, gridView);
|
const auto it = m_gridViews.insert(section, gridView);
|
||||||
|
|
||||||
auto sectionLabel = new QLabel(section.name);
|
using namespace Layouting;
|
||||||
|
auto seeAllLink = new QLabel("<a href=\"link\">" + Tr::tr("Show All") + " ></a>", 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);
|
m_sectionLabels.append(sectionLabel);
|
||||||
sectionLabel->setContentsMargins(0, Core::WelcomePageHelpers::ItemGap, 0, 0);
|
sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
|
||||||
sectionLabel->setFont(Core::WelcomePageHelpers::brandFont());
|
sectionLabel->setFont(Core::WelcomePageHelpers::brandFont());
|
||||||
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
|
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
|
||||||
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
|
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
|
||||||
@@ -753,4 +786,47 @@ void SectionedGridView::clear()
|
|||||||
m_allItemsView.reset();
|
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("<a href=\"link\">< " + Tr::tr("Back") + "</a>", 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<int> maxRows)
|
||||||
|
: name(name)
|
||||||
|
, priority(priority)
|
||||||
|
, maxRows(maxRows)
|
||||||
|
{}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@@ -51,8 +51,16 @@ class CORE_EXPORT SectionGridView : public GridView
|
|||||||
public:
|
public:
|
||||||
explicit SectionGridView(QWidget *parent);
|
explicit SectionGridView(QWidget *parent);
|
||||||
|
|
||||||
bool hasHeightForWidth() const;
|
void setMaxRows(std::optional<int> max);
|
||||||
int heightForWidth(int width) const;
|
std::optional<int> maxRows() const;
|
||||||
|
|
||||||
|
bool hasHeightForWidth() const override;
|
||||||
|
int heightForWidth(int width) const override;
|
||||||
|
|
||||||
|
void wheelEvent(QWheelEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<int> m_maxRows;
|
||||||
};
|
};
|
||||||
|
|
||||||
using OptModelIndex = std::optional<QModelIndex>;
|
using OptModelIndex = std::optional<QModelIndex>;
|
||||||
@@ -165,6 +173,9 @@ private:
|
|||||||
class CORE_EXPORT Section
|
class CORE_EXPORT Section
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Section(const QString &name, int priority);
|
||||||
|
Section(const QString &name, int priority, std::optional<int> maxRows);
|
||||||
|
|
||||||
friend bool operator<(const Section &lhs, const Section &rhs)
|
friend bool operator<(const Section &lhs, const Section &rhs)
|
||||||
{
|
{
|
||||||
if (lhs.priority < rhs.priority)
|
if (lhs.priority < rhs.priority)
|
||||||
@@ -179,6 +190,7 @@ public:
|
|||||||
|
|
||||||
QString name;
|
QString name;
|
||||||
int priority;
|
int priority;
|
||||||
|
std::optional<int> maxRows;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CORE_EXPORT SectionedGridView : public QStackedWidget
|
class CORE_EXPORT SectionedGridView : public QStackedWidget
|
||||||
@@ -196,11 +208,14 @@ public:
|
|||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void zoomInSection(const Section §ion);
|
||||||
|
|
||||||
QMap<Section, Core::ListModel *> m_sectionModels;
|
QMap<Section, Core::ListModel *> m_sectionModels;
|
||||||
QList<QWidget *> m_sectionLabels;
|
QList<QWidget *> m_sectionLabels;
|
||||||
QMap<Section, Core::GridView *> m_gridViews;
|
QMap<Section, Core::GridView *> m_gridViews;
|
||||||
std::unique_ptr<Core::ListModel> m_allItemsModel;
|
std::unique_ptr<Core::ListModel> m_allItemsModel;
|
||||||
std::unique_ptr<Core::GridView> m_allItemsView;
|
std::unique_ptr<Core::GridView> m_allItemsView;
|
||||||
|
QPointer<QWidget> m_zoomedInWidget;
|
||||||
Core::ListModel::PixmapFunction m_pixmapFunction;
|
Core::ListModel::PixmapFunction m_pixmapFunction;
|
||||||
QAbstractItemDelegate *m_itemDelegate = nullptr;
|
QAbstractItemDelegate *m_itemDelegate = nullptr;
|
||||||
};
|
};
|
||||||
|
@@ -329,7 +329,7 @@ static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second)
|
|||||||
return first->name.compare(second->name, Qt::CaseInsensitive) < 0;
|
return first->name.compare(second->name, Qt::CaseInsensitive) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<std::pair<QString, QList<ExampleItem *>>> getCategories(
|
static QList<std::pair<Section, QList<ExampleItem *>>> getCategories(
|
||||||
const QList<ExampleItem *> &items)
|
const QList<ExampleItem *> &items)
|
||||||
{
|
{
|
||||||
static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples");
|
static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples");
|
||||||
@@ -345,7 +345,7 @@ static QList<std::pair<QString, QList<ExampleItem *>>> getCategories(
|
|||||||
other.append(item);
|
other.append(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QList<std::pair<QString, QList<ExampleItem *>>> categories;
|
QList<std::pair<Section, QList<ExampleItem *>>> categories;
|
||||||
if (categoryMap.isEmpty()) {
|
if (categoryMap.isEmpty()) {
|
||||||
// The example set doesn't define categories. Consider the "highlighted" ones as "featured"
|
// The example set doesn't define categories. Consider the "highlighted" ones as "featured"
|
||||||
QList<ExampleItem *> featured;
|
QList<ExampleItem *> featured;
|
||||||
@@ -354,15 +354,19 @@ static QList<std::pair<QString, QList<ExampleItem *>>> getCategories(
|
|||||||
return i->isHighlighted;
|
return i->isHighlighted;
|
||||||
});
|
});
|
||||||
if (!featured.isEmpty())
|
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())
|
if (!allOther.isEmpty())
|
||||||
categories.append({otherDisplayName, allOther});
|
categories.append({{otherDisplayName, 1}, allOther});
|
||||||
} else {
|
} else {
|
||||||
|
int index = 0;
|
||||||
const auto end = categoryMap.constKeyValueEnd();
|
const auto end = categoryMap.constKeyValueEnd();
|
||||||
for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it)
|
for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) {
|
||||||
categories.append(*it);
|
categories.append({{it->first, index, /*maxRows=*/index == 0 ? 2 : 1}, it->second});
|
||||||
|
++index;
|
||||||
|
}
|
||||||
if (!other.isEmpty())
|
if (!other.isEmpty())
|
||||||
categories.append({otherDisplayName, other});
|
categories.append({{otherDisplayName, index, /*maxRows=*/1}, other});
|
||||||
}
|
}
|
||||||
const auto end = categories.end();
|
const auto end = categories.end();
|
||||||
for (auto it = categories.begin(); it != end; ++it)
|
for (auto it = categories.begin(); it != end; ++it)
|
||||||
@@ -414,9 +418,9 @@ void ExamplesViewController::updateExamples()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QList<std::pair<QString, QList<ExampleItem *>>> sections = getCategories(items);
|
const QList<std::pair<Section, QList<ExampleItem *>>> sections = getCategories(items);
|
||||||
for (int i = 0; i < sections.size(); ++i) {
|
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<ListItem *>(sections.at(i).second));
|
static_container_cast<ListItem *>(sections.at(i).second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,7 +523,7 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, QStrin
|
|||||||
const QStringList demosPattern(QLatin1String("demos-manifest.xml"));
|
const QStringList demosPattern(QLatin1String("demos-manifest.xml"));
|
||||||
QFileInfoList fis;
|
QFileInfoList fis;
|
||||||
const QFileInfoList subDirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
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(examplesPattern);
|
||||||
fis << QDir(subDir.absoluteFilePath()).entryInfoList(demosPattern);
|
fis << QDir(subDir.absoluteFilePath()).entryInfoList(demosPattern);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user