From c98e4f0521822693359a4873b301b645b368fd61 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 20 Mar 2025 19:12:48 +0800 Subject: [PATCH] checkbox: Add `children` to Radio, Checkbox to display long texts. (#724) This change is going to solve the wrapped text line-height. Before this change, the Label is must set `line-height: 100%` to render at same line with the checkbox. But if there have a long text in label, wrapped lines need more line-height to leave lines spaces. But if we set line-height over the 100%, that the checkbox will not align horizontal center. So, this change to use other child to solve this issue, we can set line-height to the child not to the label. image --- crates/story/src/switch_story.rs | 35 +++++++++++++++++----------- crates/ui/src/checkbox.rs | 40 +++++++++++++++++++++++++------- crates/ui/src/radio.rs | 39 +++++++++++++++++++++---------- 3 files changed, 80 insertions(+), 34 deletions(-) diff --git a/crates/story/src/switch_story.rs b/crates/story/src/switch_story.rs index 812b8f3c..3e13916b 100644 --- a/crates/story/src/switch_story.rs +++ b/crates/story/src/switch_story.rs @@ -1,5 +1,5 @@ use gpui::{ - px, App, AppContext, Context, Div, Entity, Focusable, IntoElement, ParentElement, Render, + div, px, App, AppContext, Context, Div, Entity, Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Window, }; @@ -205,20 +205,27 @@ impl Render for SwitchStory { v.check3 = !v.check3; })), ) - .child(Checkbox::new("longlong-checkbox").w(px(300.)).label( - "The long long label text, \ - it should wrap when the text is too long.", - )) + .child( + Checkbox::new("longlong-checkbox") + .w(px(300.)) + .label("The long long label text.") + .child(div().text_color(cx.theme().muted_foreground).child( + "This is a long long label text that \ + should wrap when the text is too long.", + )), + ) .child( Checkbox::new("longlong-markdown-checkbox") .w(px(300.)) - .label( + .label("Label with description") + .child(div().text_color(cx.theme().muted_foreground).child( TextView::markdown( "longlong-markdown-checkbox", - "The [long long label](https://github.com) text used markdown, \ - it should wrap when the text is too long.", - ) - ), + "The [long long label](https://github.com) \ + text used markdown, \ + it should wrap when the text is too long.", + ), + )), ), ), ) @@ -269,9 +276,11 @@ impl Render for SwitchStory { ) .child( Radio::new("radio3") - .label( - "The long long label text, \ - it should wrap when the text is too long.", + .label("The long long label text.") + .child( + div().text_color(cx.theme().muted_foreground).child( + "This line should wrap when the text is too long.", + ), ) .w(px(300.)) .checked(true) diff --git a/crates/ui/src/checkbox.rs b/crates/ui/src/checkbox.rs index d364ed35..5fbb674c 100644 --- a/crates/ui/src/checkbox.rs +++ b/crates/ui/src/checkbox.rs @@ -1,7 +1,8 @@ use crate::{h_flex, text::Text, v_flex, ActiveTheme, Disableable, IconName, Selectable}; use gpui::{ - div, prelude::FluentBuilder as _, px, relative, svg, App, Div, ElementId, InteractiveElement, - IntoElement, ParentElement, RenderOnce, StatefulInteractiveElement as _, Styled, Window, + div, prelude::FluentBuilder as _, px, relative, svg, AnyElement, App, Div, ElementId, + InteractiveElement, IntoElement, ParentElement, RenderOnce, StatefulInteractiveElement as _, + Styled, Window, }; /// A Checkbox element. @@ -10,6 +11,7 @@ pub struct Checkbox { id: ElementId, base: Div, label: Option, + children: Vec, checked: bool, disabled: bool, on_click: Option>, @@ -21,6 +23,7 @@ impl Checkbox { id: id.into(), base: div(), label: None, + children: Vec::new(), checked: false, disabled: false, on_click: None, @@ -66,6 +69,12 @@ impl Selectable for Checkbox { } } +impl ParentElement for Checkbox { + fn extend(&mut self, elements: impl IntoIterator) { + self.children.extend(elements); + } +} + impl RenderOnce for Checkbox { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { let (color, icon_color) = if self.disabled { @@ -110,13 +119,26 @@ impl RenderOnce for Checkbox { }), ), ) - .map(|this| { - if let Some(label) = self.label { - this.child(div().size_full().line_height(relative(1.)).child(label)) - } else { - this - } - }) + .child( + v_flex() + .w_full() + .line_height(relative(1.2)) + .gap_1() + .map(|this| { + if let Some(label) = self.label { + this.child( + div() + .size_full() + .text_color(cx.theme().foreground) + .line_height(relative(1.)) + .child(label), + ) + } else { + this + } + }) + .children(self.children), + ) .when(self.disabled, |this| { this.cursor_not_allowed() .text_color(cx.theme().muted_foreground) diff --git a/crates/ui/src/radio.rs b/crates/ui/src/radio.rs index 268f70d4..0596066a 100644 --- a/crates/ui/src/radio.rs +++ b/crates/ui/src/radio.rs @@ -2,9 +2,9 @@ use std::rc::Rc; use crate::{h_flex, text::Text, v_flex, ActiveTheme, AxisExt, IconName}; use gpui::{ - div, prelude::FluentBuilder, relative, svg, App, Axis, Div, ElementId, InteractiveElement, - IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, - StyleRefinement, Styled, Window, + div, prelude::FluentBuilder, relative, svg, AnyElement, App, Axis, Div, ElementId, + InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString, + StatefulInteractiveElement, StyleRefinement, Styled, Window, }; /// A Radio element. @@ -15,6 +15,7 @@ pub struct Radio { base: Div, id: ElementId, label: Option, + children: Vec, checked: bool, disabled: bool, on_click: Option>, @@ -26,6 +27,7 @@ impl Radio { id: id.into(), base: div(), label: None, + children: Vec::new(), checked: false, disabled: false, on_click: None, @@ -59,6 +61,12 @@ impl Styled for Radio { } } +impl ParentElement for Radio { + fn extend(&mut self, elements: impl IntoIterator) { + self.children.extend(elements); + } +} + impl RenderOnce for Radio { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { let color = if self.disabled { @@ -100,15 +108,22 @@ impl RenderOnce for Radio { }), ), ) - .when_some(self.label, |this, label| { - this.child( - div() - .size_full() - .overflow_hidden() - .line_height(relative(1.)) - .child(label), - ) - }) + .child( + v_flex() + .w_full() + .line_height(relative(1.2)) + .gap_1() + .when_some(self.label, |this, label| { + this.child( + div() + .size_full() + .overflow_hidden() + .line_height(relative(1.)) + .child(label), + ) + }) + .children(self.children), + ) .when_some( self.on_click.filter(|_| !self.disabled), |this, on_click| {