Skip to content

fix(annotation): use text layout positions for ruled lines in note editor#262

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
pengfeixx:fix/annotation-ruled-lines-overlap
Apr 22, 2026
Merged

fix(annotation): use text layout positions for ruled lines in note editor#262
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
pengfeixx:fix/annotation-ruled-lines-overlap

Conversation

@pengfeixx
Copy link
Copy Markdown
Contributor

Use QTextDocument's actual layout (QTextBlock/QTextLine) instead of fixed font metrics height to draw ruled lines, preventing accumulated drift that causes text/line overlap after 10+ lines.

使用QTextDocument实际布局信息绘制注释编辑框横线,
替代固定字体度量的等差递增方式,修复多行文字与行线重叠问题。

Log: 修复注释编辑框多行文字与横线重叠
PMS: BUG-338065
Influence: 注释编辑框输入超过10行时,横线与文字不再错位重叠。

@pengfeixx pengfeixx force-pushed the fix/annotation-ruled-lines-overlap branch from 472e10a to 0290251 Compare April 20, 2026 09:22
…itor

Use QTextDocument's actual layout (QTextBlock/QTextLine) instead of
fixed font metrics height to draw ruled lines, preventing accumulated
drift that causes text/line overlap after 10+ lines.

使用QTextDocument实际布局信息绘制注释编辑框横线,
替代固定字体度量的等差递增方式,修复多行文字与行线重叠问题。

Log: 修复注释编辑框多行文字与横线重叠
PMS: BUG-338065
Influence: 注释编辑框输入超过10行时,横线与文字不再错位重叠。
@pengfeixx pengfeixx force-pushed the fix/annotation-ruled-lines-overlap branch from 0290251 to 941ab59 Compare April 22, 2026 07:25
@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: lzwind, pengfeixx

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

@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这段代码主要重写了 TransparentTextEditpaintEvent 方法,用于绘制类似信纸的横线背景。我将从语法逻辑、代码质量、代码性能和代码安全四个方面进行审查。

1. 语法逻辑

  • 改进点: 旧代码通过固定的字体高度 fontmetricsf.height() 来计算横线位置。这在处理换行或不同字体大小时,横线与文字的实际位置可能无法精确对齐。
  • 评价: 新代码逻辑正确且更健壮。它通过遍历 QTextDocumentQTextBlock 并获取 QTextLayout 来计算每一行的精确位置(lineY)。这确保了横线严格跟随文本的排版绘制,无论字体如何变化或文本如何换行,线条都能准确对齐在文字下方。
  • 建议: 逻辑上非常合理,特别是增加了 if (lineY < topY) continue;if (lineY > viewportHeight) break; 的视口裁剪判断,避免了绘制不可见区域的线条。

2. 代码质量

  • 改进点:
    • 常量定义: 新代码将魔法数字(如 2, 4, 1.0)提取为具有语义的常量(topLineHeight, leftMargin 等),显著提高了代码的可读性和可维护性。
    • 代码结构: 新代码将绘制逻辑分为了“顶部线绘制”和“循环绘制规则线”两个部分,逻辑层次更清晰。
    • 注释: 增加了必要的注释(如 // Top line...// Draw ruled lines...),有助于理解代码意图。
  • 评价: 代码质量有显著提升,符合现代 C++ 和 Qt 的编码规范。
  • 建议: 可以考虑将这些常量(颜色、边距、线宽)提取为类的成员变量或配置项,以便在 UI 设计需要调整时无需修改源代码。

3. 代码性能

  • 潜在问题: 新代码在 paintEvent 中进行了大量的遍历操作(while (block.isValid()) 以及内部的 for 循环)。paintEvent 是高频调用的函数(尤其在滚动时)。
  • 分析:
    • 虽然增加了遍历,但引入了 if (lineY > viewportHeight) break; 提前终止循环,这意味着它只处理当前屏幕可见的行,而不是整个文档。
    • 旧代码虽然循环简单,但在滚动到底部时需要额外的计算和判断来绘制底部粗线。
  • 评价: 性能方面,新代码虽然逻辑更复杂,但通过视口裁剪优化,性能损失在可接受范围内,且换来了更准确的渲染效果。
  • 建议: 如果文档非常长且性能成为瓶颈,可以考虑缓存 QTextBlock 的布局信息,但在常规阅读器场景下,当前的实现通常足够流畅。

4. 代码安全

  • 改进点: 新代码增加了对 layout 指针的空指针检查 if (layout)
  • 评价: 这是一个很好的防御性编程实践,防止了潜在的空指针解引用崩溃。
  • 建议:
    • this->document() 返回的指针通常由 QTextEdit 管理,不太可能为空,但在 paintEvent 中调用 this->verticalScrollBar()->value() 等是安全的。
    • 代码整体安全性良好。

总结与改进建议

这段代码修改是正向的,它修复了横线与文本对齐不准的问题,并提升了代码的可读性。

进一步优化的建议:

  1. 性能微调: painter.setPen(pen) 在循环内部被调用了多次(尤其是当切换线宽时)。虽然 Qt 的 setPen 开销不大,但可以进一步优化逻辑,尽量减少状态切换。例如,先画完所有普通线,再判断是否需要画最后一根粗线,或者在循环外记录最后一根线的坐标,循环结束后单独绘制。

    • 当前写法是为了逻辑清晰(判断是否为最后一行),但在极端性能敏感场景下可优化。
  2. 坐标计算精度:

    • QPointF 的使用是正确的,保证了抗锯齿效果下的绘制精度。
    • lineY 的计算公式 blockPos.y() + textLine.position().y() + textLine.height() - scrollY 精确地定位到了文本行的底部。
  3. 版权年份更新:

    • Copyright (C) 2019 ~ 2026:年份更新到了 2026,这通常是预留给未来的。如果是当前提交,建议只更新到当前年份(如 2023 或 2024),除非有特殊的长远规划。

修改后的代码片段示例(针对性能优化的建议):

    // ... 前面的代码保持不变 ...

    // Draw ruled lines aligned with actual text layout
    pen.setWidth(normalLineHeight);
    painter.setPen(pen);

    // 用于存储最后一行可见线的坐标,以便在循环外统一处理粗线
    QPointF lastVisibleLinePos;
    bool hasLastVisibleLine = false;

    QTextDocument *doc = this->document();
    QTextBlock block = doc->begin();
    while (block.isValid()) {
        QTextLayout *layout = block.layout();
        if (layout) {
            QPointF blockPos = layout->position();
            for (int i = 0; i < layout->lineCount(); ++i) {
                QTextLine textLine = layout->lineAt(i);
                qreal lineY = blockPos.y() + textLine.position().y() + textLine.height() - scrollY;
                
                if (lineY < topY)
                    continue;
                if (lineY > viewportHeight)
                    break;

                // 绘制线条
                painter.drawLine(QPointF(leftMargin, lineY), QPointF(viewWidth - rightMargin, lineY));

                // 记录当前绘制的线条作为“最后一个可见线条”的候选
                // 只有当真正滚动到底部且该行接近视口底部时,才需要加粗
                if (atBottom && lineY + textLine.height() > viewportHeight) {
                     lastVisibleLinePos = QPointF(leftMargin, lineY);
                     hasLastVisibleLine = true;
                }
            }
        }
        block = block.next();
    }

    // 循环结束后,如果需要加粗最后一行,则重绘最后一条线
    if (hasLastVisibleLine) {
        pen.setWidth(topLineHeight);
        painter.setPen(pen);
        // 重新绘制最后一条线,覆盖原来的细线
        painter.drawLine(lastVisibleLinePos, QPointF(viewWidth - rightMargin, lastVisibleLinePos.y()));
    }

这个优化减少了 setPen 在循环中的切换次数,逻辑上将“普通线绘制”和“特殊线绘制”分离开来,理论上会有微小的性能提升,且逻辑依然清晰。

@pengfeixx
Copy link
Copy Markdown
Contributor Author

/merge

@deepin-bot deepin-bot Bot merged commit ab6a35d into linuxdeepin:master Apr 22, 2026
10 checks passed
@pengfeixx pengfeixx deleted the fix/annotation-ruled-lines-overlap branch April 22, 2026 07:36
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