123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- #include "MultiSelectComboBox.h"
- #include <QDebug>
- #include <QEvent>
- #include <QLineEdit>
- #include <QMouseEvent>
- #include <QStandardItem>
- #include <QStandardItemModel>
- #include <QStyleOptionComboBox>
- #include <QStylePainter>
- MultiSelectComboBox::MultiSelectComboBox(QWidget *parent) : QComboBox(parent), skipNextHide(false) { initialize(); }
- MultiSelectComboBox::~MultiSelectComboBox() {}
- void MultiSelectComboBox::initialize() {
- // Set up the model
- setModel(new QStandardItemModel(this));
- // Set up the view
- QListView *listView = new QListView(this);
- setView(listView);
- // Make the combo box read-only but clickable
- setEditable(true);
- setInsertPolicy(QComboBox::NoInsert);
- lineEdit()->setReadOnly(true);
- // 设置输入框样式,使其看起来可点击
- lineEdit()->setCursor(Qt::PointingHandCursor);
- lineEdit()->setStyleSheet("QLineEdit { background-color: white; }");
- // Install event filter to handle mouse events
- view()->viewport()->installEventFilter(this);
- lineEdit()->installEventFilter(this);
- // Connect to the item changed signal
- connect(model(), &QStandardItemModel::itemChanged, this, &MultiSelectComboBox::onItemChanged);
- // Set minimum width for better display
- setMinimumWidth(150);
- // Set the default text
- updateText();
- }
- void MultiSelectComboBox::addItem(const QString &key, const QString &value) {
- QStandardItem *item = new QStandardItem(value);
- item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
- item->setData(Qt::Unchecked, Qt::CheckStateRole);
- item->setData(key, Qt::UserRole);
- model()->appendRow(item);
- updateText();
- }
- void MultiSelectComboBox::addItems(const QList<QPair<QString, QString>> &items) {
- for (const auto &item : items) {
- addItem(item.first, item.second);
- }
- }
- QStringList MultiSelectComboBox::checkedKeys() const {
- QStringList keys;
- for (int i = 0; i < model()->rowCount(); ++i) {
- QStandardItem *item = model()->item(i);
- if (item->checkState() == Qt::Checked) {
- keys.append(item->data(Qt::UserRole).toString());
- }
- }
- return keys;
- }
- QList<QPair<QString, QString>> MultiSelectComboBox::checkedItemsWithKeys() const {
- QList<QPair<QString, QString>> items;
- for (int i = 0; i < model()->rowCount(); ++i) {
- QStandardItem *item = model()->item(i);
- if (item->checkState() == Qt::Checked) {
- items.append({item->data(Qt::UserRole).toString(), item->text()});
- }
- }
- return items;
- }
- void MultiSelectComboBox::setCheckedKeys(const QStringList &keys) {
- for (int i = 0; i < model()->rowCount(); ++i) {
- QStandardItem *item = model()->item(i);
- QString key = item->data(Qt::UserRole).toString();
- item->setData(keys.contains(key) ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
- }
- updateText();
- }
- bool MultiSelectComboBox::eventFilter(QObject *object, QEvent *event) {
- if (object == view()->viewport()) {
- if (event->type() == QEvent::MouseButtonPress) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- if (mouseEvent->button() == Qt::LeftButton) {
- QModelIndex index = view()->indexAt(mouseEvent->pos());
- if (index.isValid()) {
- // 切换选中状态
- QStandardItem *item = model()->itemFromIndex(index);
- if (item) {
- Qt::CheckState currentState =
- static_cast<Qt::CheckState>(item->data(Qt::CheckStateRole).toInt());
- Qt::CheckState newState = (currentState == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
- item->setData(newState, Qt::CheckStateRole);
- // 防止点击选项后立即关闭弹出菜单
- skipNextHide = true;
- return true; // 阻止默认处理
- }
- }
- }
- }
- if (event->type() == QEvent::MouseButtonRelease) {
- // 防止点击选项后立即关闭弹出菜单
- skipNextHide = true;
- return false;
- }
- } else if (object == lineEdit()) {
- if (event->type() == QEvent::MouseButtonPress) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- if (mouseEvent->button() == Qt::LeftButton) {
- if (!view()->isVisible()) {
- showPopup();
- } else {
- hidePopup();
- }
- return true; // 事件已处理
- }
- }
- }
- return QComboBox::eventFilter(object, event);
- }
- void MultiSelectComboBox::showPopup() { QComboBox::showPopup(); }
- void MultiSelectComboBox::hidePopup() {
- if (skipNextHide) {
- skipNextHide = false;
- return;
- }
- QComboBox::hidePopup();
- }
- void MultiSelectComboBox::mousePressEvent(QMouseEvent *event) {
- // 如果点击的是输入框区域,显示弹出菜单
- if (event->button() == Qt::LeftButton) {
- if (!view()->isVisible()) {
- showPopup();
- } else {
- hidePopup();
- }
- event->accept();
- return;
- }
- // 其他情况调用父类方法
- QComboBox::mousePressEvent(event);
- }
- void MultiSelectComboBox::onItemChanged(QStandardItem *item) {
- Q_UNUSED(item);
- updateText();
- }
- void MultiSelectComboBox::updateText() {
- QStringList texts;
- for (int i = 0; i < model()->rowCount(); ++i) {
- QStandardItem *item = model()->item(i);
- if (item->checkState() == Qt::Checked) {
- texts.append(item->text());
- }
- }
- displayText = texts.isEmpty() ? "请选择..." : texts.join(", ");
- setEditText(displayText);
- emit selectionChanged();
- }
- void MultiSelectComboBox::paintEvent(QPaintEvent *event) {
- Q_UNUSED(event);
- QStylePainter painter(this);
- painter.setPen(palette().color(QPalette::Text));
- // Draw the combobox frame, focusrect and selected etc.
- QStyleOptionComboBox opt;
- initStyleOption(&opt);
- // If we have selected items, use displayText, otherwise use placeholderText
- if (!displayText.isEmpty()) {
- opt.currentText = displayText;
- } else {
- opt.currentText = "Select items...";
- }
- painter.drawComplexControl(QStyle::CC_ComboBox, opt);
- painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
- }
- QStandardItemModel *MultiSelectComboBox::model() const {
- return qobject_cast<QStandardItemModel *>(QComboBox::model());
- }
|