Add label to Switch
This commit is contained in:
parent
2f2203b45b
commit
e4fcd24382
9 changed files with 116 additions and 58 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}),
|
||||
),
|
||||
))
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue