Improve details/summary design.

This commit is contained in:
John Preston
2026-05-26 18:13:22 +04:00
parent b8b4a2440a
commit ccc0b22b93
5 changed files with 161 additions and 85 deletions
@@ -688,10 +688,16 @@ HistoryItem::HistoryItem(
const auto dropText = fields.ignoreForwardCaptions
&& _media
&& (_media->allowsEditCaption() || _media->poll());
const auto forwardRichPage = dropText
? std::shared_ptr<const Iv::RichPage>()
: original->richPage();
const auto forwardText = [&] {
if (dropText) {
return TextWithEntities();
}
if (forwardRichPage) {
return Iv::FlattenRichPageSummary(forwardRichPage);
}
const auto &text = original->originalText();
if (!text.text.isEmpty()) {
return dropForwardInfo
@@ -703,6 +709,9 @@ HistoryItem::HistoryItem(
}
return TextWithEntities();
}();
if (forwardRichPage) {
setRichPage(forwardRichPage);
}
setText(forwardText);
if (fields.groupedId) {
+11 -13
View File
@@ -418,7 +418,7 @@ defaultMarkdownDetails: MarkdownDetails {
summaryFg: windowFg;
radius: 6px;
summaryStyle: defaultMarkdownDetailsSummaryStyle;
icon: icon {{ "history_comments_open", windowSubTextFg }};
icon: icon {{ "history_down_arrow", windowSubTextFg }};
iconSkip: 8px;
}
defaultMarkdownEmbedPostAuthorStyle: TextStyle(defaultMarkdownBodyStyle) {
@@ -709,10 +709,8 @@ messageMarkdownHeading6Style: TextStyle(messageMarkdownBodyStyle) {
font: font(14px semibold);
lineHeight: 19px;
}
messageMarkdownDetailsSummaryStyle: TextStyle(defaultTextStyle) {
font: font(11px);
messageMarkdownDetailsSummaryStyle: TextStyle(messageMarkdownBodyStyle) {
linkUnderline: kLinkUnderlineNever;
lineHeight: 16px;
}
messageMarkdownAudioTitleStyle: TextStyle(messageMarkdownBodyStyle) {
font: font(14px semibold);
@@ -739,15 +737,15 @@ messageMarkdownBlockSkips: MarkdownBlockSkips {
placeholder: 8px;
}
messageMarkdownQuoteBgOpacity: 0.12;
messageMarkdownDetailsIcon: icon{{ "history_comments_open", msgInReplyBarColor }};
messageMarkdownDetailsIcon: icon{{ "history_down_arrow", msgInDateFg }};
messageMarkdownDetailsIconSelected: icon{{
"history_comments_open",
msgInReplyBarSelColor
"history_down_arrow",
msgInDateFgSelected
}};
messageMarkdownDetailsIconOut: icon{{ "history_comments_open", msgOutReplyBarColor }};
messageMarkdownDetailsIconOut: icon{{ "history_down_arrow", msgOutDateFg }};
messageMarkdownDetailsIconOutSelected: icon{{
"history_comments_open",
msgOutReplyBarSelColor
"history_down_arrow",
msgOutDateFgSelected
}};
messageMarkdownNavPreviousIcon: icon{{ "box_button_back", msgInDateFg }};
messageMarkdownNavPreviousIconSelected: icon{{
@@ -810,14 +808,14 @@ messageMarkdownDisplayMath: MarkdownDisplayMath(defaultMarkdownDisplayMath) {
overflowFg: msgInDateFg;
}
messageMarkdownDetails: MarkdownDetails(defaultMarkdownDetails) {
headerPadding: margins(6px, 8px, 6px, 8px);
bodyPadding: margins(8px, 8px, 8px, 8px);
headerPadding: margins(4px, 11px, 4px, 11px);
bodyPadding: margins(4px, 11px, 6px, 11px);
bodyBg: msgInBg;
summaryFg: historyTextInFg;
radius: 8px;
summaryStyle: messageMarkdownDetailsSummaryStyle;
icon: messageMarkdownDetailsIcon;
iconSkip: 6px;
iconSkip: 8px;
}
messageMarkdownPhoto: MarkdownPhoto(defaultMarkdownPhoto) {
captionSkip: 8px;
@@ -612,6 +612,10 @@ int BlockSkip(
const PreparedBlock &block,
LayoutContext context,
const style::Markdown &st) {
if (previous.kind == PreparedBlockKind::Details
&& block.kind == PreparedBlockKind::Details) {
return 0;
}
if (context.tightList
&& IsFlowKind(previous.kind)
&& IsFlowKind(block.kind)) {
@@ -53,18 +53,18 @@ namespace {
case PreparedBlockKind::Channel:
case PreparedBlockKind::GroupedMedia:
case PreparedBlockKind::RelatedArticle:
case PreparedBlockKind::Rule:
case PreparedBlockKind::Details:
return true;
case PreparedBlockKind::Paragraph:
case PreparedBlockKind::Thinking:
case PreparedBlockKind::Heading:
case PreparedBlockKind::CodeBlock:
case PreparedBlockKind::Rule:
case PreparedBlockKind::List:
case PreparedBlockKind::ListItem:
case PreparedBlockKind::Quote:
case PreparedBlockKind::DisplayMath:
case PreparedBlockKind::Table:
case PreparedBlockKind::Details:
case PreparedBlockKind::Placeholder:
case PreparedBlockKind::EmbedPost:
return false;
@@ -460,18 +460,35 @@ void PrepareNestedContext(
block.collapsed = prepared.collapsed;
block.supplementary = prepared.supplementary;
const auto &details = st.details;
const auto headerPadding = context.useArticleBands
? QMargins(
st.textPadding.left(),
details.headerPadding.top(),
st.textPadding.right(),
details.headerPadding.bottom())
: details.headerPadding;
const auto bodyPadding = context.useArticleBands
? QMargins(
st.textPadding.left(),
details.bodyPadding.top(),
st.textPadding.right(),
details.bodyPadding.bottom())
: details.bodyPadding;
const auto headerWidth = std::max(width, 1);
const auto iconWidth = details.icon.width();
const auto iconHeight = details.icon.height();
const auto iconSize = details.icon.empty()
? 0
: TextLineHeight(details.summaryStyle);
const auto iconWidth = iconSize;
const auto iconHeight = iconSize;
const auto iconSkip = iconWidth ? details.iconSkip : 0;
const auto textLeft = left
+ details.headerPadding.left()
+ headerPadding.left()
+ iconWidth
+ iconSkip;
block.textWidth = std::max(
headerWidth
- details.headerPadding.left()
- details.headerPadding.right()
- headerPadding.left()
- headerPadding.right()
- iconWidth
- iconSkip,
1);
@@ -491,20 +508,20 @@ void PrepareNestedContext(
block.leaf.countHeight(block.textWidth, true),
TextLineHeight(details.summaryStyle));
const auto headerContentHeight = std::max(summaryHeight, iconHeight);
const auto headerHeight = details.headerPadding.top()
const auto headerHeight = headerPadding.top()
+ headerContentHeight
+ details.headerPadding.bottom();
+ headerPadding.bottom();
block.headerRect = QRect(left, top, headerWidth, headerHeight);
if (iconWidth > 0 && iconHeight > 0) {
block.iconRect = QRect(
left + details.headerPadding.left(),
left + headerPadding.left(),
top + (headerHeight - iconHeight) / 2,
iconWidth,
iconHeight);
}
block.textRect = QRect(
textLeft,
top + details.headerPadding.top()
top + headerPadding.top()
+ std::max((headerContentHeight - summaryHeight) / 2, 0),
block.textWidth,
summaryHeight);
@@ -515,12 +532,12 @@ void PrepareNestedContext(
auto bottom = top + headerHeight;
if (!prepared.collapsed) {
const auto childLeft = left + details.bodyPadding.left();
const auto childTop = bottom + details.bodyPadding.top();
const auto childLeft = left + bodyPadding.left();
const auto childTop = bottom + bodyPadding.top();
const auto childWidth = std::max(
headerWidth
- details.bodyPadding.left()
- details.bodyPadding.right(),
- bodyPadding.left()
- bodyPadding.right(),
1);
auto childContext = context;
PrepareNestedContext(&childContext, childLeft, childWidth);
@@ -538,9 +555,9 @@ void PrepareNestedContext(
childWidth,
childContext);
const auto contentHeight = std::max(childBottom - childTop, 0);
const auto bodyHeight = details.bodyPadding.top()
const auto bodyHeight = bodyPadding.top()
+ contentHeight
+ details.bodyPadding.bottom();
+ bodyPadding.bottom();
block.bodyRect = QRect(left, bottom, headerWidth, bodyHeight);
block.contentRect = QRect(
childLeft,
@@ -10,11 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "iv/markdown/iv_markdown_article_text.h"
#include "ui/dynamic_image.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/effects/toggle_arrow.h"
#include "ui/style/style_core_scale.h"
#include "ui/widgets/checkbox.h"
#include "styles/palette.h"
#include "styles/style_iv.h"
#include "styles/style_window.h"
#include "styles/style_widgets.h"
#include <QtGui/QPainterPath>
@@ -409,6 +411,83 @@ void FillThinkingGradientImage(
return WithOpacity(st.headerBg, st.headerBgOpacity);
}
[[nodiscard]] bool UseIncomingMessageDefaultPalette(
const MarkdownArticlePaintContext &context) {
return !context.outbg
&& (context.caches.st == &context.messageStyle()->richPageStyle);
}
[[nodiscard]] QColor ApplySelectedIncomingOverlay(
QColor base,
const MarkdownArticlePaintContext &context) {
if (!context.selected() || context.outbg) {
return base;
}
auto overlay = context.st->msgSelectOverlay()->c;
const auto alpha = overlay.alphaF();
overlay.setAlpha(base.alpha());
auto result = anim::color(base, overlay, alpha);
result.setAlpha(base.alpha());
return result;
}
[[nodiscard]] QColor EffectiveTableHeaderBg(
const style::Markdown &paintSt,
const MarkdownArticlePaintContext &context) {
return UseIncomingMessageDefaultPalette(context)
? ApplySelectedIncomingOverlay(
TableHeaderBg(st::defaultMarkdown.table),
context)
: TableHeaderBg(paintSt.table);
}
[[nodiscard]] QColor EffectiveTableBorderFg(
const style::Markdown &paintSt,
const MarkdownArticlePaintContext &context) {
const auto useIncoming = UseIncomingMessageDefaultPalette(context);
const auto &table = useIncoming
? st::defaultMarkdown.table
: paintSt.table;
auto result = WithOpacity(table.borderFg, table.headerBgOpacity * 3);
return useIncoming
? ApplySelectedIncomingOverlay(result, context)
: result;
}
[[nodiscard]] QColor EffectiveDividerFg(
const style::Markdown &paintSt,
const MarkdownArticlePaintContext &context) {
auto result = UseIncomingMessageDefaultPalette(context)
? ApplySelectedIncomingOverlay(
st::defaultMarkdown.rule.fg->c,
context)
: paintSt.rule.fg->c;
if (context.outbg) {
result.setAlphaF(result.alphaF() * 0.5);
}
return result;
}
void PaintDetailsIcon(
Painter &p,
QRect rect,
QColor color,
bool collapsed) {
const auto size = std::min(rect.width(), rect.height()) / 3.;
if (size <= 0.) {
return;
}
const auto center = QRectF(rect).center();
const auto path = Ui::ToggleUpDownArrowPath(
center.x(),
center.y(),
size,
st::mainMenuToggleFourStrokes,
collapsed ? 0. : 1.);
auto hq = PainterHighQualityEnabler(p);
p.fillPath(path, color);
}
void RefreshBlockThumbnail(
const LaidOutBlock &block,
const MarkdownArticlePaintContext &context) {
@@ -923,6 +1002,8 @@ void PaintWholeTable(
const auto radius = st.table.radius;
const auto shapePath = TableShapePath(block, border, radius);
const auto &paintSt = PaintStyle(context, st);
const auto headerBg = EffectiveTableHeaderBg(paintSt, context);
const auto borderFg = EffectiveTableBorderFg(paintSt, context);
p.save();
p.setClipRect(tableClip);
@@ -937,7 +1018,7 @@ void PaintWholeTable(
if (!cell.header && !striped) {
continue;
}
p.fillRect(cell.outer, TableHeaderBg(paintSt.table));
p.fillRect(cell.outer, headerBg);
}
}
p.restore();
@@ -945,11 +1026,7 @@ void PaintWholeTable(
if (border > 0 && !block.tableRect.isEmpty()) {
const auto path = TableBorderPath(block, border, shapePath);
auto hq = PainterHighQualityEnabler(p);
auto pen = QPen(
WithOpacity(
paintSt.table.borderFg,
paintSt.table.headerBgOpacity * 3),
border);
auto pen = QPen(borderFg, border);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawPath(path);
@@ -1025,6 +1102,8 @@ void PaintTableRowBand(
const auto radius = st.table.radius;
const auto shapePath = TableShapePath(block, border, radius);
const auto &paintSt = PaintStyle(context, st);
const auto headerBg = EffectiveTableHeaderBg(paintSt, context);
const auto borderFg = EffectiveTableBorderFg(paintSt, context);
const auto cells = TableCellsForRowBand(ownership, rowIndex, rowBand);
const auto rowContext = ClippedContext(
RevealSuppressedContext(context),
@@ -1042,18 +1121,14 @@ void PaintTableRowBand(
if (!cell->header && !striped) {
continue;
}
p.fillRect(cell->outer, TableHeaderBg(paintSt.table));
p.fillRect(cell->outer, headerBg);
}
p.restore();
if (border > 0 && !block.tableRect.isEmpty()) {
const auto path = TableBorderPath(block, border, shapePath);
auto hq = PainterHighQualityEnabler(p);
auto pen = QPen(
WithOpacity(
paintSt.table.borderFg,
paintSt.table.headerBgOpacity * 3),
border);
auto pen = QPen(borderFg, border);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawPath(path);
@@ -1792,54 +1867,27 @@ void PaintDetailsBlock(
const auto &details = st.details;
const auto &paintSt = PaintStyle(context, st);
const auto &paintDetails = paintSt.details;
const auto headerBg = TableHeaderBg(paintSt.table);
const auto half = details.border / 2.;
const auto outer = QRectF(block.outer).marginsRemoved({
half,
half,
half,
half,
});
auto outerPath = QPainterPath();
outerPath.addRoundedRect(outer, details.radius, details.radius);
const auto lineHeight = details.border;
const auto lineRect = QRect(
block.outer.x(),
block.outer.y() + block.outer.height() - lineHeight,
block.outer.width(),
lineHeight);
const auto lineFg = EffectiveDividerFg(paintSt, context);
p.save();
p.setClipRect(visible);
{
auto hq = PainterHighQualityEnabler(p);
p.fillPath(
outerPath,
paintDetails.bodyBg->c);
p.save();
p.setClipPath(outerPath, Qt::IntersectClip);
p.fillRect(block.headerRect, TableHeaderBg(paintSt.table));
auto pen = QPen(
WithOpacity(
paintSt.table.borderFg,
paintSt.table.headerBgOpacity * 3),
details.border);
if (!block.bodyRect.isEmpty()) {
p.setPen(pen);
const auto separatorY = block.bodyRect.top() + half;
p.drawLine(
QPointF(outer.left(), separatorY),
QPointF(outer.right(), separatorY));
}
p.restore();
p.setBrush(Qt::NoBrush);
p.setPen(pen);
p.drawPath(outerPath);
if (lineHeight > 0 && !lineRect.isEmpty()) {
p.fillRect(lineRect, lineFg);
}
if (!block.iconRect.isEmpty()) {
paintDetails.icon.paint(
PaintDetailsIcon(
p,
block.iconRect.x(),
block.iconRect.y(),
outerWidth);
block.iconRect,
paintSt.supplementaryTextColor->c,
block.collapsed);
}
p.setPen(paintDetails.summaryFg->c);
p.setPen(paintSt.textColor->c);
PaintTextLeaf(
p,
block.leaf,
@@ -2020,8 +2068,8 @@ void PaintBlock(
p,
context,
block.outer,
[&](Painter &p, const MarkdownArticlePaintContext &) {
p.fillRect(block.outer, paintSt.rule.fg->c);
[&](Painter &p, const MarkdownArticlePaintContext &context) {
p.fillRect(block.outer, EffectiveDividerFg(paintSt, context));
});
break;
case PreparedBlockKind::List: