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

351 lines
14 KiB
C++

#include "ElaMultiSelectComboBox.h"
#include <QApplication>
#include <QDebug>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include "DeveloperComponents/ElaComboBoxView.h"
#include "ElaApplication.h"
#include "ElaComboBoxStyle.h"
#include "ElaScrollBar.h"
#include "ElaTheme.h"
#include "private/ElaMultiSelectComboBoxPrivate.h"
Q_PROPERTY_CREATE_Q_CPP(ElaMultiSelectComboBox, int, BorderRadius)
ElaMultiSelectComboBox::ElaMultiSelectComboBox(QWidget* parent)
: QComboBox(parent), d_ptr(new ElaMultiSelectComboBoxPrivate())
{
Q_D(ElaMultiSelectComboBox);
d->q_ptr = this;
d->_pBorderRadius = 3;
d->_pExpandIconRotate = 0;
d->_pExpandMarkWidth = 0;
d->_themeMode = eTheme->getThemeMode();
setFixedHeight(35);
d->_comboBoxStyle = new ElaComboBoxStyle(style());
setStyle(d->_comboBoxStyle);
//调用view 让container初始化
d->_comboView = new ElaComboBoxView(this);
setView(d->_comboView);
QAbstractItemView* comboBoxView = this->view();
comboBoxView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
ElaScrollBar* scrollBar = new ElaScrollBar(this);
comboBoxView->setVerticalScrollBar(scrollBar);
ElaScrollBar* floatVScrollBar = new ElaScrollBar(scrollBar, comboBoxView);
floatVScrollBar->setIsAnimation(true);
comboBoxView->setAutoScroll(false);
comboBoxView->setSelectionMode(QAbstractItemView::NoSelection);
comboBoxView->setSelectionBehavior(QAbstractItemView::SelectRows);
comboBoxView->setObjectName("ElaComboBoxView");
comboBoxView->setStyleSheet("#ElaComboBoxView{background-color:transparent;}");
comboBoxView->setStyle(d->_comboBoxStyle);
QWidget* container = this->findChild<QFrame*>();
if (container)
{
container->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
container->setAttribute(Qt::WA_TranslucentBackground);
container->setObjectName("ElaComboBoxContainer");
container->setStyle(d->_comboBoxStyle);
QLayout* layout = container->layout();
while (layout->count())
{
layout->takeAt(0);
}
layout->addWidget(view());
layout->setContentsMargins(6, 0, 6, 6);
#ifndef Q_OS_WIN
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
container->setStyleSheet("background-color:transparent;");
#endif
#endif
}
QComboBox::setMaxVisibleItems(5);
connect(d->_comboView, &ElaComboBoxView::itemPressed, d, &ElaMultiSelectComboBoxPrivate::onItemPressed);
connect(this, QOverload<int>::of(&ElaMultiSelectComboBox::currentIndexChanged), d, &ElaMultiSelectComboBoxPrivate::_refreshCurrentIndexs);
d->_itemSelection.resize(32);
d->_itemSelection.fill(false);
d->_itemSelection[0] = true;
QComboBox::setMaxVisibleItems(5);
connect(eTheme, &ElaTheme::themeModeChanged, this, [=](ElaThemeType::ThemeMode themeMode) {
d->_themeMode = themeMode;
});
}
ElaMultiSelectComboBox::~ElaMultiSelectComboBox()
{
Q_D(ElaMultiSelectComboBox);
delete d->_comboBoxStyle;
}
void ElaMultiSelectComboBox::setCurrentSelection(QString selection)
{
Q_D(ElaMultiSelectComboBox);
d->_itemSelection.fill(false);
d->_comboView->selectionModel()->clearSelection();
for (int i = 0; i < this->count(); i++)
{
if (selection == itemText(i))
{
QModelIndex index = model()->index(i, 0);
d->_comboView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
d->_itemSelection[index.row()] = true;
}
}
d->_refreshCurrentIndexs();
}
void ElaMultiSelectComboBox::setCurrentSelection(QStringList selection)
{
Q_D(ElaMultiSelectComboBox);
d->_comboView->selectionModel()->clearSelection();
d->_itemSelection.fill(false);
for (int i = 0; i < this->count(); i++)
{
if (selection.contains(itemText(i)))
{
QModelIndex index = model()->index(i, 0);
d->_comboView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
d->_itemSelection[index.row()] = true;
}
}
d->_refreshCurrentIndexs();
}
void ElaMultiSelectComboBox::setCurrentSelection(int index)
{
Q_D(ElaMultiSelectComboBox);
if (index >= this->count() || index < 0)
{
return;
}
d->_comboView->selectionModel()->clearSelection();
d->_itemSelection.fill(false);
QModelIndex currentIndex = model()->index(index, 0);
d->_comboView->selectionModel()->select(currentIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
d->_itemSelection[index] = true;
d->_refreshCurrentIndexs();
}
void ElaMultiSelectComboBox::setCurrentSelection(QList<int> selectionIndex)
{
Q_D(ElaMultiSelectComboBox);
d->_itemSelection.fill(false);
d->_comboView->selectionModel()->clearSelection();
for (auto index: selectionIndex)
{
if (index >= this->count() || index < 0)
{
continue;
}
QModelIndex currentIndex = model()->index(index, 0);
d->_comboView->selectionModel()->select(currentIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
d->_itemSelection[index] = true;
}
d->_refreshCurrentIndexs();
}
QStringList ElaMultiSelectComboBox::getCurrentSelection() const
{
return d_ptr->_selectedTextList;
}
QList<int> ElaMultiSelectComboBox::getCurrentSelectionIndex() const
{
QList<int> indexList;
for (int i = 0; i < d_ptr->_itemSelection.count(); i++)
{
if (d_ptr->_itemSelection[i])
{
indexList.append(i);
}
}
return indexList;
}
void ElaMultiSelectComboBox::paintEvent(QPaintEvent* e)
{
Q_D(ElaMultiSelectComboBox);
QPainter painter(this);
painter.save();
painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing | QPainter::TextAntialiasing);
painter.setPen(ElaThemeColor(d->_themeMode, BasicBorder));
painter.setBrush(isEnabled() ? underMouse() ? ElaThemeColor(d->_themeMode, BasicHover) : ElaThemeColor(d->_themeMode, BasicBase) : Qt::transparent);
QRect foregroundRect = rect();
foregroundRect.adjust(6, 1, -6, -1);
painter.drawRoundedRect(foregroundRect, d->_pBorderRadius, d->_pBorderRadius);
// 底边线绘制
painter.setPen(ElaThemeColor(d->_themeMode, BasicBaseLine));
painter.drawLine(foregroundRect.x() + d->_pBorderRadius, foregroundRect.y() + foregroundRect.height(), foregroundRect.x() + foregroundRect.width() - d->_pBorderRadius, foregroundRect.y() + foregroundRect.height());
//文字绘制
painter.setPen(isEnabled() ? ElaThemeColor(d->_themeMode, BasicText) : ElaThemeColor(d->_themeMode, BasicTextDisable));
QString currentText = painter.fontMetrics().elidedText(d->_currentText, Qt::ElideRight, foregroundRect.width() - 27 - width() * 0.05);
painter.drawText(15, height() / 2 + painter.fontMetrics().ascent() / 2 - 1, currentText);
//展开指示器绘制
painter.setPen(Qt::NoPen);
painter.setBrush(d->_themeMode == ElaThemeType::Light ? QColor(0x0E, 0x6F, 0xC3) : QColor(0x4C, 0xA0, 0xE0));
painter.drawRoundedRect(QRectF(width() / 2 - d->_pExpandMarkWidth, height() - 3, d->_pExpandMarkWidth * 2, 3), 2, 2);
// 展开图标绘制
if (count() > 0)
{
QFont iconFont = QFont("ElaAwesome");
iconFont.setPixelSize(17);
painter.setFont(iconFont);
painter.setPen(isEnabled() ? ElaThemeColor(d->_themeMode, BasicText) : ElaThemeColor(d->_themeMode, BasicTextDisable));
QRectF expandIconRect(width() - 25, 0, 20, height());
painter.translate(expandIconRect.x() + (qreal)expandIconRect.width() / 2 - 2, expandIconRect.y() + (qreal)expandIconRect.height() / 2);
painter.rotate(d->_pExpandIconRotate);
painter.translate(-expandIconRect.x() - (qreal)expandIconRect.width() / 2 + 2, -expandIconRect.y() - (qreal)expandIconRect.height() / 2);
painter.drawText(expandIconRect, Qt::AlignVCenter, QChar((unsigned short)ElaIconType::AngleDown));
painter.restore();
}
}
void ElaMultiSelectComboBox::showPopup()
{
Q_D(ElaMultiSelectComboBox);
bool oldAnimationEffects = qApp->isEffectEnabled(Qt::UI_AnimateCombo);
qApp->setEffectEnabled(Qt::UI_AnimateCombo, false);
QComboBox::showPopup();
qApp->setEffectEnabled(Qt::UI_AnimateCombo, oldAnimationEffects);
if (count() > 0)
{
QWidget* container = this->findChild<QFrame*>();
if (container)
{
int containerHeight = 0;
if (count() >= maxVisibleItems())
{
containerHeight = maxVisibleItems() * 35 + 8;
}
else
{
containerHeight = count() * 35 + 8;
}
view()->resize(view()->width(), containerHeight - 8);
container->move(container->x(), container->y() + 3);
QLayout* layout = container->layout();
while (layout->count())
{
layout->takeAt(0);
}
QPropertyAnimation* fixedSizeAnimation = new QPropertyAnimation(container, "maximumHeight");
connect(fixedSizeAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) {
container->setFixedHeight(value.toUInt());
});
fixedSizeAnimation->setStartValue(1);
fixedSizeAnimation->setEndValue(containerHeight);
fixedSizeAnimation->setEasingCurve(QEasingCurve::OutCubic);
fixedSizeAnimation->setDuration(400);
fixedSizeAnimation->start(QAbstractAnimation::DeleteWhenStopped);
QPropertyAnimation* viewPosAnimation = new QPropertyAnimation(view(), "pos");
connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() {
d->_isAllowHidePopup = true;
layout->addWidget(view());
});
QPoint viewPos = view()->pos();
viewPosAnimation->setStartValue(QPoint(viewPos.x(), viewPos.y() - view()->height()));
viewPosAnimation->setEndValue(viewPos);
viewPosAnimation->setEasingCurve(QEasingCurve::OutCubic);
viewPosAnimation->setDuration(400);
viewPosAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}
//指示器动画
QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d, "pExpandIconRotate");
connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) {
update();
});
rotateAnimation->setDuration(300);
rotateAnimation->setEasingCurve(QEasingCurve::InOutSine);
rotateAnimation->setStartValue(d->_pExpandIconRotate);
rotateAnimation->setEndValue(-180);
rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped);
QPropertyAnimation* markAnimation = new QPropertyAnimation(d, "pExpandMarkWidth");
markAnimation->setDuration(300);
markAnimation->setEasingCurve(QEasingCurve::InOutSine);
markAnimation->setStartValue(d->_pExpandMarkWidth);
qreal step = (width() / 2 - d->_pBorderRadius) / count();
markAnimation->setEndValue(step * d->_selectedTextList.count());
markAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}
d->_refreshCurrentIndexs();
}
void ElaMultiSelectComboBox::hidePopup()
{
Q_D(ElaMultiSelectComboBox);
if (d->_isFirstPopup && !this->view()->underMouse())
{
d->_isFirstPopup = false;
return;
}
if (eApp->containsCursorToItem(d->_comboView))
{
return;
}
else
{
if (d->_isAllowHidePopup)
{
QWidget* container = this->findChild<QFrame*>();
int containerHeight = container->height();
if (container)
{
QLayout* layout = container->layout();
while (layout->count())
{
layout->takeAt(0);
}
QPropertyAnimation* viewPosAnimation = new QPropertyAnimation(view(), "pos");
connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() {
layout->addWidget(view());
QMouseEvent focusEvent(QEvent::MouseButtonPress, QPoint(-1, -1), QPoint(-1, -1), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QApplication::sendEvent(parentWidget(), &focusEvent);
QComboBox::hidePopup();
container->setFixedHeight(containerHeight);
});
QPoint viewPos = view()->pos();
connect(viewPosAnimation, &QPropertyAnimation::finished, this, [=]() {
view()->move(viewPos);
});
viewPosAnimation->setStartValue(viewPos);
viewPosAnimation->setEndValue(QPoint(viewPos.x(), viewPos.y() - view()->height()));
viewPosAnimation->setEasingCurve(QEasingCurve::InCubic);
viewPosAnimation->start(QAbstractAnimation::DeleteWhenStopped);
QPropertyAnimation* fixedSizeAnimation = new QPropertyAnimation(container, "maximumHeight");
connect(fixedSizeAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) {
container->setFixedHeight(value.toUInt());
});
fixedSizeAnimation->setStartValue(container->height());
fixedSizeAnimation->setEndValue(1);
fixedSizeAnimation->setEasingCurve(QEasingCurve::InCubic);
fixedSizeAnimation->start(QAbstractAnimation::DeleteWhenStopped);
d->_isAllowHidePopup = false;
}
//指示器动画
QPropertyAnimation* rotateAnimation = new QPropertyAnimation(d, "pExpandIconRotate");
connect(rotateAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant& value) {
update();
});
rotateAnimation->setDuration(300);
rotateAnimation->setEasingCurve(QEasingCurve::InOutSine);
rotateAnimation->setStartValue(d->_pExpandIconRotate);
rotateAnimation->setEndValue(0);
rotateAnimation->start(QAbstractAnimation::DeleteWhenStopped);
QPropertyAnimation* markAnimation = new QPropertyAnimation(d, "pExpandMarkWidth");
markAnimation->setDuration(300);
markAnimation->setEasingCurve(QEasingCurve::InOutSine);
markAnimation->setStartValue(d->_pExpandMarkWidth);
markAnimation->setEndValue(0);
markAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}
}
}