From e03a0d4f4ee5270a16b1766be42b5e67b850e777 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 23 Jan 2025 18:05:32 +0800 Subject: [PATCH] chore: Fix Switch, Radio, Checkbox to use inline layout. (#569) --- crates/story/src/form_story.rs | 16 ++++ crates/ui/src/checkbox.rs | 117 ++++++++++++------------- crates/ui/src/radio.rs | 97 +++++++++++---------- crates/ui/src/switch.rs | 151 +++++++++++++++++---------------- 4 files changed, 203 insertions(+), 178 deletions(-) diff --git a/crates/story/src/form_story.rs b/crates/story/src/form_story.rs index 0ba2d093..2f89af39 100644 --- a/crates/story/src/form_story.rs +++ b/crates/story/src/form_story.rs @@ -4,6 +4,7 @@ use gpui::{ }; use ui::{ button::{Button, ButtonGroup}, + checkbox::Checkbox, date_picker::DatePicker, divider::Divider, form::{form_field, v_form}, @@ -196,6 +197,21 @@ impl Render for FormStory { cx.notify(); })), ), + ) + .child( + form_field().child( + Checkbox::new("use-vertical-layout") + .label("Vertical layout") + .checked(self.layout.is_vertical()) + .on_click(cx.listener(|this, checked: &bool, cx| { + this.layout = if *checked { + Axis::Vertical + } else { + Axis::Horizontal + }; + cx.notify(); + })), + ), ), ) } diff --git a/crates/ui/src/checkbox.rs b/crates/ui/src/checkbox.rs index debe093e..04fdf54f 100644 --- a/crates/ui/src/checkbox.rs +++ b/crates/ui/src/checkbox.rs @@ -70,62 +70,65 @@ impl RenderOnce for Checkbox { (cx.theme().primary, cx.theme().primary_foreground) }; - h_flex() - .id(self.id) - .gap_2() - .items_center() - .line_height(relative(1.)) - .child( - v_flex() - .relative() - .border_1() - .border_color(color) - .rounded_sm() - .size_4() - .flex_shrink_0() - .map(|this| match self.checked { - false => this.bg(cx.theme().transparent), - _ => this.bg(color), - }) - .child( - svg() - .absolute() - .top_px() - .left_px() - .size_3() - .text_color(icon_color) - .map(|this| match self.checked { - true => this.path(IconName::Check.path()), - _ => this, - }), - ), - ) - .map(|this| { - if let Some(label) = self.label { - this.text_color(cx.theme().foreground).child( - div() - .w_full() - .overflow_x_hidden() - .text_ellipsis() - .line_height(relative(1.)) - .child(label), - ) - } else { - this - } - }) - .when(self.disabled, |this| { - this.cursor_not_allowed() - .text_color(cx.theme().muted_foreground) - }) - .when_some( - self.on_click.filter(|_| !self.disabled), - |this, on_click| { - this.on_click(move |_, cx| { - let checked = !self.checked; - on_click(&checked, cx); - }) - }, - ) + // wrap a flex to patch for let Checkbox display inline + div().flex().child( + h_flex() + .id(self.id) + .gap_2() + .items_center() + .line_height(relative(1.)) + .child( + v_flex() + .relative() + .border_1() + .border_color(color) + .rounded_sm() + .size_4() + .flex_shrink_0() + .map(|this| match self.checked { + false => this.bg(cx.theme().transparent), + _ => this.bg(color), + }) + .child( + svg() + .absolute() + .top_px() + .left_px() + .size_3() + .text_color(icon_color) + .map(|this| match self.checked { + true => this.path(IconName::Check.path()), + _ => this, + }), + ), + ) + .map(|this| { + if let Some(label) = self.label { + this.text_color(cx.theme().foreground).child( + div() + .w_full() + .overflow_x_hidden() + .text_ellipsis() + .line_height(relative(1.)) + .child(label), + ) + } else { + this + } + }) + .when(self.disabled, |this| { + this.cursor_not_allowed() + .text_color(cx.theme().muted_foreground) + }) + .when_some( + self.on_click.filter(|_| !self.disabled), + |this, on_click| { + this.on_click(move |_, cx| { + let checked = !self.checked; + on_click(&checked, cx); + }) + }, + ), + ) } } diff --git a/crates/ui/src/radio.rs b/crates/ui/src/radio.rs index 312f620e..b8a43bfc 100644 --- a/crates/ui/src/radio.rs +++ b/crates/ui/src/radio.rs @@ -56,54 +56,57 @@ impl RenderOnce for Radio { cx.theme().primary }; - h_flex() - .id(self.id) - .gap_x_2() - .text_color(cx.theme().foreground) - .items_center() - .line_height(relative(1.)) - .child( - div() - .relative() - .size_4() - .flex_shrink_0() - .rounded_full() - .border_1() - .border_color(color) - .when(self.checked, |this| this.bg(color)) - .child( - svg() - .absolute() - .top_px() - .left_px() - .size_3() - .text_color(color) - .when(self.checked, |this| { - this.text_color(cx.theme().primary_foreground) - }) - .map(|this| match self.checked { - true => this.path(IconName::Check.path()), - false => this, - }), - ), - ) - .when_some(self.label, |this, label| { - this.child( + // wrap a flex to patch for let Radio display inline + div().flex().child( + h_flex() + .id(self.id) + .gap_x_2() + .text_color(cx.theme().foreground) + .items_center() + .line_height(relative(1.)) + .child( div() - .size_full() - .overflow_x_hidden() - .text_ellipsis() - .line_height(relative(1.)) - .child(label), + .relative() + .size_4() + .flex_shrink_0() + .rounded_full() + .border_1() + .border_color(color) + .when(self.checked, |this| this.bg(color)) + .child( + svg() + .absolute() + .top_px() + .left_px() + .size_3() + .text_color(color) + .when(self.checked, |this| { + this.text_color(cx.theme().primary_foreground) + }) + .map(|this| match self.checked { + true => this.path(IconName::Check.path()), + false => this, + }), + ), ) - }) - .when_some( - self.on_click.filter(|_| !self.disabled), - |this, on_click| { - this.on_click(move |_event, cx| { - on_click(&!self.checked, cx); - }) - }, - ) + .when_some(self.label, |this, label| { + this.child( + div() + .size_full() + .overflow_x_hidden() + .text_ellipsis() + .line_height(relative(1.)) + .child(label), + ) + }) + .when_some( + self.on_click.filter(|_| !self.disabled), + |this, on_click| { + this.on_click(move |_event, cx| { + on_click(&!self.checked, cx); + }) + }, + ), + ) } } diff --git a/crates/ui/src/switch.rs b/crates/ui/src/switch.rs index 4f1daad3..4acfdc10 100644 --- a/crates/ui/src/switch.rs +++ b/crates/ui/src/switch.rs @@ -122,88 +122,91 @@ impl Element for Switch { }; let inset = px(2.); - let mut element = h_flex() - .id(self.id.clone()) - .items_center() - .gap_2() - .when(self.label_side.is_left(), |this| this.flex_row_reverse()) + let mut element = div() + .flex() .child( - // Switch Bar - div() + h_flex() .id(self.id.clone()) - .w(bg_width) - .h(bg_height) - .rounded(bg_height / 2.) - .flex() .items_center() - .border(inset) - .border_color(theme.transparent) - .bg(bg) - .when(!self.disabled, |this| this.cursor_pointer()) + .gap_2() + .when(self.label_side.is_left(), |this| this.flex_row_reverse()) .child( - // Switch Toggle + // Switch Bar div() - .rounded_full() - .bg(toggle_bg) - .size(bar_width) - .map(|this| { - let prev_checked = state.prev_checked.clone(); - if !self.disabled - && prev_checked - .borrow() - .map_or(false, |prev| prev != checked) - { - let dur = Duration::from_secs_f64(0.15); - cx.spawn(|cx| async move { - cx.background_executor().timer(dur).await; + .id(self.id.clone()) + .w(bg_width) + .h(bg_height) + .rounded(bg_height / 2.) + .flex() + .items_center() + .border(inset) + .border_color(theme.transparent) + .bg(bg) + .when(!self.disabled, |this| this.cursor_pointer()) + .child( + // Switch Toggle + div().rounded_full().bg(toggle_bg).size(bar_width).map( + |this| { + let prev_checked = state.prev_checked.clone(); + if !self.disabled + && prev_checked + .borrow() + .map_or(false, |prev| prev != checked) + { + let dur = Duration::from_secs_f64(0.15); + cx.spawn(|cx| async move { + cx.background_executor().timer(dur).await; - *prev_checked.borrow_mut() = Some(checked); - }) - .detach(); - this.with_animation( - ElementId::NamedInteger( - "move".into(), - checked as usize, - ), - Animation::new(dur), - move |this, delta| { + *prev_checked.borrow_mut() = Some(checked); + }) + .detach(); + this.with_animation( + ElementId::NamedInteger( + "move".into(), + checked as usize, + ), + Animation::new(dur), + move |this, delta| { + let max_x = + bg_width - bar_width - inset * 2; + let x = if checked { + max_x * delta + } else { + max_x - max_x * delta + }; + this.left(x) + }, + ) + .into_any_element() + } else { let max_x = bg_width - bar_width - inset * 2; - let x = if checked { - max_x * delta - } else { - max_x - max_x * delta - }; - this.left(x) - }, - ) - .into_any_element() - } else { - let max_x = bg_width - bar_width - inset * 2; - let x = if checked { max_x } else { px(0.) }; - this.left(x).into_any_element() - } - }), - ), - ) - .when_some(self.label.clone(), |this, label| { - this.child(div().child(label).map(|this| match self.size { - Size::XSmall | Size::Small => this.text_sm(), - _ => this.text_base(), - })) - }) - .when_some( - on_click - .as_ref() - .map(|c| c.clone()) - .filter(|_| !self.disabled), - |this, on_click| { - let prev_checked = state.prev_checked.clone(); - this.on_mouse_down(gpui::MouseButton::Left, move |_, cx| { - cx.stop_propagation(); - *prev_checked.borrow_mut() = Some(checked); - on_click(&!checked, cx); + let x = if checked { max_x } else { px(0.) }; + this.left(x).into_any_element() + } + }, + ), + ), + ) + .when_some(self.label.clone(), |this, label| { + this.child(div().child(label).map(|this| match self.size { + Size::XSmall | Size::Small => this.text_sm(), + _ => this.text_base(), + })) }) - }, + .when_some( + on_click + .as_ref() + .map(|c| c.clone()) + .filter(|_| !self.disabled), + |this, on_click| { + let prev_checked = state.prev_checked.clone(); + this.on_mouse_down(gpui::MouseButton::Left, move |_, cx| { + cx.stop_propagation(); + *prev_checked.borrow_mut() = Some(checked); + on_click(&!checked, cx); + }) + }, + ), ) .into_any_element();