Skip to content

fix(popup): focus window popups for input#638

Merged
mhduiy merged 1 commit into
linuxdeepin:masterfrom
mhduiy:popupwindowfocus
Jun 17, 2026
Merged

fix(popup): focus window popups for input#638
mhduiy merged 1 commit into
linuxdeepin:masterfrom
mhduiy:popupwindowfocus

Conversation

@mhduiy

@mhduiy mhduiy commented Jun 16, 2026

Copy link
Copy Markdown
Contributor
  1. Enable focus for popups using window mode.
  2. Request activation when the popup window is shown.
  3. Ensure input controls inside popup windows can trigger the input method.

Log: Focus window popups on show so embedded input controls can open the input method.

fix(popup): 让窗口弹窗支持输入法

  1. 为窗口模式的弹窗启用焦点。
  2. 在弹窗窗口显示时请求激活。
  3. 确保窗口弹窗内的输入控件可以调起输入法。

Log: 显示窗口弹窗时获取焦点,使内部输入控件可以调起输入法。

Summary by Sourcery

Ensure window-based popups gain focus and activation when shown so embedded input controls can properly receive input and trigger the input method.

Bug Fixes:

  • Request focus and window activation for visible window-mode popups on show to restore input method behavior for embedded input controls.

Enhancements:

  • Set QML window-mode popups to be focusable by default to align popup focus behavior with input expectations.

@sourcery-ai

sourcery-ai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

Ensures window-mode popups request focus and activation on show so that embedded input controls can receive focus and trigger the input method, by wiring popup focus properties in QML to a new focus-request helper in the popup window handle.

Sequence diagram for popup window focus request on show

sequenceDiagram
    participant PopupWin as QQuickWindow_m_popupWin
    participant Handle as DPopupWindowHandle
    participant Popup as QObject_m_popup
    participant Item as QQuickItem_popupItem

    PopupWin->>Handle: eventFilter(watched=PopupWin, event: QEvent::Show)
    alt watched is m_popupWin and event is Show
        Handle->>Handle: requestPopupFocus()
        Handle->>Handle: QMetaObject::invokeMethod(this, lambda, QueuedConnection)
        Note over Handle: Queued lambda executes later
        Handle->>Popup: property focus
        alt isEnabled and popup focus and popupWin visible
            Handle->>PopupWin: requestActivate()
            Handle->>Item: forceActiveFocus(Qt::ActiveWindowFocusReason)
        end
    end
Loading

File-Level Changes

Change Details Files
Request focus and window activation when a window-mode popup is shown so its content can gain active focus.
  • Hook QEvent::Show for the popup window in the event filter to trigger a focus request.
  • Add a requestPopupFocus helper that validates state, then asynchronously requests window activation and forces active focus on the popup item when appropriate.
  • Guard focus-activation logic with checks for handle enabled state, popup visibility, focus property, and pointer validity.
src/private/dpopupwindowhandle.cpp
src/private/dpopupwindowhandle_p.h
Mark window-type popups as focusable from QML so the C++ focus logic is enabled.
  • Set the focus property on the Popup control when popupType is Popup.Window so that window popups are treated as focusable.
  • Rely on the existing popup focus property in C++ to decide whether to request focus on show.
qt6/src/qml/Popup.qml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In requestPopupFocus(), instead of capturing popupItem() in a QPointer<QQuickItem> before the queued invocation, consider resolving the current popup item inside the lambda to avoid focusing a stale or deleted item if the content changes between the Show event and the queued execution.
  • The check popup->property("focus").toBool() is performed both before and inside the queued lambda; you could drop the outer check and rely on the inner one to reduce duplicated property lookups and keep the decision logic in a single place.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `requestPopupFocus()`, instead of capturing `popupItem()` in a `QPointer<QQuickItem>` before the queued invocation, consider resolving the current popup item inside the lambda to avoid focusing a stale or deleted item if the content changes between the `Show` event and the queued execution.
- The check `popup->property("focus").toBool()` is performed both before and inside the queued lambda; you could drop the outer check and rely on the inner one to reduce duplicated property lookups and keep the decision logic in a single place.

## Individual Comments

### Comment 1
<location path="src/private/dpopupwindowhandle.cpp" line_range="287-306" />
<code_context>
         m_popupWin->setPosition(newPos);
 }

+void DPopupWindowHandle::requestPopupFocus()
+{
+    if (!m_popup || !isEnabled() || !m_popupWin || !m_popupWin->isVisible())
+        return;
+    if (!m_popup->property("focus").toBool())
+        return;
+
+    QPointer<QObject> popup = m_popup;
+    QPointer<QQuickWindow> popupWin = m_popupWin;
+    QPointer<QQuickItem> item = popupItem();
+
+    QMetaObject::invokeMethod(this, [this, popup, popupWin, item] {
+        if (!isEnabled() || !popup || !popup->property("focus").toBool() || !popupWin || popupWin != m_popupWin || !popupWin->isVisible())
+            return;
+
+        popupWin->requestActivate();
+        if (item && item->window() == popupWin)
+            item->forceActiveFocus(Qt::ActiveWindowFocusReason);
+    }, Qt::QueuedConnection);
+}
+
 DQUICK_END_NAMESPACE
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Avoid capturing a potentially stale popup item and consider resolving it at invocation time.

Because `item` is captured before the queued call executes, it may no longer match the actual popup item when the lambda runs (e.g. due to reparenting or delayed creation changing `popupItem()`). Instead, construct the `QPointer<QQuickItem>` inside the lambda by calling `popupItem()` there, then validate it (and its window) before requesting focus.

```suggestion
void DPopupWindowHandle::requestPopupFocus()
{
    if (!m_popup || !isEnabled() || !m_popupWin || !m_popupWin->isVisible())
        return;
    if (!m_popup->property("focus").toBool())
        return;

    QPointer<QObject> popup = m_popup;
    QPointer<QQuickWindow> popupWin = m_popupWin;

    QMetaObject::invokeMethod(this, [this, popup, popupWin] {
        if (!isEnabled() || !popup || !popup->property("focus").toBool() || !popupWin || popupWin != m_popupWin || !popupWin->isVisible())
            return;

        popupWin->requestActivate();

        QPointer<QQuickItem> item = popupItem();
        if (item && item->window() == popupWin)
            item->forceActiveFocus(Qt::ActiveWindowFocusReason);
    }, Qt::QueuedConnection);
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/private/dpopupwindowhandle.cpp
@mhduiy mhduiy force-pushed the popupwindowfocus branch from 5dd16ba to 904e41e Compare June 16, 2026 05:13
Comment thread src/private/dpopupwindowhandle.cpp Outdated
Comment thread src/private/dpopupwindowhandle.cpp Outdated
@mhduiy mhduiy force-pushed the popupwindowfocus branch from 904e41e to 4b3da07 Compare June 16, 2026 07:18
@mhduiy mhduiy requested a review from 18202781743 June 16, 2026 07:19
18202781743
18202781743 previously approved these changes Jun 16, 2026
18202781743
18202781743 previously approved these changes Jun 16, 2026
18202781743
18202781743 previously approved these changes Jun 16, 2026
@deepin-bot

deepin-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

TAG Bot

New tag: 6.7.44
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #641

@mhduiy mhduiy force-pushed the popupwindowfocus branch from fd3268d to e29f486 Compare June 17, 2026 07:10
@mhduiy mhduiy marked this pull request as draft June 17, 2026 07:14
1. Add onPopupVisibleChanged slot to detect popup hide event
2. Add restoreParentFocus to return focus to parent window
3. Save active focus item before popup takes focus
4. Restore saved focus item when popup becomes invisible
5. Handle null window case in onWindowChanged to trigger restore

Log: Fix focus not returning to parent window when popup closes

fix(DPopupWindowHandle): 弹窗关闭时恢复父窗口焦点

1. 新增 onPopupVisibleChanged 槽函数检测弹窗隐藏事件
2. 新增 restoreParentFocus 方法将焦点归还父窗口
3. 弹窗获取焦点前保存当前活动焦点项
4. 弹窗不可见时恢复之前保存的焦点项
5. 在 onWindowChanged 中处理 window 为空时触发焦点恢复

Log: 修复弹窗关闭后焦点未归还父窗口的问题
PMS: BUG-366067
@mhduiy mhduiy force-pushed the popupwindowfocus branch from e29f486 to 2eb866f Compare June 17, 2026 07:20
@mhduiy mhduiy marked this pull request as ready for review June 17, 2026 07:24

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @mhduiy, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@mhduiy

mhduiy commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

/test all

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

★ 总体评分:100分

■ 【总体评价】

代码实现了弹窗焦点申请与精准恢复逻辑,有效解决了独立弹窗无焦点及父窗口焦点丢失问题
逻辑严密且使用了安全的指针管理机制,无任何缺陷

■ 【详细分析】

  • 1.语法逻辑(完全正确)✓

代码在 DPopupWindowHandle::requestPopupFocus 中通过 QueuedConnection 延迟执行焦点激活,并在 lambda 内部进行了二次状态校验,有效规避了异步执行期间窗口状态发生变更导致的异常。在 DPopupWindowHandle::restoreParentFocus 中,通过 m_popupFocusRequested 标志位严格控制焦点恢复的触发条件,防止无意义或重复的焦点切换操作。使用 QPointer 管理 m_restoreFocusItem 彻底杜绝了悬空指针解引用的风险。
建议:保持现有的防御性编程风格

  • 2.代码质量(良好)✓

代码结构清晰,新增的 requestPopupFocus 与 restoreParentFocus 职责划分明确,符合单一职责原则。m_popupFocusRequested 状态变量的引入使得焦点生命周期管理更加严谨。使用 QPointer 保障了对象销毁时的安全性。
建议:虽然 m_popup 被定义为 QObject 指针以解耦 QML 类型,但若后续重构可考虑使用 qobject_cast 转换为具体类型以替代多次 property 字符串查找,提升类型安全性

  • 3.代码性能(无性能问题)✓

使用 QMetaObject::invokeMethod 产生的微小延迟属于处理窗口焦点所需的合理开销,避免了同步调用可能引发的渲染未完成等问题。m_popup->property 字符串查找带来的性能损耗在窗口显示与隐藏这种低频事件中完全可以忽略不计。
建议:无需优化

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
代码未引入任何内存破坏、越界访问或权限绕过等安全风险。QPointer 的使用确保了在异步回调或窗口销毁异常流程中不会出现空指针解引用崩溃。强制焦点切换操作受限于应用自身的窗口体系,不存在被外部恶意利用的攻击面。
建议:维持当前的安全编码实践

■ 【改进建议代码示例】

// 当前代码已非常优秀,以下仅展示若 m_popup 类型明确时的可选优化写法
// 假设 m_popup 可安全转换为 QQuickPopup
void DPopupWindowHandle::requestPopupFocus()
{
    if (!m_popup || !isEnabled() || !m_popupWin || !m_popupWin->isVisible())
        return;

    // 优化:避免使用字符串属性查找,提升性能与类型安全
    bool hasFocus = false;
    if (auto quickPopup = qobject_cast<QQuickPopup*>(m_popup)) {
        hasFocus = quickPopup->hasFocus();
    } else {
        hasFocus = m_popup->property("focus").toBool();
    }

    if (!hasFocus)
        return;

    m_restoreFocusItem = m_parentWindow ? m_parentWindow->activeFocusItem() : nullptr;

    QMetaObject::invokeMethod(this, [this] {
        if (!isEnabled() || !m_popupWin || !m_popupWin->isVisible())
            return;

        bool hasFocus = false;
        if (auto quickPopup = qobject_cast<QQuickPopup*>(m_popup)) {
            hasFocus = quickPopup->hasFocus();
        } else {
            hasFocus = m_popup->property("focus").toBool();
        }
        if (!hasFocus)
            return;

        QPointer<QQuickItem> item = popupItem();
        if (!item || item->window() != m_popupWin)
            return;

        m_popupFocusRequested = true;
        m_popupWin->requestActivate();
    }, Qt::QueuedConnection);
}

@mhduiy mhduiy requested a review from 18202781743 June 17, 2026 07:28
@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, mhduiy

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@mhduiy mhduiy merged commit 3db1b54 into linuxdeepin:master Jun 17, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants