Skip to main content

List View

A virtualised list of themed item rows, for a set too large to be one widget per row.

Loading preview…
#include <QApplication>
#include <QFile>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QStandardItemModel>
#include <QUrl>
#include <QVBoxLayout>
#include <array>
#ifdef SHADCN_GALLERY_MEDIA
#include <shadcn/media.hpp>
#endif
#include <shadcn/shadcn.hpp>

QWidget* example(QWidget* parent = nullptr) {
using namespace shadcn;
auto* canvas = new QWidget(parent);
canvas->setAutoFillBackground(true);
auto* outer = new QHBoxLayout(canvas);
outer->setContentsMargins(32, 32, 32, 32);
auto* host = new QWidget(canvas);
auto* layout = new QVBoxLayout(host);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(16);
outer->addStretch();
outer->addWidget(host, 0, Qt::AlignCenter);
outer->addStretch();
// A course list: the shapes a library actually fills rows with, and one
// heading row among them so the heading treatment is visible.
auto* list = new ListView(host);
list->setFixedSize(400, 320);
list->setCompactBelow(260);
auto* model = new QStandardItemModel(list);
const auto row = [](const QString& title, const QString& description,
const QString& trailing) {
auto* item = new QStandardItem(title);
item->setData(description, static_cast<int>(RowRole::Description));
item->setData(trailing, static_cast<int>(RowRole::TrailingText));
return item;
};
auto* heading = new QStandardItem(QStringLiteral("In progress"));
heading->setData(true, static_cast<int>(RowRole::Heading));
heading->setFlags(Qt::ItemIsEnabled);
model->appendRow(heading);
model->appendRow(row(QStringLiteral("Systems 101"), QStringLiteral("Lesson 4 of 12"), QStringLiteral("41%")));
model->appendRow(row(QStringLiteral("Signals 201"), QStringLiteral("Lesson 2 of 9"), QStringLiteral("22%")));
model->appendRow(row(QStringLiteral("Probability 150"), QStringLiteral("Lesson 11 of 14"), QStringLiteral("79%")));
model->appendRow(row(QStringLiteral("Linear Algebra"), QStringLiteral("Lesson 1 of 20"), QStringLiteral("5%")));
model->appendRow(row(QStringLiteral("Compilers 310"), QStringLiteral("Lesson 7 of 8"), QStringLiteral("88%")));
list->setModel(model);
list->setCurrentIndex(model->index(2, 0));
layout->addWidget(list);
return canvas;
}

Installation​

add_subdirectory(shadcn-cpp)
target_link_libraries(app PRIVATE shadcn::widgets)

Usage​

ListView is a QListView, so a caller fills a model and the rows on screen are the only rows built. The title comes from Qt::DisplayRole; the rest of a row comes from a RowRole.

#include <QStandardItemModel>
#include <shadcn/rows.hpp>

shadcn::ListView list(parent);
auto* model = new QStandardItemModel(&list);
auto* item = new QStandardItem(QStringLiteral("Systems 101"));
item->setData(QStringLiteral("Lesson 4 of 12"),
static_cast<int>(shadcn::RowRole::Description));
item->setData(QStringLiteral("41%"),
static_cast<int>(shadcn::RowRole::TrailingText));
model->appendRow(item);
list.setModel(model);
list.setCurrentIndex(model->index(0, 0));

Set RowRole::Heading on a row to make it a label for the rows under it rather than a target: it takes no fill, no ring and no selection.

auto* heading = new QStandardItem(QStringLiteral("In progress"));
heading->setData(true, static_cast<int>(shadcn::RowRole::Heading));
heading->setFlags(Qt::ItemIsEnabled);

setCompactBelow gives the rows a density switch the caller does not have to own: below the given height the rows become shorter, and a compact row is still the same row.

list.setCompactBelow(400);

API​

MemberPurpose
setCompact(bool)Sets the row density directly.
compactRows()The current density.
setCompactBelow(int)The height at which rows become compact. Zero disables the switch.
compactBelow()That height.
rowHeight()The height one row occupies now.
visibleRowRects()The rectangles of the rows on screen.

RowRole​

RoleCarries
Qt::DisplayRoleThe title.
DescriptionThe muted line under the title.
LeadingA QPixmap before the text.
TrailingA QPixmap after the text.
TrailingTextA short muted label on the trailing side.
HeadingMarks a row as a label rather than a target.

Notes​

Every row is the same height, so a description occupies the second line whether or not it has text. A view whose rows differ in height has to measure all of them to find where the next one starts, which is the difference between paging through a library and walking it.

A selected row takes the accent fill and the accent foreground. The page foreground on an accent fill is the usual way a themed list loses contrast, and it looks right until the text is measured.