diff --git a/crates/core/src/scroll_area.rs b/crates/core/src/scroll_area.rs index afad100..0b5ccb4 100644 --- a/crates/core/src/scroll_area.rs +++ b/crates/core/src/scroll_area.rs @@ -222,6 +222,9 @@ pub(crate) fn thumb_geometry( impl RenderOnce for ScrollArea { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { + let mut pane_style = self.style.clone(); + pane_style.padding = Default::default(); + let mut pane = div() .id(self.id.clone()) .overflow_hidden() @@ -239,7 +242,7 @@ impl RenderOnce for ScrollArea { // Keep wheel events from scrolling an outer scroll area too. cx.stop_propagation(); }) - .refine_style(&self.style); + .refine_style(&pane_style); // Extend with children pane.extend(self.children); @@ -248,17 +251,18 @@ impl RenderOnce for ScrollArea { wrapper.extend(std::iter::once(pane.into_any_element())); if let Some(handle) = self.handle { + let id = self.id.clone(); let autohide = match self.visibility { ScrollbarVisibility::Platform => cx.theme().scrollbar.autohide, ScrollbarVisibility::Always => false, ScrollbarVisibility::Autohide => true, }; - let id = self.id.clone(); + wrapper.extend(std::iter::once( - canvas(|_, _, _| {}, { + canvas(|bounds, _, _| bounds, { let handle = handle.clone(); - move |_, _, window, cx| { - paint_scrollbar(&handle, &id, autohide, window, cx); + move |overlay, _, window, cx| { + paint_scrollbar(&handle, &id, autohide, overlay, window, cx); } }) .absolute() @@ -276,6 +280,7 @@ fn paint_scrollbar( handle: &ScrollHandle, key: &ElementId, autohide: bool, + overlay: Bounds, window: &mut Window, cx: &mut App, ) { @@ -287,10 +292,10 @@ fn paint_scrollbar( let inset = theme.scrollbar.inset; let min_thumb = theme.scrollbar.min_thumb; + let viewport = handle.bounds(); let max_offset = handle.max_offset().y; - let bounds = handle.bounds(); - if max_offset <= Pixels::ZERO || bounds.size.height <= Pixels::ZERO { + if max_offset <= Pixels::ZERO || viewport.size.height <= Pixels::ZERO { return; } @@ -306,21 +311,21 @@ fn paint_scrollbar( _ => 1.0, }; - let track = bounds.size.height - inset * 2.0; + let track = overlay.size.height - inset * 2.0; let (thumb_length, thumb_top) = thumb_geometry( - bounds.size.height, - bounds.size.height + max_offset, + viewport.size.height, + viewport.size.height + max_offset, handle.offset().y, track, min_thumb, ); - let thumb_x = bounds.right() - width - inset; + let thumb_x = overlay.right() - width - inset; let thumb_bounds = Bounds::from_corners( - point(thumb_x, bounds.top() + inset + thumb_top), + point(thumb_x, overlay.top() + inset + thumb_top), point( thumb_x + width, - bounds.top() + inset + thumb_top + thumb_length, + overlay.top() + inset + thumb_top + thumb_length, ), ); @@ -332,7 +337,7 @@ fn paint_scrollbar( window.on_mouse_event({ let state = state.clone(); move |event: &ScrollEvent, phase, window, cx| { - if phase != DispatchPhase::Bubble || !bounds.contains(&event.position) { + if phase != DispatchPhase::Bubble || !overlay.contains(&event.position) { return; } state.touch(window, cx); diff --git a/crates/core/tests/scroll_area.rs b/crates/core/tests/scroll_area.rs index a27533b..3eb7eee 100644 --- a/crates/core/tests/scroll_area.rs +++ b/crates/core/tests/scroll_area.rs @@ -1,7 +1,7 @@ use gpui::{ - Context, IntoElement, Modifiers, ParentElement as _, Render, ScrollDelta, ScrollEvent, - ScrollHandle, SharedString, Styled as _, TestAppContext, VisualTestContext, Window, div, point, - px, + Context, InteractiveElement as _, IntoElement, Modifiers, ParentElement as _, Render, + ScrollDelta, ScrollEvent, ScrollHandle, SharedString, Styled as _, TestAppContext, + VisualTestContext, Window, div, point, px, }; use gpui_native_core::scroll_area::ScrollArea; use gpui_native_theme::Theme; @@ -116,6 +116,53 @@ impl Render for GalleryShell { } } +/// A window-filling scroll area with `p_4`, over one full-width row. +struct PaddedShell { + scroll: ScrollHandle, +} + +impl Render for PaddedShell { + fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { + div() + .size_full() + .debug_selector(|| "padded-root".to_string()) + .child( + ScrollArea::new("padded-scroll") + .handle(self.scroll.clone()) + .size_full() + .p_4() + .child( + div() + .debug_selector(|| "padded-row".to_string()) + .h(px(20.)) + .w_full(), + ), + ) + } +} + +#[test] +fn padding_insets_the_content_exactly_once() { + let mut cx = TestAppContext::single(); + cx.update(Theme::init); + let handle = ScrollHandle::new(); + let handle_for_view = handle.clone(); + let window = cx.add_window(move |_, _| PaddedShell { + scroll: handle_for_view, + }); + let mut visual = VisualTestContext::from_window(window.into(), &cx); + visual.update(|window, cx| { + window.draw(cx).clear(cx); + }); + + let root = visual.debug_bounds("padded-root").expect("root rendered"); + let row = visual.debug_bounds("padded-row").expect("row rendered"); + // p_4 = 16px: the padding reaches only the wrapper, so the content is + // inset once, not once per wrapper and scroll pane. + assert_eq!(row.origin.x - root.origin.x, px(16.)); + assert_eq!(root.size.width - row.size.width, px(32.)); +} + #[test] fn a_gallery_style_window_fill_scroll_area_scrolls_tall_flex_content() { let mut cx = TestAppContext::single(); diff --git a/crates/data/src/sidebar.rs b/crates/data/src/sidebar.rs index 4a82026..2b4d897 100644 --- a/crates/data/src/sidebar.rs +++ b/crates/data/src/sidebar.rs @@ -230,6 +230,7 @@ impl RenderOnce for Sidebar { ScrollArea::new(scroll_area_id) .handle(scroll.read(cx).clone()) .h_full() + .px_1() .gap_0p5() .children(children), ) diff --git a/crates/layout/src/dock.rs b/crates/layout/src/dock.rs index 9b92726..317bb05 100644 --- a/crates/layout/src/dock.rs +++ b/crates/layout/src/dock.rs @@ -1106,14 +1106,17 @@ fn render_pane( let state = state.clone(); let active = pane.active.clone(); let panels: Vec = pane.tabs.iter().map(|tab| tab.panel.clone()).collect(); + move |_, _| { let mut items = Vec::new(); + if let Some(active) = &active { items.push(MenuItem::new("Close").icon(IconRole::Close).handler({ let state = state.clone(); let active = active.clone(); move |_, cx| state.update(cx, |dock, cx| dock.close_panel(&active, cx)) })); + if panels.len() > 1 { items.push(MenuItem::new("Close Others").handler({ let state = state.clone(); @@ -1122,6 +1125,7 @@ fn render_pane( })); } } + if let Some(first) = panels.first() { items.push(MenuItem::new("Close Pane").handler({ let state = state.clone(); @@ -1129,6 +1133,7 @@ fn render_pane( move |_, cx| state.update(cx, |dock, cx| dock.close_pane(&first, cx)) })); } + items } }); diff --git a/crates/layout/src/resizable.rs b/crates/layout/src/resizable.rs index a9dd15f..b7c73ea 100644 --- a/crates/layout/src/resizable.rs +++ b/crates/layout/src/resizable.rs @@ -6,7 +6,6 @@ use gpui::{ InteractiveElement as _, IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, Window, canvas, div, px, }; -use gpui_native_theme::{ActiveTheme as _, Size}; use gpui_native_utils::StyledExt as _; use crate::resize_handle::ResizeHandle; @@ -58,37 +57,30 @@ impl ResizableState { } /// The equal split of the measured extent. - fn equal_split(&self, pane_count: usize, thickness: Pixels) -> Pixels { + fn equal_split(&self, pane_count: usize) -> Pixels { match self.measured { - Some(extent) => (extent - thickness * (pane_count as f32 - 1.0)) / pane_count as f32, + Some(extent) => extent / pane_count as f32, None => px(DEFAULT_PANE_SIZE), } } /// The size pane `ix` renders at: its stored size, or the equal split. - fn pane_size(&self, ix: usize, pane_count: usize, thickness: Pixels) -> Pixels { + fn pane_size(&self, ix: usize, pane_count: usize) -> Pixels { self.sizes .get(ix) .copied() - .unwrap_or_else(|| self.equal_split(pane_count, thickness)) + .unwrap_or_else(|| self.equal_split(pane_count)) } /// Applies a drag at divider `ix` (between pane ix and ix+1). - fn resize( - &mut self, - ix: usize, - delta: Pixels, - mins: &[Pixels], - thickness: Pixels, - cx: &mut Context, - ) { + fn resize(&mut self, ix: usize, delta: Pixels, mins: &[Pixels], cx: &mut Context) { let Some(&floor) = mins.get(ix) else { return; }; let pane_count = mins.len(); - let mut size = self.pane_size(ix, pane_count, thickness) + delta; + let mut size = self.pane_size(ix, pane_count) + delta; size = size.max(floor); if let Some(extent) = self.measured { @@ -97,7 +89,7 @@ impl ResizableState { .enumerate() .filter(|(j, _)| *j != ix) .fold(px(0.), |sum, (_, min)| sum + *min); - let ceiling = extent - thickness * (pane_count as f32 - 1.0) - floors; + let ceiling = extent - floors; size = size.min(ceiling); } @@ -110,14 +102,8 @@ impl ResizableState { } /// Restores pane `ix` to the equal split, as a double-click does. - fn reset_pane( - &mut self, - ix: usize, - pane_count: usize, - thickness: Pixels, - cx: &mut Context, - ) { - let split = self.equal_split(pane_count, thickness); + fn reset_pane(&mut self, ix: usize, pane_count: usize, cx: &mut Context) { + let split = self.equal_split(pane_count); while self.sizes.len() <= ix { self.sizes.push(px(DEFAULT_PANE_SIZE)); } @@ -223,7 +209,6 @@ impl Styled for Resizable { impl RenderOnce for Resizable { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { - let thickness = cx.theme().sizes.resolve(Size::default()).dense_padding_x; let axis = self.axis; let state = self.state.clone(); @@ -232,7 +217,7 @@ impl RenderOnce for Resizable { let debug_selector = self.debug_selector; let sizes: Vec = (0..pane_count) - .map(|ix| state.read(cx).pane_size(ix, pane_count, thickness)) + .map(|ix| state.read(cx).pane_size(ix, pane_count)) .collect(); let mut group = div() @@ -274,18 +259,14 @@ impl RenderOnce for Resizable { let state = state.clone(); let mins = mins.clone(); move |delta, _, cx| { - state.update(cx, |state, cx| { - state.resize(ix, delta, &mins, thickness, cx) - }); + state.update(cx, |state, cx| state.resize(ix, delta, &mins, cx)); } }) .on_reset({ let state = state.clone(); let mins = mins.clone(); move |_, cx| { - state.update(cx, |state, cx| { - state.reset_pane(ix, mins.len(), thickness, cx) - }); + state.update(cx, |state, cx| state.reset_pane(ix, mins.len(), cx)); } }); diff --git a/crates/layout/src/resize_handle.rs b/crates/layout/src/resize_handle.rs index 12bf007..a02d99f 100644 --- a/crates/layout/src/resize_handle.rs +++ b/crates/layout/src/resize_handle.rs @@ -7,8 +7,8 @@ use gpui::{ App, Axis, Bounds, CursorStyle, DispatchPhase, ElementId, HitboxBehavior, InteractiveElement as _, IntoElement, KeyDownEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, RenderOnce, Role, - SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas, div, fill, point, - px, + SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas, deferred, div, + fill, point, px, }; use gpui_native_theme::{ActiveTheme as _, Size}; use gpui_native_utils::{focus_handle, focus_ring}; @@ -100,6 +100,9 @@ impl RenderOnce for ResizeHandle { let border = theme.colors.border; let ring = theme.colors.ring; let thickness = theme.sizes.resolve(Size::default()).dense_padding_x; + // Half the handle overlaps each pane so the hairline lands on their shared boundary, + // leaving the handle no layout footprint of its own. + let straddle = -thickness / 2.0; let label = self .a11y_label @@ -114,6 +117,7 @@ impl RenderOnce for ResizeHandle { }) .clone() }); + let focus = focus_handle(&self.id, window, cx); let dragging = drag.read(cx).dragging; let axis = self.axis; @@ -126,153 +130,167 @@ impl RenderOnce for ResizeHandle { let on_reset = self.on_reset; - div() - .id(self.id.clone()) - .debug_selector(|| "resize-handle".to_string()) - .role(Role::Splitter) - .aria_label(label) - .flex_none() - .when(axis == Axis::Horizontal, |handle| { - handle.w(thickness).h_full() - }) - .when(axis == Axis::Vertical, |handle| { - handle.w_full().h(thickness) - }) - .when(self.disabled, |handle| handle.opacity(0.5)) - .when(!self.disabled, |handle| { - handle - .track_focus(&focus) - .focusable() - .tab_stop(true) - .focus_visible(|style| style.shadow(focus_ring(ring))) - .on_mouse_down(MouseButton::Left, { - let drag = drag.clone(); - let focus = focus.clone(); - move |event: &MouseDownEvent, window, cx| { - window.focus(&focus, cx); - cx.stop_propagation(); - if event.click_count >= 2 { + deferred( + div() + .id(self.id.clone()) + .debug_selector(|| "resize-handle".to_string()) + .role(Role::Splitter) + .aria_label(label) + .flex_none() + .when(axis == Axis::Horizontal, |handle| { + handle.w(thickness).h_full().mx(straddle) + }) + .when(axis == Axis::Vertical, |handle| { + handle.w_full().h(thickness).my(straddle) + }) + .when(self.disabled, |handle| handle.opacity(0.5)) + .when(!self.disabled, |handle| { + handle + .track_focus(&focus) + .focusable() + .tab_stop(true) + .focus_visible(|style| style.shadow(focus_ring(ring))) + .on_mouse_down(MouseButton::Left, { + let drag = drag.clone(); + let focus = focus.clone(); + + move |event: &MouseDownEvent, window, cx| { + window.focus(&focus, cx); + cx.stop_propagation(); + + if event.click_count >= 2 { + drag.update(cx, |drag, cx| { + drag.dragging = false; + cx.notify(); + }); + if let Some(on_reset) = on_reset.clone() { + on_reset(window, cx); + } + return; + } + drag.update(cx, |drag, cx| { - drag.dragging = false; + drag.dragging = true; + drag.last = event.position; cx.notify(); }); - if let Some(on_reset) = on_reset.clone() { - on_reset(window, cx); - } - return; } - drag.update(cx, |drag, cx| { - drag.dragging = true; - drag.last = event.position; - cx.notify(); - }); - } - }) - .on_key_down({ - let on_resize = self.on_resize.clone(); - move |event: &KeyDownEvent, window, cx| { - let delta = match (axis, event.keystroke.key.as_str()) { - (Axis::Horizontal, "left") => Some(px(-KEYBOARD_STEP)), - (Axis::Horizontal, "right") => Some(px(KEYBOARD_STEP)), - (Axis::Vertical, "up") => Some(px(-KEYBOARD_STEP)), - (Axis::Vertical, "down") => Some(px(KEYBOARD_STEP)), - _ => None, - }; - if let Some(delta) = delta { - cx.stop_propagation(); - if let Some(on_resize) = on_resize.clone() { - on_resize(delta, window, cx); - } - } - } - }) - }) - .child( - canvas( - { - move |bounds, window, _| { - let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal); - (hitbox, bounds) - } - }, - { - let drag = drag.clone(); - let on_resize = self.on_resize.clone(); - move |_, (hitbox, bounds), window, _cx| { - let hovered = hitbox.is_hovered(window); - if !disabled { - window.set_cursor_style(cursor, &hitbox); - } - - // The hairline highlights while hovered or dragged. - let line_color = if !disabled && (dragging || hovered) { - ring - } else { - border - }; - let line = match axis { - Axis::Horizontal => { - let x = bounds.center().x - px(0.5); - Bounds::from_corners( - point(x, bounds.top()), - point(x + px(1.), bounds.bottom()), - ) - } - Axis::Vertical => { - let y = bounds.center().y - px(0.5); - Bounds::from_corners( - point(bounds.left(), y), - point(bounds.right(), y + px(1.)), - ) - } - }; - window.paint_quad(fill(line, line_color)); - - window.on_mouse_event({ - let drag = drag.clone(); - let on_resize = on_resize.clone(); - move |event: &MouseMoveEvent, phase, window, cx| { - if phase != DispatchPhase::Bubble { - return; - } - let (dragging, last) = { - let drag = drag.read(cx); - (drag.dragging, drag.last) - }; - if !dragging { - return; - } - let delta = match axis { - Axis::Horizontal => event.position.x - last.x, - Axis::Vertical => event.position.y - last.y, - }; - drag.update(cx, |drag, _| drag.last = event.position); + }) + .on_key_down({ + let on_resize = self.on_resize.clone(); + move |event: &KeyDownEvent, window, cx| { + let delta = match (axis, event.keystroke.key.as_str()) { + (Axis::Horizontal, "left") => Some(px(-KEYBOARD_STEP)), + (Axis::Horizontal, "right") => Some(px(KEYBOARD_STEP)), + (Axis::Vertical, "up") => Some(px(-KEYBOARD_STEP)), + (Axis::Vertical, "down") => Some(px(KEYBOARD_STEP)), + _ => None, + }; + if let Some(delta) = delta { + cx.stop_propagation(); if let Some(on_resize) = on_resize.clone() { on_resize(delta, window, cx); } } - }); - window.on_mouse_event({ - let drag = drag.clone(); - move |event: &MouseUpEvent, phase, _, cx| { - if phase != DispatchPhase::Bubble - || event.button != MouseButton::Left - { - return; - } - if drag.read(cx).dragging { - drag.update(cx, |drag, cx| { - drag.dragging = false; - cx.notify(); - }); - } + } + }) + }) + .child( + canvas( + { + move |bounds, window, _| { + let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal); + (hitbox, bounds) + } + }, + { + let drag = drag.clone(); + let on_resize = self.on_resize.clone(); + move |_, (hitbox, bounds), window, _cx| { + let hovered = hitbox.is_hovered(window); + if !disabled { + window.set_cursor_style(cursor, &hitbox); } - }); - } - }, - ) - .absolute() - .inset_0(), - ) + + // The hairline highlights while hovered or dragged. + let line_color = if !disabled && (dragging || hovered) { + ring + } else { + border + }; + + let line = match axis { + Axis::Horizontal => { + let x = bounds.center().x - px(0.5); + Bounds::from_corners( + point(x, bounds.top()), + point(x + px(1.), bounds.bottom()), + ) + } + Axis::Vertical => { + let y = bounds.center().y - px(0.5); + Bounds::from_corners( + point(bounds.left(), y), + point(bounds.right(), y + px(1.)), + ) + } + }; + + window.paint_quad(fill(line, line_color)); + + window.on_mouse_event({ + let drag = drag.clone(); + let on_resize = on_resize.clone(); + move |event: &MouseMoveEvent, phase, window, cx| { + if phase != DispatchPhase::Bubble { + return; + } + + let (dragging, last) = { + let drag = drag.read(cx); + (drag.dragging, drag.last) + }; + + if !dragging { + return; + } + + let delta = match axis { + Axis::Horizontal => event.position.x - last.x, + Axis::Vertical => event.position.y - last.y, + }; + + drag.update(cx, |drag, _| drag.last = event.position); + + if let Some(on_resize) = on_resize.clone() { + on_resize(delta, window, cx); + } + } + }); + + window.on_mouse_event({ + let drag = drag.clone(); + move |event: &MouseUpEvent, phase, _, cx| { + if phase != DispatchPhase::Bubble + || event.button != MouseButton::Left + { + return; + } + + if drag.read(cx).dragging { + drag.update(cx, |drag, cx| { + drag.dragging = false; + cx.notify(); + }); + } + } + }); + } + }, + ) + .absolute() + .inset_0(), + ), + ) } } diff --git a/crates/layout/tests/dock.rs b/crates/layout/tests/dock.rs index 2ebb174..270162e 100644 --- a/crates/layout/tests/dock.rs +++ b/crates/layout/tests/dock.rs @@ -274,8 +274,9 @@ fn splitting_at_a_target_pane_places_the_new_pane_there() { let moved = bounds(&mut visual, "test-dock-pane-3").expect("the new pane missing"); let target = bounds(&mut visual, "test-dock-pane-1").expect("target pane missing"); + // The flex split may round the shared seam by a hair. assert!( - moved.right() <= target.left(), + moved.right() <= target.left() + px(1.), "the new pane must sit left of the anchored pane" ); assert!(bounds(&mut visual, "test-dock-pane-0").is_none()); diff --git a/crates/layout/tests/resizable.rs b/crates/layout/tests/resizable.rs index aee1461..e1c6c59 100644 --- a/crates/layout/tests/resizable.rs +++ b/crates/layout/tests/resizable.rs @@ -4,7 +4,7 @@ use gpui::{ VisualTestContext, Window, div, point, px, }; use gpui_native_layout::resizable::{Resizable, ResizablePane, ResizableState}; -use gpui_native_theme::{ActiveTheme as _, Size, Theme}; +use gpui_native_theme::Theme; const GROUP_WIDTH: f32 = 200.0; const GROUP_HEIGHT: f32 = 300.0; @@ -84,20 +84,18 @@ fn a_drag_moves_the_pane_and_its_rendered_height() { #[test] fn a_drag_stops_where_the_remainder_keeps_its_floor() { let (mut cx, state, mut visual) = split_window([px(100.)]); - let thickness = cx.update(|cx| cx.theme().sizes.resolve(Size::default()).dense_padding_x); let floor = ResizablePane::DEFAULT_MIN; drag_divider(&mut visual, px(1000.)); state.update(&mut cx, |state, _| { - assert_eq!(state.sizes(), &[px(GROUP_HEIGHT) - thickness - floor]); + assert_eq!(state.sizes(), &[px(GROUP_HEIGHT) - floor]); }); } #[test] fn a_double_click_resets_the_pane_to_the_equal_split() { let (mut cx, state, mut visual) = split_window([px(240.)]); - let thickness = cx.update(|cx| cx.theme().sizes.resolve(Size::default()).dense_padding_x); let start = divider_center(&mut visual); visual.simulate_event(MouseDownEvent { @@ -109,10 +107,7 @@ fn a_double_click_resets_the_pane_to_the_equal_split() { }); state.update(&mut cx, |state, _| { - assert_eq!( - state.sizes(), - &[px((GROUP_HEIGHT - thickness.as_f32()) / 2.)] - ); + assert_eq!(state.sizes(), &[px(GROUP_HEIGHT / 2.)]); }); } @@ -154,12 +149,8 @@ fn the_panes_and_the_divider_fill_the_group_exactly() { let divider = visual .debug_bounds("resize-handle") .expect("divider rendered"); - assert_eq!( - first.size.height + divider.size.height + second.size.height, - px(GROUP_HEIGHT) - ); - assert_eq!( - second.size.height, - px(GROUP_HEIGHT) - first.size.height - divider.size.height - ); + // The flex split may round the panes' shared edges by a hair. + assert!((first.size.height + second.size.height - px(GROUP_HEIGHT)).abs() <= px(1.)); + // The divider owns no layout space: it straddles the seam between the panes. + assert!((divider.center().y - first.bottom()).abs() <= px(1.)); } diff --git a/crates/layout/tests/resize_handle.rs b/crates/layout/tests/resize_handle.rs index 88e9dd2..860f4d9 100644 --- a/crates/layout/tests/resize_handle.rs +++ b/crates/layout/tests/resize_handle.rs @@ -1,6 +1,7 @@ use std::cell::RefCell; use std::rc::Rc; +use gpui::prelude::FluentBuilder as _; use gpui::{ Axis, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Render, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px, @@ -26,11 +27,17 @@ impl Render for Harness { ) -> impl gpui::IntoElement { let deltas = self.deltas.clone(); let resets = self.resets.clone(); + let horizontal = self.axis == Axis::Horizontal; div() .size_full() .flex() - .flex_row() - .child(div().w(px(120.)).h_full()) + .when(horizontal, |layout| layout.flex_row()) + .when(!horizontal, |layout| layout.flex_col()) + .child(if horizontal { + div().w(px(120.)).h_full() + } else { + div().h(px(120.)).w_full() + }) .child( ResizeHandle::new("test-handle") .axis(self.axis) @@ -38,7 +45,11 @@ impl Render for Harness { .on_resize(move |delta, _, _| deltas.borrow_mut().push(delta)) .on_reset(move |_, _| *resets.borrow_mut() += 1), ) - .child(div().flex_1().h_full()) + .child(if horizontal { + div().flex_1().h_full() + } else { + div().flex_1().w_full() + }) } }