Add Switch.
This commit is contained in:
parent
4ae4cd615b
commit
2f2203b45b
5 changed files with 187 additions and 8 deletions
|
|
@ -20,7 +20,8 @@ https://github.com/huacnlee/gpui-app/assets/5518/ad103f02-697a-40ed-a876-8b13e42
|
||||||
- [x] Icon
|
- [x] Icon
|
||||||
- [x] Checkbox
|
- [x] Checkbox
|
||||||
- [x] With label
|
- [x] With label
|
||||||
- [ ] Switch
|
- [x] Switch
|
||||||
|
- [ ] Toggle Animation
|
||||||
- [ ] Radio & RadioGroup
|
- [ ] Radio & RadioGroup
|
||||||
- [ ] Dropdown
|
- [ ] Dropdown
|
||||||
- [ ] Combobox
|
- [ ] Combobox
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ pub mod text_field;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
pub mod title_bar;
|
pub mod title_bar;
|
||||||
pub use styled_ext::StyledExt;
|
pub use styled_ext::StyledExt;
|
||||||
|
pub mod switch;
|
||||||
pub mod tab;
|
pub mod tab;
|
||||||
|
|
||||||
pub use clickable::Clickable;
|
pub use clickable::Clickable;
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,12 @@ use gpui::{
|
||||||
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled as _, View, ViewContext,
|
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled as _, View, ViewContext,
|
||||||
VisualContext, WindowContext,
|
VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
use switch_story::SwitchStory;
|
||||||
|
|
||||||
mod button_story;
|
mod button_story;
|
||||||
mod checkbox_story;
|
mod checkbox_story;
|
||||||
mod input_story;
|
mod input_story;
|
||||||
|
mod switch_story;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
button::Button,
|
button::Button,
|
||||||
|
|
@ -75,6 +77,7 @@ enum StoryType {
|
||||||
Button,
|
Button,
|
||||||
Input,
|
Input,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
Switch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for StoryType {
|
impl Display for StoryType {
|
||||||
|
|
@ -83,6 +86,7 @@ impl Display for StoryType {
|
||||||
Self::Button => write!(f, "Button"),
|
Self::Button => write!(f, "Button"),
|
||||||
Self::Input => write!(f, "Input"),
|
Self::Input => write!(f, "Input"),
|
||||||
Self::Checkbox => write!(f, "Checkbox"),
|
Self::Checkbox => write!(f, "Checkbox"),
|
||||||
|
Self::Switch => write!(f, "Switch"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +96,7 @@ pub struct Stories {
|
||||||
|
|
||||||
button_story: View<ButtonStory>,
|
button_story: View<ButtonStory>,
|
||||||
input_story: View<InputStory>,
|
input_story: View<InputStory>,
|
||||||
|
switch_story: View<SwitchStory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stories {
|
impl Stories {
|
||||||
|
|
@ -100,6 +105,7 @@ impl Stories {
|
||||||
active: StoryType::Button,
|
active: StoryType::Button,
|
||||||
button_story: cx.new_view(|cx| ButtonStory {}),
|
button_story: cx.new_view(|cx| ButtonStory {}),
|
||||||
input_story: cx.new_view(|cx| InputStory::new(cx)),
|
input_story: cx.new_view(|cx| InputStory::new(cx)),
|
||||||
|
switch_story: cx.new_view(|cx| SwitchStory::new(cx)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,18 +119,17 @@ impl Stories {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_story_buttons(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render_story_buttons(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let tabs = vec![
|
|
||||||
self.swith_button("story-button", StoryType::Button, cx),
|
|
||||||
self.swith_button("story-input", StoryType::Input, cx),
|
|
||||||
self.swith_button("story-checkbox", StoryType::Checkbox, cx),
|
|
||||||
];
|
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
.w_full()
|
.w_full()
|
||||||
.child(TabBar::new("story-tabs").children(tabs))
|
.child(TabBar::new("story-tabs").children(vec![
|
||||||
|
self.swith_button("story-button", StoryType::Button, cx),
|
||||||
|
self.swith_button("story-input", StoryType::Input, cx),
|
||||||
|
self.swith_button("story-checkbox", StoryType::Checkbox, cx),
|
||||||
|
self.swith_button("story-switch", StoryType::Switch, cx),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn swith_button(
|
fn swith_button(
|
||||||
|
|
@ -156,6 +161,7 @@ impl Render for Stories {
|
||||||
StoryType::Button => this.child(self.button_story.clone()),
|
StoryType::Button => this.child(self.button_story.clone()),
|
||||||
StoryType::Input => this.child(self.input_story.clone()),
|
StoryType::Input => this.child(self.input_story.clone()),
|
||||||
StoryType::Checkbox => this.child(CheckboxStory::new(cx).into_any_element()),
|
StoryType::Checkbox => this.child(CheckboxStory::new(cx).into_any_element()),
|
||||||
|
StoryType::Switch => this.child(self.switch_story.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
crates/ui/src/story/switch_story.rs
Normal file
81
crates/ui/src/story/switch_story.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
use gpui::{
|
||||||
|
div, ClickEvent, IntoElement, ParentElement, Render, RenderOnce,
|
||||||
|
StatefulInteractiveElement as _, Styled, ViewContext, VisualContext as _, WindowContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
checkbox::Checkbox,
|
||||||
|
disableable::Disableable as _,
|
||||||
|
selectable::Selection,
|
||||||
|
stock::{h_flex, v_flex},
|
||||||
|
switch::Switch,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::story_case;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SwitchStory {
|
||||||
|
switch1: bool,
|
||||||
|
switch2: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwitchStory {
|
||||||
|
pub(crate) fn new(cx: &mut WindowContext) -> Self {
|
||||||
|
Self {
|
||||||
|
switch1: false,
|
||||||
|
switch2: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for SwitchStory {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
story_case(
|
||||||
|
"Switch",
|
||||||
|
"A control that allows the user to toggle between checked and not checked.",
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.items_start()
|
||||||
|
.justify_center()
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
Switch::new("switch1")
|
||||||
|
.checked(self.switch1)
|
||||||
|
.on_click(cx.listener(move |view, _, cx| {
|
||||||
|
view.switch1 = !view.switch1;
|
||||||
|
cx.notify();
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Switch::new("switch2")
|
||||||
|
.checked(self.switch2)
|
||||||
|
.on_click(cx.listener(move |view, _, cx| {
|
||||||
|
view.switch2 = !view.switch2;
|
||||||
|
cx.notify();
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_4()
|
||||||
|
.child(Switch::new("switch3").disabled(true).on_click(|ev, cx| {
|
||||||
|
println!("Switch value changed: {:?}", ev);
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
Switch::new("switch3_1")
|
||||||
|
.checked(true)
|
||||||
|
.disabled(true)
|
||||||
|
.on_click(|ev, cx| {
|
||||||
|
println!("Switch value changed: {:?}", ev);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
90
crates/ui/src/switch.rs
Normal file
90
crates/ui/src/switch.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
use crate::{
|
||||||
|
theme::{ActiveTheme, Colorize},
|
||||||
|
Disableable,
|
||||||
|
};
|
||||||
|
use gpui::{
|
||||||
|
div, prelude::FluentBuilder as _, ClickEvent, Div, InteractiveElement, IntoElement,
|
||||||
|
ParentElement as _, RenderOnce, SharedString, Stateful, StatefulInteractiveElement,
|
||||||
|
Styled as _, WindowContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
type OnClick = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct Switch {
|
||||||
|
base: Stateful<Div>,
|
||||||
|
checked: bool,
|
||||||
|
disabled: bool,
|
||||||
|
label: Option<SharedString>,
|
||||||
|
on_click: Option<OnClick>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Switch {
|
||||||
|
pub fn new(id: impl Into<SharedString>) -> Self {
|
||||||
|
Self {
|
||||||
|
base: div().id(id.into()),
|
||||||
|
checked: false,
|
||||||
|
disabled: false,
|
||||||
|
label: None,
|
||||||
|
on_click: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked(mut self, checked: bool) -> Self {
|
||||||
|
self.checked = checked;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||||
|
self.label = Some(label.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||||
|
self.on_click = Some(Box::new(handler));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disableable for Switch {
|
||||||
|
fn disabled(mut self, disabled: bool) -> Self {
|
||||||
|
self.disabled = disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for Switch {
|
||||||
|
fn render(self, cx: &mut gpui::WindowContext) -> impl IntoElement {
|
||||||
|
let theme = cx.theme();
|
||||||
|
|
||||||
|
let (bg, toggle_bg) = match self.checked {
|
||||||
|
true => (theme.primary, theme.background),
|
||||||
|
false => (theme.input, theme.background),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (bg, toggle_bg) = match self.disabled {
|
||||||
|
true => (bg.opacity(0.3), toggle_bg.opacity(0.8)),
|
||||||
|
false => (bg, toggle_bg),
|
||||||
|
};
|
||||||
|
|
||||||
|
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.on_click.filter(|_| !self.disabled),
|
||||||
|
|this, on_click| this.on_click(move |ev, cx| on_click(ev, cx)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue