Skip to main content

TaffyLayoutEngine

Struct TaffyLayoutEngine 

Source
pub struct TaffyLayoutEngine { /* private fields */ }
Expand description

The taffy-backed layout tree for a window.

Implementations§

Source§

impl TaffyLayoutEngine

Source

pub fn new() -> Self

Creates an empty layout engine with rounding disabled.

Source

pub fn clear(&mut self)

Discards every node and cached bounds, ready for a fresh frame.

Source

pub fn stretch_auto_size_to_fill( &mut self, id: LayoutId, size: Size<Pixels>, scale_factor: f32, )

Treats any auto dimension of the given node’s style as filling size.

This is applied to window roots before layout so they behave like the root element on the web, which stretches to fill the initial containing block (the viewport) unless given an explicit size. Explicitly styled dimensions are preserved.

Source

pub fn compute_layout( &mut self, id: LayoutId, available_space: Size<AvailableSpace>, scale_factor: f32, context: &mut dyn MeasureContext, )

Computes the layout of id within available_space, invoking stored measure callbacks with context.

Source

pub fn layout_bounds( &mut self, id: LayoutId, scale_factor: f32, ) -> Bounds<Pixels>

Returns the bounds of id relative to the window, pixel-snapped.

Pixel snapping

Painting primitives at non-integer pixel coordinates produces blurry output. Pixel snapping converts layout coordinates into integer device-pixel coordinates so painted edges land exactly on physical pixel boundaries.

Non-integer coordinates can arise for several reasons, including:

  • flex distribution, percentages, centering, and text measurement can produce fractional element sizes and positions;
  • at fractional scale factors (for example 125% or 150%), integer logical-pixel values can map to non-integer device-pixel values.

We pixel-snap by rounding in device-pixel space, after multiplying by scale_factor, so that snapping targets physical pixels. Bounds are divided by scale_factor before being returned to GPUI.

Midpoints are rounded toward zero. This is a stylistic choice: a 1-logical-pixel line at 150% scale should render as 1 dp rather than 2 dp.

Pixel snapping is done in two phases:

  1. Pre-layout metric snapping. Before Taffy computes layout, all authored absolute lengths are rounded in to_taffy. This includes borders, padding, gaps, and explicit sizes. Custom-measured leaf nodes have their measured sizes rounded up to integer device-pixel lengths.

  2. Post-layout edge snapping. After Taffy resolves the tree, layout relationships such as flex shares, grid tracks, percentages, and centering can produce new fractional edge positions. Boxes now have edges in absolute coordinates, and snapping must decide where those edges land on the device-pixel grid.

Ideally, post-layout snapping would satisfy:

  • Edge closure. Two raw layout edges at the same absolute position should snap to the same pixel column.
  • Translation stability. A component’s internal geometry should not change when it moves to a new absolute position.

These goals are in tension because rounding is not associative. The simple local schemes make different tradeoffs:

  • Absolute edge rounding gives each window coordinate one answer, so coincident edges always close globally. But a span’s snapped length is round(far) - round(near), which may change by 1 dp as its absolute origin moves.

  • Parent-relative edge rounding rounds each child inside its parent’s coordinate space. This guarantees translation stability, but a shared edge reached through different parents can accumulate different rounding, causing non-closure between cousins.

  • Length rounding rounds each width, height, and thickness independently and then places boxes from those rounded lengths. Sizes stay stable under translation, but neighboring boxes derive their shared boundary from different sources, so closure is not guaranteed.

We apply absolute edge rounding for each element’s outer box in post-layout rounding to preserve closure. Border and padding widths are not touched by post-layout rounding; they keep their pre-layout rounded value so that they remain stable under translation.

This gives both closure and translation stability in the case that all local metrics are integer device-pixel lengths. Pre-layout rounding covers that in most cases. The exception is metrics resolved by layout relationships, such as percentages. Outer box edges will still close globally, and painted border widths are still snapped independently, but the raw content-box origin can carry a 1dp residual into descendants.

Trait Implementations§

Source§

impl LayoutEngine for TaffyLayoutEngine

Source§

fn clear(&mut self)

Discards every node and cached bounds, ready for a fresh frame.
Source§

fn request_layout( &mut self, style: &EngineLayoutStyle, rem_size: Pixels, scale_factor: f32, children: &[LayoutId], ) -> LayoutId

Adds a leaf or container node built from style.
Source§

fn request_measured_layout( &mut self, style: &EngineLayoutStyle, rem_size: Pixels, scale_factor: f32, measure: BoxedMeasureFn, ) -> LayoutId

Adds a leaf whose size is resolved by measure during layout.
Source§

fn stretch_auto_size_to_fill( &mut self, id: LayoutId, size: Size<Pixels>, scale_factor: f32, )

Treats any auto dimension of id’s style as filling size.
Source§

fn compute_layout( &mut self, id: LayoutId, available_space: Size<AvailableSpace>, scale_factor: f32, context: &mut dyn MeasureContext, )

Computes the layout of id within available_space, invoking stored measure callbacks with context.
Source§

fn layout_bounds(&mut self, id: LayoutId, scale_factor: f32) -> Bounds<Pixels>

Returns the pixel-snapped bounds of id relative to the window.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.