Lists: ListItem::editor draws a field in the title's place, for a row renamed in place; the title stays the row's name
This commit is contained in:
4 files changed
+66
-4
No files matched your search
@@ -276,3 +276,4 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
|
|||||||
- 2026-10-05: `SelectableList` is a list in the AX tree, named by `label`; each `ListItem` is a list item with its title as name, its description, its selection, and, in a selectable list, its place and count (`ListItem::place`). Before, the rows had no role and were missing from the AX tree.
|
- 2026-10-05: `SelectableList` is a list in the AX tree, named by `label`; each `ListItem` is a list item with its title as name, its description, its selection, and, in a selectable list, its place and count (`ListItem::place`). Before, the rows had no role and were missing from the AX tree.
|
||||||
- 2026-10-05: `NumberInput::blank` shows a placeholder, not a number, as for a mixed selection; Enter or blur on the empty field commits nothing, a typed number commits as usual. Before, a mixed field held a real number the owner had to invent, and Enter sent it. `ScrubInput` moved to `number/scrub.rs`.
|
- 2026-10-05: `NumberInput::blank` shows a placeholder, not a number, as for a mixed selection; Enter or blur on the empty field commits nothing, a typed number commits as usual. Before, a mixed field held a real number the owner had to invent, and Enter sent it. `ScrubInput` moved to `number/scrub.rs`.
|
||||||
- 2026-10-07: gpui comes from crates.io's `gpui-pre` at the exact snapshot GPUI Kit pins, not from Zed's git. Two sources build two gpui crates whose types do not mix, so Ely and Kit could not share a window. The code is still Zed's; the publisher is not. This replaces the 2026-09-28 choice of Zed's git, and the pin moves when Kit's does.
|
- 2026-10-07: gpui comes from crates.io's `gpui-pre` at the exact snapshot GPUI Kit pins, not from Zed's git. Two sources build two gpui crates whose types do not mix, so Ely and Kit could not share a window. The code is still Zed's; the publisher is not. This replaces the 2026-09-28 choice of Zed's git, and the pin moves when Kit's does.
|
||||||
|
- 2026-10-19: `ListItem::editor` draws a field in the title's place, for a row renamed in place, as ELYTerminal's session rows rename; the title stays the row's name in the AX tree, and the row's press, selection and description stay as they were.
|
||||||
+15
-4
@@ -19,6 +19,8 @@ type OnClick = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>;
|
|||||||
pub struct ListItem {
|
pub struct ListItem {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
title: SharedString,
|
title: SharedString,
|
||||||
|
/// A field drawn in the title's place.
|
||||||
|
editor: Option<AnyElement>,
|
||||||
description: Option<SharedString>,
|
description: Option<SharedString>,
|
||||||
detail: Option<SharedString>,
|
detail: Option<SharedString>,
|
||||||
leading: Option<AnyElement>,
|
leading: Option<AnyElement>,
|
||||||
@@ -39,6 +41,7 @@ impl ListItem {
|
|||||||
Self {
|
Self {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
title: title.into(),
|
title: title.into(),
|
||||||
|
editor: None,
|
||||||
description: None,
|
description: None,
|
||||||
detail: None,
|
detail: None,
|
||||||
leading: None,
|
leading: None,
|
||||||
@@ -72,6 +75,12 @@ impl ListItem {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A field in place of the title, as a row renamed in place has it; the title stays the row's name.
|
||||||
|
pub fn editor(mut self, editor: impl IntoElement) -> Self {
|
||||||
|
self.editor = Some(editor.into_any_element());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// A second line, quieter than the title.
|
/// A second line, quieter than the title.
|
||||||
pub fn description(mut self, text: impl Into<SharedString>) -> Self {
|
pub fn description(mut self, text: impl Into<SharedString>) -> Self {
|
||||||
self.description = Some(text.into());
|
self.description = Some(text.into());
|
||||||
@@ -191,8 +200,9 @@ impl RenderOnce for ListItem {
|
|||||||
.min_w_0()
|
.min_w_0()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.child(
|
.child(match self.editor {
|
||||||
line(self.title)
|
Some(editor) => div().min_w_0().child(editor).into_any_element(),
|
||||||
|
None => line(self.title)
|
||||||
.text_size(theme.text_size(TextSize::Base))
|
.text_size(theme.text_size(TextSize::Base))
|
||||||
.when(self.strong, |title| title.font_weight(FontWeight::SEMIBOLD))
|
.when(self.strong, |title| title.font_weight(FontWeight::SEMIBOLD))
|
||||||
.when(self.struck, |title| title.line_through())
|
.when(self.struck, |title| title.line_through())
|
||||||
@@ -200,8 +210,9 @@ impl RenderOnce for ListItem {
|
|||||||
colors.fg_muted
|
colors.fg_muted
|
||||||
} else {
|
} else {
|
||||||
colors.fg
|
colors.fg
|
||||||
}),
|
})
|
||||||
)
|
.into_any_element(),
|
||||||
|
})
|
||||||
.children(self.description.map(|text| {
|
.children(self.description.map(|text| {
|
||||||
line(text.clone())
|
line(text.clone())
|
||||||
.debug_selector(|| format!("item-description-{text}"))
|
.debug_selector(|| format!("item-description-{text}"))
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
use gpui::{
|
||||||
|
Context, InteractiveElement, IntoElement, ParentElement, Render, Styled, TestAppContext,
|
||||||
|
Window, div, px,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{settle, setup};
|
||||||
|
use crate::lists::{List, ListItem};
|
||||||
|
|
||||||
|
/// Two rows, the first renamed in place.
|
||||||
|
struct Renaming;
|
||||||
|
|
||||||
|
impl Render for Renaming {
|
||||||
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let field = div()
|
||||||
|
.debug_selector(|| "row-editor".into())
|
||||||
|
.h(px(12.0))
|
||||||
|
.w_full();
|
||||||
|
div().w(px(240.0)).child(
|
||||||
|
List::new()
|
||||||
|
.child(
|
||||||
|
ListItem::new("renamed", "Old name")
|
||||||
|
.description("~/work")
|
||||||
|
.selected(true)
|
||||||
|
.editor(field),
|
||||||
|
)
|
||||||
|
.child(ListItem::new("other", "Other").description("~/other")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn an_editor_takes_the_title_s_place_in_its_row(cx: &mut TestAppContext) {
|
||||||
|
setup(cx);
|
||||||
|
let (_, cx) = cx.add_window_view(|_, _| Renaming);
|
||||||
|
settle(cx);
|
||||||
|
let row = cx
|
||||||
|
.debug_bounds("item-selected-Old name")
|
||||||
|
.expect("the renamed row");
|
||||||
|
let field = cx.debug_bounds("row-editor").expect("its editor");
|
||||||
|
let line = cx
|
||||||
|
.debug_bounds("item-description-~/work")
|
||||||
|
.expect("its description");
|
||||||
|
assert!(row.contains(&field.center()), "{field:?} in {row:?}");
|
||||||
|
assert!(
|
||||||
|
field.bottom() <= line.top(),
|
||||||
|
"the editor stands over the description"
|
||||||
|
);
|
||||||
|
assert!(cx.debug_bounds("item-description-~/other").is_some());
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ use crate::{
|
|||||||
theme::Theme,
|
theme::Theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod editor;
|
||||||
mod keys;
|
mod keys;
|
||||||
|
|
||||||
fn setup(cx: &mut TestAppContext) {
|
fn setup(cx: &mut TestAppContext) {
|
||||||
|
|||||||
Reference in new issue
Block a user