Skip to content

Feathers new select control and caption helper#24847

Open
gagnus wants to merge 5 commits into
bevyengine:mainfrom
gagnus:feathers-new-select-control-and-caption-helper
Open

Feathers new select control and caption helper#24847
gagnus wants to merge 5 commits into
bevyengine:mainfrom
gagnus:feathers-new-select-control-and-caption-helper

Conversation

@gagnus

@gagnus gagnus commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Objective

  • I wanted to use a select drop down but didn't want the boilerplate of making a menu which I think is the current best fit.
  • I also kept forgetting to put ThemedText next to Text in places like a button caption, so added a simple helper for that, similar to icon()

Solution

  • Added a new FeathersSelect control
  • Added a caption() function

Testing

  • Added a select to feathers gallery which scrolls

Showcase

image

gagnus added 3 commits July 2, 2026 17:52
… functionality, also added a simple caption helper that emits Text and a ThemedTest
… functionality, also added a simple caption helper that emits Text and a ThemedTest
@gagnus

gagnus commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

I'm going to have a go at making the popup be at least same width as control


impl Plugin for ControlsPlugin {
fn build(&self, app: &mut bevy_app::App) {
// arbitrary split as too many for one set

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #24784 I changed this to a PluginGroup.

/// String options
pub options: Vec<String>,
/// Which one is currently selected (ie an index into `options`)
pub selected: usize,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this was an EntityId, then you wouldn't have to worry about index renumbering.

};

/// A caption within, say, a button.
pub fn caption(text: impl Into<String>) -> impl Scene {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be a separate PR?

}

// Implements as a [`FeathersMenu`] under the hood with a row per option
impl FeathersSelect {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so a long digression here.

In the React/Solid world, select widgets are generic: you have widgets like Select<number> or Select<String>, such that the individual options don't store an index or an id, but rather they store the value of the option - which could be any data type that supports equality. The selected option's visible state is just the reactive formula option.value == select.current_value. This is much simpler than mucking around with indices or entity ids.

Unfortunately for us, generic widgets are off the table for Feathers - because we would not only have to register the component types, but also register the complete set of observers for every specialization of the widget. This would increase the number of observers in proportion to the number of unique specializations.

The approach I took for radio buttons (which has similar semantics to select widgets and tab groups - that is, all three have mutual exclusion behavior) is to key on entity id, and then allow the user to attach a custom component for each option that represented the value associated with that id.

So my suggestion here is to try and make this more consistent with radio buttons.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the API docs for Joy UI's select: https://v7.mui.com/joy-ui/react-select/

};
let current = props.options.get(selected).cloned().unwrap_or_default();

let rows: Box<dyn SceneList> = Box::new(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it would be good if we can support the appropriate a11y attributes: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-selected

gagnus added 2 commits July 2, 2026 23:33
… to review comments. Options is a scenelist rather than a list of strings, selection is by entity and that is picked up and sent on by a system.

Note this has not gone as far as other controls in that it still manages its own events with on_select, I think the next step if we need to would be to make that optional like other controls do.

This change also has a system for keeping the width of the popup in sync with the parent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants