New ListView widget, based off of QGraphicsView, for fast and memory efficient list views
This commit is contained in:
@@ -20,6 +20,10 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/ExpandingHeightScrollAreaWidget.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/ExpandingHeightScrollAreaWidget.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/PushButton.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/PushButton.cpp
|
||||||
|
|
||||||
|
# ListView widget
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/ListView/ListView.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/ListView/ListScene.cpp
|
||||||
|
|
||||||
# Insight worker tasks
|
# Insight worker tasks
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/InsightWorker/Tasks/InsightWorkerTask.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/InsightWorker/Tasks/InsightWorkerTask.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/InsightWorker/Tasks/ReadTargetRegisters.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/InsightWorker/Tasks/ReadTargetRegisters.cpp
|
||||||
|
|||||||
@@ -220,3 +220,8 @@ QCheckBox::indicator:unchecked {
|
|||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
image: url(":/compiled/src/Insight/UserInterfaces/InsightWindow/Images/unchecked-action-icon.svg");
|
image: url(":/compiled/src/Insight/UserInterfaces/InsightWindow/Images/unchecked-action-icon.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#list-graphics-view {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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