Skip to content

fix: Main interface does not gray out when losing focus#3013

Open
JWWTSL wants to merge 1 commit intolinuxdeepin:masterfrom
JWWTSL:master
Open

fix: Main interface does not gray out when losing focus#3013
JWWTSL wants to merge 1 commit intolinuxdeepin:masterfrom
JWWTSL:master

Conversation

@JWWTSL
Copy link
Contributor

@JWWTSL JWWTSL commented Feb 6, 2026

log: When the “New User” dialog is a top-level DialogWindow, it fails to reliably bind to the main window's transient parent/fail to reliably activate as the active window. This prevents Qt.WindowModal from triggering the main window to enter a grayed-out state upon losing focus in certain environments. The fix involves prioritizing the setting of transientParent with DccApp.mainWindow() in CreateAccountDialog.qml and calling requestActivate() when the dialog appears. This ensures the main interface grayscale behavior aligns with other modal dialogs.

pms: bug-336201

Translated with DeepL.com (free version)

Summary by Sourcery

Bug Fixes:

  • Fix main window not graying out when the Create Account dialog is opened by reliably setting the dialog's transient parent and requesting activation when it becomes visible.

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: JWWTSL

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

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 6, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Ensures the Create Account dialog correctly sets and resolves its transient parent and activates itself when shown so that Qt.WindowModal properly grays out the main window on focus loss, aligning behavior with other modal dialogs.

Sequence diagram for CreateAccountDialog transient parent resolution and activation

sequenceDiagram
    actor User
    participant MainWindow as DccAppMainWindow
    participant CreateAccountDialog
    participant QtApplication

    User->>MainWindow: OpenCreateAccountDialog
    MainWindow->>CreateAccountDialog: create and show

    activate CreateAccountDialog
    CreateAccountDialog->>CreateAccountDialog: Component.onCompleted
    CreateAccountDialog->>CreateAccountDialog: resolveTransientParent
    CreateAccountDialog->>DccAppMainWindow: DccApp.mainWindow()
    alt DccApp.mainWindow available
        DccAppMainWindow-->>CreateAccountDialog: mainWindow reference
    else DccApp.mainWindow not available
        CreateAccountDialog->>QtApplication: Qt.application.activeWindow
        QtApplication-->>CreateAccountDialog: activeWindow reference
    end
    CreateAccountDialog->>CreateAccountDialog: transientParentWindow = resolved window
    CreateAccountDialog->>CreateAccountDialog: transientParent bound to transientParentWindow

    CreateAccountDialog->>CreateAccountDialog: visible = true
    CreateAccountDialog->>CreateAccountDialog: onVisibleChanged
    alt transientParentWindow null or self
        CreateAccountDialog->>CreateAccountDialog: transientParentWindow = resolveTransientParent
    end
    CreateAccountDialog->>QtApplication: Qt.callLater(dialog.requestActivate)
    QtApplication-->>CreateAccountDialog: requestActivate invoked

    CreateAccountDialog->>DccAppMainWindow: enforce Qt.WindowModal
    DccAppMainWindow->>DccAppMainWindow: enter grayed_out state
    deactivate CreateAccountDialog
Loading

Class diagram for updated CreateAccountDialog QML component

classDiagram
    class DialogWindow

    class CreateAccountDialog {
        +var transientParentWindow
        +int width
        +int minimumWidth
        +int minimumHeight
        +int maximumWidth
        +int maximumHeight
        +string icon
        +var transientParent
        +var modality
        +string title
        +signal accepted()
        +var visible
        +var id
        +var mainLayout
        +function resolveTransientParent()
        +handler Component_onCompleted()
        +handler onVisibleChanged()
    }

    CreateAccountDialog --|> DialogWindow

    class DccApp {
        +function mainWindow()
    }

    class QtApplication {
        +var activeWindow
        +function callLater(callback)
    }

    CreateAccountDialog ..> DccApp : uses
    CreateAccountDialog ..> QtApplication : uses
Loading

File-Level Changes

Change Details Files
Make the Create Account dialog reliably behave as a window-modal dialog by resolving its transient parent and activating it when shown.
  • Introduce a transientParentWindow property and bind the dialog's transientParent to it.
  • Add a resolveTransientParent() helper that prefers DccApp.mainWindow() and falls back to Qt.application.activeWindow.
  • Set transientParentWindow on Component.onCompleted using the resolver.
  • Update onVisibleChanged to refresh transientParentWindow when needed and to call dialog.requestActivate asynchronously when the dialog becomes visible.
src/plugin-accounts/qml/CreateAccountDialog.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

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In resolveTransientParent(), you should avoid returning dialog itself (e.g., when Qt.application.activeWindow is the dialog and DccApp.mainWindow is unavailable), otherwise transientParent ends up pointing to the dialog, defeating the purpose of having a real parent window.
  • Consider deferring the first call to resolveTransientParent() until the dialog is actually becoming visible (and possibly after a short Qt.callLater) instead of running it in Component.onCompleted, to reduce the chance of racing with initialization of DccApp.mainWindow.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `resolveTransientParent()`, you should avoid returning `dialog` itself (e.g., when `Qt.application.activeWindow` is the dialog and `DccApp.mainWindow` is unavailable), otherwise `transientParent` ends up pointing to the dialog, defeating the purpose of having a real parent window.
- Consider deferring the first call to `resolveTransientParent()` until the dialog is actually becoming visible (and possibly after a short `Qt.callLater`) instead of running it in `Component.onCompleted`, to reduce the chance of racing with initialization of `DccApp.mainWindow`.

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.

log: When the “New User” dialog is a top-level DialogWindow, it fails to reliably bind to the main window's transient parent/fail to reliably activate as the active window. This prevents Qt.WindowModal from triggering the main window to enter a grayed-out state upon losing focus in certain environments. The fix involves prioritizing the setting of transientParent with DccApp.mainWindow() in CreateAccountDialog.qml and calling requestActivate() when the dialog appears. This ensures the main interface grayscale behavior aligns with other modal dialogs.

pms: bug-336201

Translated with DeepL.com (free version)
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码主要是在 CreateAccountDialog.qml 中增加了对对话框父窗口(transient parent)的管理逻辑,目的是确保对话框能够正确地显示在活动窗口之上,并在可见时自动激活。以下是对这段代码的审查意见,包括语法逻辑、代码质量、代码性能和代码安全四个方面:

1. 语法逻辑

  • 问题点:在 Component.onCompleted 中初始化 transientParentWindow

    • 分析:在 QML 组件创建完成时(onCompleted),Qt.application.activeWindow 可能尚未完全准备好,或者当前并没有活动的窗口(例如在启动时直接弹出)。这可能导致 transientParentWindow 被赋值为 null 或错误的窗口。
    • 改进建议:虽然 onVisibleChanged 中有兜底逻辑,但 onCompleted 中的初始化可能是不必要的,或者可以移除,完全依赖 onVisibleChanged 来动态获取父窗口。如果必须保留,建议增加有效性检查。
  • 问题点transientParentWindow 的类型声明为 var

    • 分析:虽然 var 在 QML 中很灵活,但为了代码清晰和类型安全,如果可能,建议声明具体类型。不过,由于 Qt.application.activeWindow 返回的是 QWindow 指针,在 QML 中通常映射为 varQObject 派生类型,这一点在 QML 层面通常是可以接受的。

2. 代码质量

  • 优点:使用了 Qt.callLater

    • 分析:在 onVisibleChanged 中使用 Qt.callLater 是一个很好的实践。它将 raise()requestActivate() 的调用推迟到事件循环的下一帧,避免了在属性绑定链或窗口状态变化过程中立即操作窗口可能导致的竞争条件或闪烁。
  • 潜在问题onVisibleChanged 中的逻辑判断。

    • 分析if (!visible) return; 确保了只在窗口显示时执行逻辑。随后的检查 if (!transientParentWindow || transientParentWindow === dialog) 试图更新父窗口。
    • 改进建议:逻辑看起来是合理的,但 transientParentWindow === dialog 的检查略显奇怪。通常对话框不会(也不应该)成为自己的父窗口。如果这只是为了防止某种极端的边缘情况,建议添加注释说明原因。

3. 代码性能

  • 分析:这段代码的性能开销极小。Qt.application.activeWindow 的获取是一个轻量级操作,Qt.callLater 也是标准的事件循环机制。没有明显的性能瓶颈。

4. 代码安全

  • 问题点Qt.application.activeWindow 的依赖。

    • 分析:代码强依赖于 Qt.application.activeWindow。在某些复杂的应用场景(如多桌面环境、Wayland 协议下,或应用处于最小化/无焦点状态)下,activeWindow 可能不是预期的父窗口,或者为 null
    • 改进建议:虽然 transientParent 属性设置为 null 通常也是合法的(由窗口管理器决定位置),但为了更好的用户体验,建议在 C++ 层面(如果可能)提供更明确的父窗口传递机制,而不是在 QML 中猜测。
  • 问题点raise()requestActivate() 的调用。

    • 分析:强制激活窗口(requestActivate)可能会抢夺用户的焦点,在某些操作系统或桌面环境下可能会被视为打扰用户的行为。
    • 改进建议:确认这是否符合产品的交互设计规范。如果是模态对话框(modality: Qt.WindowModal),这种行为通常是符合预期的。

综合改进建议代码

D.DialogWindow {
    id: dialog
    // 建议明确类型,如果 DTK 或 Qt 模块提供了 QWindow 的 QML 封装类型
    // property var transientParentWindow: null 
    // 或者保持 var,但添加注释说明
    
    width: 460
    minimumWidth: width
    minimumHeight: height
    maximumWidth: minimumWidth
    maximumHeight: minimumHeight
    icon: "preferences-system"
    // 初始设为 null,由 onVisibleChanged 统一管理
    transientParent: transientParentWindow 
    modality: Qt.WindowModal
    title: qsTr("Create a new account")

    signal accepted()

    // 移除 Component.onCompleted,避免过早获取 activeWindow 可能导致的不确定性

    onVisibleChanged: {
        if (!visible) {
            return;
        }

        // 更新 transientParent
        // 只有当当前父窗口无效,或者当前父窗口是自身时才更新
        // (自身作为父窗口是无效状态,需要修正)
        if (!transientParentWindow || transientParentWindow === dialog) {
            var candidate = Qt.application.activeWindow;
            // 确保获取到的窗口不是对话框自身,且不是 null
            if (candidate && candidate !== dialog) {
                transientParentWindow = candidate;
            }
        }

        // 延迟激活,确保窗口状态稳定
        Qt.callLater(function () {
            dialog.raise();
            dialog.requestActivate();
        });
    }

    ColumnLayout {
        id: mainLayout
        width: dialog.width - 20
        // ...
    }
}

总结

这段代码的主要目的是为了解决对话框在某些情况下无法获得焦点或显示层级不正确的问题。目前的实现基本可行,但在初始化时机和边缘情况处理上可以更严谨一些。建议移除 Component.onCompleted 中的逻辑,完全依赖 onVisibleChanged,并在获取 activeWindow 时增加额外的非空和非自身检查。

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.

2 participants