pub struct FlowLayout {
pub blocks: HashMap<usize, BlockLayout>,
pub tables: HashMap<usize, TableLayout>,
pub frames: HashMap<usize, FrameLayout>,
pub flow_order: Vec<FlowItem>,
pub content_height: f32,
pub viewport_width: f32,
pub viewport_height: f32,
pub cached_max_content_width: f32,
pub scale_factor: f32,
pub font_scale: f32,
/* private fields */
}Fields§
§blocks: HashMap<usize, BlockLayout>§tables: HashMap<usize, TableLayout>§frames: HashMap<usize, FrameLayout>§flow_order: Vec<FlowItem>§content_height: f32§viewport_width: f32§viewport_height: f32§cached_max_content_width: f32§scale_factor: f32Device pixel ratio passed to shapers and rasterizers. Layout is always stored in logical pixels; this only affects precision (physical ppem) and glyph bitmap resolution.
font_scale: f32Per-document logical text-magnification factor (1.0 = none). Set from
DocumentFlow::font_scale at layout time and threaded into block layout
alongside scale_factor; multiplies the resolved font size so all text
grows logically (advances, line heights, content height) and reflows.
Implementations§
Source§impl FlowLayout
impl FlowLayout
pub fn new() -> Self
Sourcepub fn add_table(
&mut self,
registry: &FontRegistry,
params: &TableLayoutParams,
available_width: f32,
)
pub fn add_table( &mut self, registry: &FontRegistry, params: &TableLayoutParams, available_width: f32, )
Add a table to the flow at the current y position.
Sourcepub fn add_frame(
&mut self,
registry: &FontRegistry,
params: &FrameLayoutParams,
available_width: f32,
)
pub fn add_frame( &mut self, registry: &FontRegistry, params: &FrameLayoutParams, available_width: f32, )
Add a frame to the flow.
- Inline: placed in normal flow, advances content_height.
- FloatLeft: placed at current y, x=0. Does not advance content_height (surrounding content wraps around it).
- FloatRight: placed at current y, x=available_width - frame_width.
- Absolute: placed at (margin_left, margin_top) from document origin. Does not affect flow at all.
Sourcepub fn clear(&mut self)
pub fn clear(&mut self)
Clear all layout state. Call before rebuilding from a new FlowSnapshot.
Sourcepub fn apply_paint_spans_for(
&mut self,
spans_by_block: HashMap<usize, Vec<PaintSpan>>,
)
pub fn apply_paint_spans_for( &mut self, spans_by_block: HashMap<usize, Vec<PaintSpan>>, )
Replace the paint-only color overlay for the whole flow.
spans_by_block maps block_id → its disjoint paint spans (from
text_document’s extract_paint_spans). Every block is re-derived from
its captured base, so colors/decorations change but glyph positions,
advances, line breaks, and heights do NOT — no reshape, no reflow.
Blocks absent from the map reset to base colors.
Sourcepub fn apply_block_paint_spans(
&mut self,
block_id: usize,
spans: &[PaintSpan],
) -> bool
pub fn apply_block_paint_spans( &mut self, block_id: usize, spans: &[PaintSpan], ) -> bool
Apply (or clear, when spans is empty) the paint overlay for a single
block, re-derived from its base. Returns false if block_id has no
captured base.
Sourcepub fn add_block(
&mut self,
registry: &FontRegistry,
params: &BlockLayoutParams,
available_width: f32,
)
pub fn add_block( &mut self, registry: &FontRegistry, params: &BlockLayoutParams, available_width: f32, )
Add a single block to the flow at the current y position.
Bulk-path primitive. This deliberately does not capture the
block’s paint-overlay base; layout_blocks calls
it in a loop and captures every base once at the end, which keeps the
bulk path single-pass.
Appending to an existing layout has no such follow-up pass, so use
append_block instead. Reaching for this one is
silent when wrong: the block lays out and renders fine, but
apply_block_paint_spans then
early-returns false for it forever, so it never receives syntax,
search, or spell highlighting and nothing reports a problem.
Sourcepub fn append_block(
&mut self,
registry: &FontRegistry,
params: &BlockLayoutParams,
available_width: f32,
)
pub fn append_block( &mut self, registry: &FontRegistry, params: &BlockLayoutParams, available_width: f32, )
Append one block to the tail of an existing layout, in O(1).
This is add_block plus the paint-overlay
bookkeeping that a bulk layout would otherwise do afterwards.
layout_blocks calls add_block in a loop and
then re-captures every block’s base in one pass
(refresh_base_blocks, O(N)); add_block on its own therefore leaves
the appended block absent from base_blocks, and a later
apply_paint_spans_for would re-derive it from a missing base.
Reusing the bulk refresh for a tail append would re-clone the whole
document once per appended line — precisely the O(N)-per-line cost this
method exists to avoid — so it refreshes just the appended block
(refresh_base_and_overlay_block, the same O(1) call
relayout_block already relies on).
add_block itself is left untouched: layout_blocks depends on its
current no-base-refresh behaviour to keep the bulk path single-pass.
Sourcepub fn remove_leading(&mut self, n: usize) -> usize
pub fn remove_leading(&mut self, n: usize) -> usize
Drop the first n top-level blocks, returning how many were removed.
The eviction half of a bounded streaming buffer (a log viewer’s
scrollback cap). Usually O(n) map removals plus one Vec memmove of the
surviving flow_order entries — no reshaping. The return value is the
count actually evicted, which is less than n when the run of leading
blocks is shorter than n or a table/frame stops the walk.
Surviving blocks deliberately keep their absolute y. The vacated
band at the top simply becomes empty, and content_height is unchanged,
so nothing below moves and no scroll position shifts under the user.
Rewriting every survivor’s y would make eviction O(remaining) and
would yank the viewport; leaving them is both cheaper and correct.
flow_order’s ascending-y ordering — which hit_test’s binary search
relies on — is preserved, since removing a prefix of a sorted run leaves
it sorted.
Only top-level blocks are considered; a leading table or frame stops the eviction (a streaming buffer is a flat run of blocks by construction).
If the widest block in the flow is among those evicted,
max_content_width is recomputed from the survivors — O(remaining) for
that call only. Evicting narrower blocks (the overwhelmingly common
case) cannot lower the maximum and skips the recompute.
Sourcepub fn set_uniform_extent(&mut self, total_rows: usize, row_height: f32)
pub fn set_uniform_extent(&mut self, total_rows: usize, row_height: f32)
Declare the total extent of a uniform-row-height document without shaping anything.
In windowed mode content_height describes the whole document, not the
shaped window, so it cannot come from the usual accumulator. Use this to
keep the scrollbar honest when the row count changes outside the shaped
window (a line appended while the user is scrolled away from the tail).
Leaves the shaped window untouched.
Sourcepub fn layout_window(
&mut self,
registry: &FontRegistry,
window: &[(usize, BlockLayoutParams)],
total_rows: usize,
row_height: f32,
available_width: f32,
)
pub fn layout_window( &mut self, registry: &FontRegistry, window: &[(usize, BlockLayoutParams)], total_rows: usize, row_height: f32, available_width: f32, )
Shape only window — a slice of a much larger uniform-row-height
document — placing each row at an arithmetic y = index * row_height.
Shaping every line is what makes a large document expensive: a resident
shaped line costs ~6.5 KB, so a 100 000-line buffer costs ~623 MB fully
laid out, against ~1 MB for a viewport-sized window
(docs/streaming-baseline.md). Rendering already culls to the viewport,
so shaping the off-screen remainder buys nothing.
y is arithmetic rather than accumulated precisely so that a row can be
placed without having laid out — or even having in memory — any of the
rows above it, which is what lets the window start at an arbitrary
scroll position. content_height is likewise derived from
total_rows, so the scrollbar reflects the whole document rather than
the shaped window.
§Invariants
The arithmetic placement is only correct for a document whose rows are
genuinely uniform: one row = one visual line of exactly row_height
— no wrapping (non_breakable_lines), no embedded newlines, no
per-row margins, one font size throughout. That suits log/console
output and monospaced code; it does not suit prose. Callers with
variable-height content must use layout_blocks.
window must be sorted ascending by index, which keeps flow_order
ascending by y as hit_test’s binary search requires. Both are
checked in debug builds.
After this call, appending at the tail with
append_block stays correct: content_height is
exactly total_rows * row_height, which is where row total_rows
belongs. Trim the window’s front with
remove_leading.
§Behaviour worth knowing
Like layout_blocks, this drops any paint
overlay: re-apply spans after re-windowing, or the new rows render in
base colours. Because this runs on every visible-range change, that
re-apply is part of the scroll path, not a one-off.
max_content_width accumulates across windows rather than being
re-derived per window: it reports the widest row seen so far, not the
widest row in the document (unknowable without shaping all of it) and
not the widest row on screen (which would make the horizontal scrollbar
jump on every vertical scroll). It therefore only grows during a
windowed session.
y is index * row_height in f32, which represents consecutive
integers exactly only to 2^24: past ~840 000 rows at a 20 px row height,
row positions begin quantizing and neighbouring rows drift out of
alignment. Well beyond the sizes this targets, but not infinite.
Sourcepub fn layout_blocks(
&mut self,
registry: &FontRegistry,
block_params: Vec<BlockLayoutParams>,
available_width: f32,
)
pub fn layout_blocks( &mut self, registry: &FontRegistry, block_params: Vec<BlockLayoutParams>, available_width: f32, )
Lay out a sequence of blocks vertically.
Sourcepub fn relayout_block(
&mut self,
registry: &FontRegistry,
params: &BlockLayoutParams,
available_width: f32,
)
pub fn relayout_block( &mut self, registry: &FontRegistry, params: &BlockLayoutParams, available_width: f32, )
Update a single block’s layout and shift subsequent items if height changed.
Finds the block in top-level blocks, table cells, or frames, re-layouts it, and propagates any height delta to subsequent flow items.