limn
    ↑↓ ChooseEnter OpenEsc Close
    GitHub

    Components/Inputs

    RadioGroup

    One choice among several, all of them in view: a circle for each option, with a dot in the chosen one. Use it for two to about seven options that deserve to be read before choosing; for a few short ones in little space, a SegmentedControl does the same, and for many, a Select.

    Native componentGuide source

    Preview

    CodeSource
    fn view(state: &State, palette: ComponentPalette) -> Element {
        RadioGroup::new(
            "theme",
            vec![
                RadioOption::new("theme:light", "Light"),
                RadioOption::new("theme:dark", "Dark"),
                RadioOption::new("theme:system", "Match the system"),
            ],
            Some(&state.theme),
            palette,
        )
        .accessibility_label("Theme")
        .render()
    }
    
    fn update(state: &mut State, action: &UiAction) {
        if let UiAction::Clicked { id } = action {
            if id.starts_with("theme:") {
                state.theme = id.clone();
            }
        }
    }

    Examples

    Each one is the library drawing it. Press, type and hover.

    Descriptions
    CodeSource
    group(cx, "radio-plan", "Plan", plans("radio-plan"), "team").render()
    Icons
    CodeSource
    group(
        cx,
        "radio-kind",
        "Channel type",
        vec![
            RadioOption::new("radio-kind=text", "Text").icon(Icon::Hash).description("Messages, images and links."),
            RadioOption::new("radio-kind=voice", "Voice").icon(Icon::Volume).description("Talk with whoever is in the room."),
            RadioOption::new("radio-kind=forum", "Forum").icon(Icon::Chat).description("Threads around a topic.").disabled(),
        ],
        "text",
    )
    .render()
    Horizontal
    CodeSource
    group(cx, "radio-row", "Theme", themes("radio-row"), "light").horizontal().render()
    Sizes1 / 4
    CodeSource

    Read the source of this example.

    Sizes2 / 4
    CodeSource

    Read the source of this example.

    Sizes3 / 4
    CodeSource

    Read the source of this example.

    Sizes4 / 4
    CodeSource

    Read the source of this example.

    Disabled1 / 2
    CodeSource
    group(cx, "radio-off", "Theme, disabled", themes("radio-off"), "dark").disabled().render()
    Disabled2 / 2
    CodeSource
    group(
        cx,
        "radio-part",
        "Plan",
        plans("radio-part").into_iter().enumerate().map(|(index, plan)| plan.enabled(index != 2)).collect(),
        "free",
    )
    .render()
    Edge cases1 / 2
    CodeSource
    group(cx, "radio-none", "Theme, not yet chosen", themes("radio-none"), "").render()
    Edge cases2 / 2
    CodeSource
    Element::column(vec![group(
        cx,
        "radio-long",
        "Retention",
        vec![
            RadioOption::new("radio-long=keep", "Keep every run until someone deletes it by hand")
                .description("Storage grows without limit, and old runs stay searchable for audits."),
            RadioOption::new("radio-long=month", "Thirty days"),
        ],
        "keep",
    )
    .render()])
    .size(Some(cx.width.min(240.0)), None)

    RadioGroup

    One choice among several, all of them in view: a circle for each option, with a dot in the chosen one. Use it for two to about seven options that deserve to be read before choosing; for a few short ones in little space, a SegmentedControl does the same, and for many, a Select.

    Import

    use limn::{Component, Icon, RadioGroup, RadioOption};

    Usage

    A radio group is controlled: pass the chosen option’s value each frame, and keep the value an option reports when it is clicked. Each option’s value is the ID of its UiAction::Clicked, as a tab’s action_id is, so it must be unique in the window: prefix it with the group’s name.

    fn view(state: &State, palette: ComponentPalette) -> Element {
        RadioGroup::new(
            "theme",
            vec![
                RadioOption::new("theme:light", "Light"),
                RadioOption::new("theme:dark", "Dark"),
                RadioOption::new("theme:system", "Match the system"),
            ],
            Some(&state.theme),
            palette,
        )
        .accessibility_label("Theme")
        .render()
    }
    
    fn update(state: &mut State, action: &UiAction) {
        if let UiAction::Clicked { id } = action {
            if id.starts_with("theme:") {
                state.theme = id.clone();
            }
        }
    }

    selected is None, or a value no option has, while nothing is chosen yet: every circle is empty.

    The circle, the label and the description are one click target. The empty circle lights while the pointer is anywhere on its option.

    Description

    RadioOption::new("plan:team", "Team").description("Unlimited projects, ten seats.")

    A muted line under the label. It is clicked with the label, and the circle stays centered on the label.

    Icon

    RadioOption::new("kind:text", "Text").icon(Icon::Hash).description("Messages, images and links.")

    The icon stands between the circle and the label, in the label’s color and at the 16 pixels of every icon in a control. It is clicked with the label, and a description starts under the label, past the icon.

    Horizontal

    RadioGroup::new("theme", options, Some(&state.theme), palette).horizontal()

    The options stand side by side, 16 pixels apart, instead of one under the other, 8 pixels apart.

    Size

    RadioGroup::new("theme", options, Some(&state.theme), palette).size(ControlSize::Compact)
    SizeCircleLabel text
    Dense1412
    Compact1613
    Regular, the default1614
    Spacious1816

    These are a Checkbox’s, so the two line up in one form. A description is one pixel smaller than its label. An option is as high as its text, not as a control of that size.

    Narrow widths

    A label or a description that does not fit wraps inside the width the group is given. The circle centers on its label, so beside a label of two lines it stands between them.

    Disabled

    RadioGroup::new("theme", options, Some(&state.theme), palette).disabled()
    RadioGroup::new("theme", options, Some(&state.theme), palette).enabled(state.signed_in)
    RadioOption::new("plan:company", "Company").disabled()
    RadioOption::new("plan:company", "Company").enabled(state.may_upgrade)

    A disabled group shows its choice in the muted color and takes no pointer or focus. A single disabled option does the same while the others work. Nothing moves: a disabled option is laid out as an enabled one.

    Keyboard

    KeyEffect
    Tab, Shift+TabMove the focus to the next or the previous option; every enabled option is a stop.
    Enter, SpaceChoose the focused option.
    Up, Left, Down, RightMove the focus to the previous or the next enabled option and choose it, going round at the ends, however the group is laid out.
    Home, EndMove to the first or the last enabled option and choose it.

    Motion

    The circle’s fill and its dot cross-fade over hover_ms when the choice changes, as the empty circle does under the pointer. With reduced motion they change at once.

    Actions

    ActionWhen
    UiAction::Clicked { id }, where id is the option’s valueAn option is chosen by pointer, Enter or Space, or reached with Left, Right, Home or End. Choosing the option that is already chosen reports it again.

    Accessibility

    Each option is told to the platform as a radio button named by its label, with whether it is the selected one, its description, and whether it is disabled. A disabled option is still there, known by its value.

    The options do not say what they are options of. Name the group, and it becomes a node of its own around them, known by the group’s ID:

    RadioGroup::new("theme", options, Some(&state.theme), palette).accessibility_label("Theme")

    API reference

    The guide covers composition, options and actions. Generate the complete Rust type and builder reference from the checkout:

    cargo doc -p limn --open

    Component API overview · State and actions

    The example is drawn by crates/limn-gallery/src/pages/radio.rs.