Skip to content

fix: correct scroll button enabled state after lazy layoutTabs in DTabBar - #769

Draft
Johnson-zs wants to merge 1 commit into
linuxdeepin:masterfrom
Johnson-zs:fix/dtabbar-scroll-button-state
Draft

fix: correct scroll button enabled state after lazy layoutTabs in DTabBar#769
Johnson-zs wants to merge 1 commit into
linuxdeepin:masterfrom
Johnson-zs:fix/dtabbar-scroll-button-state

Conversation

@Johnson-zs

@Johnson-zs Johnson-zs commented Aug 13, 2026

Copy link
Copy Markdown

根因分析

窗口缩至最小、多 tab 溢出时,DTabBar 左右滚动按钮可点击状态反转:左侧 disabled(实际最左 tab 已隐藏,应可点),右侧 enabled(最右 tab 已可见,应不可点)。

根因:Qt QTabBarPrivate::layoutTabs() 硬编码 leftB->setEnabled(false)makeVisible() 才根据实际 scrollOffset 修正为正确状态。layoutTabs() 存在惰性调用路径(经 tabRect()paintEvent 中触发),该路径不调用 makeVisible()。DTK 的 DTabBar::resizeEvent 不触发 QTabBar 子部件的 makeVisible,而文管 TabBar::resizeEventsetIconSize(iconSize()) 临时方案会设置 layoutDirty=true 触发惰性 layoutTabs,导致按钮状态被重置为默认值且无人修正。

修复方案

DTabBarPrivate::tabLayoutChange()layoutTabs() 末尾调用的虚函数)中增加 makeVisible(currentIndex()) 调用,确保每次 layoutTabs() 完成后(包括惰性路径)滚动按钮的 enabled 状态根据实际 scrollOffset 修正。

改动安全评估

低风险:不修改函数签名,不删除公开函数,仅增加一行对已有方法 makeVisible 的调用(该方法已在 startMovestartTabFlash 等处使用)。makeVisible 内部有 validIndex 守卫,无递归风险。

Summary by Sourcery

Bug Fixes:

  • Fix left/right scroll button enabled state being inverted after lazy layoutTabs execution by recalculating visibility based on the current tab index.

…bBar

1. 在 DTabBarPrivate::tabLayoutChange() 中增加 makeVisible(currentIndex()) 调用;
2. 修复惰性 layoutTabs 路径(如 paintEvent 经 tabRect() 触发)下左右滚动按钮 enabled 状态反转的问题;

=====================================

1. added makeVisible(currentIndex()) call in DTabBarPrivate::tabLayoutChange();
2. fixed scroll button enabled state inversion in lazy layoutTabs path (e.g. triggered via tabRect() in paintEvent);

Log: 修复窗口缩至最小、多 tab 溢出时 DTabBar 左右滚动按钮可点击状态反转的问题,确保惰性布局后按钮状态与实际滚动位置一致
@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Johnson-zs

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

sourcery-ai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Ensures DTabBar scroll button enabled/disabled state is correctly recalculated after every tab layout, including lazy layoutTabs paths triggered via paintEvent/tabRect, by invoking makeVisible(currentIndex()) in DTabBarPrivate::tabLayoutChange().

Sequence diagram for corrected scroll button state after lazy layoutTabs

sequenceDiagram
    participant DTabBar as DTabBar
    participant QTabBarPrivate as QTabBarPrivate
    participant DTabBarPrivate as DTabBarPrivate

    DTabBar->>DTabBar: paintEvent
    DTabBar->>DTabBar: tabRect
    DTabBar->>QTabBarPrivate: layoutTabs
    QTabBarPrivate-->>DTabBarPrivate: tabLayoutChange
    DTabBarPrivate->>DTabBar: tabLayoutChange
    DTabBarPrivate->>DTabBarPrivate: updateCloseButtonVisible
    DTabBarPrivate->>DTabBarPrivate: makeVisible(currentIndex)
Loading

File-Level Changes

Change Details Files
Recalculate scroll button enabled state after every tab layout, including lazy layoutTabs invocations, by calling makeVisible(currentIndex()) at the end of DTabBarPrivate::tabLayoutChange().
  • Append a makeVisible(currentIndex()) call to DTabBarPrivate::tabLayoutChange() after updating close button visibility.
  • Document in comments that layoutTabs() resets the left scroll button to disabled and that makeVisible must run to correct enabled state based on scrollOffset, especially for lazy layout paths triggered via paintEvent/tabRect.
src/widgets/dtabbar.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 left some high level feedback:

  • Calling makeVisible(currentIndex()) on every tabLayoutChange may unintentionally re-center the scroll position around the current tab after any layout, so it’s worth double-checking whether there are scenarios where the user has intentionally scrolled away from the current tab and this call would now override that behavior; if so, consider a condition to limit when makeVisible is invoked.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Calling `makeVisible(currentIndex())` on every `tabLayoutChange` may unintentionally re-center the scroll position around the current tab after any layout, so it’s worth double-checking whether there are scenarios where the user has intentionally scrolled away from the current tab and this call would now override that behavior; if so, consider a condition to limit when `makeVisible` is invoked.

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.

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

★ 总体评分:100分

■ 【总体评价】

代码精准修复了惰性布局路径下滚动按钮状态反转的问题,逻辑严密且无副作用。
逻辑完全正确,注释详尽规范,无任何扣分项,给予满分。

■ 【详细分析】

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

DTabBarPrivate::tabLayoutChange() 函数末尾调用 makeVisible(currentIndex()),准确触发了基于当前 scrollOffset 的边界重算,有效覆盖了 layoutTabs() 中将左滚动按钮强制置为 disabled 的副作用,彻底解决了 paintEvent 惰性调用链路中的状态反转缺陷。

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

新增代码附带了大段高质量的多行注释,清晰阐述了问题根因(layoutTabs() 的强制重置行为)、触发场景(paintEvent 中的 tabRect() 触发)以及修复意图,极大提升了代码的可维护性与可读性。

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

makeVisible() 仅涉及当前索引的边界判定与按钮状态设置,属于轻量级计算。tabLayoutChange() 本身就在布局变更时低频调用,新增的调用不会引发性能瓶颈或多余的绘制开销。

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

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
本次修改纯粹聚焦于 UI 控件内部状态机的修正,不涉及任何外部输入解析、内存指针越界、权限校验或跨进程通信,攻击面为零,无安全风险。

  • 建议:无需安全修复。

■ 【改进建议代码示例】

diff --git a/src/widgets/dtabbar.cpp b/src/widgets/dtabbar.cpp
index 7584a4867..b2ad850e5 100644
--- a/src/widgets/dtabbar.cpp
+++ b/src/widgets/dtabbar.cpp
@@ -1589,6 +1589,10 @@ void DTabBarPrivate::tabLayoutChange()
     q->tabLayoutChange();
     // 更新关闭按钮的显示
     updateCloseButtonVisible();
+    // 修正滚动按钮的启用状态:layoutTabs() 会将 leftB 重置为 disabled,
+    // 需根据实际 scrollOffset 重新计算,修复惰性布局路径(如 paintEvent
+    // 中经 tabRect() 触发的 layoutTabs)下左右滚动按钮 enabled 状态反转的问题。
+    makeVisible(currentIndex());
 }
 
 void DTabBarPrivate::initStyleOption(QStyleOptionTab *option, int tabIndex) const

@Johnson-zs
Johnson-zs marked this pull request as draft August 16, 2026 06:17
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