1use crate::{
2 AbsoluteLength, App, Bounds, DefiniteLength, Edges, GridTemplate, Length, Pixels, Point, Size,
3 Style, Window, point, size,
4};
5use collections::{FxHashMap, FxHashSet};
6use stacksafe::{StackSafe, stacksafe};
7use std::{fmt::Debug, ops::Range};
8use taffy::{
9 TaffyTree, TraversePartialTree as _,
10 geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
11 prelude::{max_content, min_content},
12 style::AvailableSpace as TaffyAvailableSpace,
13 tree::NodeId,
14};
15
16type NodeMeasureFn = StackSafe<
17 Box<
18 dyn FnMut(
19 Size<Option<Pixels>>,
20 Size<AvailableSpace>,
21 &mut Window,
22 &mut App,
23 ) -> Size<Pixels>,
24 >,
25>;
26
27struct NodeContext {
28 measure: NodeMeasureFn,
29}
30pub struct TaffyLayoutEngine {
31 taffy: TaffyTree<NodeContext>,
32 absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
33 computed_layouts: FxHashSet<LayoutId>,
34 layout_bounds_scratch_space: Vec<LayoutId>,
35}
36
37const EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by construction if possible";
38
39impl TaffyLayoutEngine {
40 pub fn new() -> Self {
41 let mut taffy = TaffyTree::new();
42 taffy.enable_rounding();
43 TaffyLayoutEngine {
44 taffy,
45 absolute_layout_bounds: FxHashMap::default(),
46 computed_layouts: FxHashSet::default(),
47 layout_bounds_scratch_space: Vec::new(),
48 }
49 }
50
51 pub fn clear(&mut self) {
52 self.taffy.clear();
53 self.absolute_layout_bounds.clear();
54 self.computed_layouts.clear();
55 }
56
57 pub fn request_layout(
58 &mut self,
59 style: Style,
60 rem_size: Pixels,
61 scale_factor: f32,
62 children: &[LayoutId],
63 ) -> LayoutId {
64 let taffy_style = style.to_taffy(rem_size, scale_factor);
65
66 if children.is_empty() {
67 self.taffy
68 .new_leaf(taffy_style)
69 .expect(EXPECT_MESSAGE)
70 .into()
71 } else {
72 self.taffy
73 // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
74 .new_with_children(taffy_style, LayoutId::to_taffy_slice(children))
75 .expect(EXPECT_MESSAGE)
76 .into()
77 }
78 }
79
80 pub fn request_measured_layout(
81 &mut self,
82 style: Style,
83 rem_size: Pixels,
84 scale_factor: f32,
85 measure: impl FnMut(
86 Size<Option<Pixels>>,
87 Size<AvailableSpace>,
88 &mut Window,
89 &mut App,
90 ) -> Size<Pixels>
91 + 'static,
92 ) -> LayoutId {
93 let taffy_style = style.to_taffy(rem_size, scale_factor);
94
95 self.taffy
96 .new_leaf_with_context(
97 taffy_style,
98 NodeContext {
99 measure: StackSafe::new(Box::new(measure)),
100 },
101 )
102 .expect(EXPECT_MESSAGE)
103 .into()
104 }
105
106 // Used to understand performance
107 #[allow(dead_code)]
108 fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
109 let mut count = 0;
110
111 for child in self.taffy.children(parent.0)? {
112 // Count this child.
113 count += 1;
114
115 // Count all of this child's children.
116 count += self.count_all_children(LayoutId(child))?
117 }
118
119 Ok(count)
120 }
121
122 // Used to understand performance
123 #[allow(dead_code)]
124 fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result<u32> {
125 println!(
126 "{parent:?} at depth {depth} has {} children",
127 self.taffy.child_count(parent.0)
128 );
129
130 let mut max_child_depth = 0;
131
132 for child in self.taffy.children(parent.0)? {
133 max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?);
134 }
135
136 Ok(depth + 1 + max_child_depth)
137 }
138
139 // Used to understand performance
140 #[allow(dead_code)]
141 fn get_edges(&self, parent: LayoutId) -> anyhow::Result<Vec<(LayoutId, LayoutId)>> {
142 let mut edges = Vec::new();
143
144 for child in self.taffy.children(parent.0)? {
145 edges.push((parent, LayoutId(child)));
146
147 edges.extend(self.get_edges(LayoutId(child))?);
148 }
149
150 Ok(edges)
151 }
152
153 #[stacksafe]
154 pub fn compute_layout(
155 &mut self,
156 id: LayoutId,
157 available_space: Size<AvailableSpace>,
158 window: &mut Window,
159 cx: &mut App,
160 ) {
161 // Leaving this here until we have a better instrumentation approach.
162 // println!("Laying out {} children", self.count_all_children(id)?);
163 // println!("Max layout depth: {}", self.max_depth(0, id)?);
164
165 // Output the edges (branches) of the tree in Mermaid format for visualization.
166 // println!("Edges:");
167 // for (a, b) in self.get_edges(id)? {
168 // println!("N{} --> N{}", u64::from(a), u64::from(b));
169 // }
170 //
171
172 if !self.computed_layouts.insert(id) {
173 let mut stack = &mut self.layout_bounds_scratch_space;
174 stack.push(id);
175 while let Some(id) = stack.pop() {
176 self.absolute_layout_bounds.remove(&id);
177 stack.extend(
178 self.taffy
179 .children(id.into())
180 .expect(EXPECT_MESSAGE)
181 .into_iter()
182 .map(LayoutId::from),
183 );
184 }
185 }
186
187 let scale_factor = window.scale_factor();
188
189 let transform = |v: AvailableSpace| match v {
190 AvailableSpace::Definite(pixels) => {
191 AvailableSpace::Definite(Pixels(pixels.0 * scale_factor))
192 }
193 AvailableSpace::MinContent => AvailableSpace::MinContent,
194 AvailableSpace::MaxContent => AvailableSpace::MaxContent,
195 };
196 let available_space = size(
197 transform(available_space.width),
198 transform(available_space.height),
199 );
200
201 self.taffy
202 .compute_layout_with_measure(
203 id.into(),
204 available_space.into(),
205 |known_dimensions, available_space, _id, node_context, _style| {
206 let Some(node_context) = node_context else {
207 return taffy::geometry::Size::default();
208 };
209
210 let known_dimensions = Size {
211 width: known_dimensions.width.map(|e| Pixels(e / scale_factor)),
212 height: known_dimensions.height.map(|e| Pixels(e / scale_factor)),
213 };
214
215 let available_space: Size<AvailableSpace> = available_space.into();
216 let untransform = |ev: AvailableSpace| match ev {
217 AvailableSpace::Definite(pixels) => {
218 AvailableSpace::Definite(Pixels(pixels.0 / scale_factor))
219 }
220 AvailableSpace::MinContent => AvailableSpace::MinContent,
221 AvailableSpace::MaxContent => AvailableSpace::MaxContent,
222 };
223 let available_space = size(
224 untransform(available_space.width),
225 untransform(available_space.height),
226 );
227
228 let a: Size<Pixels> =
229 (node_context.measure)(known_dimensions, available_space, window, cx);
230 size(a.width.0 * scale_factor, a.height.0 * scale_factor).into()
231 },
232 )
233 .expect(EXPECT_MESSAGE);
234 }
235
236 pub fn layout_bounds(&mut self, id: LayoutId, scale_factor: f32) -> Bounds<Pixels> {
237 if let Some(layout) = self.absolute_layout_bounds.get(&id).cloned() {
238 return layout;
239 }
240
241 let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE);
242 let mut bounds = Bounds {
243 origin: point(
244 Pixels(layout.location.x / scale_factor),
245 Pixels(layout.location.y / scale_factor),
246 ),
247 size: size(
248 Pixels(layout.size.width / scale_factor),
249 Pixels(layout.size.height / scale_factor),
250 ),
251 };
252
253 if let Some(parent_id) = self.taffy.parent(id.0) {
254 let parent_bounds = self.layout_bounds(parent_id.into(), scale_factor);
255 bounds.origin += parent_bounds.origin;
256 }
257 self.absolute_layout_bounds.insert(id, bounds);
258
259 bounds
260 }
261}
262
263/// A unique identifier for a layout node, generated when requesting a layout from Taffy
264#[derive(Copy, Clone, Eq, PartialEq, Debug)]
265#[repr(transparent)]
266pub struct LayoutId(NodeId);
267
268impl LayoutId {
269 fn to_taffy_slice(node_ids: &[Self]) -> &[taffy::NodeId] {
270 // SAFETY: LayoutId is repr(transparent) to taffy::tree::NodeId.
271 unsafe { std::mem::transmute::<&[LayoutId], &[taffy::NodeId]>(node_ids) }
272 }
273}
274
275impl std::hash::Hash for LayoutId {
276 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
277 u64::from(self.0).hash(state);
278 }
279}
280
281impl From<NodeId> for LayoutId {
282 fn from(node_id: NodeId) -> Self {
283 Self(node_id)
284 }
285}
286
287impl From<LayoutId> for NodeId {
288 fn from(layout_id: LayoutId) -> NodeId {
289 layout_id.0
290 }
291}
292
293trait ToTaffy<Output> {
294 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> Output;
295}
296
297impl ToTaffy<taffy::style::Style> for Style {
298 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Style {
299 use taffy::style_helpers::{fr, length, minmax, repeat};
300
301 fn to_grid_line(
302 placement: &Range<crate::GridPlacement>,
303 ) -> taffy::Line<taffy::GridPlacement> {
304 taffy::Line {
305 start: placement.start.into(),
306 end: placement.end.into(),
307 }
308 }
309
310 fn to_grid_repeat<T: taffy::style::CheapCloneStr>(
311 unit: &Option<GridTemplate>,
312 ) -> Vec<taffy::GridTemplateComponent<T>> {
313 unit.map(|template| {
314 match template.min_size {
315 // grid-template-*: repeat(<number>, minmax(0, 1fr));
316 crate::TemplateColumnMinSize::Zero => {
317 vec![repeat(
318 template.repeat,
319 vec![minmax(length(0.0_f32), fr(1.0_f32))],
320 )]
321 }
322 // grid-template-*: repeat(<number>, minmax(min-content, 1fr));
323 crate::TemplateColumnMinSize::MinContent => {
324 vec![repeat(
325 template.repeat,
326 vec![minmax(min_content(), fr(1.0_f32))],
327 )]
328 }
329 // grid-template-*: repeat(<number>, minmax(0, max-content))
330 crate::TemplateColumnMinSize::MaxContent => {
331 vec![repeat(
332 template.repeat,
333 vec![minmax(length(0.0_f32), max_content())],
334 )]
335 }
336 }
337 })
338 .unwrap_or_default()
339 }
340
341 taffy::style::Style {
342 display: self.display.into(),
343 overflow: self.overflow.into(),
344 scrollbar_width: self.scrollbar_width.to_taffy(rem_size, scale_factor),
345 position: self.position.into(),
346 inset: self.inset.to_taffy(rem_size, scale_factor),
347 size: self.size.to_taffy(rem_size, scale_factor),
348 min_size: self.min_size.to_taffy(rem_size, scale_factor),
349 max_size: self.max_size.to_taffy(rem_size, scale_factor),
350 aspect_ratio: self.aspect_ratio,
351 margin: self.margin.to_taffy(rem_size, scale_factor),
352 padding: self.padding.to_taffy(rem_size, scale_factor),
353 border: self.border_widths.to_taffy(rem_size, scale_factor),
354 align_items: self.align_items.map(|x| x.into()),
355 align_self: self.align_self.map(|x| x.into()),
356 align_content: self.align_content.map(|x| x.into()),
357 justify_content: self.justify_content.map(|x| x.into()),
358 gap: self.gap.to_taffy(rem_size, scale_factor),
359 flex_direction: self.flex_direction.into(),
360 flex_wrap: self.flex_wrap.into(),
361 flex_basis: self.flex_basis.to_taffy(rem_size, scale_factor),
362 flex_grow: self.flex_grow,
363 flex_shrink: self.flex_shrink,
364 grid_template_rows: to_grid_repeat(&self.grid_rows),
365 grid_template_columns: to_grid_repeat(&self.grid_cols),
366 grid_row: self
367 .grid_location
368 .as_ref()
369 .map(|location| to_grid_line(&location.row))
370 .unwrap_or_default(),
371 grid_column: self
372 .grid_location
373 .as_ref()
374 .map(|location| to_grid_line(&location.column))
375 .unwrap_or_default(),
376 ..Default::default()
377 }
378 }
379}
380
381impl ToTaffy<f32> for AbsoluteLength {
382 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> f32 {
383 match self {
384 AbsoluteLength::Pixels(pixels) => {
385 let pixels: f32 = pixels.into();
386 pixels * scale_factor
387 }
388 AbsoluteLength::Rems(rems) => {
389 let pixels: f32 = (*rems * rem_size).into();
390 pixels * scale_factor
391 }
392 }
393 }
394}
395
396impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
397 fn to_taffy(
398 &self,
399 rem_size: Pixels,
400 scale_factor: f32,
401 ) -> taffy::prelude::LengthPercentageAuto {
402 match self {
403 Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
404 Length::Auto => taffy::prelude::LengthPercentageAuto::auto(),
405 }
406 }
407}
408
409impl ToTaffy<taffy::style::Dimension> for Length {
410 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::prelude::Dimension {
411 match self {
412 Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
413 Length::Auto => taffy::prelude::Dimension::auto(),
414 }
415 }
416}
417
418impl ToTaffy<taffy::style::LengthPercentage> for DefiniteLength {
419 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
420 match self {
421 DefiniteLength::Absolute(length) => match length {
422 AbsoluteLength::Pixels(pixels) => {
423 let pixels: f32 = pixels.into();
424 taffy::style::LengthPercentage::length(pixels * scale_factor)
425 }
426 AbsoluteLength::Rems(rems) => {
427 let pixels: f32 = (*rems * rem_size).into();
428 taffy::style::LengthPercentage::length(pixels * scale_factor)
429 }
430 },
431 DefiniteLength::Fraction(fraction) => {
432 taffy::style::LengthPercentage::percent(*fraction)
433 }
434 }
435 }
436}
437
438impl ToTaffy<taffy::style::LengthPercentageAuto> for DefiniteLength {
439 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto {
440 match self {
441 DefiniteLength::Absolute(length) => match length {
442 AbsoluteLength::Pixels(pixels) => {
443 let pixels: f32 = pixels.into();
444 taffy::style::LengthPercentageAuto::length(pixels * scale_factor)
445 }
446 AbsoluteLength::Rems(rems) => {
447 let pixels: f32 = (*rems * rem_size).into();
448 taffy::style::LengthPercentageAuto::length(pixels * scale_factor)
449 }
450 },
451 DefiniteLength::Fraction(fraction) => {
452 taffy::style::LengthPercentageAuto::percent(*fraction)
453 }
454 }
455 }
456}
457
458impl ToTaffy<taffy::style::Dimension> for DefiniteLength {
459 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension {
460 match self {
461 DefiniteLength::Absolute(length) => match length {
462 AbsoluteLength::Pixels(pixels) => {
463 let pixels: f32 = pixels.into();
464 taffy::style::Dimension::length(pixels * scale_factor)
465 }
466 AbsoluteLength::Rems(rems) => {
467 taffy::style::Dimension::length((*rems * rem_size * scale_factor).into())
468 }
469 },
470 DefiniteLength::Fraction(fraction) => taffy::style::Dimension::percent(*fraction),
471 }
472 }
473}
474
475impl ToTaffy<taffy::style::LengthPercentage> for AbsoluteLength {
476 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
477 match self {
478 AbsoluteLength::Pixels(pixels) => {
479 let pixels: f32 = pixels.into();
480 taffy::style::LengthPercentage::length(pixels * scale_factor)
481 }
482 AbsoluteLength::Rems(rems) => {
483 let pixels: f32 = (*rems * rem_size).into();
484 taffy::style::LengthPercentage::length(pixels * scale_factor)
485 }
486 }
487 }
488}
489
490impl<T, T2> From<TaffyPoint<T>> for Point<T2>
491where
492 T: Into<T2>,
493 T2: Clone + Debug + Default + PartialEq,
494{
495 fn from(point: TaffyPoint<T>) -> Point<T2> {
496 Point {
497 x: point.x.into(),
498 y: point.y.into(),
499 }
500 }
501}
502
503impl<T, T2> From<Point<T>> for TaffyPoint<T2>
504where
505 T: Into<T2> + Clone + Debug + Default + PartialEq,
506{
507 fn from(val: Point<T>) -> Self {
508 TaffyPoint {
509 x: val.x.into(),
510 y: val.y.into(),
511 }
512 }
513}
514
515impl<T, U> ToTaffy<TaffySize<U>> for Size<T>
516where
517 T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
518{
519 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffySize<U> {
520 TaffySize {
521 width: self.width.to_taffy(rem_size, scale_factor),
522 height: self.height.to_taffy(rem_size, scale_factor),
523 }
524 }
525}
526
527impl<T, U> ToTaffy<TaffyRect<U>> for Edges<T>
528where
529 T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
530{
531 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffyRect<U> {
532 TaffyRect {
533 top: self.top.to_taffy(rem_size, scale_factor),
534 right: self.right.to_taffy(rem_size, scale_factor),
535 bottom: self.bottom.to_taffy(rem_size, scale_factor),
536 left: self.left.to_taffy(rem_size, scale_factor),
537 }
538 }
539}
540
541impl<T, U> From<TaffySize<T>> for Size<U>
542where
543 T: Into<U>,
544 U: Clone + Debug + Default + PartialEq,
545{
546 fn from(taffy_size: TaffySize<T>) -> Self {
547 Size {
548 width: taffy_size.width.into(),
549 height: taffy_size.height.into(),
550 }
551 }
552}
553
554impl<T, U> From<Size<T>> for TaffySize<U>
555where
556 T: Into<U> + Clone + Debug + Default + PartialEq,
557{
558 fn from(size: Size<T>) -> Self {
559 TaffySize {
560 width: size.width.into(),
561 height: size.height.into(),
562 }
563 }
564}
565
566/// The space available for an element to be laid out in
567#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
568pub enum AvailableSpace {
569 /// The amount of space available is the specified number of pixels
570 Definite(Pixels),
571 /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
572 #[default]
573 MinContent,
574 /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
575 MaxContent,
576}
577
578impl AvailableSpace {
579 /// Returns a `Size` with both width and height set to `AvailableSpace::MinContent`.
580 ///
581 /// This function is useful when you want to create a `Size` with the minimum content constraints
582 /// for both dimensions.
583 ///
584 /// # Examples
585 ///
586 /// ```
587 /// use gpui::AvailableSpace;
588 /// let min_content_size = AvailableSpace::min_size();
589 /// assert_eq!(min_content_size.width, AvailableSpace::MinContent);
590 /// assert_eq!(min_content_size.height, AvailableSpace::MinContent);
591 /// ```
592 pub const fn min_size() -> Size<Self> {
593 Size {
594 width: Self::MinContent,
595 height: Self::MinContent,
596 }
597 }
598}
599
600impl From<AvailableSpace> for TaffyAvailableSpace {
601 fn from(space: AvailableSpace) -> TaffyAvailableSpace {
602 match space {
603 AvailableSpace::Definite(Pixels(value)) => TaffyAvailableSpace::Definite(value),
604 AvailableSpace::MinContent => TaffyAvailableSpace::MinContent,
605 AvailableSpace::MaxContent => TaffyAvailableSpace::MaxContent,
606 }
607 }
608}
609
610impl From<TaffyAvailableSpace> for AvailableSpace {
611 fn from(space: TaffyAvailableSpace) -> AvailableSpace {
612 match space {
613 TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
614 TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
615 TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent,
616 }
617 }
618}
619
620impl From<Pixels> for AvailableSpace {
621 fn from(pixels: Pixels) -> Self {
622 AvailableSpace::Definite(pixels)
623 }
624}
625
626impl From<Size<Pixels>> for Size<AvailableSpace> {
627 fn from(size: Size<Pixels>) -> Self {
628 Size {
629 width: AvailableSpace::Definite(size.width),
630 height: AvailableSpace::Definite(size.height),
631 }
632 }
633}