Improve list item (#61)
This commit is contained in:
parent
1b715c431f
commit
b4862082f1
8 changed files with 86 additions and 62 deletions
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
// )
|
||||
// }),
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue