Skip to content

feat: support KWin split menus with legacy fallback - #781

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
18202781743:feat/kwin-split-menu
Sep 20, 2026
Merged

deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
18202781743:feat/kwin-split-menu

Conversation

@18202781743

@18202781743 18202781743 commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

最大化按钮通过原有 ToolTip/300ms 长按时机请求分屏菜单;平台接口与窗管支持时使用 KWin 菜单,否则保留 DSplitScreenWidget 回退。长按后释放不触发最大化,也不主动关闭菜单;鼠标离开和功能禁用仍发送对应隐藏请求。窗口隐藏时仅重置本地状态,窗口生命周期清理由窗管负责。

平台接口直接在 dtkwidget 内调用,无需修改 dtkgui。

关联平台接口:linuxdeepin/dde-qtplatform-plugins#345

验证:实现工作目录中 Qt5/Qt6 构建通过(最多 6 个核心);使用本地构建库及模拟平台函数验证窗管菜单、离开、长按、旧窗管回退和缺失显示接口。PR 基于最新 master,差异检查通过;真实 KWin 桌面显示效果尚未实测。

Summary by Sourcery

Add native KWin split-menu support while preserving legacy split-menu behavior and improving long-press interaction handling.

New Features:

  • Support KWin-provided split menus through platform integration, with the existing client-side split menu retained as a fallback.

Bug Fixes:

  • Prevent a long press that opens a split menu from also triggering window maximization.
  • Ensure split menus are hidden when the pointer leaves or split-screen functionality is disabled, while preserving menu state correctly across window hiding.

Enhancements:

  • Refine title-bar split-menu interaction and window-manager lifecycle handling for both native and legacy menus.

@sourcery-ai

sourcery-ai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

The titlebar now integrates with KWin split menus through optional Qt platform hooks, with native-menu state and lifecycle handling, while retaining the legacy DSplitScreenWidget fallback and preventing long-press interactions from triggering maximize.

Sequence diagram for titlebar split-menu selection

sequenceDiagram
    participant User
    participant DTitlebar
    participant PlatformHooks
    participant DSplitScreenWidget

    User->>DTitlebar: MouseButtonPress
    DTitlebar->>DTitlebar: start(300)
    DTitlebar->>DTitlebar: showSplitScreenWidget()
    alt KWin hooks supported
        DTitlebar->>PlatformHooks: showSplitMenu(wid, buttonRect)
        PlatformHooks-->>DTitlebar: native menu shown
    else legacy window manager
        DTitlebar->>DSplitScreenWidget: supportSplitScreenByWM(window)
        DTitlebar->>DSplitScreenWidget: show(position)
    end
    User->>DTitlebar: MouseButtonRelease
    alt long press used native menu
        DTitlebar->>DTitlebar: suppress maximize
    else menu opened on release path
        DTitlebar->>PlatformHooks: hideSplitMenu(wid, false)
    end
Loading

Sequence diagram for split-menu dismissal and lifecycle handling

sequenceDiagram
    participant User
    participant DTitlebar
    participant PlatformHooks

    User->>DTitlebar: Mouse leaves maximize button
    DTitlebar->>DTitlebar: stop()
    DTitlebar->>PlatformHooks: hideSplitMenu(wid, true)
    User->>DTitlebar: Disable split screen
    DTitlebar->>DTitlebar: stop()
    DTitlebar->>PlatformHooks: hideSplitMenu(wid, false)
    DTitlebar->>DTitlebar: reset native-menu state
    User->>DTitlebar: Window hidden
    DTitlebar->>DTitlebar: reset local native-menu state
Loading

File-Level Changes

Change Details Files
Adds runtime detection and invocation of KWin split-menu platform hooks while preserving the client-side split widget fallback.
  • Resolves support, show, and hide functions through Qt platformFunction APIs.
  • Uses the native window ID and maximize-button geometry to request the window-manager menu.
  • Falls back to DSplitScreenWidget when the platform menu is unavailable or unsupported.
  • Tracks the active menu window and performs immediate or delayed hiding as appropriate.
src/widgets/dtitlebar.cpp
Refines press-and-hold and mouse lifecycle behavior so opening the split menu does not accidentally maximize the window.
  • Requests the split menu after the existing 300 ms hold interval.
  • Suppresses maximize toggling on release after a long press.
  • Hides the menu on mouse leave or when disabled, while retaining local-state reset on titlebar hide.
  • Stops the hold timer and limits activation to left-button presses.
src/widgets/dtitlebar.cpp

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

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/widgets/dtitlebar.cpp" line_range="1257-1261" />
<code_context>
             d->hideSplitScreenWidget();
             break;
         }
+        case QEvent::Hide:
+            d->maxButtonPressAndHoldTimer->stop();
+            d->splitMenuByWM = false;
+            d->splitMenuWindowId = 0;
+            break;
         case QEvent::MouseButtonPress: {
-            d->maxButtonPressAndHoldTimer->start(300);
</code_context>
<issue_to_address>
**issue (bug_risk):** When the maximize button is hidden while the legacy `DSplitScreenWidget` is visible, the Hide handler stops the timer and clears only WM-menu state; it never calls `hideSplitScreenWidget()`, so the client-side split menu remains visible with a hidden title-bar button.

**Triggers:** When the maximize button or title bar is hidden while the legacy fallback menu is open.

**Suggested fix:** Hide the client-side split widget in the Hide handler, while retaining the WM-specific local-state reset behavior.

```suggestion
        case QEvent::Hide:
            d->maxButtonPressAndHoldTimer->stop();
            d->hideSplitScreenWidget();
            d->splitMenuByWM = false;
            d->splitMenuWindowId = 0;
            break;
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/widgets/dtitlebar.cpp
Comment on lines +1257 to +1261
case QEvent::Hide:
d->maxButtonPressAndHoldTimer->stop();
d->splitMenuByWM = false;
d->splitMenuWindowId = 0;
break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): When the maximize button is hidden while the legacy DSplitScreenWidget is visible, the Hide handler stops the timer and clears only WM-menu state; it never calls hideSplitScreenWidget(), so the client-side split menu remains visible with a hidden title-bar button.

Triggers: When the maximize button or title bar is hidden while the legacy fallback menu is open.

Suggested fix: Hide the client-side split widget in the Hide handler, while retaining the WM-specific local-state reset behavior.

Suggested change
case QEvent::Hide:
d->maxButtonPressAndHoldTimer->stop();
d->splitMenuByWM = false;
d->splitMenuWindowId = 0;
break;
case QEvent::Hide:
d->maxButtonPressAndHoldTimer->stop();
d->hideSplitScreenWidget();
d->splitMenuByWM = false;
d->splitMenuWindowId = 0;
break;

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 97 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 代码实现与PR描述一致,新增KWin原生分屏菜单支持并保留旧版回退机制。语法逻辑正确,边界处理完善,无安全漏洞。建议为新增辅助函数添加注释以提升可维护性。

🔍 详细分析

1. 语法逻辑 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: hideSplitScreenWidget(delay=true)时splitMenuWindowId不重置,建议添加注释说明设计意图:延迟隐藏后可能需要通过splitMenuWindowId关联后续隐藏操作


2. 代码质量 ✅

评价: 良好 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: 1.为nativeWindowId()添加注释:// 获取QWidget关联的原生窗口ID,无效时返回0
2.为showSplitMenuByWM()添加注释:// 通过窗管平台接口显示分屏菜单,成功返回true
3.为hideSplitMenuByWM()添加注释:// 通过窗管平台接口隐藏分屏菜单,delay控制是否延迟隐藏
4.新增类成员变量splitMenuByWM、splitMenuWindowId、splitMenuByWMPressAndHold建议添加行内注释说明用途


3. 代码性能 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: 无性能优化建议


4. 代码安全 🔒

评价: 优秀 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: 无安全加固建议


💡 改进建议代码示例

// 建议为新增辅助函数添加注释

// 获取 QWidget 关联的原生窗口 ID,无效时返回 0
static WId nativeWindowId(const QWidget *widget)
{
    if (!widget || !widget->windowHandle() || !widget->windowHandle()->handle())
        return 0;
    return widget->windowHandle()->winId();
}

// 通过窗管平台接口显示分屏菜单,成功返回 true
static bool showSplitMenuByWM(WId wid, const QRect &buttonRect)
{
    ...
}

// 通过窗管平台接口隐藏分屏菜单,delay 控制是否延迟隐藏
static void hideSplitMenuByWM(WId wid, bool delay)
{
    ...
}

本报告由 AI 代码审查工具自动生成

1. Remove direct QPlatformWindow include and add platformFunction-based
WM split menu integration
2. Add SUPPORTSPLITMENU, SHOWSPLITMENU, HIDESPLITMENU platform function
variables
3. Introduce nativeWindowId and show/hideSplitMenuByWM helper functions
4. Track splitMenuByWM, splitMenuWindowId, and splitMenuByWMPressAndHold
states in DTitlebarPrivate
5. Prioritize WM-provided split menu over client-side DSplitScreenWidget
when supported
6. Fall back to legacy client-side split menu only for older window
managers without support
7. Add immediate hide mode (hideImmediately) for split screen widget
when delay is false
8. Guard showSplitScreenWidget against invisible/disabled max button and
active WM menu
9. Handle QEvent::Hide and QEvent::Leave by stopping the press-and-hold
timer and resetting WM menu state
10. Only start press-and-hold timer on left mouse button press and
suppress toggle on release if WM menu was triggered
11. Stop timer and hide split menu immediately when split screen is
disabled via setSplitScreenEnabled
12. Fix minor trailing whitespace formatting issues

Log: Split screen menu now uses the window manager's native menu when
supported, with graceful fallback for older window managers

Influence:
1. Verify split menu appears via window manager on systems that support
it
2. Test fallback to client-side split menu on older window managers
3. Test press-and-hold on the maximize button to trigger the menu and
verify release does not toggle window state
4. Verify left-click on maximize button still toggles maximize/restore
normally
5. Test QEvent::Leave and QEvent::Hide behaviors to confirm menu hides
and timer stops
6. Test disabling split screen via setSplitScreenEnabled hides the menu
immediately
7. Verify behavior when max button is hidden or disabled (menu should
not show)
8. Confirm no regression in compositing-disabled or maximize-hint-
disabled scenarios

fix: 将分屏菜单委托给窗管处理

1. 移除直接包含 QPlatformWindow 头文件,改用 platformFunction 机制集成窗
管分屏菜单
2. 新增 SUPPORTSPLITMENU、SHOWSPLITMENU、HIDESPLITMENU 平台函数变量
3. 引入 nativeWindowId 及 show/hideSplitMenuByWM 辅助函数
4. 在 DTitlebarPrivate 中跟踪 splitMenuByWM、splitMenuWindowId 和
splitMenuByWMPressAndHold 状态
5. 当窗管支持时优先使用窗管提供的分屏菜单而非客户端 DSplitScreenWidget
6. 仅在不支持的老版本窗管下回退到客户端分屏菜单
7. 为分屏控件新增立即隐藏模式(hideImmediately),用于 delay 为 false 的
场景
8. 在最大按钮不可见/禁用或窗管菜单激活时跳过显示分屏控件
9. 处理 QEvent::Hide 和 QEvent::Leave,停止长按定时器并重置窗管菜单状态
10. 仅左键按下时启动长按定时器,若窗管菜单已触发则在释放时抑制窗口状态
切换
11. 通过 setSplitScreenEnabled 禁用分屏时立即停止定时器并隐藏分屏菜单
12. 修复少量行尾空白格式问题

Log: 分屏菜单在支持时改用窗管原生菜单,对老版本窗管提供优雅回退

Influence:
1. 验证在支持窗管分屏菜单的系统上菜单通过窗管弹出
2. 测试在老版本窗管上回退到客户端分屏菜单
3. 测试长按最大化按钮触发菜单,并确认释放时不会切换窗口状态
4. 验证左键单击最大化按钮仍能正常切换最大化/还原
5. 测试 QEvent::Leave 和 QEvent::Hide 行为,确认菜单隐藏且定时器停止
6. 测试通过 setSplitScreenEnabled 禁用分屏时菜单立即隐藏
7. 验证最大化按钮隐藏或禁用时菜单不显示
8. 确认在合成关闭或最大化提示禁用场景下无回归问题

PMS: BUG-370181
@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

This pr force merged! (status: blocked)

@deepin-bot
deepin-bot Bot merged commit 82cf982 into linuxdeepin:master Sep 20, 2026
21 of 23 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