Improve list item (#61)

This commit is contained in:
Jason Lee 2024-07-23 22:08:21 +08:00 committed by GitHub
parent 1b715c431f
commit b4862082f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 86 additions and 62 deletions

View file

@ -1,5 +1,5 @@
use gpui::{
ClickEvent, IntoElement, ParentElement as _, Render, Styled as _, View, ViewContext,
px, ClickEvent, IntoElement, ParentElement as _, Render, Styled as _, View, ViewContext,
VisualContext as _, WindowContext,
};
@ -267,6 +267,12 @@ impl Render for ButtonStory {
.icon(IconName::Close)
.size(Size::XSmall)
.style(ButtonStyle::Danger),
)
.child(
Button::new("icon-button-9", cx)
.icon(IconName::Heart)
.size(px(24.))
.style(ButtonStyle::Ghost),
),
)
}

View file

@ -91,7 +91,7 @@ impl RenderOnce for CompanyListItem {
.text_color(text_color)
.child(
v_flex()
.gap_2()
.gap_1()
.max_w(px(500.))
.overflow_x_hidden()
.flex_nowrap()
@ -274,32 +274,5 @@ impl Render for ListStory {
.border_color(cx.theme().border)
.child(self.company_list.clone()),
)
// .child(
// div()
// .invisible()
// .flex_1()
// .size_full()
// .border_1()
// .border_color(cx.theme().border)
// .py_1()
// .px_4()
// .rounded_md()
// .when_some(self.selected_company.clone(), |this, company| {
// this.child(
// div()
// .flex_1()
// .gap_2()
// .child(
// h_flex()
// .items_start()
// .justify_between()
// .child(div().text_3xl().mb_6().child(company.name.clone()))
// .child(format!("{:.2}", company.last_done)),
// )
// .child(company.industry.clone())
// .child(company.description.clone()),
// )
// }),
// )
}
}

View file

@ -8,7 +8,7 @@ use gpui::{
};
use ui::{
button::Button,
button::{Button, ButtonStyle},
h_flex,
list::{List, ListDelegate, ListItem},
theme::ActiveTheme as _,
@ -54,7 +54,7 @@ impl ListDelegate for ListItemDeletegate {
})
}
fn render_item(&self, ix: usize, _cx: &mut ViewContext<List<Self>>) -> Option<Self::Item> {
fn render_item(&self, ix: usize, _: &mut ViewContext<List<Self>>) -> Option<Self::Item> {
let selected = ix == self.selected_index;
if let Some(item) = self.matches.get(ix) {
let list_item = ListItem::new(("item", ix))
@ -62,7 +62,24 @@ impl ListDelegate for ListItemDeletegate {
.selected(selected)
.py_1()
.px_3()
.child(item.to_string());
.child(
h_flex()
.items_center()
.justify_between()
.child(item.to_string()),
)
.suffix(|cx| {
Button::new("like", cx)
.icon(IconName::Heart)
.style(ButtonStyle::Ghost)
.size(px(18.))
.on_click(move |_, cx| {
cx.stop_propagation();
cx.prevent_default();
println!("You have clicked like.");
})
});
Some(list_item)
} else {
None

View file

@ -184,6 +184,10 @@ impl RenderOnce for Button {
let style: ButtonStyle = self.style;
let normal_style = style.normal(cx);
let focused = self.focus_handle.is_focused(cx);
let icon_size = match self.size {
Size::Size(v) => Size::Size(v * 0.75),
_ => self.size,
};
self.base
.id(self.id)
@ -191,6 +195,7 @@ impl RenderOnce for Button {
.flex()
.items_center()
.justify_center()
.cursor_pointer()
.when_some(self.width, |this, width| this.w(width))
.when_some(self.height, |this, height| this.h(height))
.map(|this| {
@ -205,6 +210,7 @@ impl RenderOnce for Button {
} else {
// Normal Button
match self.size {
Size::Size(size) => this.p(size * 0.2),
Size::XSmall => this.px_1().py_1().h_5(),
Size::Small => this.px_3().py_2().h_6(),
_ => this.px_4().py_2().h_8(),
@ -239,8 +245,9 @@ impl RenderOnce for Button {
.when_some(
self.on_click.filter(|_| !self.disabled),
|this, on_click| {
this.on_mouse_down(MouseButton::Left, |_, cx| cx.prevent_default())
this.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
.on_click(move |event, cx| {
cx.prevent_default();
cx.stop_propagation();
(on_click)(event, cx)
})
@ -267,7 +274,7 @@ impl RenderOnce for Button {
.text_color(text_color)
.when(!self.loading, |this| {
this.when_some(self.icon, |this, icon| {
this.child(div().text_color(text_color).child(icon.size(self.size)))
this.child(div().text_color(text_color).child(icon.size(icon_size)))
})
})
.when(self.loading, |this| {

View file

@ -111,8 +111,8 @@ pub struct TextInput {
text: SharedString,
history: History,
blink_cursor: Model<BlinkCursor>,
prefix: Option<Box<dyn Fn(&WindowContext) -> AnyElement + 'static>>,
suffix: Option<Box<dyn Fn(&WindowContext) -> AnyElement + 'static>>,
prefix: Option<Box<dyn Fn(&mut WindowContext) -> AnyElement + 'static>>,
suffix: Option<Box<dyn Fn(&mut WindowContext) -> AnyElement + 'static>>,
loading: bool,
placeholder: SharedString,
selected_range: Range<usize>,
@ -237,7 +237,7 @@ impl TextInput {
/// Set the prefix element of the input field, for example a search Icon.
pub fn prefix<F, E>(mut self, builder: F) -> Self
where
F: Fn(&WindowContext) -> E + 'static,
F: Fn(&mut WindowContext) -> E + 'static,
E: IntoElement,
{
self.prefix = Some(Box::new(move |cx| builder(cx).into_any_element()));
@ -247,7 +247,7 @@ impl TextInput {
/// Set the suffix element of the input field, for example a clear button.
pub fn suffix<F, E>(mut self, builder: F) -> Self
where
F: Fn(&WindowContext) -> E + 'static,
F: Fn(&mut WindowContext) -> E + 'static,
E: IntoElement,
{
self.suffix = Some(Box::new(move |cx| builder(cx).into_any_element()));

View file

@ -310,6 +310,7 @@ where
.when_some(self.query_input.clone(), |this, input| {
this.child(
div()
.py_1()
.px_2()
.border_b_1()
.border_color(cx.theme().border)

View file

@ -1,8 +1,9 @@
use gpui::{
div, prelude::FluentBuilder as _, ClickEvent, Div, ElementId, InteractiveElement, IntoElement,
MouseButton, MouseDownEvent, ParentElement, Pixels, RenderOnce, Stateful,
div, prelude::FluentBuilder as _, AnyElement, ClickEvent, Div, ElementId, InteractiveElement,
IntoElement, MouseButton, MouseDownEvent, ParentElement, RenderOnce, Stateful,
StatefulInteractiveElement as _, Styled, WindowContext,
};
use smallvec::SmallVec;
use crate::{h_flex, theme::ActiveTheme, Disableable, Icon, IconName, Selectable};
@ -12,21 +13,23 @@ pub struct ListItem {
disabled: bool,
selected: bool,
check_icon: Option<Icon>,
border_radius: Option<Pixels>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
suffix: Option<Box<dyn Fn(&mut WindowContext) -> AnyElement + 'static>>,
children: SmallVec<[AnyElement; 2]>,
}
impl ListItem {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
base: div().id(id.into()),
base: h_flex().id(id.into()).gap_x_1().py_1().px_2().text_base(),
disabled: false,
selected: false,
on_click: None,
on_secondary_mouse_down: None,
check_icon: None,
border_radius: None,
suffix: None,
children: SmallVec::new(),
}
}
@ -45,8 +48,13 @@ impl ListItem {
self
}
pub fn rounded(mut self, r: impl Into<Pixels>) -> Self {
self.border_radius = Some(r.into());
/// Set the suffix element of the input field, for example a clear button.
pub fn suffix<F, E>(mut self, builder: F) -> Self
where
F: Fn(&mut WindowContext) -> E + 'static,
E: IntoElement,
{
self.suffix = Some(Box::new(move |cx| builder(cx).into_any_element()));
self
}
@ -86,21 +94,17 @@ impl Styled for ListItem {
impl ParentElement for ListItem {
fn extend(&mut self, elements: impl IntoIterator<Item = gpui::AnyElement>) {
self.base.extend(elements)
self.children.extend(elements);
}
}
impl RenderOnce for ListItem {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
h_flex()
.id("list-item")
self.base
.text_color(cx.theme().foreground)
.relative()
.gap_x_2()
.items_center()
.justify_between()
.text_base()
.text_color(cx.theme().foreground)
.when_some(self.border_radius, |this, r| this.rounded(r))
.when_some(self.on_click, |this, on_click| {
if !self.disabled {
this.cursor_pointer().on_click(on_click)
@ -108,9 +112,9 @@ impl RenderOnce for ListItem {
this
}
})
.when(self.selected, |this| this.bg(cx.theme().accent))
.when(self.selected, |this| this.bg(cx.theme().list_item_active))
.when(!self.selected && !self.disabled, |this| {
this.hover(|this| this.bg(cx.theme().accent))
this.hover(|this| this.bg(cx.theme().list_item_hover))
})
// Right click
.when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
@ -120,13 +124,25 @@ impl RenderOnce for ListItem {
this
}
})
.child(self.base.w_full())
.when(self.selected, |this| {
if let Some(icon) = self.check_icon {
this.child(icon.text_color(cx.theme().muted_foreground).mr_2())
} else {
this
}
})
.child(
h_flex()
.w_full()
.items_center()
.justify_between()
.gap_x_1()
.child(div().w_full().children(self.children))
.when_some(self.check_icon, |this, icon| {
this.child(
div()
.w_5()
.items_center()
.justify_center()
.when(self.selected, |this| {
this.child(icon.text_color(cx.theme().muted_foreground))
}),
)
}),
)
.when_some(self.suffix, |this, suffix| this.child(suffix(cx)))
}
}

View file

@ -329,6 +329,8 @@ pub struct Theme {
pub progress_bar: Hsla,
pub slider_bar: Hsla,
pub slider_thumb: Hsla,
pub list_item_active: Hsla,
pub list_item_hover: Hsla,
}
impl Global for Theme {}
@ -394,6 +396,8 @@ impl From<Colors> for Theme {
progress_bar: colors.primary,
slider_bar: colors.primary,
slider_thumb: colors.background,
list_item_active: colors.secondary_active,
list_item_hover: colors.secondary,
}
}
}