fix: preserve intrinsic row sizing across containing blocks
This commit is contained in:
parent
b25328198c
commit
175ae2ed4e
@ -87,6 +87,12 @@ container children are normally supplied to `ebox-row`, `ebox-column`,
|
||||
use the variadic row/column helpers for more than two children. Nil children
|
||||
are ignored. An empty row or column becomes a spacer.
|
||||
|
||||
In a `row`, omitted or `auto` widths use each child’s intrinsic width when the
|
||||
children are concatenated; the parent viewport is not reused as every child’s
|
||||
width. Explicit `viewport`, `stretch`, and definite widths still resolve against
|
||||
their containing block. A `column` fills narrower layout rows to its containing
|
||||
block, while a visual box paints its border and padding at its own used width.
|
||||
|
||||
`:key` is the stable application identity for a sibling. `:id` is a logical
|
||||
selector/update identifier. `:host-ref` is an opaque application-owned anchor
|
||||
used by host-reference lookup and logical candidate replacement. Host refs
|
||||
|
||||
@ -78,6 +78,12 @@ window,再切换到渲染后的 buffer。如果窗口选择由调用方负责
|
||||
`ebox-stack` 接受两个节点;超过两个子节点时使用 row/column。nil 子节点会
|
||||
被忽略;空 row/column 会变成 spacer。
|
||||
|
||||
`row` 中省略或 `auto` 的宽度按子节点自身的 intrinsic width 参与横向拼接;
|
||||
父级 viewport 不会被重复当成每个子节点的宽度。显式 `viewport`、`stretch`
|
||||
或 definite width 仍按指定 containing block 解析。`column` 会把较窄的布局
|
||||
行补齐到自己的 containing block,但带视觉 box 的子节点会在自己的 used width
|
||||
上绘制边框和 padding。
|
||||
|
||||
`:key` 是兄弟节点范围内的稳定业务 identity;`:id` 是 logical selector/update
|
||||
标识;`:host-ref` 是应用拥有的不透明锚点,用于 host-reference 查询和逻辑
|
||||
candidate replacement。一个 source tree 内的非 nil host-ref 必须唯一。
|
||||
|
||||
@ -424,12 +424,14 @@ this avoids signature and retained-cost work for one-off responsive sizes."
|
||||
(context
|
||||
(list (ebox--ensure-node-id source)
|
||||
axis main cross align viewport
|
||||
ebox--intrinsic-layout-measurement)))
|
||||
ebox--intrinsic-layout-measurement
|
||||
ebox--inline-auto-width-intrinsic-p)))
|
||||
(if (gethash context observations)
|
||||
(let ((ebox-viewport-width viewport))
|
||||
(list (ebox--current-display-signature)
|
||||
viewport ebox-viewport-height
|
||||
ebox--intrinsic-layout-measurement
|
||||
ebox--inline-auto-width-intrinsic-p
|
||||
ebox--render-region-id
|
||||
ebox--scroll-window-initial-lookahead-lines-override
|
||||
ebox--scroll-window-render-disabled
|
||||
@ -964,6 +966,8 @@ Return a sized render entry containing the rendered string and dimensions."
|
||||
source axis main cross align item-viewport
|
||||
(list :intrinsic-layout-measurement
|
||||
ebox--intrinsic-layout-measurement
|
||||
:inline-auto-width-intrinsic
|
||||
ebox--inline-auto-width-intrinsic-p
|
||||
:scroll-window-render-disabled
|
||||
ebox--scroll-window-render-disabled
|
||||
:viewport-height-dependent
|
||||
|
||||
@ -333,6 +333,8 @@ the short runway that makes immediate post-resize smooth scrolling cheap."
|
||||
(defvar ebox--viewport-dependent-subtree-cache nil
|
||||
"Dynamic render-pass cache for viewport dependency existence checks.")
|
||||
|
||||
(defvar ebox--inline-auto-width-intrinsic-p)
|
||||
|
||||
(defvar ebox--viewport-height-dependent-subtree-cache nil
|
||||
"Dynamic render-pass cache for viewport-height dependency checks.")
|
||||
|
||||
@ -420,6 +422,7 @@ Kept as a narrow predicate for callers that need box-only sizing semantics."
|
||||
ebox-viewport-width)
|
||||
(when (ebox--viewport-height-dependent-subtree-p node)
|
||||
ebox-viewport-height)
|
||||
ebox--inline-auto-width-intrinsic-p
|
||||
(ebox--render-cache-node-body-signature node)))
|
||||
|
||||
(defun ebox--render-cache-box-signature (box)
|
||||
|
||||
@ -69,6 +69,12 @@
|
||||
Descendants still see the current containing block, but stack/column containers
|
||||
must not stretch their own outer width to that containing block.")
|
||||
|
||||
(defvar ebox--inline-auto-width-intrinsic-p nil
|
||||
"Non-nil while a horizontal layout sizes auto-width children intrinsically.
|
||||
The ambient viewport remains available to explicit `viewport' and `stretch'
|
||||
widths, but an omitted or `auto' width must not consume the whole horizontal
|
||||
containing block merely because the row itself is being rendered in it.")
|
||||
|
||||
(defvar ebox--box-content-render-cache nil
|
||||
"Dynamic render-pass cache for rendered lazy child content.")
|
||||
|
||||
@ -882,6 +888,7 @@ FALLBACK is used for nil, auto, or unavailable viewport-height values."
|
||||
(list ebox-viewport-width
|
||||
ebox-viewport-height
|
||||
ebox--intrinsic-layout-measurement
|
||||
ebox--inline-auto-width-intrinsic-p
|
||||
ebox--render-region-id))
|
||||
|
||||
(defun ebox--render-box-content-node (box node)
|
||||
@ -1021,7 +1028,8 @@ property parsers."
|
||||
props (ebox-get props :max-width)))
|
||||
(preferred-width
|
||||
(or (ebox--definite-content-size-pixel props width-value)
|
||||
(and (or (null width-value)
|
||||
(and (not ebox--inline-auto-width-intrinsic-p)
|
||||
(or (null width-value)
|
||||
(eq width-value 'auto)
|
||||
(eq width-value 'fit-content))
|
||||
(ebox--stretch-content-pixel props))
|
||||
@ -1040,11 +1048,16 @@ an inherited viewport."
|
||||
(let ((content-viewport (ebox--wrapper-content-viewport-pixel props)))
|
||||
(cond
|
||||
((eq (ebox-get props :width) 'max-content)
|
||||
(let ((ebox-viewport-width nil)
|
||||
(ebox--intrinsic-layout-measurement t))
|
||||
(let ((ebox-viewport-width nil)
|
||||
(ebox--intrinsic-layout-measurement t)
|
||||
(ebox--inline-auto-width-intrinsic-p nil))
|
||||
(ebox-render node)))
|
||||
(content-viewport
|
||||
(let ((ebox-viewport-width content-viewport))
|
||||
(let ((ebox-viewport-width content-viewport)
|
||||
;; A definite wrapper establishes a new containing block. Its
|
||||
;; child layout must resume normal cross-axis stretch semantics,
|
||||
;; even when the wrapper itself is an item of an intrinsic row.
|
||||
(ebox--inline-auto-width-intrinsic-p nil))
|
||||
(if (and (hash-table-p ebox--render-cache-table)
|
||||
(not ebox--intrinsic-layout-measurement)
|
||||
(null ebox--render-region-id)
|
||||
@ -1058,6 +1071,8 @@ an inherited viewport."
|
||||
:content-viewport content-viewport
|
||||
:viewport-height ebox-viewport-height
|
||||
:intrinsic ebox--intrinsic-layout-measurement
|
||||
:auto-width-intrinsic
|
||||
ebox--inline-auto-width-intrinsic-p
|
||||
:render-region-id ebox--render-region-id
|
||||
:scroll-lookahead
|
||||
ebox--scroll-window-initial-lookahead-lines-override
|
||||
@ -1103,7 +1118,8 @@ FALLBACK is used for nil and auto values."
|
||||
(preferred-width
|
||||
(cond
|
||||
((or (null width-value) (eq width-value 'auto))
|
||||
(or (ebox--stretch-content-pixel box)
|
||||
(or (and (not ebox--inline-auto-width-intrinsic-p)
|
||||
(ebox--stretch-content-pixel box))
|
||||
(ebox--content-max-pixel box)))
|
||||
((or (eq width-value 'viewport)
|
||||
(memq width-value '(stretch contain)))
|
||||
@ -1688,10 +1704,19 @@ A sole child Range keeps a material row parent so the Range stays addressable."
|
||||
|
||||
(defun ebox--render-concat (node)
|
||||
"Render a concat NODE to a string (internal, called by `ebox-render').
|
||||
Children are rendered; shorter children are padded with blank lines so all
|
||||
reach the same height before horizontal concatenation."
|
||||
(let* ((rendered (mapcar #'ebox--render-with-cache
|
||||
(ebox--layout-children node)))
|
||||
Auto-width children are rendered at intrinsic inline size while explicit
|
||||
viewport/definite widths keep their containing-block semantics. Shorter
|
||||
children are padded with blank lines so all reach the same height before
|
||||
horizontal concatenation."
|
||||
(let* ((rendered
|
||||
(mapcar (lambda (child)
|
||||
;; A row owns the horizontal concatenation. Its auto
|
||||
;; width children must therefore measure intrinsically;
|
||||
;; explicit viewport/stretch widths still resolve against
|
||||
;; the ambient containing block.
|
||||
(let ((ebox--inline-auto-width-intrinsic-p t))
|
||||
(ebox--render-with-cache child)))
|
||||
(ebox--layout-children node)))
|
||||
(max-h (if rendered
|
||||
(apply #'max (mapcar #'ebox-string-height rendered))
|
||||
0))
|
||||
@ -1925,7 +1950,9 @@ Return a plist with :lines and :complete. This mirrors the normal stack
|
||||
renderer for the top window of a scroll container, but stops before hidden
|
||||
leaves are rendered. When CACHE is supplied, rendering continues from the
|
||||
last cached leaf instead of walking the stack from the beginning."
|
||||
(let* ((target-w (or (ebox--viewport-pixel-width nil) 0))
|
||||
(let* ((target-w (if ebox--inline-auto-width-intrinsic-p
|
||||
0
|
||||
(or (ebox--viewport-pixel-width nil) 0)))
|
||||
(cached (and (ebox--stack-window-cache-compatible-p
|
||||
cache node target-w)
|
||||
cache))
|
||||
@ -3054,7 +3081,9 @@ Stack leaves are rendered independently. When a containing-block viewport is
|
||||
bound, narrower rows are padded to that available width, while wider definite
|
||||
children keep their own width and overflow only on their own rows. During
|
||||
intrinsic measurement, descendants still receive the containing block but the
|
||||
stack itself uses its natural max child width."
|
||||
stack itself uses its natural max child width. A stack used as an intrinsic
|
||||
inline child follows the same natural-width rule and does not consume the
|
||||
ambient horizontal viewport."
|
||||
(let* ((rendered-items
|
||||
(mapcar (lambda (leaf)
|
||||
(let ((rendered (ebox--render-with-cache leaf)))
|
||||
@ -3064,7 +3093,8 @@ stack itself uses its natural max child width."
|
||||
(max-w (if rendered-items
|
||||
(apply #'max (mapcar #'cdr rendered-items))
|
||||
0))
|
||||
(target-w (if ebox--intrinsic-layout-measurement
|
||||
(target-w (if (or ebox--intrinsic-layout-measurement
|
||||
ebox--inline-auto-width-intrinsic-p)
|
||||
max-w
|
||||
(or (ebox--viewport-pixel-width nil) max-w))))
|
||||
(let ((rendered
|
||||
|
||||
@ -330,6 +330,7 @@ pub struct LayoutContext {
|
||||
pub viewport_width: i64,
|
||||
pub viewport_width_known: bool,
|
||||
pub viewport_height: i64,
|
||||
pub inline_auto_width_intrinsic: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -3416,7 +3417,9 @@ fn resolve_child_viewport_width(
|
||||
let minimum = definite(min_width).unwrap_or(0);
|
||||
let maximum = definite(max_width);
|
||||
let auto_stretch = if matches!(width, Size::Auto) {
|
||||
stretch
|
||||
(!context.inline_auto_width_intrinsic)
|
||||
.then_some(stretch)
|
||||
.flatten()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -3512,7 +3515,11 @@ fn resolve_definite_box_content_width(
|
||||
let maximum = definite(max_width);
|
||||
let preferred = override_width
|
||||
.or_else(|| definite(width))
|
||||
.or_else(|| matches!(width, Size::Auto).then_some(stretch).flatten())
|
||||
.or_else(|| {
|
||||
(matches!(width, Size::Auto) && !context.inline_auto_width_intrinsic)
|
||||
.then_some(stretch)
|
||||
.flatten()
|
||||
})
|
||||
.or(maximum);
|
||||
preferred.map(|preferred| minimum.max(preferred.max(0).min(maximum.unwrap_or(i64::MAX))))
|
||||
}
|
||||
@ -4177,6 +4184,7 @@ fn measure_flex_item<'a>(
|
||||
},
|
||||
viewport_width_known: uses_inline_viewport && inline_viewport.is_some(),
|
||||
viewport_height: context.viewport_height,
|
||||
inline_auto_width_intrinsic: context.inline_auto_width_intrinsic,
|
||||
};
|
||||
let rendered = render_node(&item.node, measurement_context, uses_inline_viewport)?;
|
||||
let min_main = flex_min_main(&item.node, &rendered, axis, context)?;
|
||||
@ -4546,6 +4554,7 @@ fn render_flex_sized_entry(
|
||||
item_viewport.is_some() || context.viewport_width_known
|
||||
},
|
||||
viewport_height: context.viewport_height,
|
||||
inline_auto_width_intrinsic: context.inline_auto_width_intrinsic,
|
||||
};
|
||||
let override_size = box_override_for_flex(item.source, axis, main, cross, stretch);
|
||||
let mut rendered =
|
||||
@ -4706,7 +4715,11 @@ fn render_node_window(
|
||||
return Some(render_node(node, context, intrinsic));
|
||||
}
|
||||
match node {
|
||||
LayoutNode::Column { children } if !intrinsic && context.viewport_width_known => {
|
||||
LayoutNode::Column { children }
|
||||
if !intrinsic
|
||||
&& !context.inline_auto_width_intrinsic
|
||||
&& context.viewport_width_known =>
|
||||
{
|
||||
Some(render_column_window(children, context, start, height))
|
||||
}
|
||||
_ => Some(
|
||||
@ -5286,6 +5299,8 @@ fn render_node_with_override(
|
||||
viewport_width_known: !intrinsic_child
|
||||
&& (preliminary_child_width.is_some() || context.viewport_width_known),
|
||||
viewport_height: preliminary_child_height.unwrap_or(context.viewport_height),
|
||||
inline_auto_width_intrinsic: context.inline_auto_width_intrinsic
|
||||
&& matches!(effective_width, Size::Auto),
|
||||
};
|
||||
if simple_scroll_window {
|
||||
if let Some(total_height) = exact_rendered_height(child, child_context) {
|
||||
@ -5331,7 +5346,11 @@ fn render_node_with_override(
|
||||
let rendered = child_rendered.as_ref().expect("validated child");
|
||||
(rendered.min_content_width(*wrap_mode), rendered.max_width())
|
||||
};
|
||||
let auto_width = stretch.unwrap_or(max_content);
|
||||
let auto_width = if context.inline_auto_width_intrinsic {
|
||||
max_content
|
||||
} else {
|
||||
stretch.unwrap_or(max_content)
|
||||
};
|
||||
let minimum = resolve_width(
|
||||
min_width,
|
||||
Some(0),
|
||||
@ -5630,9 +5649,13 @@ fn render_node_with_override(
|
||||
Ok(rendered)
|
||||
}
|
||||
LayoutNode::Row { children } => {
|
||||
let child_context = LayoutContext {
|
||||
inline_auto_width_intrinsic: true,
|
||||
..context
|
||||
};
|
||||
let rendered = children
|
||||
.iter()
|
||||
.map(|child| render_node(child, context, intrinsic))
|
||||
.map(|child| render_node(child, child_context, intrinsic))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let height = rendered
|
||||
.iter()
|
||||
@ -5665,7 +5688,10 @@ fn render_node_with_override(
|
||||
.map(Rendered::first_width)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let target = if intrinsic || !context.viewport_width_known {
|
||||
let target = if intrinsic
|
||||
|| context.inline_auto_width_intrinsic
|
||||
|| !context.viewport_width_known
|
||||
{
|
||||
maximum
|
||||
} else {
|
||||
context.viewport_width.max(0)
|
||||
@ -5810,6 +5836,19 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_text_box(
|
||||
region_id: i64,
|
||||
content: MeasuredText,
|
||||
surface_template_id: Option<u32>,
|
||||
) -> LayoutNode {
|
||||
let mut node = text_box(region_id, content, surface_template_id);
|
||||
let LayoutNode::Box { width, .. } = &mut node else {
|
||||
unreachable!();
|
||||
};
|
||||
*width = Size::Auto;
|
||||
node
|
||||
}
|
||||
|
||||
fn child_box(
|
||||
region_id: i64,
|
||||
child: LayoutNode,
|
||||
@ -5899,6 +5938,7 @@ mod tests {
|
||||
viewport_width: 80,
|
||||
viewport_width_known: true,
|
||||
viewport_height: 24,
|
||||
inline_auto_width_intrinsic: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -5953,6 +5993,7 @@ mod tests {
|
||||
viewport_width: 80,
|
||||
viewport_width_known: true,
|
||||
viewport_height: MAX_LAYOUT_WORK_UNITS as i64 + 1,
|
||||
inline_auto_width_intrinsic: false,
|
||||
},
|
||||
&mut context_work,
|
||||
)
|
||||
@ -6279,6 +6320,7 @@ mod tests {
|
||||
viewport_width: 320,
|
||||
viewport_width_known: true,
|
||||
viewport_height: 10,
|
||||
inline_auto_width_intrinsic: false,
|
||||
};
|
||||
let fit_content = Size::FitContent {
|
||||
limit: Some(Box::new(Size::Pixels { value: 180 })),
|
||||
@ -6319,6 +6361,7 @@ mod tests {
|
||||
viewport_width: 0,
|
||||
viewport_width_known: false,
|
||||
viewport_height: 0,
|
||||
inline_auto_width_intrinsic: false,
|
||||
};
|
||||
assert_eq!(
|
||||
resolve_width(
|
||||
@ -6347,6 +6390,7 @@ mod tests {
|
||||
viewport_width: 298,
|
||||
viewport_width_known: true,
|
||||
viewport_height: 20,
|
||||
inline_auto_width_intrinsic: false,
|
||||
},
|
||||
BoxSizing::BorderBox,
|
||||
0,
|
||||
@ -6365,11 +6409,13 @@ mod tests {
|
||||
viewport_width: 0,
|
||||
viewport_width_known: true,
|
||||
viewport_height: 10,
|
||||
inline_auto_width_intrinsic: false,
|
||||
};
|
||||
let unavailable = LayoutContext {
|
||||
viewport_width: 0,
|
||||
viewport_width_known: false,
|
||||
viewport_height: 10,
|
||||
inline_auto_width_intrinsic: false,
|
||||
};
|
||||
|
||||
assert_eq!(flex_horizontal_size(&Size::Auto, known_zero), Some(0));
|
||||
@ -6874,6 +6920,50 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn row_auto_children_do_not_duplicate_column_viewport() {
|
||||
let row = LayoutNode::Row {
|
||||
children: vec![
|
||||
auto_text_box(2, measured_text(vec![vec![cluster("Left", 4, None)]]), None),
|
||||
auto_text_box(
|
||||
3,
|
||||
measured_text(vec![vec![cluster("Right", 5, None)]]),
|
||||
None,
|
||||
),
|
||||
],
|
||||
};
|
||||
let mut root = child_box(
|
||||
1,
|
||||
LayoutNode::Column {
|
||||
children: vec![
|
||||
row,
|
||||
auto_text_box(4, measured_text(vec![vec![cluster("Body", 4, None)]]), None),
|
||||
],
|
||||
},
|
||||
None,
|
||||
);
|
||||
let LayoutNode::Box { width, .. } = &mut root else {
|
||||
unreachable!();
|
||||
};
|
||||
*width = Size::Pixels { value: 120 };
|
||||
let document = LayoutDocument {
|
||||
version: LAYOUT_VERSION,
|
||||
space_width: 1,
|
||||
style_count: 0,
|
||||
property_template_count: 0,
|
||||
styles: Vec::new(),
|
||||
root,
|
||||
};
|
||||
document.validate().unwrap();
|
||||
|
||||
let tape = document.layout_tape(test_context(), None).unwrap();
|
||||
assert!(
|
||||
tape.lines.iter().all(|line| line.width <= 120),
|
||||
"row children duplicated the containing column width: {:?}",
|
||||
tape.lines.iter().map(|line| line.width).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_height_scroll_layout_uses_nonzero_offset_window() {
|
||||
let document = fixed_scroll_column_document(7);
|
||||
|
||||
@ -995,6 +995,7 @@ fn checked_layout_context(
|
||||
viewport_width,
|
||||
viewport_width_known,
|
||||
viewport_height,
|
||||
inline_auto_width_intrinsic: false,
|
||||
};
|
||||
document.validate_context(context)?;
|
||||
Ok(context)
|
||||
|
||||
@ -4246,6 +4246,16 @@
|
||||
(equal (ebox--render-cache-value-signature value)
|
||||
value))))
|
||||
|
||||
(ert-deftest ebox-render-cache-signature-includes-inline-auto-width-context ()
|
||||
"Row intrinsic sizing must not reuse a cross-axis stretch render."
|
||||
(ebox-test--reset-runtime-state)
|
||||
(let ((node (ebox-create :content "context-sensitive")))
|
||||
(let ((normal (let ((ebox--inline-auto-width-intrinsic-p nil))
|
||||
(ebox--render-cache-node-signature node)))
|
||||
(intrinsic (let ((ebox--inline-auto-width-intrinsic-p t))
|
||||
(ebox--render-cache-node-signature node))))
|
||||
(should-not (equal normal intrinsic)))))
|
||||
|
||||
(ert-deftest ebox-render-cache-signature-includes-rendered-region-identity ()
|
||||
"Cached render strings should never cross distinct region identities."
|
||||
(ebox-test--reset-runtime-state)
|
||||
|
||||
@ -237,6 +237,53 @@
|
||||
(dolist (width widths)
|
||||
(should (<= width 180)))))
|
||||
|
||||
(ert-deftest ebox-build-column-contains-auto-row-children ()
|
||||
"A column wrapper must not let an auto-width row child duplicate its viewport."
|
||||
(let* ((node (ebox-build
|
||||
'(column :width (120)
|
||||
(row
|
||||
(box :content "Left" :border t)
|
||||
(box :content "Right" :border t))
|
||||
(box :content "Body" :border t))))
|
||||
(widths (ebox-dsl-test--line-widths node)))
|
||||
(should (cl-every (lambda (width) (<= width 120)) widths))))
|
||||
|
||||
(ert-deftest ebox-row-keeps-explicit-viewport-widths-intrinsic-context ()
|
||||
"Row intrinsic sizing must not change explicit viewport-sized children."
|
||||
(let* ((ebox-viewport-width 120)
|
||||
(node (ebox-row
|
||||
(ebox-create :content "Left" :width 'viewport)
|
||||
(ebox-create :content "Right" :width 'stretch)))
|
||||
(widths (ebox-dsl-test--line-widths node)))
|
||||
(should (equal widths '(240)))))
|
||||
|
||||
(ert-deftest ebox-row-keeps-composite-column-intrinsic-width ()
|
||||
"A composite column child must not consume the row viewport as auto width."
|
||||
(let* ((ebox-viewport-width 120)
|
||||
(node (ebox-row
|
||||
(ebox-column
|
||||
(ebox-create :content "Top")
|
||||
(ebox-create :content "Bottom"))
|
||||
(ebox-create :content "Tail")))
|
||||
(widths (ebox-dsl-test--line-widths node)))
|
||||
(should (cl-every (lambda (width) (<= width 120)) widths))))
|
||||
|
||||
(ert-deftest ebox-build-lazy-column-contains-auto-row-children ()
|
||||
"Lazy column windows must apply the same intrinsic row sizing rule."
|
||||
(let* ((ebox-viewport-width 120)
|
||||
(ebox-viewport-height 4)
|
||||
(node (ebox-build
|
||||
'(box :width (viewport)
|
||||
:height (viewport-height)
|
||||
:overflow scroll
|
||||
(column
|
||||
(row
|
||||
(box :content "Left" :border t)
|
||||
(box :content "Right" :border t))
|
||||
(box :content "Body" :border t)))))
|
||||
(widths (ebox-dsl-test--line-widths node)))
|
||||
(should (cl-every (lambda (width) (<= width 120)) widths))))
|
||||
|
||||
(ert-deftest ebox-build-wrapper-viewport-width-bounds-child-layout-viewport ()
|
||||
"A wrapper's `(viewport)' width should pass its content box to child layouts."
|
||||
(let ((cases
|
||||
|
||||
@ -738,6 +738,17 @@
|
||||
(should (= (nth 2 widths) (nth 0 widths)))
|
||||
(should (= (nth 3 widths) (nth 0 widths)))))
|
||||
|
||||
(ert-deftest ebox-flex-composite-auto-row-respects-assigned-width ()
|
||||
"A composite auto-width row must not duplicate its Flex item viewport."
|
||||
(let* ((layout
|
||||
(ebox-flex :width '(120)
|
||||
(ebox-row
|
||||
(ebox-create :content "Left" :border t)
|
||||
(ebox-create :content "Right" :border t))))
|
||||
(widths (mapcar #'ebox--string-pixel-width
|
||||
(ebox-string-lines (ebox-render layout)))))
|
||||
(should (cl-every (lambda (width) (<= width 120)) widths))))
|
||||
|
||||
(ert-deftest ebox-flex-sized-persistent-hit-populates-pass-local-cache ()
|
||||
"Persistent sized renders should become reusable in the current pass."
|
||||
(let* ((source (ebox-create :content "A" :width '(80)))
|
||||
|
||||
@ -80,6 +80,18 @@
|
||||
(widths (mapcar #'ebox--string-pixel-width lines)))
|
||||
(should (equal widths (make-list (length widths) 120)))))
|
||||
|
||||
(ert-deftest ebox-grid-composite-auto-row-respects-track-width ()
|
||||
"A composite auto-width row must not duplicate its Grid track viewport."
|
||||
(let* ((node (ebox-grid
|
||||
:width '(120)
|
||||
:grid-template-columns '((120))
|
||||
(ebox-row
|
||||
(ebox-create :content "Left" :border t)
|
||||
(ebox-create :content "Right" :border t))))
|
||||
(widths (mapcar #'ebox--string-pixel-width
|
||||
(ebox-string-lines (ebox-render node)))))
|
||||
(should (equal widths '(120)))))
|
||||
|
||||
(ert-deftest ebox-grid-sizes-auto-row-after-assigned-width-wrap ()
|
||||
"Auto rows should use the height of width-constrained child content."
|
||||
(let* ((node (ebox-grid
|
||||
|
||||
Loading…
Reference in New Issue
Block a user