Make categorized product/example view reusable

Extract a SectionedGridView from the SectionedProducts that are used in
the MarketPlace plugin, and make item delegate and pixmap fetching
function to be used with the model(s) pluggable.

Change-Id: I02aba87b27afd8ad18ff23346d1ac98da906db4b
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Eike Ziller
2023-01-10 12:15:49 +01:00
parent 6d79c5c2b3
commit e3acf9262b
4 changed files with 223 additions and 163 deletions

View File

@@ -13,12 +13,14 @@
#include <QEasingCurve>
#include <QFontDatabase>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QHoverEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QPainter>
#include <QPixmapCache>
#include <QScrollArea>
#include <QTimer>
#include <qdrawutil.h>
@@ -117,6 +119,22 @@ void GridView::leaveEvent(QEvent *)
viewportEvent(&hev); // Seemingly needed to kill the hover paint.
}
SectionGridView::SectionGridView(QWidget *parent)
: GridView(parent)
{}
bool SectionGridView::hasHeightForWidth() const
{
return true;
}
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 QSize ListModel::defaultImageSize(214, 160);
ListModel::ListModel(QObject *parent)
@@ -126,10 +144,23 @@ ListModel::ListModel(QObject *parent)
ListModel::~ListModel()
{
qDeleteAll(m_items);
if (m_ownsItems)
qDeleteAll(m_items);
m_items.clear();
}
void ListModel::appendItems(const QList<ListItem *> &items)
{
beginInsertRows(QModelIndex(), m_items.size(), m_items.size() + items.size());
m_items.append(items);
endInsertRows();
}
const QList<ListItem *> ListModel::items() const
{
return m_items;
}
int ListModel::rowCount(const QModelIndex &) const
{
return m_items.size();
@@ -166,6 +197,11 @@ void ListModel::setPixmapFunction(const PixmapFunction &fetchPixmapAndUpdatePixm
m_fetchPixmapAndUpdatePixmapCache = fetchPixmapAndUpdatePixmapCache;
}
void ListModel::setOwnsItems(bool owns)
{
m_ownsItems = owns;
}
ListModelFilter::ListModelFilter(ListModel *sourceModel, QObject *parent) :
QSortFilterProxyModel(parent)
{
@@ -346,6 +382,11 @@ void ListModelFilter::setSearchString(const QString &arg)
delayedUpdateFilter();
}
ListModel *ListModelFilter::sourceListModel() const
{
return static_cast<ListModel *>(sourceModel());
}
bool ListModelFilter::leaveFilterAcceptsRowBeforeFiltering(const ListItem *, bool *) const
{
return false;
@@ -583,4 +624,93 @@ void ListItemDelegate::goon()
m_currentWidget->update(m_previousIndex);
}
SectionedGridView::SectionedGridView(QWidget *parent)
: QStackedWidget(parent)
, m_allItemsView(new Core::GridView(this))
{
auto allItemsModel = new ListModel(this);
allItemsModel->setPixmapFunction(m_pixmapFunction);
// it just "borrows" the items from the section models:
allItemsModel->setOwnsItems(false);
m_filteredAllItemsModel = new Core::ListModelFilter(allItemsModel, this);
auto area = new QScrollArea(this);
area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
area->setFrameShape(QFrame::NoFrame);
area->setWidgetResizable(true);
auto sectionedView = new QWidget;
auto layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addStretch();
sectionedView->setLayout(layout);
area->setWidget(sectionedView);
addWidget(area);
m_allItemsView->setModel(m_filteredAllItemsModel);
addWidget(m_allItemsView);
}
SectionedGridView::~SectionedGridView() = default;
void SectionedGridView::setItemDelegate(QAbstractItemDelegate *delegate)
{
m_allItemsView->setItemDelegate(delegate);
for (GridView *view : std::as_const(m_gridViews))
view->setItemDelegate(delegate);
}
void SectionedGridView::setPixmapFunction(const Core::ListModel::PixmapFunction &pixmapFunction)
{
m_pixmapFunction = pixmapFunction;
auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
allProducts->setPixmapFunction(pixmapFunction);
for (ListModel *model : std::as_const(m_sectionModels))
model->setPixmapFunction(pixmapFunction);
}
void SectionedGridView::setSearchString(const QString &searchString)
{
int view = searchString.isEmpty() ? 0 // sectioned view
: 1; // search view
setCurrentIndex(view);
m_filteredAllItemsModel->setSearchString(searchString);
}
ListModel *SectionedGridView::addSection(const Section &section, const QList<ListItem *> &items)
{
auto model = new ListModel(this);
model->setPixmapFunction(m_pixmapFunction);
model->appendItems(items);
auto gridView = new SectionGridView(this);
gridView->setItemDelegate(m_allItemsView->itemDelegate());
gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gridView->setModel(model);
m_sectionModels.insert(section, model);
const auto it = m_gridViews.insert(section, gridView);
auto sectionLabel = new QLabel(section.name);
sectionLabel->setContentsMargins(0, Core::WelcomePageHelpers::ItemGap, 0, 0);
sectionLabel->setFont(Core::WelcomePageHelpers::brandFont());
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
// insert new section depending on its priority, but before the last (stretch) item
int position = std::distance(m_gridViews.begin(), it) * 2; // a section has a label and a grid
QTC_ASSERT(position <= vbox->count() - 1, position = vbox->count() - 1);
vbox->insertWidget(position, sectionLabel);
vbox->insertWidget(position + 1, gridView);
// add the items also to the all products model to be able to search correctly
auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
allProducts->appendItems(items);
return model;
}
} // namespace Core