Capture · Roots: the roots round 2 missed
A sweep that read every render found 28 roots still on w_full, reached by 64 components: the field button under Select and every picker, the checked and mention fields, the slider track, NumberInput, Steps, the state views, the agent previews, and every chart and finance frame. All drop it. LivePreview's size_full becomes h_full, since its card no longer fills. Every host of those roots was listed and read, 231 in all. Five tests that mounted a picker as the window root sit in a size_full box, the index manager gives its method select a flex_1 box, and the notification center and the gallery's state frames stack their state views in a column so they stretch. Each dropped root carries a debug selector, and six chapter tests mount 43 components in a padded card whose column is measured by content, through layout::tests::narrow_width: each root must take the full 240px. With w_full back on the roots, all six fail. AGENTS.md names the roots that keep w_full and why: picture frames that keep a ratio, and rows that hand a text field its share. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ca5c967f17
commit
e482bac865
43 files changed
+465
-108
No files matched your search
@@ -153,7 +153,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
|
||||
- A row that holds text beside fixed parts lets the text give way: the text takes `flex_1().min_w_0()` and an `Ellipsis`; counts, icons, dates and chevrons take `flex_none`. A label that must stay whole wraps in that block instead, as `forms::Checkbox`'s does.
|
||||
- A mark over a text field reads the field at paint time, from a canvas laid over it (`collab::RemoteCursor`): by then `TextInput::bounds_for` holds this frame's layout, and a still page never renders again to catch up.
|
||||
- A callback field holds a named `type` alias; clippy's type_complexity rejects `Option<Rc<dyn Fn(..)>>`. A chapter shares one alias per shape.
|
||||
- A component that fills its container takes no width on its root: a block or a flex column stretches it. Percents fill badly in taffy 0.9. `w_full` in a flex column that a block measures by content keeps its content's width: taffy lays the column out once at unknown width and keeps that layout when the width comes out the same. `min_w_full` resolves against a block's outer width, padding and all (`forms::FormField`). In a flex row the host gives it `flex_1`; as a window's root it sits in a `size_full` box. A root centered under a cap keeps `w_full`, since `mx_auto` needs a width to fill (`layout::Container`); so does one that keeps a ratio, since taffy 0.9 follows a ratio from a set width, not a stretch (`layout::AspectRatio`). A row that hands a text field a `flex_1` share keeps `w_full` too: stretched without a width, taffy 0.9 can keep a pass that laid the share at no width, and the field draws nothing (`forms::Input`, `InputGroup`, `TagInput`).
|
||||
- A component that fills its container takes no width on its root: a block or a flex column stretches it. Percents fill badly in taffy 0.9. `w_full` in a flex column that a block measures by content keeps its content's width: taffy lays the column out once at unknown width and keeps that layout when the width comes out the same. `min_w_full` resolves against a block's outer width, padding and all (`forms::FormField`). In a flex row the host gives it `flex_1`; as a window's root it sits in a `size_full` box. A root centered under a cap keeps `w_full`, since `mx_auto` needs a width to fill (`layout::Container`); so does one that keeps a ratio, since taffy 0.9 follows a ratio from a set width, not a stretch (`layout::AspectRatio`, and `primitives::framed` under VideoPlayer, CameraPreview, ModelViewer, ParticipantTile and InpaintCanvas). A row that hands the text field element itself a `flex_1` share keeps `w_full` too: stretched without a width, taffy 0.9 can keep a pass that laid the share at no width, and the field draws nothing (`forms::Input`, `InputGroup`, `TagInput`); a row around one of those, as `NumberInput`'s is, takes none. `Button::full_width` is its host asking, since a button takes no style. Each chapter's test holds its filling roots at the full width of such a column through `layout::tests::narrow_width`, which reads a root by its `debug_selector`.
|
||||
- A field's `Highlight` sets color, wash, weight, slant and strike per span; measuring and painting share one set of runs, so wrapped heights match.
|
||||
- A block editor keeps one undo for the document: text and structure together, as snapshots through `forms::History`; it takes Undo and Redo before its fields do.
|
||||
- gpui calls focus listeners only while the window is active. A test that needs focus events activates its window with `window.activate_window()`.
|
||||
|
||||
@@ -473,3 +473,4 @@ Per-component lines live in `tasks/`. Tags there:
|
||||
| T43c | 1 | FAIL | the audit left fg_subtle on active and every syntax color unread, both under 4.5:1; the viewer left out avatar and container sizes while claiming every token |
|
||||
| T43c | 2 | PASS | the AGENTS.md decision narrowed to the pairs the audit holds, in the pass commit |
|
||||
| T44a | 1 | FAIL | twelve roots in nine components still said w_full: a root held in a variable, a match arm or an animation passed the sweep |
|
||||
| T44a | 2 | FAIL | roots built in render(mut self), returned by shared helpers or passed through them still said w_full: fields, charts, state views, Steps, previews; five measured at content width in a padded card |
|
||||
@@ -358,7 +358,7 @@ pub fn empties(cx: &mut App) -> impl IntoElement + use<> {
|
||||
.min_w_0()
|
||||
.h_80()
|
||||
.flex()
|
||||
.items_center()
|
||||
.flex_col()
|
||||
.justify_center()
|
||||
.rounded(theme.radius(Radius::Lg))
|
||||
.border_1()
|
||||
|
||||
@@ -26,7 +26,7 @@ fn frame(cx: &App) -> Div {
|
||||
.min_w_0()
|
||||
.h_80()
|
||||
.flex()
|
||||
.items_center()
|
||||
.flex_col()
|
||||
.justify_center()
|
||||
.rounded(theme.radius(Radius::Lg))
|
||||
.border_1()
|
||||
|
||||
@@ -56,7 +56,7 @@ fn address_bar(
|
||||
fn card(cx: &App) -> Div {
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.w_full()
|
||||
.debug_selector(|| "preview-root".into())
|
||||
.flex()
|
||||
.flex_col()
|
||||
.rounded(theme.radius(Radius::Lg))
|
||||
@@ -395,7 +395,7 @@ impl RenderOnce for LivePreview {
|
||||
.into_any_element()
|
||||
});
|
||||
card(cx)
|
||||
.size_full()
|
||||
.h_full()
|
||||
.child(address_bar(&self.id, self.url, self.updating, reload, cx))
|
||||
.child(
|
||||
div()
|
||||
|
||||
@@ -7,8 +7,9 @@ use gpui::{
|
||||
|
||||
use super::{press, settle};
|
||||
use crate::{
|
||||
agent::{ArtifactPanel, BrowserPreview, ComputerUseViewer},
|
||||
agent::{ArtifactPanel, BrowserPreview, ComputerUseViewer, LivePreview},
|
||||
forms,
|
||||
layout::tests::narrow_width,
|
||||
primitives::{FocusNext, IconName},
|
||||
theme::{ActiveTheme, AvatarSize, Theme},
|
||||
};
|
||||
@@ -288,3 +289,24 @@ fn a_screen_takes_its_pictures_shape(cx: &mut TestAppContext) {
|
||||
height.get()
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn previews_fill_a_column_their_block_measures_by_content(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let widths = [
|
||||
narrow_width(cx, "preview-root", |_, _| {
|
||||
BrowserPreview::new("browser", "localhost", ["a.png"], 0).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "preview-root", |_, _| {
|
||||
ComputerUseViewer::new("screen", "a.png").into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "preview-root", |_, _| {
|
||||
LivePreview::new("live", "localhost", div()).into_any_element()
|
||||
}),
|
||||
];
|
||||
assert_eq!(
|
||||
widths,
|
||||
[px(240.0); 3],
|
||||
"each preview spans the card inside its padding"
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
App, Div, ElementId, Entity, Hsla, IntoElement, ParentElement, Pixels, Refineable, RenderOnce,
|
||||
StyleRefinement, Styled, Window, canvas, div, fill,
|
||||
App, Div, ElementId, Entity, Hsla, InteractiveElement, IntoElement, ParentElement, Pixels,
|
||||
Refineable, RenderOnce, StyleRefinement, Styled, Window, canvas, div, fill,
|
||||
};
|
||||
use jiff::{Span, civil::Date};
|
||||
|
||||
@@ -157,7 +157,9 @@ impl RenderOnce for CalendarHeatmap {
|
||||
);
|
||||
let hit = days.iter().map(|(rect, _)| *rect).collect::<Vec<_>>();
|
||||
let legend = key(shades, ("Less", "More"), cx);
|
||||
let mut root = div().w_full().h(sizes.height * 0.5);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.h(sizes.height * 0.5);
|
||||
root.style().refine(self.base.style());
|
||||
let chart = tracked(
|
||||
div().relative().flex_1().min_h_0(),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::{collections::HashSet, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
App, Div, ElementId, IntoElement, Refineable, RenderOnce, SharedString, StyleRefinement,
|
||||
Styled, Window, div,
|
||||
App, Div, ElementId, InteractiveElement, IntoElement, Refineable, RenderOnce, SharedString,
|
||||
StyleRefinement, Styled, Window, div,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -131,7 +131,9 @@ impl Chart {
|
||||
|
||||
/// Renders a plot at the theme's chart height unless the owner sizes it.
|
||||
pub(crate) fn sized(base: &mut Div, plotted: Plot, window: &mut Window, cx: &mut App) -> Div {
|
||||
let mut frame = div().w_full().h(cx.theme().chart().height);
|
||||
let mut frame = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.h(cx.theme().chart().height);
|
||||
frame.style().refine(base.style());
|
||||
plot(plotted, frame, window, cx)
|
||||
}
|
||||
|
||||
+4
-1
@@ -342,7 +342,10 @@ impl RenderOnce for SankeyChart {
|
||||
let palette = colors.clone();
|
||||
let (hit_nodes, hit_ribbons) = (nodes, ribbons);
|
||||
let (moved, left) = (flows.clone(), flows.clone());
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
root.id((self.id.clone(), "sankey"))
|
||||
.on_mouse_move(move |event: &MouseMoveEvent, _, cx| {
|
||||
|
||||
@@ -184,7 +184,10 @@ impl RenderOnce for FunnelChart {
|
||||
frame.h / self.stages.len().max(1) as f32,
|
||||
);
|
||||
let count = self.stages.len();
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
root.id((self.id.clone(), "funnel"))
|
||||
.on_mouse_move(move |event: &MouseMoveEvent, _, cx| {
|
||||
|
||||
@@ -287,7 +287,10 @@ impl RenderOnce for NetworkGraph {
|
||||
graph.clone(),
|
||||
);
|
||||
let press_hit = hit.clone();
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
root.id((self.id.clone(), "network"))
|
||||
.on_mouse_down(MouseButton::Left, move |event: &MouseDownEvent, _, cx| {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
App, Div, ElementId, Entity, IntoElement, ParentElement, PathBuilder, Pixels, Refineable,
|
||||
RenderOnce, SharedString, StyleRefinement, Styled, Window, canvas, div,
|
||||
App, Div, ElementId, Entity, InteractiveElement, IntoElement, ParentElement, PathBuilder,
|
||||
Pixels, Refineable, RenderOnce, SharedString, StyleRefinement, Styled, Window, canvas, div,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -215,7 +215,10 @@ impl RenderOnce for ParallelCoordinates {
|
||||
lines.clone(),
|
||||
);
|
||||
let reach = pixels(sizes.inset);
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
tracked(
|
||||
root,
|
||||
|
||||
+1
-1
@@ -302,7 +302,7 @@ impl RenderOnce for PieChart {
|
||||
|legend, (ix, (name, _))| legend.entry(tint(&colors, ix), name.clone(), false),
|
||||
);
|
||||
let (moved, left, measured, hit) = (ring.clone(), ring.clone(), ring.clone(), arcs.clone());
|
||||
let mut frame = div().w_full().h(sizes.height);
|
||||
let mut frame = div().debug_selector(|| "chart-root".into()).h(sizes.height);
|
||||
frame.style().refine(self.base.style());
|
||||
frame.flex().flex_col().gap_2().child(legend).child(
|
||||
div()
|
||||
|
||||
+1
-1
@@ -245,7 +245,7 @@ impl RenderOnce for RadarChart {
|
||||
let (stroke, hairline) = (sizes.stroke.to_pixels(rem), sizes.hairline);
|
||||
let palette = colors.clone();
|
||||
let (moved, left, measured) = (radar.clone(), radar.clone(), radar.clone());
|
||||
let mut frame = div().w_full().h(sizes.height);
|
||||
let mut frame = div().debug_selector(|| "chart-root".into()).h(sizes.height);
|
||||
frame.style().refine(self.base.style());
|
||||
frame.flex().flex_col().gap_2().children(legend).child(
|
||||
div()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use gpui::{
|
||||
App, Bounds, ContentMask, Div, ElementId, Entity, IntoElement, ParentElement, PathBuilder,
|
||||
Pixels, Refineable, RenderOnce, StyleRefinement, Styled, Window, canvas, div, fill, size,
|
||||
App, Bounds, ContentMask, Div, ElementId, Entity, InteractiveElement, IntoElement,
|
||||
ParentElement, PathBuilder, Pixels, Refineable, RenderOnce, StyleRefinement, Styled, Window,
|
||||
canvas, div, fill, size,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -128,7 +129,10 @@ impl RenderOnce for RealtimeChart {
|
||||
pixels(sizes.inset),
|
||||
);
|
||||
let measured = live.clone();
|
||||
let mut chart = div().relative().w_full().h(sizes.height * 0.6);
|
||||
let mut chart = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height * 0.6);
|
||||
chart.style().refine(self.base.style());
|
||||
chart
|
||||
.child(
|
||||
|
||||
@@ -222,7 +222,10 @@ impl RenderOnce for ChordDiagram {
|
||||
})
|
||||
.collect();
|
||||
let (palette, arcs, hit) = (colors.clone(), groups.clone(), groups);
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
tracked(
|
||||
root,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use gpui::{
|
||||
App, Bounds, Div, ElementId, Entity, IntoElement, ParentElement, PathBuilder, Pixels,
|
||||
Refineable, RenderOnce, SharedString, StyleRefinement, Styled, Window, canvas, div, fill, size,
|
||||
App, Bounds, Div, ElementId, Entity, InteractiveElement, IntoElement, ParentElement,
|
||||
PathBuilder, Pixels, Refineable, RenderOnce, SharedString, StyleRefinement, Styled, Window,
|
||||
canvas, div, fill, size,
|
||||
};
|
||||
use jiff::{
|
||||
Span,
|
||||
@@ -267,8 +268,8 @@ impl RenderOnce for GanttChart {
|
||||
);
|
||||
let painted = bars.clone();
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.w_full()
|
||||
.h(sizes.foot + theme.table_row(Density::Compact) * count as f32);
|
||||
root.style().refine(self.base.style());
|
||||
tracked(
|
||||
|
||||
@@ -187,7 +187,10 @@ impl RenderOnce for Sunburst {
|
||||
);
|
||||
let (moved, left, measured, hit) = (ring.clone(), ring.clone(), ring.clone(), arcs.clone());
|
||||
let painted = arcs.clone();
|
||||
let mut frame = div().relative().w_full().h(sizes.height);
|
||||
let mut frame = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
frame.style().refine(self.base.style());
|
||||
frame
|
||||
.id((self.id.clone(), "ring"))
|
||||
|
||||
@@ -193,8 +193,8 @@ impl RenderOnce for BulletChart {
|
||||
colors.clone(),
|
||||
);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.w_full()
|
||||
.h(theme.table_row(Density::Comfortable) * count as f32);
|
||||
root.style().refine(self.base.style());
|
||||
tracked(
|
||||
|
||||
+126
-4
@@ -10,11 +10,12 @@ use gpui::{
|
||||
use jiff::civil::date;
|
||||
|
||||
use super::{
|
||||
BarChart, Bullet, BulletChart, CalendarHeatmap, ChartLegend, ChordDiagram, FunnelChart,
|
||||
GanttChart, HeatmapChart, LineChart, NetworkGraph, ParallelCoordinates, ProgressChart,
|
||||
RadarChart, SankeyChart, Series, Task, Treemap,
|
||||
AreaChart, BarChart, BoxPlot, Bullet, BulletChart, CalendarHeatmap, ChartLegend, ChordDiagram,
|
||||
FunnelChart, GanttChart, HeatmapChart, Histogram, LineChart, NetworkGraph, ParallelCoordinates,
|
||||
PieChart, Points, ProgressChart, RadarChart, RealtimeChart, SankeyChart, ScatterChart, Series,
|
||||
Slice, Sunburst, Task, Treemap, ViolinPlot, WaterfallChart,
|
||||
};
|
||||
use crate::theme::Theme;
|
||||
use crate::{layout::tests::narrow_width, theme::Theme};
|
||||
|
||||
#[gpui::test]
|
||||
fn a_chart_exports_as_svg_and_csv(cx: &mut TestAppContext) {
|
||||
@@ -208,3 +209,124 @@ fn tab_reaches_a_legend_entry_and_enter_toggles_it(cx: &mut TestAppContext) {
|
||||
settle(cx);
|
||||
assert_eq!(*heard.borrow(), ["Held out"]);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn every_chart_fills_a_column_its_block_measures_by_content(cx: &mut TestAppContext) {
|
||||
cx.update(Theme::init);
|
||||
let two = || Series::new("s", [1.0, 2.0]);
|
||||
let pairs = [vec![0.0, 1.0], vec![1.0, 0.0]];
|
||||
let widths = [
|
||||
narrow_width(cx, "chart-root", move |_, _| {
|
||||
LineChart::new("line", ["a", "b"])
|
||||
.series(two())
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", move |_, _| {
|
||||
AreaChart::new("area", ["a", "b"])
|
||||
.series(two())
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", move |_, _| {
|
||||
BarChart::new("bar", ["a", "b"])
|
||||
.series(two())
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
let points = Points::new("p", [(0.0, 1.0), (1.0, 2.0)]);
|
||||
ScatterChart::new("scatter")
|
||||
.points(points)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
Histogram::new("histogram", [1.0, 2.0, 2.0, 3.0]).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
WaterfallChart::new("waterfall")
|
||||
.step("a", 2.0)
|
||||
.step("b", -1.0)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
BoxPlot::new("box")
|
||||
.group("a", [1.0, 2.0, 3.0])
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
ViolinPlot::new("violin")
|
||||
.group("a", [1.0, 2.0, 3.0])
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
CalendarHeatmap::new("days", date(2026, 1, 5), [1.0; 14]).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
SankeyChart::new("sankey", ["a", "b"])
|
||||
.link(0, 1, 5.0)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
FunnelChart::new("funnel")
|
||||
.stage("a", 2.0)
|
||||
.stage("b", 1.0)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
NetworkGraph::new("network")
|
||||
.node("a", 0)
|
||||
.node("b", 0)
|
||||
.edge(0, 1)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
ParallelCoordinates::new("parallel", ["a", "b"])
|
||||
.record("r", [1.0, 2.0])
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
PieChart::new("pie")
|
||||
.slice("a", 1.0)
|
||||
.slice("b", 2.0)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", move |_, _| {
|
||||
RadarChart::new("radar", ["a", "b", "c"])
|
||||
.series(Series::new("s", [1.0, 2.0, 3.0]))
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
RealtimeChart::new("live", [1.0, 2.0], 2).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", move |_, _| {
|
||||
ChordDiagram::new("chord", ["a", "b"], pairs.to_vec()).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
GanttChart::new("gantt")
|
||||
.task(Task::new("t", date(2026, 3, 1), date(2026, 3, 9)))
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
Sunburst::new("sun", [Slice::new("a", 1.0)]).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
BulletChart::new("bullets")
|
||||
.bullet(Bullet::new("b", 5.0, 8.0))
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
Treemap::new("treemap")
|
||||
.tile("a", 1.0)
|
||||
.tile("b", 2.0)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
HeatmapChart::new("heatmap", ["a"])
|
||||
.row("a", [1.0])
|
||||
.into_any_element()
|
||||
}),
|
||||
];
|
||||
assert_eq!(
|
||||
widths,
|
||||
[px(240.0); 22],
|
||||
"each chart spans the card inside its padding"
|
||||
);
|
||||
}
|
||||
+8
-2
@@ -208,7 +208,10 @@ impl RenderOnce for Treemap {
|
||||
rects.clone(),
|
||||
rects,
|
||||
);
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
tracked(
|
||||
root,
|
||||
@@ -406,7 +409,10 @@ impl RenderOnce for HeatmapChart {
|
||||
sizes.hairline,
|
||||
);
|
||||
let legend = key(steps(ink), ("Less", "More"), cx);
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
let chart = tracked(
|
||||
div().relative().flex_1().min_h_0(),
|
||||
|
||||
+12
-13
@@ -410,6 +410,17 @@ impl RenderOnce for IndexManager {
|
||||
);
|
||||
let ready = !name.read(cx).text().trim().is_empty() && !picked.is_empty();
|
||||
let [chose, flipped, pick_method, made] = [(); 4].map(|_| draft.clone());
|
||||
let methods = Select::new(
|
||||
(id.clone(), "method"),
|
||||
METHODS.iter().map(|method| Choice::new(*method, *method)),
|
||||
)
|
||||
.selected(method)
|
||||
.on_change(move |value, _, cx| {
|
||||
pick_method.update(cx, |draft, cx| {
|
||||
draft.method = value.clone();
|
||||
cx.notify();
|
||||
})
|
||||
});
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
@@ -453,19 +464,7 @@ impl RenderOnce for IndexManager {
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Select::new(
|
||||
(id.clone(), "method"),
|
||||
METHODS.iter().map(|method| Choice::new(*method, *method)),
|
||||
)
|
||||
.selected(method)
|
||||
.on_change(move |value, _, cx| {
|
||||
pick_method.update(cx, |draft, cx| {
|
||||
draft.method = value.clone();
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
),
|
||||
.child(div().flex_1().min_w_0().child(methods)),
|
||||
)
|
||||
.child(
|
||||
Button::new((id, "create"), "Create index")
|
||||
|
||||
@@ -248,7 +248,7 @@ impl RenderOnce for NotificationCenter {
|
||||
div()
|
||||
.flex_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.flex_col()
|
||||
.justify_center()
|
||||
.child(
|
||||
EmptyState::new(
|
||||
|
||||
@@ -53,7 +53,7 @@ pub(super) fn state_view(
|
||||
let theme = cx.theme();
|
||||
let colors = &theme.colors;
|
||||
div()
|
||||
.w_full()
|
||||
.debug_selector(|| "state-root".into())
|
||||
.flex()
|
||||
.flex_col()
|
||||
.items_center()
|
||||
|
||||
@@ -10,8 +10,10 @@ use super::{press, settle, setup, wait};
|
||||
use crate::{
|
||||
buttons::Button,
|
||||
feedback::{
|
||||
ConfirmationCard, Countdown, EmptyState, ErrorBoundary, SaveState, SavingIndicator,
|
||||
ConfirmationCard, Countdown, EmptyState, ErrorBoundary, ErrorView, ResultView, SaveState,
|
||||
SavingIndicator,
|
||||
},
|
||||
layout::tests::narrow_width,
|
||||
primitives::{FocusScope, IconName, Measure},
|
||||
};
|
||||
|
||||
@@ -200,3 +202,24 @@ fn a_long_inline_message_wraps_inside_its_box(cx: &mut TestAppContext) {
|
||||
.expect("the words draw");
|
||||
assert!(text.right() <= px(200.0), "{text:?}");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn state_views_fill_a_column_their_block_measures_by_content(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let widths = [
|
||||
narrow_width(cx, "state-root", |_, _| {
|
||||
EmptyState::new("empty", IconName::Search, "Nothing here").into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "state-root", |_, _| {
|
||||
ErrorView::not_found("missing").into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "state-root", |_, _| {
|
||||
ResultView::success("done", "Saved").into_any_element()
|
||||
}),
|
||||
];
|
||||
assert_eq!(
|
||||
widths,
|
||||
[px(240.0); 3],
|
||||
"each view spans the card inside its padding"
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use gpui::{
|
||||
App, Div, ElementId, Entity, IntoElement, ParentElement, PathBuilder, Pixels, Point,
|
||||
Refineable, RenderOnce, StyleRefinement, Styled, Window, canvas, div, fill,
|
||||
App, Div, ElementId, Entity, InteractiveElement, IntoElement, ParentElement, PathBuilder,
|
||||
Pixels, Point, Refineable, RenderOnce, StyleRefinement, Styled, Window, canvas, div, fill,
|
||||
};
|
||||
|
||||
use super::quotes::moves;
|
||||
@@ -250,7 +250,10 @@ impl RenderOnce for DepthChart {
|
||||
let (stroke, bottom) = (sizes.stroke.to_pixels(rem), frame.y + frame.h);
|
||||
let mark = reading.map(|(price, _, total)| (xs.at(price), ys.at(total)));
|
||||
let palette = colors.clone();
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
tracked(
|
||||
root,
|
||||
|
||||
@@ -165,7 +165,10 @@ impl RenderOnce for MarketHeatmap {
|
||||
theme.radius(Radius::Sm).to_pixels(rem),
|
||||
colors.fg,
|
||||
);
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(self.base.style());
|
||||
tracked(
|
||||
root,
|
||||
|
||||
@@ -422,7 +422,10 @@ impl RenderOnce for CandlestickChart {
|
||||
},
|
||||
};
|
||||
let height = sizes.height + sizes.height * 0.3 * self.studies.len() as f32;
|
||||
let mut root = div().relative().w_full().h(height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(height);
|
||||
root.style().refine(self.base.style());
|
||||
let steering = Steering {
|
||||
stage: stage.clone(),
|
||||
|
||||
@@ -196,7 +196,9 @@ impl RenderOnce for PayoffDiagram {
|
||||
}
|
||||
chart.labels = labels;
|
||||
chart.format = Rc::new(|value| format::currency(value, "USD"));
|
||||
let mut base = div().w_full().h(cx.theme().chart().height);
|
||||
let mut base = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.h(cx.theme().chart().height);
|
||||
base.style().refine(self.base.style());
|
||||
plot(chart, base, window, cx)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
App, Div, ElementId, Entity, IntoElement, ParentElement, PathBuilder, Pixels, Refineable,
|
||||
RenderOnce, StyleRefinement, Styled, Window, canvas, div, fill,
|
||||
App, Div, ElementId, Entity, InteractiveElement, IntoElement, ParentElement, PathBuilder,
|
||||
Pixels, Refineable, RenderOnce, StyleRefinement, Styled, Window, canvas, div, fill,
|
||||
};
|
||||
|
||||
use jiff::tz::TimeZone;
|
||||
@@ -146,7 +146,10 @@ fn board(
|
||||
theme.radius(crate::theme::Radius::Sm).to_pixels(rem),
|
||||
);
|
||||
let box_px = boxes.map(|size| (ys.at(0.0) - ys.at(size)).abs());
|
||||
let mut root = div().relative().w_full().h(sizes.height);
|
||||
let mut root = div()
|
||||
.debug_selector(|| "chart-root".into())
|
||||
.relative()
|
||||
.h(sizes.height);
|
||||
root.style().refine(base.style());
|
||||
tracked(root, (id, "marks").into(), &tiles, move |place| {
|
||||
hit.iter().position(|(rect, _)| rect.contains(place))
|
||||
|
||||
+43
-2
@@ -6,8 +6,11 @@ use gpui::{
|
||||
};
|
||||
use jiff::Timestamp;
|
||||
|
||||
use crate::finance::{CandlestickChart, ChartSync, candles::Candle, stage::Visible};
|
||||
use crate::theme::Theme;
|
||||
use crate::finance::{
|
||||
CandlestickChart, ChartSync, DepthChart, Leg, MarketHeatmap, PayoffDiagram, PointFigureChart,
|
||||
RenkoChart, candles::Candle, stage::Visible,
|
||||
};
|
||||
use crate::{layout::tests::narrow_width, theme::Theme};
|
||||
|
||||
fn candles(count: usize) -> Rc<Vec<Candle>> {
|
||||
Rc::new(
|
||||
@@ -106,3 +109,41 @@ fn a_lagging_chart_leaves_the_shared_window_alone(cx: &mut TestAppContext) {
|
||||
"the lagging chart's append is already shown"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn every_chart_fills_a_column_its_block_measures_by_content(cx: &mut TestAppContext) {
|
||||
cx.update(Theme::init);
|
||||
let leg = Leg {
|
||||
call: true,
|
||||
strike: 100.0,
|
||||
quantity: 1.0,
|
||||
premium: 2.0,
|
||||
};
|
||||
let widths = [
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
DepthChart::new("depth", [(99.0, 2.0)], [(101.0, 3.0)]).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
MarketHeatmap::new("heatmap")
|
||||
.tile("A", 1.0, 0.5)
|
||||
.into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
CandlestickChart::new("candles", candles(30)).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", move |_, _| {
|
||||
PayoffDiagram::new("payoff", [leg], (80.0, 120.0), 100.0).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
RenkoChart::new("renko", candles(30), 0.1).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "chart-root", |_, _| {
|
||||
PointFigureChart::new("figure", candles(30), 0.1, 3).into_any_element()
|
||||
}),
|
||||
];
|
||||
assert_eq!(
|
||||
widths,
|
||||
[px(240.0); 6],
|
||||
"each chart spans the card inside its padding"
|
||||
);
|
||||
}
|
||||
@@ -43,10 +43,10 @@ fn checked(
|
||||
let theme = cx.theme();
|
||||
let (subtle, danger) = (theme.colors.fg_subtle, theme.colors.danger);
|
||||
div()
|
||||
.debug_selector(|| "checked-root".into())
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
Input::new(state)
|
||||
.size(size)
|
||||
|
||||
@@ -172,9 +172,9 @@ impl Suggestions {
|
||||
let (up_rows, down_rows, enter_rows) = (rows.clone(), rows.clone(), rows.clone());
|
||||
let (enter, trigger) = (self.pick.clone(), self.trigger);
|
||||
div()
|
||||
.debug_selector(|| "mention-root".into())
|
||||
.id(self.id)
|
||||
.relative()
|
||||
.w_full()
|
||||
.capture_action(move |_: &Up, _, cx| {
|
||||
if open {
|
||||
cx.stop_propagation();
|
||||
|
||||
+1
-1
@@ -401,10 +401,10 @@ impl RenderOnce for NumberInput {
|
||||
.child(stepper),
|
||||
);
|
||||
div()
|
||||
.debug_selector(|| "number-root".into())
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.children(label)
|
||||
.child(
|
||||
div()
|
||||
|
||||
+1
-1
@@ -88,13 +88,13 @@ pub(crate) fn field_button(
|
||||
let theme = cx.theme();
|
||||
let colors = &theme.colors;
|
||||
div()
|
||||
.debug_selector(|| "field-root".into())
|
||||
.id(id)
|
||||
.track_focus(focus)
|
||||
.relative()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.h(theme.control_height(size))
|
||||
.px(theme.control_padding(size))
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
|
||||
+2
-1
@@ -243,13 +243,14 @@ impl Track {
|
||||
})
|
||||
});
|
||||
div()
|
||||
.debug_selector(|| "slider-root".into())
|
||||
.id(self.id)
|
||||
.flex_none()
|
||||
.map(|outer| {
|
||||
if vertical {
|
||||
outer.h_full().w(thumb).py(thumb * 0.5)
|
||||
} else {
|
||||
outer.w_full().h(thumb).px(thumb * 0.5)
|
||||
outer.h(thumb).px(thumb * 0.5)
|
||||
}
|
||||
})
|
||||
.when(self.disabled, |outer| outer.opacity(0.5))
|
||||
|
||||
+15
-11
@@ -67,14 +67,16 @@ struct Picker {
|
||||
impl Render for Picker {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
Select::new("select", abc(false))
|
||||
.selected(self.chosen.clone())
|
||||
.on_change(move |value, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.chosen = value.clone();
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
div().size_full().child(
|
||||
Select::new("select", abc(false))
|
||||
.selected(self.chosen.clone())
|
||||
.on_change(move |value, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.chosen = value.clone();
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,9 +427,11 @@ struct Empty(bool);
|
||||
|
||||
impl Render for Empty {
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
Select::new("empty", [])
|
||||
.placeholder("Nothing yet")
|
||||
.disabled(self.0)
|
||||
div().size_full().child(
|
||||
Select::new("empty", [])
|
||||
.placeholder("Nothing yet")
|
||||
.disabled(self.0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-26
@@ -1,4 +1,7 @@
|
||||
use gpui::{Context, IntoElement, KeyUpEvent, Keystroke, Render, TestAppContext, Window};
|
||||
use gpui::{
|
||||
Context, IntoElement, KeyUpEvent, Keystroke, ParentElement, Render, Styled, TestAppContext,
|
||||
Window, div,
|
||||
};
|
||||
use jiff::civil::{Date, Time, date, time};
|
||||
|
||||
use super::setup;
|
||||
@@ -14,15 +17,17 @@ struct Day {
|
||||
impl Render for Day {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
DatePicker::new("day", self.value)
|
||||
.today(TODAY)
|
||||
.disabled(self.disabled)
|
||||
.on_change(move |picked, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.value = Some(picked);
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
div().size_full().child(
|
||||
DatePicker::new("day", self.value)
|
||||
.today(TODAY)
|
||||
.disabled(self.disabled)
|
||||
.on_change(move |picked, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.value = Some(picked);
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,14 +53,16 @@ struct Span {
|
||||
impl Render for Span {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
DateRangePicker::new("span", self.value)
|
||||
.today(TODAY)
|
||||
.on_change(move |picked, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.value = Some(picked);
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
div().size_full().child(
|
||||
DateRangePicker::new("span", self.value)
|
||||
.today(TODAY)
|
||||
.on_change(move |picked, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.value = Some(picked);
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,14 +85,18 @@ struct Clock {
|
||||
impl Render for Clock {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
TimePicker::new("clock", self.value)
|
||||
.step(15)
|
||||
.on_change(move |picked, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.value = Some(picked);
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
div()
|
||||
.size_full()
|
||||
.child(
|
||||
TimePicker::new("clock", self.value)
|
||||
.step(15)
|
||||
.on_change(move |picked, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.value = Some(picked);
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use gpui::{
|
||||
AppContext as _, Context, Entity, InteractiveElement, IntoElement, KeyUpEvent, Keystroke,
|
||||
App, AppContext as _, Context, Entity, InteractiveElement, IntoElement, KeyUpEvent, Keystroke,
|
||||
ParentElement, Render, Styled, TestAppContext, VisualTestContext, Window, div, px,
|
||||
};
|
||||
|
||||
use super::setup;
|
||||
use crate::{
|
||||
buttons::Button,
|
||||
forms::{FieldArray, Form, FormField, Input, InputEvent, TextInput},
|
||||
forms::{
|
||||
Choice, DatePicker, EmailInput, FieldArray, Form, FormField, Input, InputEvent,
|
||||
MentionInput, NumberInput, Select, Slider, TextInput,
|
||||
},
|
||||
layout::tests::narrow_width,
|
||||
};
|
||||
|
||||
struct Signup {
|
||||
@@ -195,3 +199,35 @@ fn a_field_keeps_inside_its_cards_padding(cx: &mut TestAppContext) {
|
||||
"a percent min would take the card's outer width"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn fields_fill_a_column_their_block_measures_by_content(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let field =
|
||||
|window: &mut Window, cx: &mut App| window.use_keyed_state("field", cx, TextInput::new);
|
||||
let widths = [
|
||||
narrow_width(cx, "field-root", |_, _| {
|
||||
Select::new("select", [Choice::new("a", "A")]).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "field-root", |_, _| {
|
||||
DatePicker::new("date", None).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "checked-root", move |window, cx| {
|
||||
EmailInput::new(&field(window, cx)).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "mention-root", move |window, cx| {
|
||||
MentionInput::new("mention", &field(window, cx)).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "slider-root", |_, _| {
|
||||
Slider::new("slider", 0.5).into_any_element()
|
||||
}),
|
||||
narrow_width(cx, "number-root", |_, _| {
|
||||
NumberInput::new("number", 42.0).into_any_element()
|
||||
}),
|
||||
];
|
||||
assert_eq!(
|
||||
widths,
|
||||
[px(240.0); 6],
|
||||
"each field spans the card inside its padding"
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,8 @@ mod sidebar;
|
||||
mod split;
|
||||
mod stack;
|
||||
mod surface;
|
||||
#[cfg(all(test, feature = "test-support"))]
|
||||
pub(crate) mod tests;
|
||||
mod viewport;
|
||||
mod workspace;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
use gpui::{
|
||||
AnyElement, App, Context, IntoElement, ParentElement, Pixels, Render, Styled, TestAppContext,
|
||||
Window, div, px,
|
||||
};
|
||||
|
||||
type Build = Box<dyn Fn(&mut Window, &mut App) -> AnyElement>;
|
||||
|
||||
/// One element in a column that a padded block measures by content.
|
||||
struct Narrow(Build);
|
||||
|
||||
impl Render for Narrow {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div().w(px(280.0)).flex().flex_col().child(
|
||||
div().p_5().child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child((self.0)(window, cx))
|
||||
.child("A line long enough to wrap in a narrow card, so the column fills it."),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The width of `selector` in that column; a root that fills it takes all 240px.
|
||||
pub(crate) fn narrow_width(
|
||||
cx: &mut TestAppContext,
|
||||
selector: &'static str,
|
||||
build: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
|
||||
) -> Pixels {
|
||||
let (_, cx) = cx.add_window_view(|_, _| Narrow(Box::new(build)));
|
||||
cx.run_until_parked();
|
||||
cx.debug_bounds(selector).expect(selector).size.width
|
||||
}
|
||||
@@ -200,10 +200,10 @@ impl RenderOnce for Steps {
|
||||
}
|
||||
});
|
||||
div()
|
||||
.debug_selector(|| "steps-root".into())
|
||||
.id(self.id)
|
||||
.flex()
|
||||
.when(vertical, |steps| steps.flex_col())
|
||||
.w_full()
|
||||
.children(steps)
|
||||
}
|
||||
}
|
||||
|
||||
+15
-1
@@ -6,10 +6,11 @@ use gpui::{
|
||||
|
||||
use super::{
|
||||
BackForwardNavigation, Breadcrumb, Crumb, EditorTab, EditorTabs, GoToLine, LoadMore,
|
||||
NavigationMenu, Tabs, Wizard,
|
||||
NavigationMenu, Steps, Tabs, Wizard,
|
||||
};
|
||||
use crate::{
|
||||
forms::{Choice, TextInput},
|
||||
layout::tests::narrow_width,
|
||||
theme::Theme,
|
||||
};
|
||||
|
||||
@@ -456,3 +457,16 @@ fn load_more_asks_once_per_press_and_goes_once_all_show(cx: &mut TestAppContext)
|
||||
"all shown, no button"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn steps_fill_a_column_their_block_measures_by_content(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let width = narrow_width(cx, "steps-root", |_, _| {
|
||||
Steps::new("steps", [Choice::new("a", "A"), Choice::new("b", "B")], 0).into_any_element()
|
||||
});
|
||||
assert_eq!(
|
||||
width,
|
||||
px(240.0),
|
||||
"the steps span the card inside its padding"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user