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

#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
#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;

        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();
}

相关推荐

  1. Android:定义

    2024-07-20 20:34:02       45 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 20:34:02       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 20:34:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 20:34:02       45 阅读
  4. Python语言-面向对象

    2024-07-20 20:34:02       55 阅读

热门阅读

  1. 牛客周赛51:小红走矩阵(二分+bfs)

    2024-07-20 20:34:02       19 阅读
  2. 设计模式--外观模式

    2024-07-20 20:34:02       16 阅读
  3. Kylin Cube Designer:数据洞察的魔法画布

    2024-07-20 20:34:02       15 阅读
  4. opencv读写路径包含中文的文件

    2024-07-20 20:34:02       18 阅读
  5. 探索Web世界:WebKit的地理位置API

    2024-07-20 20:34:02       20 阅读
  6. OpenCV从基础到入门(基于python)

    2024-07-20 20:34:02       12 阅读
  7. 运维 | Linux 系统中 MySQL 的安装与使用记录

    2024-07-20 20:34:02       15 阅读
  8. GPT-4o 与 GPT-4o Mini:两者的区别和特点

    2024-07-20 20:34:02       16 阅读
  9. GO Channel使用详解(各种场景下的最佳实践)

    2024-07-20 20:34:02       14 阅读