Picker (built-in)

Authoring

use day::prelude::*;

let size = Signal::new(1usize);
picker(["Small", "Medium", "Large"], size).segmented().id("size")   // horizontal one-of-N
picker(colors, color).menu()                                         // dropdown / pop-up
picker(plans, plan).inline()                                         // vertical radio group

picker(options, selected) takes fixed options (impl IntoIterator<Item: Into<String>>) and a Signal<usize> bound two-way (the widget writes the selected index back; setting the signal moves the widget). Default style is .menu(); .segmented() / .inline() / .style(PickerStyle) switch it. Picker implements Piece, so .id()/.a11y()/.frame() chain via Decorate.

Options that come from data

The labels are fixed by default. When they are not (a list of open documents, a tab that names its own contents), .options_reactive(f) recomputes them:

picker(tab_labels(), tab)                    // seeds the control
    .segmented()
    .options_reactive(tab_labels)            // …and patches it whenever they change

f runs like any other reactive read, so it re-fires when the signals it touches change, and the new labels patch the native items in place: the pop-up refills, the segmented control relabels and grows or shrinks its buttons, the radio group does the same. The selected index is preserved where it still exists and clamped to the last option otherwise, so a shrinking list cannot leave a native control with nothing chosen. The app still owns the value: write a valid index to the binding when the count changes under it.

Use it only when the options change. Rebuilding a native menu costs more than moving a mark, and a picker whose choices are constant should stay constant.

Per-backend native realization

styleAppKitUIKitGTKQtAndroidXAMLArkUI
MenuNSPopUpButtonUIButton+UIMenu pull-downGtkDropDownQComboBoxSpinnerComboBoxTextPicker wheel
SegmentedNSSegmentedControlUISegmentedControl.linked grouped GtkToggleButtonscheckable QPushButtons in a QButtonGroupbutton-row LinearLayout (dim unselected)horizontal RadioButton StackPanelTextPicker wheel
Inlinevertical NSStackView of radio NSButtonscheckmark-row UIStackViewgrouped GtkCheckButtons (radio)QRadioButtons in a QButtonGroupRadioGroupvertical RadioButton StackPanelTextPicker wheel

HarmonyOS has no segmented control, so ArkUI renders every style as the native ARKUI_NODE_TEXT_PICKER wheel, the platform’s option-selection idiom. The Qt and XAML renderers each carry a C++ shim in the matching -sys crate (toolkits/day-qt-sys/src/shim-picker.cpp, toolkits/day-xaml-sys/src/shim-picker.cpp); the XAML shim boxes its XAML element into a Day handle through the day_xaml_box/day_xaml_unbox functions. Android’s Java factory (toolkits/day-android/java/dev/daybrite/day/piece/picker/DayPicker.java) rides the framework shim. All backends report selection through Event::SelectionChanged(i64); programmatic selection is echo-guarded per backend so it never loops.

Verification

The showcase Controls page (controls.rs pickers_section) shows all three styles bound to one shared selection signal, each with a live value label. Rendering and correct initial selection are screenshot-verified on all 5 local targets (AppKit, GTK, Qt, iOS-sim, Android-emu); a mock-backend test (crates/day-pieces/tests/mock_e2e.rs picker_and_text_area_are_built_in) asserts the two-way binding round-trips. The walkthrough drives select through each styling and asserts the readouts follow.

Follow-ups

  • Reactive options (currently fixed at build; only selected patches), mirroring the combobox’s Items.
  • Disabled/enabled state; per-option a11y labels.