diff --git a/src/widgets/dcrumbedit.cpp b/src/widgets/dcrumbedit.cpp index 41bb51f81..3a909da4c 100644 --- a/src/widgets/dcrumbedit.cpp +++ b/src/widgets/dcrumbedit.cpp @@ -556,17 +556,28 @@ class DCrumbEditPrivate : public DCORE_NAMESPACE::DObjectPrivate QSizeF CrumbObjectInterface::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) { - Q_UNUSED(doc) Q_UNUSED(posInDocument) const DCrumbTextFormat crumb_format(format); const QFontMetricsF font_metrics(crumb_format.font()); int radius = crumb_format.backgroundRadius(); - if (crumb_format.tagColor().isValid()) - return QSizeF(font_metrics.horizontalAdvance(crumb_format.text()) + font_metrics.height() + radius + 2, font_metrics.height() + 2); + if (crumb_format.tagColor().isValid()) { + qreal width = font_metrics.horizontalAdvance(crumb_format.text()) + + font_metrics.height() + radius + 2; + // 限制单个 crumb 不超过文档可用内容宽度,避免超宽标记撑开文档导致水平滚动条。 + // 超宽时由 drawObject 做省略显示。仅对带标记颜色的 crumb 生效(本 issue 场景)。 + const qreal textWidth = doc->textWidth(); + if (textWidth > 0) { + const qreal avail = textWidth - 2 * doc->documentMargin(); + if (avail > 0 && width > avail) + width = avail; + } + return QSizeF(width, font_metrics.height() + 2); + } - return QSizeF(font_metrics.horizontalAdvance(crumb_format.text()) + 2 * radius + 2, font_metrics.height() + 2 + TopMargin *2); + return QSizeF(font_metrics.horizontalAdvance(crumb_format.text()) + 2 * radius + 2, + font_metrics.height() + 2 + TopMargin * 2); } void CrumbObjectInterface::drawObject(QPainter *painter, const QRectF &rect, @@ -582,24 +593,32 @@ void CrumbObjectInterface::drawObject(QPainter *painter, const QRectF &rect, QPainterPath background_path; QPainterPath tag_path; - const QRectF tag_rect(new_rect.x() + 2, new_rect.y() + 2, font_metrics.height() - 4, font_metrics.height() - 4); + const QRectF tag_rect(new_rect.x() + 2, new_rect.y() + 2, + font_metrics.height() - 4, font_metrics.height() - 4); tag_path.addEllipse(tag_rect); background_path.addRoundedRect(new_rect, radius, crumb_format.backgroundRadius()); painter->setRenderHint(QPainter::Antialiasing); + // 极窄宽度下色块可能超出收缩后的矩形,裁剪兜底防绘制越界 + painter->save(); + painter->setClipRect(new_rect); painter->fillPath(background_path, backgroundBrush(new_rect, crumb_format.background())); if (crumb_format.tagColor().isValid()) { painter->fillPath(tag_path, crumb_format.tagColor()); - painter->setPen(crumb_format.textColor()); - painter->drawText(new_rect.adjusted(tag_rect.width() + 2, 0, -radius, 0), - crumb_format.text(), Qt::AlignVCenter | Qt::AlignRight); + const QRectF textRect = new_rect.adjusted(tag_rect.width() + 2, 0, -radius, 0); + // intrinsicSize 已将超宽 crumb 收缩到可用宽度,此处按实际可用宽度省略文字 + QString displayText = crumb_format.text(); + if (font_metrics.horizontalAdvance(displayText) > textRect.width()) + displayText = font_metrics.elidedText(displayText, Qt::ElideRight, textRect.width()); + painter->drawText(textRect, displayText, Qt::AlignVCenter | Qt::AlignLeft); } else { painter->setPen(crumb_format.textColor()); painter->drawText(new_rect, Qt::AlignCenter, crumb_format.text()); } + painter->restore(); } QBrush CrumbObjectInterface::backgroundBrush(const QRect &rect, const QBrush &brush) diff --git a/tests/testcases/widgets/ut_dcrumbedit.cpp b/tests/testcases/widgets/ut_dcrumbedit.cpp index 737741c02..3513a0f0d 100644 --- a/tests/testcases/widgets/ut_dcrumbedit.cpp +++ b/tests/testcases/widgets/ut_dcrumbedit.cpp @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include +#include #include "dcrumbedit.h" #include @@ -104,3 +108,70 @@ TEST_F(ut_DCrumbedit, createMimeDataFromSelection) ASSERT_EQ(qApp->clipboard()->text(), "测试1 人物 测试2 儿童 测试3 照片 测试代码调试添加GTest"); delete data; } + +// 验证:容器(文档)可用宽度小于 crumb 固有宽度时,带标记颜色的 crumb 文字会被省略且不溢出框外。 +// 对应 issue:放大缩小右侧边栏时预览区标记自适应不好(缩小溢出、放大留白)。 +TEST_F(ut_DCrumbedit, tagCrumbElidedAndNoOverflowWhenContainerNarrow) +{ + // 创建带标记颜色 + 超长文字的 crumb + DCrumbTextFormat format = edit->makeTextFormat(DCrumbEdit::red); + ASSERT_TRUE(format.tagColor().isValid()); + + const QString longText = QString::fromUtf8("标记文字").repeated(20); // 远超容器宽度的超长文字 + format.setText(longText); + // 用纯色背景/文字,便于按像素检测绘制范围 + format.setBackground(QBrush(Qt::yellow)); + format.setTextColor(Qt::black); + + ASSERT_TRUE(edit->appendCrumb(format)); + ASSERT_EQ(edit->crumbList().size(), 1); + ASSERT_EQ(edit->crumbList().first(), longText); + + QTextDocument *doc = edit->document(); + const QFontMetricsF fm(format.font()); + // crumb 固有宽度(与 intrinsicSize 口径一致) + const qreal intrinsicWidth = + fm.horizontalAdvance(longText) + fm.height() + format.backgroundRadius() + 2; + // 将文档可用宽度设为远小于固有宽度,模拟缩小侧边栏 + const int narrowWidth = int(intrinsicWidth / 3); + ASSERT_GT(narrowWidth, 0); + ASSERT_LT(qreal(narrowWidth), intrinsicWidth); + doc->setTextWidth(narrowWidth); + + // 修复前 intrinsicSize 未限宽,单个超宽 crumb 会撑开文档宽度导致水平滚动条; + // 修复后 idealWidth 应不超出文档可用宽度。 + EXPECT_LE(doc->idealWidth(), qreal(narrowWidth)) + << "单个超宽 crumb 撑开了文档宽度(" << doc->idealWidth() + << " > " << narrowWidth << "),会产生水平滚动条"; + + // 渲染文档到一张比 narrowWidth 更宽的图片,检测 crumb 是否溢出文档可用宽度 + QImage img(int(intrinsicWidth) + 40, 100, QImage::Format_ARGB32); + img.fill(Qt::white); + QPainter p(&img); + QAbstractTextDocumentLayout::PaintContext ctx; + doc->documentLayout()->draw(&p, ctx); + p.end(); + + // 找出最右侧的非背景像素,crumb 不应溢出到 narrowWidth 右侧(留少量抗锯齿容差) + int rightMostContentX = -1; + for (int y = 0; y < img.height(); ++y) { + for (int x = img.width() - 1; x > rightMostContentX; --x) { + if (img.pixelColor(x, y) != Qt::white) { + rightMostContentX = x; + break; + } + } + } + // 容差 8 像素用于抗锯齿/圆角;无修复时 crumb 会溢出到约 intrinsicWidth 处 + EXPECT_LT(rightMostContentX, narrowWidth + 8) + << "crumb 内容溢出文档可用宽度(" << narrowWidth + << "),rightMostContentX=" << rightMostContentX; + + // 绘制层省略不改变实际 crumb 文本 + EXPECT_EQ(edit->crumbList().first(), longText); + + // 省略逻辑:对窄宽度做 elidedText 应得到含省略号的较短文本 + const QString elided = fm.elidedText(longText, Qt::ElideRight, narrowWidth); + EXPECT_LT(fm.horizontalAdvance(elided), fm.horizontalAdvance(longText)); + EXPECT_TRUE(elided.contains(QChar(0x2026))); // 包含省略号 “…” +}