Checkbox

A checkbox component for binary choices. Supports labels, disabled state, and different sizes.

Install

Use on_change for requested values. The owner stores the value and calls cx.notify(). The existing on_click name remains a compatibility alias; setting either replaces the same handler, so the last call wins.

#Import

use gpui_kit::component::checkbox::Checkbox;
// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { Checkbox } from "checkbox.slint";
use iced::widget::{column, container, mouse_area, row, scrollable, svg, text};
use iced::{Alignment, Element, Length, Task, Theme};

#Usage

#Basic

Checkbox::new("my-checkbox")
    .label("Accept terms and conditions")
    .checked(false)
    .on_change(|checked, _, _| {
        println!("Checkbox is now: {}", checked);
    })
Checkbox {
    label: "Accept terms and conditions";
    checked: true;
}
Checkbox {
    label: "Receive product updates";
    description: "Occasional emails about new releases.";
}
Checkbox {
    label: "Indeterminate";
    indeterminate: true;
}
Checkbox {
    label: "Disabled";
    enabled: false;
}
// The four looks the Slint example opens with: the plain choice, the one
// with a supporting line, the indeterminate dash, and a disabled control.
let basic = column![
    Checkbox::new(app.terms)
        .label("Accept terms and conditions")
        .on_toggle(Message::Terms),
    Checkbox::new(app.product_updates)
        .label("Receive product updates")
        .description("Occasional emails about new releases.")
        .on_toggle(Message::ProductUpdates),
    // `indeterminate(true)` draws the minus while the box is unchecked;
    // toggling it on draws the tick, as Slint's does.
    Checkbox::new(app.some)
        .indeterminate(true)
        .label("Indeterminate")
        .on_toggle(Message::Indeterminate),
    // No message is how iced draws a control that cannot be pressed.
    Checkbox::new(false).label("Disabled"),
]
.spacing(12.0);

The on_change callback is triggered when the user toggles the checkbox, receiving the new checked state.

#Default

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// Checked and unchecked options mixed freely in one column.
let default = column![
    Checkbox::new(app.default_updates)
        .label("Product updates")
        .on_toggle(Message::DefaultUpdates),
    Checkbox::new(app.default_remember)
        .label("Remember this device")
        .on_toggle(Message::DefaultRemember),
]
.spacing(12.0);

Checked and unchecked options can be mixed freely.

Checkbox::new("updates")
    .label("Product updates")
    .checked(false)

Checkbox::new("remember")
    .label("Remember this device")
    .checked(true)
Checkbox {
    label: "Product updates";
}
Checkbox {
    label: "Remember this device";
    checked: true;
}

#Sizes

use gpui_kit::component::Sizable as _;

Checkbox::new("cb").text_xs().label("Extra Small")
Checkbox::new("cb").text_sm().label("Small")
Checkbox::new("cb").label("Medium") // default
Checkbox::new("cb").text_lg().label("Large")
GridLayout {
    horizontal-stretch: 1;
    spacing: 16px;
    Checkbox {
        horizontal-stretch: 0;
        label: "XS";
        size: ControlSize.xsmall;
        checked: true;
    }
    Checkbox {
        horizontal-stretch: 0;
        label: "SM";
        size: ControlSize.small;
        checked: true;
    }
    Checkbox {
        horizontal-stretch: 0;
        label: "MD";
        checked: true;
    }
    Checkbox {
        horizontal-stretch: 0;
        label: "LG";
        size: ControlSize.large;
        checked: true;
    }
}
// The box's side is 12 / 14 / 16 / 18 across the size ladder.
let sizes = row![
    Checkbox::new(app.size_xs)
        .size(ControlSize::XSmall)
        .label("XS")
        .on_toggle(Message::SizeXs),
    Checkbox::new(app.size_sm)
        .size(ControlSize::Small)
        .label("SM")
        .on_toggle(Message::SizeSm),
    Checkbox::new(app.size_md)
        .size(ControlSize::Medium)
        .label("MD")
        .on_toggle(Message::SizeMd),
    Checkbox::new(app.size_lg)
        .size(ControlSize::Large)
        .label("LG")
        .on_toggle(Message::SizeLg),
]
.spacing(16.0);

#Controlled Checkbox

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The value lives on the application: `on_toggle` reports the requested
// value, `update` stores it, and the next frame draws it — what Slint's
// `checked <=> root.updates` binding does.
let controlled = column![
    Checkbox::new(app.controlled)
        .label("Receive product updates")
        .on_toggle(Message::Controlled),
    muted_line(if app.controlled {
        "Subscribed"
    } else {
        "Not subscribed"
    }),
]
.spacing(8.0);

This complete Tested consumer recipe keeps the value on the rendering owner and applies the requested value from on_change before notifying:

use gpui_kit::component::checkbox::Checkbox;
use gpui_kit::{Context, IntoElement, Render, Window};

pub struct ControlledCheckbox {
    checked: bool,
}

impl ControlledCheckbox {
    pub fn new() -> Self {
        Self { checked: false }
    }

    pub fn is_checked(&self) -> bool {
        self.checked
    }
}

impl Render for ControlledCheckbox {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        Checkbox::new("marketing-emails")
            .label("Receive product updates")
            .checked(self.checked)
            .on_change(cx.listener(|this, checked, _, cx| {
                this.checked = *checked;
                cx.notify();
            }))
    }
}
Checkbox {
    label: "Receive product updates";
    checked <=> root.updates;
}
Text {
    text: root.updates ? "Subscribed" : "Not subscribed";
    color: Theme.muted-foreground;
    font-size: Theme.font-size-xs;
}

#Disabled State

use gpui_kit::component::Disableable as _;

Checkbox::new("disabled-checked")
    .label("Checked")
    .checked(true)
    .disabled(true)

Checkbox::new("disabled-unchecked")
    .label("Unchecked")
    .checked(false)
    .disabled(true)
Checkbox {
    label: "Checked";
    checked: true;
    enabled: false;
}
Checkbox {
    label: "Unchecked";
    enabled: false;
}
// The two disabled looks, one on and one off.
let disabled = column![
    Checkbox::new(true).label("Checked"),
    Checkbox::new(false).label("Unchecked"),
]
.spacing(12.0);

#Without Label

Checkbox::new("checkbox")
    .checked(true)
Checkbox {
    checked: true;
}
// The box alone, with no line beside it.
let without_label = Checkbox::new(app.without_label).on_toggle(Message::WithoutLabel);

#Labels

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// iced has one supporting slot under the label, `description`, where Slint
// nests a second `Text`. The page's wrapping and markdown children have no
// iced counterpart, so the supporting sentence is the description here.
let labels = column![
    Checkbox::new(app.label_updates)
        .label("Automatic updates")
        .description("Download updates when the application is idle.")
        .on_toggle(Message::LabelUpdates),
    Checkbox::new(app.label_notify)
        .label("Notify me when a new device signs in to my account")
        .on_toggle(Message::LabelNotify),
    Checkbox::new(app.label_terms)
        .label("Accept the terms")
        .description("Read the terms of service before continuing.")
        .on_toggle(Message::LabelTerms),
]
.spacing(12.0);

Labels can wrap and include supporting content.

Checkbox::new("description")
    .label("Automatic updates")
    .child(
        div()
            .text_xs()
            .child("Download updates when the application is idle."),
    )

Checkbox::new("wrapping")
    .label("Notify me when a new device signs in to my account")

Checkbox::new("markdown")
    .label("Accept the terms")
    .child(markdown(
        "Read the [terms of service](https://github.com) before continuing.",
    ))
Checkbox {
    label: "Automatic updates";
    description: "Download updates when the application is idle.";
    checked: true;
}
Checkbox {
    label: "Notify me when a new device signs in to my account";
}
Checkbox {
    label: "Accept the terms";
    description: "Read the terms of service before continuing.";
}

#Custom Tab Order

Checkbox::new("checkbox")
    .label("Custom tab order")
    .tab_index(2)
    .tab_stop(true)
SlintNo Slint version of this example yet.

#Checkbox List

v_flex()
    .gap_2()
    .child(Checkbox::new("cb1").label("Option 1").checked(true))
    .child(Checkbox::new("cb2").label("Option 2").checked(false))
    .child(Checkbox::new("cb3").label("Option 3").checked(false))
Checkbox {
    label: "Option 1";
    checked: true;
}
Checkbox {
    label: "Option 2";
}
Checkbox {
    label: "Option 3";
}
// Independent choices: each box carries its own value.
let list = column![
    Checkbox::new(app.option_1)
        .label("Option 1")
        .on_toggle(Message::Option1),
    Checkbox::new(app.option_2)
        .label("Option 2")
        .on_toggle(Message::Option2),
    Checkbox::new(app.option_3)
        .label("Option 3")
        .on_toggle(Message::Option3),
]
.spacing(12.0);

#Form Integration

struct FormView {
    agree_terms: bool,
    subscribe: bool,
}

v_flex()
    .gap_3()
    .child(
        Checkbox::new("terms")
            .label("I agree to the terms and conditions")
            .checked(self.agree_terms)
            .on_change(cx.listener(|view, checked, _, cx| {
                view.agree_terms = *checked;
                cx.notify();
            }))
    )
    .child(
        Checkbox::new("subscribe")
            .label("Subscribe to newsletter")
            .checked(self.subscribe)
            .on_change(cx.listener(|view, checked, _, cx| {
                view.subscribe = *checked;
                cx.notify();
            }))
    )
Checkbox {
    label: "I agree to the terms and conditions";
    checked <=> root.terms;
}
Checkbox {
    label: "Subscribe to newsletter";
    checked <=> root.newsletter;
}
// Two boxes of one form, each with its own value on the owner.
let form = column![
    Checkbox::new(app.form_terms)
        .label("I agree to the terms and conditions")
        .on_toggle(Message::FormTerms),
    Checkbox::new(app.form_newsletter)
        .label("Subscribe to newsletter")
        .on_toggle(Message::FormNewsletter),
]
.spacing(12.0);

#API Reference

#Styling

Implements Sizable and Disableable traits:

  • text_xs() - Extra small text
  • text_sm() - Small text
  • text_base() - Base text (default)
  • text_lg() - Large text
  • disabled(bool) - Disabled state

#Checkbox

A binary choice. Mirrors GPUI Component's `Checkbox`: a label, optional description, indeterminate dash, sizes, and a disabled state.

NameKindTypeDefault
checkedin-outbool
indeterminateinbool
labelinstring
descriptioninstring
enabledinbooltrue
sizeinControlSizeControlSize.medium
changedcallback(checked: bool)