Files
cbh/ElaWidgetTools/ElaRoller.cpp
2025-09-19 22:40:52 +08:00

259 lines
7.8 KiB
C++

#include "ElaRoller.h"
#include "ElaRollerPrivate.h"
#include "ElaTheme.h"
#include <QPainter>
#include <QPropertyAnimation>
#include <QWheelEvent>
#include <cmath>
Q_PROPERTY_CREATE_Q_CPP(ElaRoller, int, BorderRadius)
ElaRoller::ElaRoller(QWidget* parent)
: QWidget{parent}, d_ptr(new ElaRollerPrivate())
{
Q_D(ElaRoller);
d->q_ptr = this;
d->_pScrollOffset = 0;
d->_pItemHeight = 35;
d->_pBorderRadius = 3;
d->_pMaxVisibleItems = 5;
d->_pCurrentIndex = 0;
setFixedSize(90, 175);
setMouseTracking(true);
setObjectName("ElaRoller");
setStyleSheet("#ElaRoller{background-color:transparent;}");
QFont font = this->font();
font.setPixelSize(16);
setFont(font);
d->_scrollAnimation = new QPropertyAnimation(d, "pScrollOffset");
connect(d->_scrollAnimation, &QPropertyAnimation::valueChanged, this, [=]() {
update();
});
connect(d->_scrollAnimation, &QPropertyAnimation::finished, this, [=]() {
while (d->_pScrollOffset < -d->_pItemList.size() * d->_pItemHeight)
{
d->_pScrollOffset += d->_pItemList.size() * d->_pItemHeight;
}
while (d->_pScrollOffset > d->_pItemList.size() * d->_pItemHeight)
{
d->_pScrollOffset -= d->_pItemList.size() * d->_pItemHeight;
}
d->_targetScrollOffset = d->_pScrollOffset;
int currentIndex = 0;
if (d->_pScrollOffset >= 0)
{
currentIndex = d->_pScrollOffset / d->_pItemHeight;
}
else if (d->_pScrollOffset <= -d->_pItemHeight)
{
currentIndex = d->_pItemList.count() + d->_pScrollOffset / d->_pItemHeight;
}
if (currentIndex != d->_pCurrentIndex)
{
d->_pCurrentIndex = currentIndex;
Q_EMIT pCurrentIndexChanged();
}
update();
});
d->_scrollAnimation->setDuration(300);
d->_scrollAnimation->setEasingCurve(QEasingCurve::OutCubic);
d->_themeMode = eTheme->getThemeMode();
connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) {
d->_themeMode = themeMode;
});
}
ElaRoller::~ElaRoller()
{
}
QString ElaRoller::getCurrentData() const
{
Q_D(const ElaRoller);
return d->_pItemList[d->_pCurrentIndex];
}
void ElaRoller::setItemList(QStringList itemList)
{
Q_D(ElaRoller);
d->_pItemList = std::move(itemList);
update();
Q_EMIT pItemListChanged();
}
QStringList ElaRoller::getItemList() const
{
Q_D(const ElaRoller);
return d->_pItemList;
}
void ElaRoller::setItemHeight(int itemHeight)
{
Q_D(ElaRoller);
d->_pItemHeight = itemHeight;
setFixedHeight(d->_pMaxVisibleItems * d->_pItemHeight);
update();
Q_EMIT pItemHeightChanged();
}
int ElaRoller::getItemHeight() const
{
Q_D(const ElaRoller);
return d->_pItemHeight;
}
void ElaRoller::setMaxVisibleItems(int maxVisibleItems)
{
Q_D(ElaRoller);
d->_pMaxVisibleItems = maxVisibleItems;
setFixedHeight(d->_pMaxVisibleItems * d->_pItemHeight);
update();
Q_EMIT pMaxVisibleItemsChanged();
}
int ElaRoller::getMaxVisibleItems() const
{
Q_D(const ElaRoller);
return d->_pMaxVisibleItems;
}
void ElaRoller::setCurrentIndex(int currentIndex)
{
Q_D(ElaRoller);
if (currentIndex >= d->_pItemList.count())
{
return;
}
d->_pCurrentIndex = currentIndex;
d->_pScrollOffset = d->_pItemHeight * currentIndex;
d->_targetScrollOffset = d->_pScrollOffset;
update();
Q_EMIT pCurrentIndexChanged();
}
int ElaRoller::getCurrentIndex() const
{
Q_D(const ElaRoller);
return d->_pCurrentIndex;
}
void ElaRoller::wheelEvent(QWheelEvent* event)
{
Q_D(ElaRoller);
d->_scroll(event->angleDelta().y());
event->accept();
}
void ElaRoller::mouseReleaseEvent(QMouseEvent* event)
{
Q_D(ElaRoller);
int centerIndex = d->_pMaxVisibleItems / 2;
int clickIndex = event->pos().y() / d->_pItemHeight;
int jumpCount = abs(d->_pMaxVisibleItems / 2 - clickIndex);
if (clickIndex > centerIndex)
{
for (int i = 0; i < jumpCount; i++)
{
d->_scroll(-120);
}
}
else if (clickIndex < centerIndex)
{
for (int i = 0; i < jumpCount; i++)
{
d->_scroll(120);
}
}
}
void ElaRoller::mouseMoveEvent(QMouseEvent* event)
{
Q_D(ElaRoller);
d->_mousePoint = event->pos();
update();
QWidget::mouseMoveEvent(event);
}
void ElaRoller::leaveEvent(QEvent* event)
{
Q_D(ElaRoller);
d->_mousePoint = QPoint();
update();
QWidget::leaveEvent(event);
}
void ElaRoller::paintEvent(QPaintEvent* event)
{
Q_D(ElaRoller);
QPainter painter(this);
painter.save();
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
// 背景绘制
QRect foregroundRect(d->_penBorderWidth, d->_penBorderWidth, width() - 2 * d->_penBorderWidth, height() - 2 * d->_penBorderWidth);
painter.setPen(QPen(ElaThemeColor(d->_themeMode, BasicBorder), d->_penBorderWidth));
painter.setBrush(isEnabled() ? ElaThemeColor(d->_themeMode, BasicBase) : ElaThemeColor(d->_themeMode, BasicDisable));
painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius);
// 中心指示区域绘制
painter.setPen(Qt::NoPen);
painter.setBrush(ElaThemeColor(d->_themeMode, PrimaryNormal));
bool isEven = d->_pMaxVisibleItems % 2 == 0;
if (isEven)
{
painter.drawRoundedRect(QRect(d->_indicatorMargin, d->_pItemHeight * d->_pMaxVisibleItems / 2 + d->_indicatorMargin, width() - 2 * d->_indicatorMargin, d->_pItemHeight - 2 * d->_indicatorMargin), d->_pBorderRadius, d->_pBorderRadius);
}
else
{
painter.drawRoundedRect(QRect(d->_indicatorMargin, (height() - d->_pItemHeight) / 2 + d->_indicatorMargin, width() - 2 * d->_indicatorMargin, d->_pItemHeight - 2 * d->_indicatorMargin), d->_pBorderRadius, d->_pBorderRadius);
}
int yStart = -d->_pItemHeight;
int yEnd = height();
int centerYStart = isEven ? d->_pItemHeight * d->_pMaxVisibleItems / 2 : height() / 2 - d->_pItemHeight;
int centerYEnd = isEven ? d->_pItemHeight * d->_pMaxVisibleItems / 2 + d->_pItemHeight / 2 : height() / 2;
int startIndexOffset = d->_pMaxVisibleItems / 2;
for (int i = 0; i < d->_pItemList.size(); i++)
{
qreal y = (i + startIndexOffset) * d->_pItemHeight - d->_pScrollOffset;
// 范围控制
while (y <= yStart)
{
y += d->_pItemList.size() * d->_pItemHeight;
}
while (y >= yEnd)
{
y -= d->_pItemList.size() * d->_pItemHeight;
}
// 可见区域绘制
if (y >= yStart && y <= yEnd)
{
painter.save();
painter.translate(0, y);
// 文字绘制
if (y >= centerYStart && y <= centerYEnd)
{
painter.setPen(ElaThemeColor(d->_themeMode, BasicTextInvert));
}
else
{
// 覆盖效果绘制
if (!d->_mousePoint.isNull() && d->_scrollAnimation->state() == QPropertyAnimation::State::Stopped)
{
QRectF itemRect = QRectF(0, y, width(), d->_pItemHeight);
if (itemRect.contains(d->_mousePoint))
{
painter.setBrush(ElaThemeColor(d->_themeMode, BasicHover));
painter.drawRoundedRect(QRect(d->_indicatorMargin, d->_indicatorMargin, width() - 2 * d->_indicatorMargin, d->_pItemHeight - 2 * d->_indicatorMargin), d->_pBorderRadius, d->_pBorderRadius);
}
}
painter.setPen(ElaThemeColor(d->_themeMode, BasicText));
}
painter.drawText(QRect(0, 0, width(), d->_pItemHeight), Qt::AlignCenter | Qt::TextSingleLine, d->_pItemList[i]);
painter.restore();
}
}
painter.restore();
}