New ListView widget, based off of QGraphicsView, for fast and memory efficient list views
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QGraphicsItem>
|
||||
#include <QSize>
|
||||
#include <QRectF>
|
||||
#include <QPainter>
|
||||
#include <QWidget>
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsScene>
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class ListItem: public QGraphicsItem
|
||||
{
|
||||
public:
|
||||
bool selected = false;
|
||||
QSize size = QSize();
|
||||
|
||||
ListItem() = default;
|
||||
|
||||
[[nodiscard]] QRectF boundingRect() const override {
|
||||
return QRectF(QPointF(0, 0), this->size);
|
||||
}
|
||||
|
||||
virtual void onGeometryChanged() {
|
||||
return;
|
||||
}
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "ListScene.hpp"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QByteArray>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
ListScene::ListScene(
|
||||
std::vector<ListItem*> items,
|
||||
QGraphicsView* parent
|
||||
)
|
||||
: listItems(std::move(items))
|
||||
, parent(parent)
|
||||
, QGraphicsScene(parent)
|
||||
{
|
||||
this->setItemIndexMethod(QGraphicsScene::NoIndex);
|
||||
|
||||
for (const auto& listItem : this->listItems) {
|
||||
this->addItem(listItem);
|
||||
}
|
||||
}
|
||||
|
||||
void ListScene::refreshGeometry() {
|
||||
const auto* viewport = this->viewport();
|
||||
const auto viewportWidth = viewport != nullptr ? viewport->width() : 0;
|
||||
const auto viewportHeight = viewport != nullptr ? viewport->height() : 0;
|
||||
|
||||
const auto startXPosition = this->margins.left();
|
||||
auto startYPosition = this->margins.top();
|
||||
|
||||
for (auto& listItem : this->listItems) {
|
||||
if (!listItem->isVisible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
listItem->size.setWidth(viewportWidth);
|
||||
listItem->setPos(startXPosition, startYPosition);
|
||||
|
||||
listItem->onGeometryChanged();
|
||||
|
||||
startYPosition += listItem->size.height();
|
||||
}
|
||||
|
||||
this->setSceneRect(0, 0, viewportWidth, std::max(viewportHeight, startYPosition));
|
||||
this->update();
|
||||
}
|
||||
|
||||
void ListScene::setEnabled(bool enabled) {
|
||||
if (this->enabled == enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->enabled = enabled;
|
||||
|
||||
for (auto& item : this->listItems) {
|
||||
item->setEnabled(this->enabled);
|
||||
}
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
void ListScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) {
|
||||
const auto button = mouseEvent->button();
|
||||
|
||||
if (this->selectedItem != nullptr) {
|
||||
this->selectedItem->selected = false;
|
||||
this->selectedItem->update();
|
||||
this->selectedItem = nullptr;
|
||||
}
|
||||
|
||||
const auto mousePosition = mouseEvent->buttonDownScenePos(button);
|
||||
|
||||
const auto items = this->items(mousePosition);
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* clickedListItem = dynamic_cast<ListItem*>(items.first());
|
||||
if (clickedListItem == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->selectedItem = clickedListItem;
|
||||
|
||||
clickedListItem->selected = true;
|
||||
clickedListItem->update();
|
||||
|
||||
emit this->selectionChanged(this->selectedItem);
|
||||
emit this->itemClicked(clickedListItem);
|
||||
}
|
||||
|
||||
void ListScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent) {
|
||||
if (mouseEvent->button() != Qt::MouseButton::LeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mousePosition = mouseEvent->buttonDownScenePos(Qt::MouseButton::LeftButton);
|
||||
|
||||
const auto items = this->items(mousePosition);
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* clickedListItem = dynamic_cast<ListItem*>(items.first());
|
||||
if (clickedListItem == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit this->itemDoubleClicked(clickedListItem);
|
||||
}
|
||||
|
||||
void ListScene::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
|
||||
const auto items = this->items(event->scenePos());
|
||||
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* listItem = dynamic_cast<ListItem*>(items.first());
|
||||
if (listItem == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit this->itemContextMenu(listItem, event->screenPos());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QMargins>
|
||||
#include <vector>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QGraphicsSceneWheelEvent>
|
||||
#include <QGraphicsSceneContextMenuEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QPointF>
|
||||
|
||||
#include "ListItem.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class ListScene: public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QMargins margins = QMargins(0, 0, 0, 10);
|
||||
|
||||
ListScene(
|
||||
std::vector<ListItem*> items,
|
||||
QGraphicsView* parent
|
||||
);
|
||||
|
||||
void refreshGeometry();
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
signals:
|
||||
void selectionChanged(ListItem* selectedItem);
|
||||
void itemClicked(ListItem* item);
|
||||
void itemDoubleClicked(ListItem* item);
|
||||
void itemContextMenu(ListItem* item, QPoint sourcePosition);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
||||
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
||||
void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override;
|
||||
|
||||
private:
|
||||
const std::vector<ListItem*> listItems;
|
||||
QGraphicsView* const parent;
|
||||
bool enabled = false;
|
||||
ListItem* selectedItem = nullptr;
|
||||
|
||||
QWidget* viewport() const {
|
||||
const auto views = this->views();
|
||||
|
||||
if (!views.empty()) {
|
||||
return views.first()->viewport();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "ListView.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
ListView::ListView(
|
||||
const std::vector<ListItem*>& items,
|
||||
QWidget* parent
|
||||
)
|
||||
: QGraphicsView(parent)
|
||||
{
|
||||
this->setObjectName("list-graphics-view");
|
||||
this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
|
||||
this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
|
||||
this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
|
||||
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
this->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
|
||||
this->setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
|
||||
this->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true);
|
||||
this->setCacheMode(QGraphicsView::CacheModeFlag::CacheNone);
|
||||
this->setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
this->scene = new ListScene(std::move(items), this);
|
||||
this->setScene(this->scene);
|
||||
}
|
||||
|
||||
bool ListView::event(QEvent* event) {
|
||||
const auto eventType = event->type();
|
||||
if (eventType == QEvent::Type::EnabledChange) {
|
||||
this->scene->setEnabled(this->isEnabled());
|
||||
}
|
||||
|
||||
return QGraphicsView::event(event);
|
||||
}
|
||||
|
||||
void ListView::resizeEvent(QResizeEvent* event) {
|
||||
QGraphicsView::resizeEvent(event);
|
||||
|
||||
this->scene->refreshGeometry();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <vector>
|
||||
#include <QEvent>
|
||||
|
||||
#include "ListScene.hpp"
|
||||
#include "ListItem.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class ListView: public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListView(
|
||||
const std::vector<ListItem*>& items,
|
||||
QWidget* parent
|
||||
);
|
||||
|
||||
ListScene* listScene() const {
|
||||
return this->scene;
|
||||
}
|
||||
|
||||
protected:
|
||||
ListScene* scene = nullptr;
|
||||
|
||||
bool event(QEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user