WinterGram/submodules/InstantPageUI/Sources/InstantPageLayoutSpacings.swift
isaac 1872832e28 Improve InstantPage V2 layout: true font-height line boxes + code-block style
- layoutTextItem now sizes a text item to the true font line height (A + D)
  instead of the cap box: the line stack starts at lineBoxTopInset (the
  ascender headroom, max(0, fontAscent - fontLineHeight)) and the returned
  height is padded by the last line's descender. Inter-line advance is
  unchanged and per-line frames stay the cap box, so the baseline draw,
  decorations, reveal mask, and inline attachments translate consistently;
  the page grows.
- Size inline custom emoji to ~the font line height
  (font.ascender - font.descender + 4*pointSize/17) so they fit the taller
  line box instead of overflowing it; the line is not inflated.
- Add a dedicated code-block text style for rich messages (monospace font +
  codeBlock theme attribute) threaded through the rich-data bubble and send
  preview; unify the bubble code-block/separator colors.
- Adjust inter-block spacings and assorted V2 layout/divider details.
- Document the true-font-height box and emoji sizing in CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 17:33:01 +02:00

153 lines
4.3 KiB
Swift

import Foundation
import UIKit
import TelegramCore
enum BlockSequenceKind {
case topLevel
case detail
case cell
case list
}
func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fitToWidth: Bool, kind: BlockSequenceKind) -> CGFloat {
if let upper, let lower {
switch (upper, lower) {
case (_, .cover), (_, .channelBanner), (.details, .details), (.relatedArticles, _), (_, .anchor):
return 0.0
case (.divider, _), (_, .divider):
if fitToWidth {
return 21.0
} else {
return 25.0
}
case (_, .blockQuote), (.blockQuote, _), (_, .pullQuote), (.pullQuote, _):
if fitToWidth {
return 11.0
} else {
return 27.0
}
case (.kicker, .title), (.cover, .title):
return 16.0
case (_, .title):
return 20.0
case (.title, .authorDate), (.subtitle, .authorDate):
return 18.0
case (_, .authorDate):
return 20.0
case (.title, .paragraph), (.authorDate, .paragraph):
return 34.0
case (.header, .paragraph), (.subheader, .paragraph), (.heading, .paragraph):
if fitToWidth {
return 14.0
} else {
return 25.0
}
case (.list, .paragraph):
if fitToWidth {
return 14.0
} else {
return 31.0
}
case (.paragraph, .list):
if fitToWidth {
return 14.0
} else {
return 31.0
}
case (.formula, .paragraph):
return 19.0
case (.paragraph, .paragraph):
if fitToWidth {
return 2.0
} else {
return 25.0
}
case (.title, .formula), (.authorDate, .formula):
return 34.0
case (.header, .formula), (.subheader, .formula), (.heading, .formula):
if fitToWidth {
return 10.0
} else {
return 25.0
}
case (.list, .formula):
return 31.0
case (.paragraph, .formula):
return 19.0
case (_, .formula):
return 20.0
case (.title, .list), (.authorDate, .list):
return 34.0
case (.header, .list), (.subheader, .list), (.heading, .list):
return 31.0
case (.preformatted, _), (_, .preformatted):
if fitToWidth {
return 12.0
} else {
return 19.0
}
case (.formula, .list):
if fitToWidth {
return 10.0
} else {
return 25.0
}
case (_, .list):
if fitToWidth {
return 10.0
} else {
return 25.0
}
case (_, .header), (_, .subheader), (_, .heading):
return 32.0
default:
return 20.0
}
} else if let lower {
switch lower {
case .cover, .channelBanner, .details, .anchor:
return 0.0
default:
if fitToWidth {
switch kind {
case .topLevel:
switch lower {
case .heading:
return 6.0
case .table:
return 10.0
default:
return 5.0
}
case .cell:
return 0.0
case .detail, .list:
return 4.0
}
} else {
return 25.0
}
}
} else if let upper {
switch kind {
case .topLevel:
if case .relatedArticles = upper {
return 0.0
} else if case .thinking = upper {
return 2.0
} else {
if fitToWidth {
return 5.0
} else {
return 25.0
}
}
case .detail, .list:
return 16.0
case .cell:
return 0.0
}
} else {
return 0.0
}
}