Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/collections/buttonexample.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2020 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

Expand Down Expand Up @@ -855,7 +855,7 @@ DComboBoxExample::DComboBoxExample(QWidget *parent)
}
pHBoxLayout_1->addWidget(pComboBox_1);

QComboBox *pComboBox_1_count = new QComboBox();
DComboBox *pComboBox_1_count = new DComboBox();
pComboBox_1_count->setFixedWidth(100);
pComboBox_1_count->addItem(QString::number(10));
pComboBox_1_count->addItem(QString::number(20));
Expand Down
3 changes: 2 additions & 1 deletion include/widgets/dcombobox.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

Expand Down Expand Up @@ -26,6 +26,7 @@ class LIBDTKWIDGETSHARED_EXPORT DComboBox : public QComboBox, public DCORE_NAMES
// QComboBox interface
public:
virtual void showPopup() override;
virtual bool eventFilter(QObject *watched, QEvent *event) override;
};

DWIDGET_END_NAMESPACE
Expand Down
70 changes: 67 additions & 3 deletions src/widgets/dcombobox.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

Expand All @@ -14,19 +14,21 @@
#endif

#include <private/qabstractscrollarea_p.h>
#include <private/qguiapplication_p.h>

Check warning on line 17 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <private/qguiapplication_p.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 17 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <private/qguiapplication_p.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <qpa/qplatformtheme.h>

Check warning on line 18 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <qpa/qplatformtheme.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 18 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <qpa/qplatformtheme.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <QAbstractItemView>

Check warning on line 20 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QAbstractItemView> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 20 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QAbstractItemView> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QListView>

Check warning on line 21 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QListView> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 21 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QListView> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTableView>

Check warning on line 22 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTableView> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 22 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QTableView> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QItemDelegate>

Check warning on line 23 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QItemDelegate> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 23 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QItemDelegate> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTreeView>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <QDesktopWidget>
#endif
#include <QLayout>

Check warning on line 28 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QLayout> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 28 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QLayout> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QScrollBar>

Check warning on line 29 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QScrollBar> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 29 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QScrollBar> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QHeaderView>

Check warning on line 30 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QHeaderView> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 30 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QHeaderView> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QCursor>

Check warning on line 31 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QCursor> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 31 in src/widgets/dcombobox.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QCursor> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QScreen>
#include <QStack>
#include <QWindow>
Expand Down Expand Up @@ -147,10 +149,28 @@
}
return count;
};

auto installPopupEventFilters = [this] {
QComboBoxPrivateContainer *popupContainer = this->findChild<QComboBoxPrivateContainer *>();
if (!popupContainer)
return;

popupContainer->installEventFilter(this);
view()->installEventFilter(this);
view()->viewport()->installEventFilter(this);
};

d->popupIndexBeforeLeave = QModelIndex();
d->popupIndexClearedByLeave = false;

// When the value of maxVisibleItems() is less than 16, use the default value of qt and return it directly to avoid displaying excess whitespace
QComboBoxPrivateContainer *container = this->findChild<QComboBoxPrivateContainer *>();
if (getRowCount() <= maxVisibleItems() || !container)
return QComboBox::showPopup();
if (getRowCount() <= maxVisibleItems() || !container) {
QComboBox::showPopup();
installPopupEventFilters();
return;
}
installPopupEventFilters();

// Calculate maximum height by maximum item size
QStyle * const style = this->style();
Expand Down Expand Up @@ -263,4 +283,48 @@
container->move(container->x(), newY);
}

bool DComboBox::eventFilter(QObject *watched, QEvent *event)
{
D_D(DComboBox);

QComboBoxPrivateContainer *container = this->findChild<QComboBoxPrivateContainer *>();
QAbstractItemView *popupView = view();
QWidget *viewport = popupView ? popupView->viewport() : nullptr;
const bool isPopupObject = watched == container || watched == popupView || watched == viewport;

if (isPopupObject) {
switch (event->type()) {
case QEvent::Leave:
if (popupView && popupView->currentIndex().isValid() && container
&& !container->rect().contains(container->mapFromGlobal(QCursor::pos()))) {
d->popupIndexBeforeLeave = popupView->currentIndex();
d->popupIndexClearedByLeave = true;
popupView->setCurrentIndex(QModelIndex());
}
break;
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
d->popupIndexClearedByLeave = false;
break;
case QEvent::KeyPress:
case QEvent::ShortcutOverride:
if (d->popupIndexClearedByLeave && popupView && !popupView->currentIndex().isValid()
&& d->popupIndexBeforeLeave.isValid()) {
popupView->setCurrentIndex(d->popupIndexBeforeLeave);
}
d->popupIndexClearedByLeave = false;
break;
case QEvent::Hide:
d->popupIndexBeforeLeave = QModelIndex();
d->popupIndexClearedByLeave = false;
break;
default:
break;
}
}

return QComboBox::eventFilter(watched, event);
}

DWIDGET_END_NAMESPACE
7 changes: 5 additions & 2 deletions src/widgets/private/dcombobox_p.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

Expand All @@ -7,6 +7,7 @@

#include "dcombobox.h"
#include <DObjectPrivate>
#include <QPersistentModelIndex>

DWIDGET_BEGIN_NAMESPACE

Expand All @@ -26,9 +27,11 @@ class DComboBoxPrivate : public DCORE_NAMESPACE::DObjectPrivate

// 最大显示项数
static const int MaxVisibleItems = 16;

QPersistentModelIndex popupIndexBeforeLeave;
bool popupIndexClearedByLeave = false;
};

DWIDGET_END_NAMESPACE

#endif // DCOMBOBOX_P_H

35 changes: 33 additions & 2 deletions tests/testcases/widgets/ut_dcombobox.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#include <gtest/gtest.h>
#include <QTest>

#include "private/qcombobox_p.h"
#include "dcombobox.h"
DWIDGET_USE_NAMESPACE
Expand Down Expand Up @@ -46,3 +45,35 @@ TEST_F(ut_DComboBox, maxVisibleItems)
QTest::mouseClick(target, Qt::LeftButton, Qt::KeyboardModifiers(), arrowPos);
ASSERT_EQ(container->rect().height(), oldRect.height());
}

TEST_F(ut_DComboBox, popupLeaveClearsHighlightButPreservesKeyboardBase)
{
for (int i = 0; i < 5; ++i)
target->addItem(QString::number(i));

target->setCurrentIndex(2);
target->show();
(void)QTest::qWaitForWindowExposed(target->windowHandle());

const QPoint arrowPos(target->rect().right() - 1, target->rect().center().y());
QTest::mouseClick(target, Qt::LeftButton, Qt::KeyboardModifiers(), arrowPos);

auto container = target->findChild<QComboBoxPrivateContainer *>();
ASSERT_TRUE(container);

auto popupView = target->view();
ASSERT_TRUE(popupView);

popupView->setCurrentIndex(target->model()->index(4, target->modelColumn(), target->rootModelIndex()));
ASSERT_TRUE(popupView->currentIndex().isValid());

QCursor::setPos(target->mapToGlobal(QPoint(-20, -20)));
QEvent leaveEvent(QEvent::Leave);
QCoreApplication::sendEvent(container, &leaveEvent);
ASSERT_FALSE(popupView->currentIndex().isValid());

QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
QCoreApplication::sendEvent(popupView, &keyEvent);
ASSERT_TRUE(popupView->currentIndex().isValid());
ASSERT_EQ(popupView->currentIndex().row(), 3);
}
Loading