toggle: Refactor Toggle, ToggleGroup API. (#1515)
## Break Changes
- Refactor `Toggle` new API to require a `id`, and renamed `on_change`
to `on_click.
- Renamed `on_change` method to `on_click` for `ToggleGroup`.
```diff
- Toggle::label("Hello").id("hello").on_change(..)
+ Toggle::new("hello).label("Hello").on_click(..)
- ToggleGroup::new("group1).on_change(..)
+ ToggleGroup::new("group1).on_click(..)
```
This commit is contained in:
parent
a4e93132f1
commit
0524c51b3e
4 changed files with 225 additions and 273 deletions
|
|
@ -77,6 +77,7 @@ impl Gallery {
|
|||
StoryContainer::panel::<TabsStory>(window, cx),
|
||||
StoryContainer::panel::<TagStory>(window, cx),
|
||||
StoryContainer::panel::<TextareaStory>(window, cx),
|
||||
StoryContainer::panel::<ToggleStory>(window, cx),
|
||||
StoryContainer::panel::<TooltipStory>(window, cx),
|
||||
StoryContainer::panel::<TreeStory>(window, cx),
|
||||
StoryContainer::panel::<VirtualListStory>(window, cx),
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@ use gpui::{
|
|||
};
|
||||
|
||||
use gpui_component::{
|
||||
IconName, Sizable, StyledExt,
|
||||
button::{Toggle, ToggleGroup, ToggleVariants},
|
||||
h_flex, v_flex, IconName, Sizable,
|
||||
v_flex,
|
||||
};
|
||||
|
||||
use crate::section;
|
||||
|
||||
pub struct ToggleStory {
|
||||
focus_handle: FocusHandle,
|
||||
single_toggle: usize,
|
||||
|
|
@ -51,16 +54,16 @@ impl Focusable for ToggleStory {
|
|||
impl Render for ToggleStory {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.size_full()
|
||||
.gap_6()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
section("Toggle")
|
||||
.child(
|
||||
Toggle::label("Single Toggle Item 1")
|
||||
.id("single-toggle-item-1")
|
||||
Toggle::new("item1")
|
||||
.label("Single Toggle Item 1")
|
||||
.large()
|
||||
.checked(self.single_toggle == 1)
|
||||
.on_change(cx.listener(|view, checked, _, cx| {
|
||||
.on_click(cx.listener(|view, checked, _, cx| {
|
||||
if *checked {
|
||||
view.single_toggle = 1;
|
||||
}
|
||||
|
|
@ -68,11 +71,11 @@ impl Render for ToggleStory {
|
|||
})),
|
||||
)
|
||||
.child(
|
||||
Toggle::label("Single Toggle Item 2")
|
||||
.id("single-toggle-item-2")
|
||||
Toggle::new("item2")
|
||||
.label("Single Toggle Item 2")
|
||||
.large()
|
||||
.checked(self.single_toggle == 2)
|
||||
.on_change(cx.listener(|view, checked, _, cx| {
|
||||
.on_click(cx.listener(|view, checked, _, cx| {
|
||||
if *checked {
|
||||
view.single_toggle = 2;
|
||||
}
|
||||
|
|
@ -80,11 +83,11 @@ impl Render for ToggleStory {
|
|||
})),
|
||||
)
|
||||
.child(
|
||||
Toggle::icon(IconName::Eye)
|
||||
.id("single-toggle-item-3")
|
||||
Toggle::new("item3")
|
||||
.icon(IconName::Eye)
|
||||
.large()
|
||||
.checked(self.single_toggle == 3)
|
||||
.on_change(cx.listener(|view, checked, _, cx| {
|
||||
.on_click(cx.listener(|view, checked, _, cx| {
|
||||
if *checked {
|
||||
view.single_toggle = 3;
|
||||
}
|
||||
|
|
@ -93,32 +96,50 @@ impl Render for ToggleStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_5()
|
||||
section("Toggle Group with Ghost Style")
|
||||
.v_flex()
|
||||
.gap_4()
|
||||
.child(
|
||||
ToggleGroup::new("toggle-button-group1")
|
||||
.child(Toggle::icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(Toggle::icon(IconName::Inbox).checked(self.checked[2]))
|
||||
.child(Toggle::icon(IconName::Check).checked(self.checked[3]))
|
||||
.child(Toggle::label("Other").checked(self.checked[4]))
|
||||
.on_change(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::new(1).icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(
|
||||
Toggle::new(2)
|
||||
.icon(IconName::Inbox)
|
||||
.checked(self.checked[2]),
|
||||
)
|
||||
.child(
|
||||
Toggle::new(3)
|
||||
.icon(IconName::Check)
|
||||
.checked(self.checked[3]),
|
||||
)
|
||||
.child(Toggle::new(4).label("Other").checked(self.checked[4]))
|
||||
.on_click(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
view.checked[0] = checkeds[0];
|
||||
view.checked[1] = checkeds[1];
|
||||
view.checked[2] = checkeds[2];
|
||||
view.checked[3] = checkeds[3];
|
||||
view.checked[4] = checkeds[4];
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
ToggleGroup::new("toggle-button-group1-sm")
|
||||
.small()
|
||||
.child(Toggle::icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(Toggle::icon(IconName::Inbox).checked(self.checked[2]))
|
||||
.child(Toggle::icon(IconName::Check).checked(self.checked[3]))
|
||||
.child(Toggle::label("Other").checked(self.checked[4]))
|
||||
.on_change(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::new(1).icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(
|
||||
Toggle::new(2)
|
||||
.icon(IconName::Inbox)
|
||||
.checked(self.checked[2]),
|
||||
)
|
||||
.child(
|
||||
Toggle::new(3)
|
||||
.icon(IconName::Check)
|
||||
.checked(self.checked[3]),
|
||||
)
|
||||
.child(Toggle::new(4).label("Other").checked(self.checked[4]))
|
||||
.on_click(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
view.checked[0] = checkeds[0];
|
||||
view.checked[1] = checkeds[1];
|
||||
view.checked[2] = checkeds[2];
|
||||
|
|
@ -130,12 +151,20 @@ impl Render for ToggleStory {
|
|||
.child(
|
||||
ToggleGroup::new("toggle-button-group1-xs")
|
||||
.xsmall()
|
||||
.child(Toggle::icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(Toggle::icon(IconName::Inbox).checked(self.checked[2]))
|
||||
.child(Toggle::icon(IconName::Check).checked(self.checked[3]))
|
||||
.child(Toggle::label("Other").checked(self.checked[4]))
|
||||
.on_change(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::new(1).icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(
|
||||
Toggle::new(2)
|
||||
.icon(IconName::Inbox)
|
||||
.checked(self.checked[2]),
|
||||
)
|
||||
.child(
|
||||
Toggle::new(3)
|
||||
.icon(IconName::Check)
|
||||
.checked(self.checked[3]),
|
||||
)
|
||||
.child(Toggle::new(4).label("Other").checked(self.checked[4]))
|
||||
.on_click(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
view.checked[0] = checkeds[0];
|
||||
view.checked[1] = checkeds[1];
|
||||
view.checked[2] = checkeds[2];
|
||||
|
|
@ -146,17 +175,26 @@ impl Render for ToggleStory {
|
|||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_5()
|
||||
section("Toggle Group with Outline Style")
|
||||
.v_flex()
|
||||
.gap_4()
|
||||
.child(
|
||||
ToggleGroup::new("toggle-button-group2")
|
||||
.outline()
|
||||
.child(Toggle::icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(Toggle::icon(IconName::Inbox).checked(self.checked[2]))
|
||||
.child(Toggle::icon(IconName::Check).checked(self.checked[3]))
|
||||
.child(Toggle::label("Other").checked(self.checked[4]))
|
||||
.on_change(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::new(1).icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(
|
||||
Toggle::new(2)
|
||||
.icon(IconName::Inbox)
|
||||
.checked(self.checked[2]),
|
||||
)
|
||||
.child(
|
||||
Toggle::new(3)
|
||||
.icon(IconName::Check)
|
||||
.checked(self.checked[3]),
|
||||
)
|
||||
.child(Toggle::new(4).label("Other").checked(self.checked[4]))
|
||||
.on_click(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
view.checked[0] = checkeds[0];
|
||||
view.checked[1] = checkeds[1];
|
||||
view.checked[2] = checkeds[2];
|
||||
|
|
@ -169,12 +207,20 @@ impl Render for ToggleStory {
|
|||
ToggleGroup::new("toggle-button-group2-sm")
|
||||
.outline()
|
||||
.small()
|
||||
.child(Toggle::icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(Toggle::icon(IconName::Inbox).checked(self.checked[2]))
|
||||
.child(Toggle::icon(IconName::Check).checked(self.checked[3]))
|
||||
.child(Toggle::label("Other").checked(self.checked[4]))
|
||||
.on_change(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::new(1).icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(
|
||||
Toggle::new(2)
|
||||
.icon(IconName::Inbox)
|
||||
.checked(self.checked[2]),
|
||||
)
|
||||
.child(
|
||||
Toggle::new(3)
|
||||
.icon(IconName::Check)
|
||||
.checked(self.checked[3]),
|
||||
)
|
||||
.child(Toggle::new(4).label("Other").checked(self.checked[4]))
|
||||
.on_click(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
view.checked[0] = checkeds[0];
|
||||
view.checked[1] = checkeds[1];
|
||||
view.checked[2] = checkeds[2];
|
||||
|
|
@ -187,12 +233,20 @@ impl Render for ToggleStory {
|
|||
ToggleGroup::new("toggle-button-group2-xs")
|
||||
.outline()
|
||||
.xsmall()
|
||||
.child(Toggle::icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(Toggle::icon(IconName::Inbox).checked(self.checked[2]))
|
||||
.child(Toggle::icon(IconName::Check).checked(self.checked[3]))
|
||||
.child(Toggle::label("Other").checked(self.checked[4]))
|
||||
.on_change(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell).checked(self.checked[0]))
|
||||
.child(Toggle::new(1).icon(IconName::Bot).checked(self.checked[1]))
|
||||
.child(
|
||||
Toggle::new(2)
|
||||
.icon(IconName::Inbox)
|
||||
.checked(self.checked[2]),
|
||||
)
|
||||
.child(
|
||||
Toggle::new(3)
|
||||
.icon(IconName::Check)
|
||||
.checked(self.checked[3]),
|
||||
)
|
||||
.child(Toggle::new(4).label("Other").checked(self.checked[4]))
|
||||
.on_click(cx.listener(|view, checkeds: &Vec<bool>, _, cx| {
|
||||
view.checked[0] = checkeds[0];
|
||||
view.checked[1] = checkeds[1];
|
||||
view.checked[2] = checkeds[2];
|
||||
|
|
|
|||
|
|
@ -17,10 +17,13 @@ pub enum ToggleVariant {
|
|||
}
|
||||
|
||||
pub trait ToggleVariants: Sized {
|
||||
/// Set the variant of the toggle.
|
||||
fn with_variant(self, variant: ToggleVariant) -> Self;
|
||||
/// Set the variant to ghost.
|
||||
fn ghost(self) -> Self {
|
||||
self.with_variant(ToggleVariant::Ghost)
|
||||
}
|
||||
/// Set the variant to outline.
|
||||
fn outline(self) -> Self {
|
||||
self.with_variant(ToggleVariant::Outline)
|
||||
}
|
||||
|
|
@ -28,53 +31,58 @@ pub trait ToggleVariants: Sized {
|
|||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Toggle {
|
||||
id: ElementId,
|
||||
style: StyleRefinement,
|
||||
checked: bool,
|
||||
size: Size,
|
||||
variant: ToggleVariant,
|
||||
disabled: bool,
|
||||
children: SmallVec<[AnyElement; 1]>,
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct InteractiveToggle {
|
||||
id: ElementId,
|
||||
toggle: Toggle,
|
||||
on_change: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
|
||||
on_click: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
|
||||
}
|
||||
|
||||
impl Toggle {
|
||||
fn new() -> Self {
|
||||
/// Create a new Toggle element.
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
style: StyleRefinement::default(),
|
||||
checked: false,
|
||||
size: Size::default(),
|
||||
variant: ToggleVariant::default(),
|
||||
disabled: false,
|
||||
children: smallvec![],
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(label: impl Into<SharedString>) -> Self {
|
||||
Self::new().child(label.into())
|
||||
/// Add a label to the toggle.
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
let label: SharedString = label.into();
|
||||
self.children.push(label.into_any_element());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon(icon: impl Into<Icon>) -> Self {
|
||||
Self::new().child(icon.into())
|
||||
}
|
||||
|
||||
pub fn id(self, id: impl Into<ElementId>) -> InteractiveToggle {
|
||||
InteractiveToggle {
|
||||
id: id.into(),
|
||||
toggle: self,
|
||||
on_change: None,
|
||||
}
|
||||
/// Add icon to the toggle.
|
||||
pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
|
||||
let icon: Icon = icon.into();
|
||||
self.children.push(icon.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the checked state of the toggle, default: false
|
||||
pub fn checked(mut self, checked: bool) -> Self {
|
||||
self.checked = checked;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the callback to be called when the toggle is clicked.
|
||||
///
|
||||
/// The `&bool` parameter represents the new checked state of the toggle.
|
||||
pub fn on_click(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
|
||||
self.on_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToggleVariants for Toggle {
|
||||
|
|
@ -84,6 +92,12 @@ impl ToggleVariants for Toggle {
|
|||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Toggle {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.children.extend(elements);
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for Toggle {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
|
|
@ -104,12 +118,6 @@ impl Styled for Toggle {
|
|||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Toggle {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.children.extend(elements);
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Toggle {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let checked = self.checked;
|
||||
|
|
@ -117,6 +125,7 @@ impl RenderOnce for Toggle {
|
|||
let hoverable = !disabled && !checked;
|
||||
|
||||
div()
|
||||
.id(self.id)
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
|
|
@ -146,67 +155,15 @@ impl RenderOnce for Toggle {
|
|||
})
|
||||
.refine_style(&self.style)
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractiveToggle {
|
||||
/// Sets the callback to be invoked when the toggle is clicked.
|
||||
///
|
||||
/// The first argument is a boolean indicating whether the toggle is checked.
|
||||
pub fn on_change(mut self, on_change: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
|
||||
self.on_change = Some(Box::new(on_change));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn checked(mut self, checked: bool) -> Self {
|
||||
self.toggle.checked = checked;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for InteractiveToggle {
|
||||
fn with_size(mut self, size: impl Into<Size>) -> Self {
|
||||
self.toggle = self.toggle.with_size(size);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToggleVariants for InteractiveToggle {
|
||||
fn with_variant(mut self, variant: ToggleVariant) -> Self {
|
||||
self.toggle.variant = variant;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for InteractiveToggle {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.toggle.disabled = disabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for InteractiveToggle {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.toggle.extend(elements);
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for InteractiveToggle {
|
||||
fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
|
||||
let checked = self.toggle.checked;
|
||||
let disabled = self.toggle.disabled;
|
||||
|
||||
div()
|
||||
.id(self.id)
|
||||
.child(self.toggle)
|
||||
.when(!disabled, |this| {
|
||||
this.when_some(self.on_change, |this, on_change| {
|
||||
this.on_click(move |_, window, cx| on_change(&!checked, window, cx))
|
||||
this.when_some(self.on_click, |this, on_click| {
|
||||
this.on_click(move |_, window, cx| on_click(&!checked, window, cx))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A group of toggles.
|
||||
#[derive(IntoElement)]
|
||||
pub struct ToggleGroup {
|
||||
id: ElementId,
|
||||
|
|
@ -215,10 +172,11 @@ pub struct ToggleGroup {
|
|||
variant: ToggleVariant,
|
||||
disabled: bool,
|
||||
items: Vec<Toggle>,
|
||||
on_change: Option<Rc<dyn Fn(&Vec<bool>, &mut Window, &mut App) + 'static>>,
|
||||
on_click: Option<Rc<dyn Fn(&Vec<bool>, &mut Window, &mut App) + 'static>>,
|
||||
}
|
||||
|
||||
impl ToggleGroup {
|
||||
/// Create a new ToggleGroup element.
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
|
|
@ -227,7 +185,7 @@ impl ToggleGroup {
|
|||
variant: ToggleVariant::default(),
|
||||
disabled: false,
|
||||
items: Vec::new(),
|
||||
on_change: None,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,12 +203,12 @@ impl ToggleGroup {
|
|||
|
||||
/// Set the callback to be called when the toggle group changes.
|
||||
///
|
||||
/// The `&Vec<bool>` parameter represents the check state of each [`Toggle`] in the group.
|
||||
pub fn on_change(
|
||||
/// The `&Vec<bool>` parameter represents the new check state of each [`Toggle`] in the group.
|
||||
pub fn on_click(
|
||||
mut self,
|
||||
on_change: impl Fn(&Vec<bool>, &mut Window, &mut App) + 'static,
|
||||
on_click: impl Fn(&Vec<bool>, &mut Window, &mut App) + 'static,
|
||||
) -> Self {
|
||||
self.on_change = Some(Rc::new(on_change));
|
||||
self.on_click = Some(Rc::new(on_click));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
@ -300,21 +258,20 @@ impl RenderOnce for ToggleGroup {
|
|||
|(ix, item)| {
|
||||
let state = state.clone();
|
||||
item.disabled(disabled)
|
||||
.id(ix)
|
||||
.with_size(self.size)
|
||||
.with_variant(self.variant)
|
||||
.on_change(move |_, _, _| {
|
||||
.on_click(move |_, _, _| {
|
||||
state.set(Some(ix));
|
||||
})
|
||||
}
|
||||
}))
|
||||
.when(!disabled, |this| {
|
||||
this.when_some(self.on_change, |this, on_change| {
|
||||
this.when_some(self.on_click, |this, on_click| {
|
||||
this.on_click(move |_, window, cx| {
|
||||
if let Some(ix) = state.get() {
|
||||
let mut checks = checks.clone();
|
||||
checks[ix] = !checks[ix];
|
||||
on_change(&checks, window, cx);
|
||||
on_click(&checks, window, cx);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,23 +18,25 @@ use gpui_component::button::{Toggle, ToggleGroup};
|
|||
### Basic Toggle
|
||||
|
||||
```rust
|
||||
Toggle::label("Toggle me")
|
||||
.id("basic-toggle")
|
||||
Toggle::new("toggle1").
|
||||
.label("Toggle me")
|
||||
.checked(false)
|
||||
.on_change(|checked, _, _| {
|
||||
.on_click(|checked, _, _| {
|
||||
println!("Toggle is now: {}", checked);
|
||||
})
|
||||
```
|
||||
|
||||
Here, we can use `on_click` to handle toggle state changes. The callback receives the **new checked state** as a `bool`.
|
||||
|
||||
### Icon Toggle
|
||||
|
||||
```rust
|
||||
use gpui_component::IconName;
|
||||
|
||||
Toggle::icon(IconName::Eye)
|
||||
.id("visibility-toggle")
|
||||
Toggle::new("toggle2")
|
||||
.icon(IconName::Eye)
|
||||
.checked(true)
|
||||
.on_change(|checked, _, _| {
|
||||
.on_click(|checked, _, _| {
|
||||
println!("Visibility: {}", if *checked { "shown" } else { "hidden" });
|
||||
})
|
||||
```
|
||||
|
|
@ -48,10 +50,10 @@ struct MyView {
|
|||
|
||||
impl Render for MyView {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
Toggle::label("Active")
|
||||
.id("active-toggle")
|
||||
Toggle::new("active")
|
||||
.label("Active")
|
||||
.checked(self.is_active)
|
||||
.on_change(cx.listener(|view, checked, _, cx| {
|
||||
.on_click(cx.listener(|view, checked, _, cx| {
|
||||
view.is_active = *checked;
|
||||
cx.notify();
|
||||
}))
|
||||
|
|
@ -63,36 +65,37 @@ impl Render for MyView {
|
|||
|
||||
```rust
|
||||
// Ghost toggle (default)
|
||||
Toggle::label("Ghost")
|
||||
.id("ghost-toggle")
|
||||
Toggle::new("ghost-toggle")
|
||||
.ghost()
|
||||
.label("Ghost")
|
||||
|
||||
// Outline toggle
|
||||
Toggle::label("Outline")
|
||||
.id("outline-toggle")
|
||||
Toggle::new("outline-toggle")
|
||||
.outline()
|
||||
.label("Outline")
|
||||
```
|
||||
|
||||
### Different Sizes
|
||||
|
||||
```rust
|
||||
// Extra small
|
||||
Toggle::icon(IconName::Star)
|
||||
.id("xs-toggle")
|
||||
Toggle::new("xs-toggle")
|
||||
.icon(IconName::Star)
|
||||
.xsmall()
|
||||
|
||||
// Small
|
||||
Toggle::label("Small")
|
||||
.id("small-toggle")
|
||||
Toggle::new("small-toggle")
|
||||
.label("Small")
|
||||
.small()
|
||||
|
||||
// Medium (default)
|
||||
Toggle::label("Medium")
|
||||
.id("medium-toggle")
|
||||
Toggle::new("medium-toggle")
|
||||
.label("Medium")
|
||||
|
||||
|
||||
// Large
|
||||
Toggle::label("Large")
|
||||
.id("large-toggle")
|
||||
Toggle::new("large-toggle")
|
||||
.label("Large")
|
||||
.large()
|
||||
```
|
||||
|
||||
|
|
@ -100,14 +103,14 @@ Toggle::label("Large")
|
|||
|
||||
```rust
|
||||
// Disabled unchecked
|
||||
Toggle::label("Disabled")
|
||||
.id("disabled-toggle")
|
||||
Toggle::new("disabled-toggle")
|
||||
.label("Disabled")
|
||||
.disabled(true)
|
||||
.checked(false)
|
||||
|
||||
// Disabled checked
|
||||
Toggle::label("Selected (Disabled)")
|
||||
.id("disabled-checked-toggle")
|
||||
Toggle::new("disabled-checked-toggle")
|
||||
.label("Selected (Disabled)")
|
||||
.disabled(true)
|
||||
.checked(true)
|
||||
```
|
||||
|
|
@ -144,15 +147,17 @@ Toggle buttons can be grouped together using `ToggleGroup` for related options:
|
|||
|
||||
```rust
|
||||
ToggleGroup::new("filter-group")
|
||||
.child(Toggle::icon(IconName::Bell))
|
||||
.child(Toggle::icon(IconName::Bot))
|
||||
.child(Toggle::icon(IconName::Inbox))
|
||||
.child(Toggle::label("Other"))
|
||||
.on_change(|checkeds, _, _| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell))
|
||||
.child(Toggle::new(1).icon(IconName::Bot))
|
||||
.child(Toggle::new(2).icon(IconName::Inbox))
|
||||
.child(Toggle::new(3).label("Other"))
|
||||
.on_click(|checkeds, _, _| {
|
||||
println!("Selected toggles: {:?}", checkeds);
|
||||
})
|
||||
```
|
||||
|
||||
The `on_click` callback receives a `Vec<bool>` representing the **new checked state** of each toggle in the group.
|
||||
|
||||
### Toggle Group with Controlled State
|
||||
|
||||
```rust
|
||||
|
|
@ -166,11 +171,11 @@ struct FilterView {
|
|||
impl Render for FilterView {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
ToggleGroup::new("filters")
|
||||
.child(Toggle::icon(IconName::Bell).checked(self.notifications))
|
||||
.child(Toggle::icon(IconName::Bot).checked(self.bots))
|
||||
.child(Toggle::icon(IconName::Inbox).checked(self.inbox))
|
||||
.child(Toggle::label("Other").checked(self.other))
|
||||
.on_change(cx.listener(|view, checkeds, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bell).checked(self.notifications))
|
||||
.child(Toggle::new(1).icon(IconName::Bot).checked(self.bots))
|
||||
.child(Toggle::new(2).icon(IconName::Inbox).checked(self.inbox))
|
||||
.child(Toggle::new(3).label("Other").checked(self.other))
|
||||
.on_click(cx.listener(|view, checkeds, _, cx| {
|
||||
view.notifications = checkeds[0];
|
||||
view.bots = checkeds[1];
|
||||
view.inbox = checkeds[2];
|
||||
|
|
@ -188,16 +193,16 @@ impl Render for FilterView {
|
|||
ToggleGroup::new("compact-filters")
|
||||
.outline()
|
||||
.small()
|
||||
.child(Toggle::icon(IconName::Filter))
|
||||
.child(Toggle::icon(IconName::Sort))
|
||||
.child(Toggle::icon(IconName::Search))
|
||||
.child(Toggle::new(0).icon(IconName::Filter))
|
||||
.child(Toggle::new(1).icon(IconName::Sort))
|
||||
.child(Toggle::new(2).icon(IconName::Search))
|
||||
|
||||
// Ghost variant (default), extra small
|
||||
ToggleGroup::new("mini-toolbar")
|
||||
.xsmall()
|
||||
.child(Toggle::icon(IconName::Bold))
|
||||
.child(Toggle::icon(IconName::Italic))
|
||||
.child(Toggle::icon(IconName::Underline))
|
||||
.child(Toggle::new(0).icon(IconName::Bold))
|
||||
.child(Toggle::new(1).icon(IconName::Italic))
|
||||
.child(Toggle::new(2).icon(IconName::Underline))
|
||||
```
|
||||
|
||||
## Event Handling
|
||||
|
|
@ -205,9 +210,9 @@ ToggleGroup::new("mini-toolbar")
|
|||
### Individual Toggle Events
|
||||
|
||||
```rust
|
||||
Toggle::label("Subscribe")
|
||||
.id("subscribe-toggle")
|
||||
.on_change(|checked, window, cx| {
|
||||
Toggle::new("subscribe-toggle")
|
||||
.label("Subscribe")
|
||||
.on_click(|checked, window, cx| {
|
||||
if *checked {
|
||||
// Handle subscription logic
|
||||
println!("Subscribed!");
|
||||
|
|
@ -218,72 +223,6 @@ Toggle::label("Subscribe")
|
|||
})
|
||||
```
|
||||
|
||||
### Toggle Group Events
|
||||
|
||||
The `on_change` callback for `ToggleGroup` receives a `Vec<bool>` representing the state of each toggle:
|
||||
|
||||
```rust
|
||||
ToggleGroup::new("options")
|
||||
.child(Toggle::label("Option 1"))
|
||||
.child(Toggle::label("Option 2"))
|
||||
.child(Toggle::label("Option 3"))
|
||||
.on_change(|states, _, _| {
|
||||
for (i, checked) in states.iter().enumerate() {
|
||||
if *checked {
|
||||
println!("Option {} is selected", i + 1);
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Toggle
|
||||
|
||||
| Method | Description |
|
||||
| ---------------- | ----------------------------------------------------------- |
|
||||
| `label(str)` | Create toggle with text label |
|
||||
| `icon(icon)` | Create toggle with icon |
|
||||
| `id(id)` | Set element ID and make interactive |
|
||||
| `checked(bool)` | Set checked/selected state |
|
||||
| `disabled(bool)` | Set disabled state |
|
||||
| `on_change(fn)` | Callback when clicked, receives `&bool` (new checked state) |
|
||||
|
||||
### Toggle Variants
|
||||
|
||||
| Method | Description |
|
||||
| ----------- | ------------------------------------------------ |
|
||||
| `ghost()` | Ghost variant (default) - transparent background |
|
||||
| `outline()` | Outline variant - visible border |
|
||||
|
||||
### Toggle Sizing
|
||||
|
||||
Implements `Sizable` trait:
|
||||
|
||||
- `xsmall()` - Extra small toggle (20x20px)
|
||||
- `small()` - Small toggle (24x24px)
|
||||
- `medium()` - Medium toggle (32x32px, default)
|
||||
- `large()` - Large toggle (36x36px)
|
||||
- `with_size(size)` - Set explicit size
|
||||
|
||||
### ToggleGroup
|
||||
|
||||
| Method | Description |
|
||||
| ---------------- | ------------------------------------------------------- |
|
||||
| `new(id)` | Create a new toggle group with ID |
|
||||
| `child(toggle)` | Add a toggle to the group |
|
||||
| `children(iter)` | Add multiple toggles to the group |
|
||||
| `on_change(fn)` | Callback when any toggle changes, receives `&Vec<bool>` |
|
||||
| `disabled(bool)` | Disable all toggles in the group |
|
||||
|
||||
### ToggleGroup Styling
|
||||
|
||||
Implements `Sizable`, `ToggleVariants`, and `Disableable` traits:
|
||||
|
||||
- Size methods apply to all child toggles
|
||||
- Variant methods apply to all child toggles
|
||||
- `disabled(bool)` affects the entire group
|
||||
|
||||
## Examples
|
||||
|
||||
### Toolbar with Toggle Buttons
|
||||
|
|
@ -305,11 +244,11 @@ h_flex()
|
|||
.child(
|
||||
ToggleGroup::new("formatting")
|
||||
.small()
|
||||
.child(Toggle::icon(IconName::Bold).checked(self.bold))
|
||||
.child(Toggle::icon(IconName::Italic).checked(self.italic))
|
||||
.child(Toggle::icon(IconName::Underline).checked(self.underline))
|
||||
.child(Toggle::icon(IconName::Strikethrough).checked(self.strikethrough))
|
||||
.on_change(cx.listener(|view, states, _, cx| {
|
||||
.child(Toggle::new(0).icon(IconName::Bold).checked(self.bold))
|
||||
.child(Toggle::new(1).icon(IconName::Italic).checked(self.italic))
|
||||
.child(Toggle::new(2).icon(IconName::Underline).checked(self.underline))
|
||||
.child(Toggle::new(3).icon(IconName::Strikethrough).checked(self.strikethrough))
|
||||
.on_click(cx.listener(|view, states, _, cx| {
|
||||
view.bold = states[0];
|
||||
view.italic = states[1];
|
||||
view.underline = states[2];
|
||||
|
|
@ -336,10 +275,10 @@ v_flex()
|
|||
.child(
|
||||
ToggleGroup::new("status-filters")
|
||||
.outline()
|
||||
.child(Toggle::label("Completed").checked(self.show_completed))
|
||||
.child(Toggle::label("Pending").checked(self.show_pending))
|
||||
.child(Toggle::label("Cancelled").checked(self.show_cancelled))
|
||||
.on_change(cx.listener(|view, states, _, cx| {
|
||||
.child(Toggle::new(0).label("Completed").checked(self.show_completed))
|
||||
.child(Toggle::new(1).label("Pending").checked(self.show_pending))
|
||||
.child(Toggle::new(2).label("Cancelled").checked(self.show_cancelled))
|
||||
.on_click(cx.listener(|view, states, _, cx| {
|
||||
view.show_completed = states[0];
|
||||
view.show_pending = states[1];
|
||||
view.show_cancelled = states[2];
|
||||
|
|
@ -347,10 +286,10 @@ v_flex()
|
|||
}))
|
||||
)
|
||||
.child(
|
||||
Toggle::label("Show urgent only")
|
||||
.id("urgent-filter")
|
||||
Toggle::new("urgent-filter")
|
||||
.label("Show urgent only")
|
||||
.checked(self.show_urgent)
|
||||
.on_change(cx.listener(|view, checked, _, cx| {
|
||||
.on_click(cx.listener(|view, checked, _, cx| {
|
||||
view.show_urgent = *checked;
|
||||
cx.notify();
|
||||
}))
|
||||
|
|
@ -382,10 +321,10 @@ v_flex()
|
|||
)
|
||||
)
|
||||
.child(
|
||||
Toggle::icon(IconName::Mail)
|
||||
.id("email-notifications")
|
||||
Toggle::new("email-notifications")
|
||||
.icon(IconName::Mail)
|
||||
.checked(self.email_notifications)
|
||||
.on_change(cx.listener(|view, checked, _, cx| {
|
||||
.on_click(cx.listener(|view, checked, _, cx| {
|
||||
view.email_notifications = *checked;
|
||||
cx.notify();
|
||||
}))
|
||||
|
|
@ -397,10 +336,10 @@ v_flex()
|
|||
.justify_between()
|
||||
.child(Label::new("Push notifications"))
|
||||
.child(
|
||||
Toggle::icon(IconName::Bell)
|
||||
.id("push-notifications")
|
||||
Toggle::new("push-notifications")
|
||||
.icon(IconName::Bell)
|
||||
.checked(self.push_notifications)
|
||||
.on_change(cx.listener(|view, checked, _, cx| {
|
||||
.on_click(cx.listener(|view, checked, _, cx| {
|
||||
view.push_notifications = *checked;
|
||||
cx.notify();
|
||||
}))
|
||||
|
|
@ -431,11 +370,12 @@ v_flex()
|
|||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, category)| {
|
||||
Toggle::label(category)
|
||||
Toggle::new(i)
|
||||
.label(category)
|
||||
.checked(self.selected_categories.get(i).copied().unwrap_or(false))
|
||||
})
|
||||
)
|
||||
.on_change(cx.listener(|view, states, _, cx| {
|
||||
.on_click(cx.listener(|view, states, _, cx| {
|
||||
view.selected_categories = states.clone();
|
||||
cx.notify();
|
||||
}))
|
||||
|
|
|
|||
Loading…
Reference in a new issue