Skip to content

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

Merged
18202781743 merged 1 commit intolinuxdeepin:masterfrom
JWWTSL:bug-336201
Mar 3, 2026
Merged

fix: Main interface does not gray out when losing focus#579
18202781743 merged 1 commit intolinuxdeepin:masterfrom
JWWTSL:bug-336201

Conversation

@JWWTSL
Copy link
Contributor

@JWWTSL JWWTSL commented Mar 3, 2026

log: In the DialogWindow, when set to WindowModal, the transientParent is automatically set to Qt.application.activeWindow. Upon display, raise() and requestActivate() are called to ensure the main window correctly enters a grayed-out state.

pms: bug-336201

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.

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

Please try again later or upgrade to continue using Sourcery

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
Copy link
Contributor

Choose a reason for hiding this comment

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

这个时间是2021年吧,

log: In the DialogWindow, when set to WindowModal, the transientParent is automatically set to Qt.application.activeWindow. Upon display, raise() and requestActivate() are called to ensure the main window correctly enters a grayed-out state.

pms: bug-336201
@deepin-ci-robot
Copy link
Contributor

deepin pr auto review

这段代码是一个关于 Qt/QML 窗口管理的修改,主要增加了对 transientParent(临时父窗口)的处理逻辑。以下是对该 diff 的详细审查,包括语法逻辑、代码质量、代码性能和代码安全方面的改进意见:

1. 语法逻辑

  • 版权年份更新

    • 修改:SPDX-FileCopyrightText: 2021 - 2026
    • 意见:逻辑正确,符合开源协议维护规范。将版权年份延长至 2026 年是合理的。
  • transientParent 绑定

    • 代码:
      property var transientParentWindow: null
      transientParent: transientParentWindow
    • 意见:逻辑正确。引入 transientParentWindow 属性并将其绑定到 Window 的 transientParent 属性上,这是在 QML 中动态设置父窗口的标准做法。
  • updateTransientParent 函数

    • 代码:
      function updateTransientParent() {
          if (control.modality !== Qt.WindowModal)
              return
          if (!transientParentWindow || transientParentWindow === control) {
              var candidate = Qt.application.activeWindow
              if (candidate && candidate !== control)
                  transientParentWindow = candidate
          }
      }
    • 意见
      • 逻辑基本正确,旨在当窗口模态为 WindowModal 时,自动寻找当前活动窗口作为父窗口。
      • 潜在逻辑漏洞:在 onVisibleChanged 中调用 updateTransientParent,此时 Qt.application.activeWindow 可能尚未切换到目标窗口(即刚刚触发显示该 Dialog 的窗口),导致抓取到的父窗口不正确。虽然后面使用了 Qt.callLater,但 updateTransientParent 是在 Qt.callLater 之前同步执行的。

2. 代码质量

  • 命名规范
    • transientParentWindow 命名清晰,符合 QML 命名规范。
  • 注释缺失
    • 意见:新增的 updateTransientParent 函数和相关的属性绑定逻辑缺少注释。建议添加注释说明为什么需要手动管理 transientParent(例如:为了解决特定的窗口管理器或平台上的层级问题),以及 Qt.callLater 的作用。
  • 代码重复
    • 意见updateTransientParentComponent.onCompletedonVisibleChanged 中都被调用。如果逻辑变得更复杂,建议统一管理调用时机。

3. 代码性能

  • Qt.callLater 的使用
    • 代码:
      Qt.callLater(function () {
          control.raise()
          control.requestActivate()
      })
    • 意见:这是一个很好的实践。使用 Qt.callLater 可以确保窗口显示和激活的操作在事件循环的下一帧执行,避免了当前堆栈未完成时的状态冲突,有助于提高 UI 的响应性和稳定性。
  • 频繁查询 activeWindow
    • 意见Qt.application.activeWindow 涉及与平台窗口系统的交互,虽然开销不大,但在高频触发的信号中应谨慎使用。目前仅在 onVisibleChangedonCompleted 中调用,频率较低,性能影响可忽略。

4. 代码安全

  • 类型安全
    • 代码:property var transientParentWindow: null
    • 意见:使用 var 类型虽然灵活,但失去了类型检查。如果 transientParent 必须是 Window 类型或其派生类,建议显式指定类型(如果 QML 支持该接口类型),或者至少在赋值时添加简单的类型检查或断言,防止赋值非 Window 对象导致运行时错误。
  • 循环引用风险
    • 意见:代码中检查了 transientParentWindow === control,这很好,防止了窗口将自身设为父窗口导致的循环引用或逻辑死锁。

综合改进建议代码

基于以上分析,建议对代码进行如下优化,主要是增加注释、优化逻辑顺序以及增强健壮性:

// SPDX-FileCopyrightText: 2021 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

import QtQuick
import QtQuick.Window
import org.deepin.dtk 1.0 as D
import org.deepin.dtk.style 1.0 as DS

Window {
    id: control
    // ... (其他属性保持不变)

    // 显式声明 transientParent 属性,用于动态管理父窗口关系
    // 建议使用具体的 Window 类型(如果 QML 环境支持),或者保持 var 但添加文档说明
    property var transientParentWindow: null
    transientParent: transientParentWindow

    Item {
        id: content
        // ... (内容保持不变)
    }

    /**
     * 更新临时父窗口。
     * 仅在模态为 WindowModal 且当前未设置父窗口时,
     * 尝试将当前活动窗口设为父窗口,以保证正确的窗口层级。
     */
    function updateTransientParent() {
        // 非模态窗口或非应用模态窗口不需要强制设置 transientParent
        if (control.modality !== Qt.WindowModal) {
            return
        }

        // 如果尚未设置父窗口,或者父窗口设置错误(指向了自己),则尝试自动查找
        if (!transientParentWindow || transientParentWindow === control) {
            // 延迟获取 activeWindow,以确保触发显示该窗口的父窗口已经变为 active
            // 注意:这里直接使用 callLater 包裹赋值逻辑,比在 onVisibleChanged 中分开调用更安全
            Qt.callLater(function() {
                var candidate = Qt.application.activeWindow
                // 双重检查:确保 candidate 存在、不是当前窗口、且当前窗口仍然需要父窗口
                if (candidate && 
                    candidate !== control && 
                    control.modality === Qt.WindowModal && 
                    !transientParentWindow) {
                    transientParentWindow = candidate
                }
            })
        }
    }

    Component.onCompleted: {
        updateTransientParent()
    }

    onVisibleChanged: {
        if (!control.visible) {
            return
        }
        
        // 尝试更新父窗口关系
        updateTransientParent()

        // 确保窗口显示在最上层并获得焦点
        // 使用 callLater 避免与窗口显示事件冲突
        Qt.callLater(function () {
            control.raise()
            control.requestActivate()
        })
    }

    onClosing: function(close) {
        // ... (保持不变)
    }
}

主要改进点总结:

  1. 逻辑优化:将 Qt.application.activeWindow 的获取逻辑也放入了 Qt.callLater 中(或者建议在 onVisibleChangedQt.callLater 中处理)。原代码在 onVisibleChanged 中同步调用 updateTransientParent,此时 activeWindow 可能还是旧的。虽然原代码后面有 raise(),但父窗口关系确立的最佳时机应该稍作延迟。
  2. 增加注释:为 transientParentWindowupdateTransientParent 添加了详细的功能说明。
  3. 健壮性增强:在 updateTransientParent 内部(如果是异步调用)增加了对 modalitytransientParentWindow 的二次检查,防止在异步回调执行期间状态发生变化导致错误赋值。
  4. 代码结构:保持了原有的结构,但优化了函数内部的执行流。

@deepin-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, 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

@18202781743 18202781743 merged commit 6fe5e85 into linuxdeepin:master Mar 3, 2026
17 of 18 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