Skip to main content

mixed_lighting/helpers/
radio.rs

1/// Helpers to create a basic option menu using Feathers Radio Buttons.
2/// Using these helpers requires the `bevy_feathers` feature to be enabled.
3use bevy::{
4    feathers::{
5        controls::FeathersRadio,
6        display::{caption, label},
7    },
8    picking::hover::Hovered,
9    prelude::*,
10    ui::Checked,
11    ui_widgets::RadioGroup,
12};
13
14/// A component that wraps a radio button's option value
15#[derive(Clone, Copy, Component, Default)]
16pub struct RadioButtonOptionValue<T>(pub T)
17where
18    T: Clone + Default + Send + Sync + Unpin + 'static;
19
20/// Returns a [`Node`] appropriate for the outer main UI node as a `Scene`.
21///
22/// This UI is in the bottom left corner and has flex column support
23pub fn main_ui_node_scene() -> impl Scene {
24    bsn! {
25        Node {
26            flex_direction: FlexDirection::Column,
27            position_type: PositionType::Absolute,
28            row_gap: px(6),
29            left: px(10),
30            bottom: px(10),
31        }
32    }
33}
34
35/// Spawns the radio buttons that allow configuration of a setting.
36///
37/// The option at index `selected_option` in the `options` list is marked as selected.
38///
39/// To react to changes in value, create an observer that listens to
40/// `ValueChange<Entity>>`. Query for the value entity's `RadioButtonOptionValue`
41/// and unwrap the new option value.
42///
43/// Ensure the radio button self updates its own state by adding the
44/// `ui_widgets::radio_self_update` observer to the app.
45pub fn feathers_option_buttons<T>(
46    title: &'static str,
47    options: &[(T, &str)],
48    selected_option: usize,
49) -> impl Scene
50where
51    T: Clone + Default + Send + Sync + Unpin + 'static,
52{
53    let buttons = options
54        .iter()
55        .cloned()
56        .enumerate()
57        .map(|(option_index, (option_value, option_name))| {
58            feathers_option_button(option_value, option_name, option_index == selected_option)
59        })
60        .collect::<Vec<_>>();
61    // Add the parent node for the row.
62    bsn! {
63        Node {
64            align_items: AlignItems::Center,
65            column_gap: px(5),
66        }
67        RadioGroup
68        Children [
69            @label(title)
70            --
71            {buttons}
72        ]
73    }
74}
75
76/// Spawns a single feathers radio button that allows configuration of a setting.
77fn feathers_option_button<T>(
78    option_value: T,
79    option_name: &str,
80    is_selected: bool,
81) -> Box<dyn Scene>
82where
83    T: Clone + Default + Send + Sync + Unpin + 'static,
84{
85    if is_selected {
86        Box::new(bsn! {
87            @FeathersRadio {
88                @caption: bsn! { @caption(option_name) }
89            }
90            Hovered
91            Checked
92            RadioButtonOptionValue<T>(option_value)
93        })
94    } else {
95        Box::new(bsn! {
96            @FeathersRadio {
97                @caption: bsn! { @caption(option_name) }
98            }
99            Hovered
100            RadioButtonOptionValue<T>(option_value)
101        })
102    }
103}