Files
i7t5--edmund/Sources/EdmundCore/TextView/EditorTextView+ContentWidth.swift
T
wehub-resource-sync 5b99bf6bca
CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:34:54 +08:00

51 lines
2.6 KiB
Swift

import AppKit
// MARK: - Content width (centered reading column)
//
// The text column has a physical maximum width (set in cm in Settings and
// converted to points using the display's real PPI). Windows wider than the
// cap get symmetric side margins that center the column; narrower windows
// fill edge-to-edge as usual. This is CSS `max-width` semantics: the cap is
// an absolute physical size, not a fraction of the window or the screen, so
// the column doesn't widen when you make the window bigger.
extension EditorTextView {
/// Padding applied on each side of the text column at all window sizes.
static let contentBaseInset: CGFloat = 24
/// The symmetric horizontal inset for a given view width and max-column width.
/// `maxContentWidth == .greatestFiniteMagnitude` → base inset only (fills the window).
/// When the window is too narrow to fit `maxContentWidth`, the column also fills.
public static func horizontalInset(viewWidth: CGFloat, maxContentWidth: CGFloat) -> CGFloat {
let available = viewWidth - 2 * contentBaseInset
guard available > maxContentWidth else { return contentBaseInset }
return contentBaseInset + (available - maxContentWidth) / 2
}
/// Recomputes the horizontal text inset from the current bounds + max-column cap,
/// preserving the vertical inset. Usually no recompose — only the inset
/// changes and TextKit 2 reflows wrapped text on its own. The exception is
/// image overlays: their scaled-to-fit size is baked into the styled
/// attribute at render time (§4 fragmentOverlay), not recomputed at draw
/// time, so a column narrower than an already-rendered image needs those
/// blocks restyled to shrink it.
public func updateContentInset() {
let target = Self.horizontalInset(viewWidth: bounds.width,
maxContentWidth: maxContentWidthPoints)
guard abs(textContainerInset.width - target) > 0.5 else { return }
textContainerInset = NSSize(width: target, height: textContainerInset.height)
let imageBlocks = IndexSet(blocks.indices.filter { blocks[$0].content.contains("![") })
guard !imageBlocks.isEmpty else { return }
for idx in imageBlocks { blocks[idx].isStyled = false }
recomposeDirty(imageBlocks, cursorInRaw: currentCursorInRaw(), settingSelection: true)
}
/// Recompute the centered inset as the view width changes (window resize).
public override func setFrameSize(_ newSize: NSSize) {
super.setFrameSize(newSize)
updateContentInset()
}
}