Add label to Switch

This commit is contained in:
Jason Lee 2024-06-26 10:25:51 +08:00
parent 2f2203b45b
commit e4fcd24382
9 changed files with 116 additions and 58 deletions

View file

@ -21,6 +21,7 @@ https://github.com/huacnlee/gpui-app/assets/5518/ad103f02-697a-40ed-a876-8b13e42
- [x] Checkbox
- [x] With label
- [x] Switch
- [x] With Label (Left, Right side)
- [ ] Toggle Animation
- [ ] Radio & RadioGroup
- [ ] Dropdown

View file

@ -175,7 +175,7 @@ impl RenderOnce for Button {
normal_style.fg
};
Label::new(self.label, cx)
Label::new(self.label)
.text_color(text_color)
.map(|this| match self.size {
ButtonSize::Small => this.text_sm(),

View file

@ -122,7 +122,7 @@ impl RenderOnce for Checkbox {
)
.map(|this| {
if let Some(label) = self.label {
this.child(Label::new(label, cx).text_color(color))
this.child(Label::new(label).text_color(color))
} else {
this
}

View file

@ -9,22 +9,15 @@ use crate::theme::ActiveTheme;
pub struct Label {
base: Div,
label: SharedString,
color: Hsla,
multiple_lines: bool,
line_height: Option<DefiniteLength>,
text_size: Option<AbsoluteLength>,
}
impl Label {
pub fn new(label: impl Into<SharedString>, cx: &mut WindowContext) -> Self {
let theme = cx.theme();
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
base: div().text_color(theme.foreground).text_size(px(14.0)),
base: div(),
label: label.into(),
multiple_lines: false,
color: Hsla::white(),
line_height: None,
text_size: None,
multiple_lines: true,
}
}
@ -42,24 +35,17 @@ impl Styled for Label {
impl RenderOnce for Label {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let label_text = if !self.multiple_lines {
let theme = cx.theme();
let text = if !self.multiple_lines {
SharedString::from(self.label.replace('\n', ""))
} else {
self.label
};
self.base
.child(label_text)
.map(|this| {
if let Some(text_size) = self.text_size {
this.text_size(text_size)
} else {
this
}
})
.map(|this| match self.line_height {
Some(line_height) => this.line_height(line_height),
None => this,
})
.text_color(theme.foreground)
.text_size(px(theme.font_size))
.child(text)
}
}

View file

@ -65,8 +65,8 @@ impl RenderOnce for StoryContainer {
.flex()
.flex_col()
.gap_2()
.child(Label::new(self.name, cx).text_size(px(24.0)))
.child(Label::new(self.description, cx).text_size(px(16.0))),
.child(Label::new(self.name).text_size(px(24.0)))
.child(Label::new(self.description).text_size(px(16.0))),
)
.children(self.children)
}

View file

@ -1,14 +1,17 @@
use gpui::{
div, ClickEvent, IntoElement, ParentElement, Render, RenderOnce,
div, ClickEvent, Div, IntoElement, ParentElement, Render, RenderOnce, SharedString,
StatefulInteractiveElement as _, Styled, ViewContext, VisualContext as _, WindowContext,
};
use crate::{
checkbox::Checkbox,
disableable::Disableable as _,
label::Label,
selectable::Selection,
stock::{h_flex, v_flex},
switch::Switch,
switch::{LabelSide, Switch},
theme::ActiveTheme,
StyledExt,
};
use super::story_case;
@ -30,6 +33,25 @@ impl SwitchStory {
impl Render for SwitchStory {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let theme = cx.theme();
fn title(title: impl Into<SharedString>) -> Div {
v_flex().flex_1().gap_2().child(Label::new(title).text_xl())
}
fn card(cx: &ViewContext<SwitchStory>) -> Div {
let theme = cx.theme();
h_flex()
.items_center()
.gap_4()
.p_4()
.w_full()
.rounded_lg()
.border_1()
.border_color(theme.border)
}
story_case(
"Switch",
"A control that allows the user to toggle between checked and not checked.",
@ -40,17 +62,30 @@ impl Render for SwitchStory {
.justify_center()
.gap_4()
.child(
h_flex()
.items_center()
.gap_4()
card(cx)
.child(
title("Marketing emails").child(
Label::new("Receive emails about new products, features, and more.").text_color(theme.muted)
)
)
.child(
Switch::new("switch1")
.checked(self.switch1)
.label_side(LabelSide::Left)
.label("Subscribe")
.on_click(cx.listener(move |view, _, cx| {
view.switch1 = !view.switch1;
cx.notify();
})),
),
)
.child(
card(cx)
.child(
title("Security emails").child(
Label::new("Receive emails about your account security. When turn off, you never recive email again.").text_color(theme.muted)
)
)
.child(
Switch::new("switch2")
.checked(self.switch2)
@ -61,20 +96,20 @@ impl Render for SwitchStory {
),
)
.child(
h_flex()
.items_center()
.gap_4()
card(cx).v_flex() .items_start().child(title("Disabled Switchs")).child(
h_flex().items_center()
.gap_6()
.child(Switch::new("switch3").disabled(true).on_click(|ev, cx| {
println!("Switch value changed: {:?}", ev);
}))
.child(
Switch::new("switch3_1")
Switch::new("switch3_1").label("Airplane Mode")
.checked(true)
.disabled(true)
.on_click(|ev, cx| {
println!("Switch value changed: {:?}", ev);
}),
),
))
),
)
}

View file

@ -1,4 +1,6 @@
use crate::{
label::Label,
stock::h_flex,
theme::{ActiveTheme, Colorize},
Disableable,
};
@ -10,23 +12,40 @@ use gpui::{
type OnClick = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
pub enum LabelSide {
Left,
Right,
}
impl LabelSide {
fn left(&self) -> bool {
matches!(self, Self::Left)
}
}
#[derive(IntoElement)]
pub struct Switch {
id: SharedString,
base: Stateful<Div>,
checked: bool,
disabled: bool,
label: Option<SharedString>,
label_side: LabelSide,
on_click: Option<OnClick>,
}
impl Switch {
pub fn new(id: impl Into<SharedString>) -> Self {
let id: SharedString = id.into();
Self {
base: div().id(id.into()),
id: id.clone(),
base: div().id(id),
checked: false,
disabled: false,
label: None,
on_click: None,
label_side: LabelSide::Right,
}
}
@ -44,6 +63,11 @@ impl Switch {
self.on_click = Some(Box::new(handler));
self
}
pub fn label_side(mut self, label_side: LabelSide) -> Self {
self.label_side = label_side;
self
}
}
impl Disableable for Switch {
@ -56,6 +80,7 @@ impl Disableable for Switch {
impl RenderOnce for Switch {
fn render(self, cx: &mut gpui::WindowContext) -> impl IntoElement {
let theme = cx.theme();
let group_id = format!("switch_group_{:?}", self.id);
let (bg, toggle_bg) = match self.checked {
true => (theme.primary, theme.background),
@ -67,21 +92,30 @@ impl RenderOnce for Switch {
false => (bg, toggle_bg),
};
self.base
.w_11()
.h_6()
.flex()
h_flex()
.id(self.id)
.group(group_id)
.items_center()
.rounded_xl()
.border_2()
.border_color(theme.transparent)
.bg(bg)
.when(!self.disabled, |this| this.cursor_pointer())
.map(|this| match self.checked {
true => this.flex_row_reverse(),
false => this,
})
.child(div().h_4().w_4().rounded_full().bg(toggle_bg))
.gap_2()
.when(self.label_side.left(), |this| this.flex_row_reverse())
.child(
self.base
.w_11()
.h_6()
.flex()
.items_center()
.rounded_xl()
.border_2()
.border_color(theme.transparent)
.bg(bg)
.when(!self.disabled, |this| this.cursor_pointer())
.map(|this| match self.checked {
true => this.flex_row_reverse(),
false => this,
})
.child(div().h_4().w_4().rounded_full().bg(toggle_bg)),
)
.when_some(self.label, |this, label| this.child(Label::new(label)))
.when_some(
self.on_click.filter(|_| !self.disabled),
|this, on_click| this.on_click(move |ev, cx| on_click(ev, cx)),

View file

@ -206,6 +206,8 @@ pub struct Theme {
pub mode: ThemeMode,
pub transparent: Hsla,
pub title_bar_background: Hsla,
/// Basic font size
pub font_size: f32,
pub background: Hsla,
pub foreground: Hsla,
pub card: Hsla,
@ -241,6 +243,7 @@ impl From<Colors> for Theme {
Theme {
mode: ThemeMode::Dark,
transparent: Hsla::transparent_black(),
font_size: 14.0,
title_bar_background: colors.title_bar_background,
background: colors.background,
foreground: colors.foreground,

View file

@ -49,6 +49,10 @@ impl Workspace {
appears_transparent: true,
traffic_light_position: Some(point(px(9.0), px(9.0))),
}),
window_min_size: Size {
width: px(640.),
height: px(480.),
},
kind: WindowKind::Normal,
..Default::default()
};
@ -120,12 +124,7 @@ impl Render for Workspace {
})
})
// left side
.child(
div()
.flex()
.items_center()
.child(Label::new("GPUI App", cx)),
)
.child(div().flex().items_center().child(Label::new("GPUI App")))
.child(
div()
.flex()