Toggle

Toggle 是一种按钮式的二元切换组件,用于表示选中 / 未选中、开启 / 关闭等状态。与传统 Switch 不同,Toggle 更像可按下或弹起的按钮,适合工具栏、筛选器和多选项场景。

#导入

use gpui_kit::component::button::{Toggle, ToggleGroup};

#用法

#基础 Toggle

Toggle::new("toggle1")
    .label("Toggle me")
    .checked(false)
    .on_click(|checked, _, _| {
        println!("Toggle is now: {}", checked);
    })

on_click 回调接收的是切换后的新状态。

#图标 Toggle

use gpui_kit::component::IconName;

Toggle::new("toggle2")
    .icon(IconName::Eye)
    .checked(true)
    .on_click(|checked, _, _| {
        println!("Visibility: {}", if *checked { "shown" } else { "hidden" });
    })

#受控 Toggle

struct MyView {
    is_active: bool,
}

impl Render for MyView {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        Toggle::new("active")
            .label("Active")
            .checked(self.is_active)
            .on_click(cx.listener(|view, checked, _, cx| {
                view.is_active = *checked;
                cx.notify();
            }))
    }
}

#样式变体

Toggle::new("ghost-toggle")
    .ghost()
    .label("Ghost")

Toggle::new("outline-toggle")
    .outline()
    .label("Outline")

#不同尺寸

Toggle::new("xs-toggle")
    .icon(IconName::Star)
    .xsmall()

Toggle::new("small-toggle")
    .label("Small")
    .small()

Toggle::new("medium-toggle")
    .label("Medium")

Toggle::new("large-toggle")
    .label("Large")
    .large()

#禁用状态

Toggle::new("disabled-toggle")
    .label("Disabled")
    .disabled(true)
    .checked(false)

Toggle::new("disabled-checked-toggle")
    .label("Selected (Disabled)")
    .disabled(true)
    .checked(true)

#Toggle 与 Switch 的区别

特性ToggleSwitch
外观按钮式,可按下 / 弹起传统滑块式开关
场景工具栏、筛选器、二元选择设置项、偏好项、开关状态
状态表达背景和按压感变化滑块位置变化
分组能力支持 ToggleGroup主要单独使用

#与 ToggleGroup 配合使用

#基础分组

ToggleGroup::new("filter-group")
    .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);
    })

#受控分组

struct FilterView {
    notifications: bool,
    bots: bool,
    inbox: bool,
    other: bool,
}

impl Render for FilterView {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        ToggleGroup::new("filters")
            .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];
                view.other = checkeds[3];
                cx.notify();
            }))
    }
}

#分段样式 ToggleGroup

使用 segmented() 可以把一组 Toggle 渲染成贴合的分段控件。这个样式只影响外观: on_click 仍然会收到每一项最新状态组成的 Vec<bool>,除非同时开启 单选模式,否则每一项都独立切换。

ToggleGroup::new("formatting")
    .segmented()
    .outline()
    .child(Toggle::new(0).label("Bold").checked(self.bold))
    .child(Toggle::new(1).label("Italic").checked(self.italic))
    .child(Toggle::new(2).label("Code").checked(self.code))
    .on_click(cx.listener(|view, states, _, cx| {
        view.bold = states[0];
        view.italic = states[1];
        view.code = states[2];
        cx.notify();
    }))

分段样式默认使用 0px 间距,相邻项会共享一个连续边框。需要保留间距时可以传入 非零 gap:

use gpui_kit::px;

ToggleGroup::new("quick-actions")
    .segmented()
    .outline()
    .gap(px(8.))
    .small()
    .child(Toggle::new(0).label("Star"))
    .child(Toggle::new(1).label("Watch"))
    .child(Toggle::new(2).label("Pin"))

#单选模式

当一组 Toggle 表示的是”从中选一个”而不是多个独立开关时,调用 single()。 点击未选中的项会选中它并清除其他项;再次点击已选中的项会取消选中,所以分组也可以 处于无选中状态。on_click 仍然会收到每一项最新状态组成的 Vec<bool>,其中最多 只有一项为 true。

struct ReportView {
    period: Option<usize>,
}

impl Render for ReportView {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        ToggleGroup::new("period")
            .single()
            .segmented()
            .outline()
            .child(Toggle::new(0).label("Day").checked(self.period == Some(0)))
            .child(Toggle::new(1).label("Week").checked(self.period == Some(1)))
            .child(Toggle::new(2).label("Month").checked(self.period == Some(2)))
            .on_click(cx.listener(|view, states, _, cx| {
                view.period = states.iter().position(|checked| *checked);
                cx.notify();
            }))
    }
}

分组不会自行纠正多项同时 checked(true) 的状态,请在视图状态中保证最多只有一项选中。

#最佳实践

  1. 需要按钮式反馈时优先使用 Toggle,而不是 Switch。
  2. 一组相关选项应使用 ToggleGroup 统一管理。
  3. 图标型 Toggle 最好补充 tooltip 或可访问标签。
  4. Toggle 状态应与实际业务状态保持同步,避免视觉与数据不一致。

CC BY 4.0 · GPUI Kit 有权授权的原创文档正文与图示可在注明署名后复制或改编。 许可条款。请署名 GPUI Kit,链接到 本页原文及 CC BY 4.0 许可,并注明是否修改。 代码示例与软件源码仍适用 Apache-2.0。既有 Apache 权利不受影响;第三方材料保留各自许可。