From e64aac34ffa3e985ddb4b7f0c19bc3565b21e53e Mon Sep 17 00:00:00 2001 From: Ivy233 Date: Thu, 26 Mar 2026 14:52:02 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20refine=20transient=20inactive=20handling?= =?UTF-8?q?=20in=20windowed=20launcher=20=E4=BF=AE=E5=A4=8D=EF=BC=9A?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=AA=97=E5=8F=A3=E6=A8=A1=E5=BC=8F=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E5=99=A8=E7=9E=AC=E6=97=B6=E5=A4=B1=E7=84=A6=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Keep the delayed hide/cancel flow for popup active-state transitions to avoid immediate close on short inactive periods. 2. Remove transient search visual-hold and active-focus recovery helpers in windowed mode to support direct behavior comparison during testing. 3. Add active-state trace logs in launcheritem and launchercontroller during investigation, then remove those temporary logs after validation. Log: Preserve no-close behavior for short inactive windows while simplifying search focus handling paths for A/B validation. Influence: 1. Verify Ctrl+Alt+D in X11 windowed mode no longer closes launcher on short inactive transitions. 2. Compare search edit focus/activeFocus behavior before and after removing visual-hold/focus-recovery helpers. 3. Confirm normal outside-click hide path still triggers delayed hide timeout and closes launcher. 1. 保留弹窗active状态变化下的延迟隐藏/取消流程,避免短暂失焦时 立即关闭启动器。 2. 移除窗口模式下搜索框的瞬时样式保持与active焦点恢复辅助逻辑, 便于测试阶段进行直接行为对比。 3. 调试阶段在launcheritem和launchercontroller中加入active状态跟踪 日志,验证后移除临时日志。 Log: 在保留短暂失焦不关闭能力的同时,简化搜索框焦点处理链路以 支持A/B验证。 Influence: 1. 验证X11窗口模式下按Ctrl+Alt+D时,短暂失焦不再导致启动器关闭。 2. 对比移除样式保持/焦点恢复辅助逻辑前后,搜索框focus与 activeFocus的行为差异。 3. 确认点击外部区域时,延迟隐藏超时路径仍按预期关闭启动器。 PMS: BUG-301109 --- launchercontroller.cpp | 53 ++++++++++++------- launchercontroller.h | 5 +- .../package/launcheritem.qml | 18 +++++++ 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/launchercontroller.cpp b/launchercontroller.cpp index bf1c13d3..bde49fd6 100644 --- a/launchercontroller.cpp +++ b/launchercontroller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -21,13 +21,16 @@ DGUI_USE_NAMESPACE namespace { Q_LOGGING_CATEGORY(logController, "org.deepin.dde.launchpad.controller") +constexpr int kToggleGuardIntervalMs = 1000; +constexpr int kHideDelayIntervalMs = 80; } LauncherController::LauncherController(QObject *parent) : QObject(parent) , optShow(QStringList{"s", "show"}, tr("Show launcher (hidden by default)")) , optToggle(QStringList{"t", "toggle"}, tr("Toggle launcher visibility")) - , m_timer(new QTimer(this)) + , m_toggleGuardTimer(new QTimer(this)) + , m_hideDelayTimer(new QTimer(this)) , m_launcher1Adaptor(new Launcher1Adaptor(this)) , m_visible(false) { @@ -40,12 +43,18 @@ LauncherController::LauncherController(QObject *parent) qCInfo(logController) << "Current frame mode:" << m_currentFrame; // Interval set to 500=>1000ms for issue https://github.com/linuxdeepin/developer-center/issues/8137 - m_timer->setInterval(1000); - m_timer->setSingleShot(true); - connect(m_timer, &QTimer::timeout, this, [this] { + m_toggleGuardTimer->setInterval(kToggleGuardIntervalMs); + m_toggleGuardTimer->setSingleShot(true); + + m_hideDelayTimer->setInterval(kHideDelayIntervalMs); + m_hideDelayTimer->setSingleShot(true); + connect(m_hideDelayTimer, &QTimer::timeout, this, [this] { if (m_pendingHide) { m_pendingHide = false; - setVisible(false); + if (visible() && m_avoidHide) { + setVisible(false); + m_toggleGuardTimer->start(); + } } }); @@ -101,12 +110,20 @@ void LauncherController::ShowByMode(qlonglong in0) void LauncherController::Toggle() { - if (m_timer->isActive()) { + if (m_toggleGuardTimer->isActive()) { qDebug() << "hit"; m_pendingHide = false; - m_timer->stop(); + m_hideDelayTimer->stop(); return; } + + // If user toggles during delayed-hide window, treat it as explicit intent + // instead of swallowing the action. + if (m_hideDelayTimer->isActive()) { + m_pendingHide = false; + m_hideDelayTimer->stop(); + } + setVisible(!visible()); } @@ -152,7 +169,8 @@ void LauncherController::setCurrentFrame(const QString &frame) m_currentFrame = frame; qDebug() << "set current frame:" << m_currentFrame; m_pendingHide = false; - m_timer->start(); + m_hideDelayTimer->stop(); + m_toggleGuardTimer->start(); emit currentFrameChanged(); } @@ -176,21 +194,20 @@ void LauncherController::setCurrentScreen(const QString &screen) // if it's already hidden (`Toggle()` get triggered before `hideWithTimer()` get called). void LauncherController::hideWithTimer() { - if (visible()) { - if (m_timer->isActive()) { - m_pendingHide = true; - return; - } - if (m_avoidHide) { - qDebug() << "hide with timer"; - setVisible(false); - } + if (!visible() || !m_avoidHide) return; + + m_pendingHide = true; + if (!m_hideDelayTimer->isActive()) { + m_hideDelayTimer->start(); } } void LauncherController::cancelHide() { m_pendingHide = false; + if (m_hideDelayTimer->isActive()) { + m_hideDelayTimer->stop(); + } } QFont LauncherController::adjustFontWeight(const QFont &f, QFont::Weight weight) diff --git a/launchercontroller.h b/launchercontroller.h index 15954724..cc668a72 100644 --- a/launchercontroller.h +++ b/launchercontroller.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -83,7 +83,8 @@ class LauncherController : public QObject private: explicit LauncherController(QObject *parent=nullptr); - QTimer *m_timer; + QTimer *m_toggleGuardTimer; + QTimer *m_hideDelayTimer; Launcher1Adaptor * m_launcher1Adaptor; bool m_visible; QString m_currentFrame; diff --git a/shell-launcher-applet/package/launcheritem.qml b/shell-launcher-applet/package/launcheritem.qml index 8a60fe5f..72390f0e 100644 --- a/shell-launcher-applet/package/launcheritem.qml +++ b/shell-launcher-applet/package/launcheritem.qml @@ -265,6 +265,7 @@ AppletItem { PanelPopup { id: windowedModeLauncher + closeOnInactive: false property bool visibility: LauncherController.visible && (LauncherController.currentFrame === "WindowedFrame") @@ -278,6 +279,7 @@ AppletItem { width, height) WindowedFrame { + id: windowedFrameContent anchors.fill: parent } @@ -295,6 +297,22 @@ AppletItem { if (popupVisible !== visibility) { LauncherController.visible = popupVisible } + if (!popupVisible) { + LauncherController.cancelHide() + } + } + } + + Connections { + target: windowedModeLauncher.popupWindow + function onActiveChanged() { + if (LauncherController.currentFrame !== "WindowedFrame") return + if (!windowedModeLauncher.popupVisible) return + if (!windowedModeLauncher.popupWindow.active) { + LauncherController.hideWithTimer() + return + } + LauncherController.cancelHide() } }