Qt编写自定义控件:可交互的列表控件

cpp 复制代码
#ifndef LISTWIDGET_H
#define LISTWIDGET_H

#include <QWidget>

struct ListItem
{
    QString text;
    QRect rect;
    QString icon{":/setting.png"};
};

class ListWidget : public QWidget
{
    Q_OBJECT
public:
    explicit ListWidget(QWidget *parent = nullptr);
    void setSize(int newSize);

protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void resizeEvent(QResizeEvent *event) override;

private:
    std::shared_ptr<struct ListWidgetPrivate> d_ptr;
};

#endif // LISTWIDGET_H
cpp 复制代码
#include "listwidget.h"
#include <QPainter>
#include <QDebug>
#include <QMouseEvent>
#include <QDateTime>

struct ListWidgetPrivate
{
    void setSize(int newSize);
    void onMousePressed(const QPoint & pos);
    void onMouseReleased();
    void onMouseMoved(const QPoint & pos);
    void updateRect();
    void drawRect(QPainter * painter);
    ~ListWidgetPrivate();

    QPoint pressPos;
    QList<ListItem*> itemList;
    bool isPressed{false};
    int startHeight{0};
    int offsetY{0};
    int pressIndex{-1};
    int itemHeight = 50;
    int tempOffsetY{0};
    int gridLineWidth{3};
    qint64 preessTime{0};
    ListWidget * q_ptr;
    QColor backgroundColor{"#ffffffff"};
    QColor itemPressedBackgroundColor{"#f0121212"};
    QColor itemBackgroundColor{"#f0555555"};
    QColor gridColor{"#f8111111"};
    QColor textColor{"#ffffffff"};
};

void ListWidgetPrivate::setSize(int newSize)
{
    auto oldSize = itemList.size();
    if(newSize != oldSize)
    {
        if(newSize > oldSize)
        {
            for (int i = 0;i < (newSize - oldSize);++i)
            {
                auto item = new ListItem;
                item->text = "项目" + QString::number(oldSize + i + 1);
                itemList << item;
            }
        }
        else
        {
            for(int i = oldSize - 1;i != (oldSize - 1);--i)
            {
                auto item = itemList.last();
                itemList.removeLast();
                delete item;
            }
        }

        updateRect();
    }
}

void ListWidgetPrivate::updateRect()
{
    auto width = q_ptr->rect().width() + gridLineWidth * 2;
    for(int i = 0,height = 0;i < itemList.size();++i, height += itemHeight)
    {
        itemList[i]->rect = QRect(-gridLineWidth, height, width, itemHeight);
    }
}

void ListWidgetPrivate::drawRect(QPainter *painter)
{
    painter->fillRect(q_ptr->rect(),backgroundColor);
    painter->setPen(QPen(gridColor, gridLineWidth));
    painter->setBrush(itemBackgroundColor);

    for (int i = 0; i < itemList.size(); ++i)
    {
        const auto & item = itemList.at(i);
        auto itemRect = item->rect;
        itemRect.moveBottom(itemRect.bottom() - offsetY);

        if (Q_UNLIKELY(isPressed && i == pressIndex))
        {
            painter->save();
            painter->setBrush(itemPressedBackgroundColor);
            painter->drawRect(itemRect);
            painter->restore();
        }
        else
        {
            painter->drawRect(itemRect);
        }

        painter->save();
        painter->setPen(QPen(textColor));
        painter->drawText(itemRect, Qt::AlignCenter, item->text);
        painter->restore();

        painter->drawPixmap(QRect(itemRect.topLeft() + QPoint(itemHeight / 4,0),QSize(itemHeight,itemHeight)).adjusted(4,4,-4,-4),
                            QPixmap(item->icon));

        painter->save();
        painter->setPen(QPen(QColor("#C0C0C0"),2,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
        QRect symbolRect(itemRect.topRight() - QPoint(itemHeight * 1.5,0),QSize(itemHeight* 1.5,itemHeight));
        auto symbolRectCenter = symbolRect.center();
        painter->drawLine(symbolRect.topLeft() + QPoint(itemHeight / 2.5,itemHeight / 3.5),symbolRectCenter);
        painter->drawLine(symbolRect.bottomLeft() + QPoint(itemHeight / 2.5,-itemHeight / 3.5),symbolRectCenter);
        painter->restore();
    }
}

ListWidgetPrivate::~ListWidgetPrivate()
{
    qDeleteAll(itemList);
}

void ListWidgetPrivate::onMousePressed(const QPoint &pos)
{
    pressPos = pos;
    tempOffsetY = offsetY;
    isPressed = true;
    pressIndex = -1;
    preessTime = QDateTime::currentMSecsSinceEpoch();
    for (int i = 0; i < itemList.size(); ++i)
    {
        auto itemRect = itemList.at(i)->rect;
        itemRect.moveBottom(itemRect.bottom() - offsetY);
        if (itemRect.contains(pressPos))
        {
            pressIndex = i;
            break;
        }
    }
    q_ptr->update();
}

void ListWidgetPrivate::onMouseReleased()
{
    if (isPressed)
    {
        isPressed = false;
        if (offsetY < 0)
            offsetY = 0;

        auto lastItemRect = itemList.last()->rect;
        lastItemRect.moveBottom(lastItemRect.bottom() - offsetY);

        if (lastItemRect.bottom() < 0 || lastItemRect.bottom() < itemHeight)
        {
            offsetY = (itemList.size() - 1) * itemHeight;
        }

        auto wRect = q_ptr->rect();
        if(lastItemRect.bottom() < (wRect.y() + wRect.height()))
        {
            int itemAllHeight = itemList.size() * this->itemHeight;
            if(itemAllHeight <= wRect.height()) //项目总高度不如部件高度大
            {
                offsetY = 0;
            }
            else
            {
                offsetY = itemAllHeight - wRect.height();
            }
        }

        if(pressIndex != -1)
        {
            qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
            if ((currentTime - preessTime) < 1000)//从按下到松开超过1秒就不认为是按下项目操作
            {
                qDebug()<<itemList.at(pressIndex)->text;
                preessTime = currentTime;
            }

            pressIndex = -1;
        }
        q_ptr->update();
    }
}

void ListWidgetPrivate::onMouseMoved(const QPoint &pos)
{
    if (isPressed)
    {
        auto diff = pressPos - pos;
        offsetY = tempOffsetY + diff.y();
        q_ptr->update();
    }
}

ListWidget::ListWidget(QWidget *parent)
    : QWidget{parent}
{
    d_ptr = std::make_shared<ListWidgetPrivate>();
    d_ptr->q_ptr = this;

    for(int i = 1;i < 10;++i)
    {
        auto item = new ListItem;
        item->text = "项目" + QString::number(i);
        d_ptr->itemList << item;
    }
}

void ListWidget::setSize(int newSize)
{
    d_ptr->setSize(newSize);
}

void ListWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    d_ptr->drawRect(&painter);
}

void ListWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        d_ptr->onMousePressed(event->pos());
    }
}

void ListWidget::mouseReleaseEvent(QMouseEvent *event)
{
    d_ptr->onMouseReleased();
}

void ListWidget::mouseMoveEvent(QMouseEvent *event)
{
    d_ptr->onMouseMoved(event->pos());
}

void ListWidget::resizeEvent(QResizeEvent *event)
{
    QWidget::resizeEvent(event);
    d_ptr->updateRect();
    update();
}
相关推荐
小庞在加油4 小时前
WinDbg实战:QT/跨平台项目死锁与无响应问题排查指南
开发语言·qt·windbg·工具
hy.z_7776 小时前
【QT】1.QT初识(背景介绍、搭建开发环境、Qt Creator、程序、项目文件解析、编程注意事项)
qt
不会代码的小猴7 小时前
7. JSON
开发语言·c++·笔记·qt·算法·json
沫璃染墨7 小时前
《Qt从零入门系列(六):信号与槽进阶——从多种连接方式到Lambda表达式》
开发语言·c++·qt·代码规范·设计规范·qt5
橙橙笔记11 小时前
QT的安装
开发语言·qt·安装·软件
AI的探索之旅12 小时前
图像格式与转换一文讲透:相机 / OpenCV / Qt
数码相机·qt·opencv
2kπ13 小时前
QT开发笔记
开发语言·笔记·qt
Quz15 小时前
QML MouseArea:点击、悬停与坐标获取
qt
程与留15 小时前
04_Qt项目构建流程深度解析——MOC、UIC、RCC 的作用与原理
qt
Quz15 小时前
QML MouseArea 事件:单击双击、长按与多按键检测
qt