Forms: an option list stays inside the window; media ratios are checked in one place
The floating list under a Select, Combobox or MultiSelect was at least its trigger's width with no ceiling, so a long choice ran it past the window and its rows never ellipsized. It now stops at the window's width less the window margin on each side, so gpui snaps it inside. The camera preview and the model viewer check their ratio through primitives::checked_ratio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7f1cfe8c3d
commit
9872adc5e9
6 files changed
+37
-7
No files matched your search
@@ -286,10 +286,12 @@ impl Popup<'_> {
|
||||
})
|
||||
});
|
||||
let list = surface(self.id, cx)
|
||||
.debug_selector(|| "option-list".into())
|
||||
.min_w(theme.tooltip_max_width())
|
||||
.when(anchor.size.width > Pixels::ZERO, |list| {
|
||||
list.min_w(anchor.size.width)
|
||||
})
|
||||
.max_w(window.viewport_size().width - theme.window_margin() * 2.0)
|
||||
.max_h(theme.list_max_height())
|
||||
.overflow_y_scroll()
|
||||
.when_some(self.scroll, |list, handle| list.track_scroll(handle))
|
||||
|
||||
@@ -446,3 +446,32 @@ fn an_open_select_with_nothing_to_pick_fails(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
cx.add_window_view(|_, _| Empty(false));
|
||||
}
|
||||
|
||||
/// A select whose one choice runs long.
|
||||
struct Named;
|
||||
|
||||
impl Render for Named {
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
let long = Choice::new("long", "A very long microphone name that runs past the box");
|
||||
div()
|
||||
.w(px(200.0))
|
||||
.child(Select::new("long", [long, Choice::new("b", "B")]))
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_long_choice_keeps_its_list_inside_the_window(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let (_, cx) = cx.add_window_view(|_, _| Named);
|
||||
cx.simulate_resize(gpui::size(px(280.0), px(600.0)));
|
||||
cx.run_until_parked();
|
||||
cx.update(|window, _| window.focus_next());
|
||||
cx.simulate_keystrokes("enter");
|
||||
release("enter", cx);
|
||||
cx.run_until_parked();
|
||||
let list = cx.debug_bounds("option-list").expect("the list opens");
|
||||
assert!(
|
||||
list.left() >= px(0.0) && list.right() <= px(280.0),
|
||||
"{list:?}"
|
||||
);
|
||||
}
|
||||
+2
-3
@@ -7,7 +7,7 @@ use gpui::{
|
||||
|
||||
use crate::{
|
||||
forms::{Choice, OnValue, Select},
|
||||
primitives::{Icon, IconName, framed},
|
||||
primitives::{Icon, IconName, checked_ratio, framed},
|
||||
theme::{ActiveTheme, IconSize, Radius, TextSize},
|
||||
typography::Ellipsis,
|
||||
};
|
||||
@@ -24,10 +24,9 @@ pub struct CameraPreview {
|
||||
impl CameraPreview {
|
||||
/// `ratio` is the camera's width over its height.
|
||||
pub fn new(id: impl Into<ElementId>, ratio: f32) -> Self {
|
||||
assert!(ratio > 0.0, "a camera's shape of {ratio}");
|
||||
Self {
|
||||
id: id.into(),
|
||||
ratio,
|
||||
ratio: checked_ratio(ratio),
|
||||
frame: None,
|
||||
name: None,
|
||||
}
|
||||
|
||||
+2
-3
@@ -11,7 +11,7 @@ use crate::{
|
||||
layout::seeded::use_seeded,
|
||||
motion::Spinner,
|
||||
overlays::media_button,
|
||||
primitives::{FocusRing, IconName, framed, tab_stop},
|
||||
primitives::{FocusRing, IconName, checked_ratio, framed, tab_stop},
|
||||
theme::{ActiveTheme, ControlSize, Radius},
|
||||
};
|
||||
|
||||
@@ -70,7 +70,6 @@ pub struct ModelViewer {
|
||||
impl ModelViewer {
|
||||
/// `ratio` is the view's width over its height.
|
||||
pub fn new(id: impl Into<ElementId>, ratio: f32, orbit: Orbit) -> Self {
|
||||
assert!(ratio > 0.0, "a view's shape of {ratio}");
|
||||
assert!(
|
||||
(NEAR..=FAR).contains(&orbit.distance),
|
||||
"a distance of {}",
|
||||
@@ -78,7 +77,7 @@ impl ModelViewer {
|
||||
);
|
||||
Self {
|
||||
id: id.into(),
|
||||
ratio,
|
||||
ratio: checked_ratio(ratio),
|
||||
frame: None,
|
||||
orbit,
|
||||
on_orbit: None,
|
||||
|
||||
Reference in new issue
Block a user