259 lines
7.8 KiB
C++
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();
|
|
}
|