MultiSelectComboBox.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include "MultiSelectComboBox.h"
  2. #include <QDebug>
  3. #include <QEvent>
  4. #include <QLineEdit>
  5. #include <QMouseEvent>
  6. #include <QStandardItem>
  7. #include <QStandardItemModel>
  8. #include <QStyleOptionComboBox>
  9. #include <QStylePainter>
  10. MultiSelectComboBox::MultiSelectComboBox(QWidget *parent) : QComboBox(parent), skipNextHide(false) { initialize(); }
  11. MultiSelectComboBox::~MultiSelectComboBox() {}
  12. void MultiSelectComboBox::initialize() {
  13. // Set up the model
  14. setModel(new QStandardItemModel(this));
  15. // Set up the view
  16. QListView *listView = new QListView(this);
  17. setView(listView);
  18. // Make the combo box read-only but clickable
  19. setEditable(true);
  20. setInsertPolicy(QComboBox::NoInsert);
  21. lineEdit()->setReadOnly(true);
  22. // 设置输入框样式,使其看起来可点击
  23. lineEdit()->setCursor(Qt::PointingHandCursor);
  24. lineEdit()->setStyleSheet("QLineEdit { background-color: white; }");
  25. // Install event filter to handle mouse events
  26. view()->viewport()->installEventFilter(this);
  27. lineEdit()->installEventFilter(this);
  28. // Connect to the item changed signal
  29. connect(model(), &QStandardItemModel::itemChanged, this, &MultiSelectComboBox::onItemChanged);
  30. // Set minimum width for better display
  31. setMinimumWidth(150);
  32. // Set the default text
  33. updateText();
  34. }
  35. void MultiSelectComboBox::addItem(const QString &key, const QString &value) {
  36. QStandardItem *item = new QStandardItem(value);
  37. item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
  38. item->setData(Qt::Unchecked, Qt::CheckStateRole);
  39. item->setData(key, Qt::UserRole);
  40. model()->appendRow(item);
  41. updateText();
  42. }
  43. void MultiSelectComboBox::addItems(const QList<QPair<QString, QString>> &items) {
  44. for (const auto &item : items) {
  45. addItem(item.first, item.second);
  46. }
  47. }
  48. QStringList MultiSelectComboBox::checkedKeys() const {
  49. QStringList keys;
  50. for (int i = 0; i < model()->rowCount(); ++i) {
  51. QStandardItem *item = model()->item(i);
  52. if (item->checkState() == Qt::Checked) {
  53. keys.append(item->data(Qt::UserRole).toString());
  54. }
  55. }
  56. return keys;
  57. }
  58. QList<QPair<QString, QString>> MultiSelectComboBox::checkedItemsWithKeys() const {
  59. QList<QPair<QString, QString>> items;
  60. for (int i = 0; i < model()->rowCount(); ++i) {
  61. QStandardItem *item = model()->item(i);
  62. if (item->checkState() == Qt::Checked) {
  63. items.append({item->data(Qt::UserRole).toString(), item->text()});
  64. }
  65. }
  66. return items;
  67. }
  68. void MultiSelectComboBox::setCheckedKeys(const QStringList &keys) {
  69. for (int i = 0; i < model()->rowCount(); ++i) {
  70. QStandardItem *item = model()->item(i);
  71. QString key = item->data(Qt::UserRole).toString();
  72. item->setData(keys.contains(key) ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
  73. }
  74. updateText();
  75. }
  76. bool MultiSelectComboBox::eventFilter(QObject *object, QEvent *event) {
  77. if (object == view()->viewport()) {
  78. if (event->type() == QEvent::MouseButtonPress) {
  79. QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
  80. if (mouseEvent->button() == Qt::LeftButton) {
  81. QModelIndex index = view()->indexAt(mouseEvent->pos());
  82. if (index.isValid()) {
  83. // 切换选中状态
  84. QStandardItem *item = model()->itemFromIndex(index);
  85. if (item) {
  86. Qt::CheckState currentState =
  87. static_cast<Qt::CheckState>(item->data(Qt::CheckStateRole).toInt());
  88. Qt::CheckState newState = (currentState == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
  89. item->setData(newState, Qt::CheckStateRole);
  90. // 防止点击选项后立即关闭弹出菜单
  91. skipNextHide = true;
  92. return true; // 阻止默认处理
  93. }
  94. }
  95. }
  96. }
  97. if (event->type() == QEvent::MouseButtonRelease) {
  98. // 防止点击选项后立即关闭弹出菜单
  99. skipNextHide = true;
  100. return false;
  101. }
  102. } else if (object == lineEdit()) {
  103. if (event->type() == QEvent::MouseButtonPress) {
  104. QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
  105. if (mouseEvent->button() == Qt::LeftButton) {
  106. if (!view()->isVisible()) {
  107. showPopup();
  108. } else {
  109. hidePopup();
  110. }
  111. return true; // 事件已处理
  112. }
  113. }
  114. }
  115. return QComboBox::eventFilter(object, event);
  116. }
  117. void MultiSelectComboBox::showPopup() { QComboBox::showPopup(); }
  118. void MultiSelectComboBox::hidePopup() {
  119. if (skipNextHide) {
  120. skipNextHide = false;
  121. return;
  122. }
  123. QComboBox::hidePopup();
  124. }
  125. void MultiSelectComboBox::mousePressEvent(QMouseEvent *event) {
  126. // 如果点击的是输入框区域,显示弹出菜单
  127. if (event->button() == Qt::LeftButton) {
  128. if (!view()->isVisible()) {
  129. showPopup();
  130. } else {
  131. hidePopup();
  132. }
  133. event->accept();
  134. return;
  135. }
  136. // 其他情况调用父类方法
  137. QComboBox::mousePressEvent(event);
  138. }
  139. void MultiSelectComboBox::onItemChanged(QStandardItem *item) {
  140. Q_UNUSED(item);
  141. updateText();
  142. }
  143. void MultiSelectComboBox::updateText() {
  144. QStringList texts;
  145. for (int i = 0; i < model()->rowCount(); ++i) {
  146. QStandardItem *item = model()->item(i);
  147. if (item->checkState() == Qt::Checked) {
  148. texts.append(item->text());
  149. }
  150. }
  151. displayText = texts.isEmpty() ? "请选择..." : texts.join(", ");
  152. setEditText(displayText);
  153. emit selectionChanged();
  154. }
  155. void MultiSelectComboBox::paintEvent(QPaintEvent *event) {
  156. Q_UNUSED(event);
  157. QStylePainter painter(this);
  158. painter.setPen(palette().color(QPalette::Text));
  159. // Draw the combobox frame, focusrect and selected etc.
  160. QStyleOptionComboBox opt;
  161. initStyleOption(&opt);
  162. // If we have selected items, use displayText, otherwise use placeholderText
  163. if (!displayText.isEmpty()) {
  164. opt.currentText = displayText;
  165. } else {
  166. opt.currentText = "Select items...";
  167. }
  168. painter.drawComplexControl(QStyle::CC_ComboBox, opt);
  169. painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
  170. }
  171. QStandardItemModel *MultiSelectComboBox::model() const {
  172. return qobject_cast<QStandardItemModel *>(QComboBox::model());
  173. }