Skip to content

Commit 9ac510d

Browse files
committed
fix(unitedentry): reshow popup when app regains focus
The popup is a parentless Qt::Tool window that Windows hides natively on app deactivation while United Entry stays activated, so clicking back into the input left it hidden until focus cycled through another widget. Restore the existing popup on QGuiApplication::applicationStateChanged and on an input click via hide()+show()+raise() without calling processInput(), so an in-flight async entry (e.g. find) is not cancelled. Gate the re-map on a real deactivation (m_popupNeedsRestore) so repeated clicks while the app stays active do not flash the already-visible popup.
1 parent 810d496 commit 9ac510d

3 files changed

Lines changed: 89 additions & 0 deletions

File tree

src/unitedentry/entrypopup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class EntryPopup : public QFrame {
1515

1616
void setWidget(const QSharedPointer<QWidget> &p_widget);
1717

18+
bool hasWidget() const { return !m_widget.isNull(); }
19+
1820
private:
1921
void takeWidget(QWidget *p_widget);
2022

src/unitedentry/unitedentry.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <QComboBox>
77
#include <QCursor>
88
#include <QEvent>
9+
#include <QGuiApplication>
910
#include <QHBoxLayout>
1011
#include <QKeyEvent>
1112
#include <QLabel>
@@ -49,6 +50,9 @@ UnitedEntry::UnitedEntry(ServiceLocator &p_services, UnitedEntryMgr *p_mgr, QWid
4950

5051
connect(qApp, &QApplication::focusChanged, this, &UnitedEntry::handleFocusChanged);
5152

53+
connect(qApp, &QGuiApplication::applicationStateChanged, this,
54+
&UnitedEntry::handleAppStateChanged);
55+
5256
connect(m_mgr, &UnitedEntryMgr::entryFinished, this, &UnitedEntry::handleEntryFinished);
5357
connect(m_mgr, &UnitedEntryMgr::entryItemActivated, this, &UnitedEntry::handleEntryItemActivated);
5458

@@ -180,6 +184,7 @@ void UnitedEntry::deactivate() {
180184

181185
m_activated = false;
182186
m_previousFocusWidget = nullptr;
187+
m_popupNeedsRestore = false;
183188

184189
m_popup->hide();
185190
m_comboBox->lineEdit()->clearFocus();
@@ -495,6 +500,11 @@ bool UnitedEntry::eventFilter(QObject *p_watched, QEvent *p_event) {
495500
if (handleLineEditKeyPress(eve)) {
496501
return true;
497502
}
503+
} else if (p_event->type() == QEvent::MouseButtonPress) {
504+
// Returning by clicking the input keeps focus unchanged, so focusChanged
505+
// never fires; re-show the popup if we are still activated. Do NOT consume
506+
// the event, or the click won't position the text cursor.
507+
scheduleRestorePopup();
498508
}
499509
}
500510

@@ -572,6 +582,70 @@ void UnitedEntry::handleFocusChanged(QWidget *p_old, QWidget *p_now) {
572582
}
573583
}
574584

585+
void UnitedEntry::handleAppStateChanged(Qt::ApplicationState p_state) {
586+
if (p_state != Qt::ApplicationActive) {
587+
// The app is deactivating; Windows hides the parentless Qt::Tool popup at
588+
// the native level. Remember to re-map it when we become active again.
589+
if (m_activated) {
590+
m_popupNeedsRestore = true;
591+
}
592+
return;
593+
}
594+
if (!m_activated) {
595+
return;
596+
}
597+
scheduleRestorePopup();
598+
}
599+
600+
void UnitedEntry::scheduleRestorePopup() {
601+
if (!m_activated || !m_popupNeedsRestore || m_restorePending) {
602+
return;
603+
}
604+
m_restorePending = true;
605+
// Defer so focus restoration / the activating click settle first.
606+
QTimer::singleShot(0, this, [this]() {
607+
m_restorePending = false;
608+
restorePopupVisibility();
609+
});
610+
}
611+
612+
void UnitedEntry::restorePopupVisibility() {
613+
if (!m_activated) {
614+
return;
615+
}
616+
617+
// Only restore while the input still holds focus; otherwise the normal
618+
// deactivate flow in handleFocusChanged should win. Return without clearing
619+
// m_popupNeedsRestore so a later trigger can retry once focus settles.
620+
auto *fw = QApplication::focusWidget();
621+
if (fw != m_comboBox && fw != m_comboBox->lineEdit()) {
622+
return;
623+
}
624+
625+
// Suppress when the native combo dropdown is showing history items.
626+
if (m_comboBox->view()->isVisible() && m_comboBox->count() > 0) {
627+
return;
628+
}
629+
630+
m_popupNeedsRestore = false;
631+
632+
if (m_popup->hasWidget()) {
633+
// Re-show existing content WITHOUT reprocessing, so an ongoing async entry
634+
// (e.g. find) is not cancelled. Force a native re-map: Windows hides
635+
// Qt::Tool windows at the native level without clearing the widget's
636+
// visible state, so a plain show() can be a no-op.
637+
updatePopupGeometry();
638+
m_popup->hide();
639+
m_popup->show();
640+
m_popup->raise();
641+
} else {
642+
// No content yet (native dropdown suppressed the initial processInput); no
643+
// entry can be ongoing here, so building content is safe.
644+
m_processTimer->stop();
645+
processInput();
646+
}
647+
}
648+
575649
void UnitedEntry::refreshIcons() {
576650
auto *themeService = m_services.get<ThemeService>();
577651
const auto fg = themeService->paletteColor("widgets#unitedentry#icon#fg");

src/unitedentry/unitedentry.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ class UnitedEntry : public QWidget {
4747

4848
void handleFocusChanged(QWidget *p_old, QWidget *p_now);
4949

50+
void handleAppStateChanged(Qt::ApplicationState p_state);
51+
52+
void scheduleRestorePopup();
53+
54+
void restorePopupVisibility();
55+
5056
const QSharedPointer<QTreeWidget> &getEntryListWidget();
5157

5258
QSharedPointer<QLabel> getInfoWidget(const QString &p_info);
@@ -93,6 +99,13 @@ class UnitedEntry : public QWidget {
9399

94100
bool m_deactivatePending = false;
95101

102+
bool m_restorePending = false;
103+
104+
// Set only when the app is deactivated while activated (the OS natively hides
105+
// the Qt::Tool popup). Gates the re-map restore so repeated clicks while the
106+
// app stays active do not needlessly hide/show the already-visible popup.
107+
bool m_popupNeedsRestore = false;
108+
96109
QWidget *m_previousFocusWidget = nullptr;
97110

98111
QTimer *m_processTimer = nullptr;

0 commit comments

Comments
 (0)