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.


<img width="1204" alt="image"
src="https://github.com/user-attachments/assets/981cf925-01c7-48d7-9dd2-881c727a1759"
/>
This commit is contained in:
Jason Lee 2025-03-20 19:12:48 +08:00 committed by GitHub
parent 49e8b06ccb
commit c98e4f0521
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 80 additions and 34 deletions

View file

@ -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)

View file

@ -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<Text>,
children: Vec<AnyElement>,
checked: bool,
disabled: bool,
on_click: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
@ -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<Item = AnyElement>) {
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)

View file

@ -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<Text>,
children: Vec<AnyElement>,
checked: bool,
disabled: bool,
on_click: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
@ -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<Item = AnyElement>) {
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| {