Qt编写自定义控件:QWidget版Tumbler
#ifndef TUMBLERWIDGET_H
#define TUMBLERWIDGET_H
#include <QWidget>
class TumblerWidget : public QWidget
{
Q_OBJECT
public:
TumblerWidget(QWidget *parent = nullptr);
~TumblerWidget();
protected:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent*event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
private:
struct TumblerWidgetPrivate * d_ptr;
};
#endif // TUMBLERWIDGET_H
#include "tumblerwidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <QDebug>
struct TumblerWidgetPrivate
{
QStringList list;
QVector<QRect> rectVector;
QRect centerRect;
int currentIndex{0};
TumblerWidget * q_ptr;
int itemHeight{60};
bool needResetSize{false};
int isPressed : 1;
QPoint pressPos;
void findCurrentIndex();
void alignRectangle();
void changeInMoveing(int change_Y);
void resetRects();
};
TumblerWidget::TumblerWidget(QWidget *parent)
: QWidget(parent)
{
auto font = this->font();
font.setPixelSize(24);
setFont(font);
setMouseTracking(true);
d_ptr = new TumblerWidgetPrivate;
d_ptr->q_ptr = this;
for(int i = 0;i < 31;++i)
{
d_ptr->list << QString("item%1").arg(i);
}
d_ptr->rectVector.resize(d_ptr->list.size());
}
TumblerWidget::~TumblerWidget()
{
delete d_ptr;
}
void TumblerWidgetPrivate::findCurrentIndex()
{
int maxIntersectArea = -1;
int maxIntersectIndex = -1;
int closestDistance = std::numeric_limits<int>::max();
int closestIndex = -1;
for (int i = 0; i < rectVector.size(); ++i)
{
const QRect& currentRect = rectVector.at(i);
if (currentRect.intersects(centerRect))
{
int area = currentRect.intersected(centerRect).width() * currentRect.intersected(centerRect).height();
if (area > maxIntersectArea)
{
maxIntersectArea = area;
maxIntersectIndex = i;
}
}
// 计算与中心矩形最近的矩形
int distance = qAbs(centerRect.center().y() - currentRect.center().y());
if (distance < closestDistance)
{
closestDistance = distance;
closestIndex = i;
}
}
if (maxIntersectIndex != -1)
{
currentIndex = maxIntersectIndex;
}
else
{
currentIndex = closestIndex;
}
}
//对齐
void TumblerWidgetPrivate::alignRectangle()
{
auto rectWidth = q_ptr->rect().width();
for(int i = 0;i < rectVector.size();++i)
{
if(i < currentIndex)
{
rectVector[i] = QRect(0,centerRect.y() - (currentIndex - i) * itemHeight,rectWidth,itemHeight);
}
else if(i == currentIndex)
{
rectVector[i] = centerRect;
}
else
{
rectVector[i] = QRect(0,centerRect.y() + (i - currentIndex) * itemHeight,rectWidth,itemHeight);
}
}
}
void TumblerWidgetPrivate::changeInMoveing(int change_Y)
{
auto rectWidth = q_ptr->rect().width();
for(int i = 0;i < rectVector.size();++i)
{
if(i < currentIndex)
{
rectVector[i] = QRect(0,centerRect.y() - (currentIndex - i) * itemHeight - change_Y,rectWidth,itemHeight);
}
else if(i == currentIndex)
{
rectVector[i] = QRect(0,centerRect.y() - change_Y,rectWidth,itemHeight);
}
else
{
rectVector[i] = QRect(0,centerRect.y() + (i - currentIndex) * itemHeight - change_Y,rectWidth,itemHeight);
}
}
}
void TumblerWidgetPrivate::resetRects()
{
auto rect = q_ptr->rect();
auto centerRect_Y = (rect.height() - itemHeight) / 2;
centerRect = QRect(0,centerRect_Y,rect.width(),itemHeight);
changeInMoveing(0);
needResetSize = false;
}
void TumblerWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::black);
if(Q_UNLIKELY(d_ptr->needResetSize))
{
d_ptr->resetRects();
}
for(int i = 0;i < d_ptr->rectVector.size();++i)
{
painter.drawText(d_ptr->rectVector[i],Qt::AlignCenter,d_ptr->list[i]);
}
painter.fillRect(d_ptr->centerRect,QColor("#6611aa11"));
}
void TumblerWidget::resizeEvent(QResizeEvent *event)
{
d_ptr->needResetSize = true;;
auto newWidth = event->size().width();
for(int i = 0;i < d_ptr->rectVector.size();++i)
{
auto & rect = d_ptr->rectVector[i];
rect.setWidth(newWidth);
}
QWidget::resizeEvent(event);
}
void TumblerWidget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
d_ptr->pressPos = event->pos();
d_ptr->isPressed = true;
update();
}
}
void TumblerWidget::mouseReleaseEvent(QMouseEvent *event)
{
if(d_ptr->isPressed)
{
d_ptr->isPressed = false;
d_ptr->findCurrentIndex();
d_ptr->alignRectangle();
update();
}
}
void TumblerWidget::mouseMoveEvent(QMouseEvent *event)
{
if(d_ptr->isPressed)
{
auto cha = d_ptr->pressPos - event->pos();
d_ptr->changeInMoveing(cha.y());
update();
}
}
相关: QML控件类型:Tumbler