1use crate::button::ButtonLabel;
2use crate::text::{
3 LocalizedTextFormat, TypographyStyle, set_localized_text, set_localized_text_format,
4 set_plain_text,
5};
6use bevy::prelude::*;
7use bevy_localization::{Localization, TextKey};
8
9pub const SELECT_CHEVRON_CLOSED: &str = "⌄";
10pub const SELECT_CHEVRON_OPEN: &str = "⌃";
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct SelectOptionState {
14 pub entity: Entity,
15 pub value: String,
16 pub text: String,
17 pub localized_text: Option<TextKey>,
18 pub localized_text_format: Option<LocalizedTextFormat>,
19 pub disabled: bool,
20}
21
22#[derive(Component, Debug, Clone)]
23pub struct Select {
24 pub name: String,
25 pub value: String,
26 pub initial_value: String,
27 pub options: Vec<SelectOptionState>,
28 pub panel: Entity,
29 pub trigger: Entity,
30 pub chevron_glyph: Entity,
31 pub open: bool,
32 pub disabled: bool,
33}
34
35#[derive(Message, Debug, Clone, PartialEq, Eq)]
36pub struct SelectValueChangedMessage {
37 pub entity: Entity,
38 pub name: String,
39 pub value: String,
40}
41
42#[derive(Component, Debug, Clone)]
43pub(crate) struct SelectTrigger {
44 pub(crate) select: Entity,
45}
46
47#[derive(Component, Debug, Clone)]
48pub(crate) struct SelectOptionButton {
49 pub(crate) select: Entity,
50 pub(crate) value: String,
51}
52
53#[derive(Component, Debug, Clone)]
54pub struct SelectPanel;
55
56#[derive(Component, Debug, Clone)]
57pub(crate) struct SelectOptionIndicator;
58
59#[derive(Component, Debug, Clone)]
60pub(crate) struct SelectChevron;
61
62#[derive(Component, Debug, Clone)]
63pub(crate) struct SelectChevronGlyph;
64
65#[derive(Debug, Clone, PartialEq, Eq)]
66pub struct AddSelectOption {
67 pub name: String,
68 pub value: String,
69 pub text: String,
70 pub localized_text: Option<TextKey>,
71 pub localized_text_format: Option<LocalizedTextFormat>,
72 pub disabled: bool,
73}
74
75#[derive(Component, Debug, Clone, PartialEq)]
76pub struct AddSelect {
77 pub name: String,
78 pub value: String,
79 pub options: Vec<AddSelectOption>,
80 pub class: Option<String>,
81 pub trigger_class: Option<String>,
82 pub label_class: Option<String>,
83 pub label_typography: TypographyStyle,
84 pub panel_class: Option<String>,
85 pub chevron_class: Option<String>,
86 pub option_class: Option<String>,
87 pub indicator_class: Option<String>,
88 pub disabled: bool,
89}
90
91impl Default for AddSelect {
92 fn default() -> Self {
93 Self {
94 name: String::new(),
95 value: String::new(),
96 options: Vec::new(),
97 class: None,
98 trigger_class: None,
99 label_class: None,
100 label_typography: TypographyStyle::default(),
101 panel_class: None,
102 chevron_class: None,
103 option_class: None,
104 indicator_class: None,
105 disabled: false,
106 }
107 }
108}
109
110pub fn default_select_node() -> Node {
111 Node::default()
112}
113
114pub fn selected_option(select: &Select) -> Option<&SelectOptionState> {
115 select
116 .options
117 .iter()
118 .find(|option| option.value == select.value)
119}
120
121pub fn trigger_label_entity(
122 trigger_labels: &Query<&ButtonLabel>,
123 select: &Select,
124) -> Option<Entity> {
125 trigger_labels
126 .get(select.trigger)
127 .ok()
128 .map(|label| label.entity)
129}
130
131pub fn sync_select_label(
132 commands: &mut Commands,
133 localization: Option<&Localization>,
134 label_entity: Entity,
135 option: Option<&SelectOptionState>,
136 fallback_value: &str,
137) {
138 let Some(option) = option else {
139 set_plain_text(commands, label_entity, fallback_value);
140 return;
141 };
142
143 if let (Some(localization), Some(format)) = (localization, option.localized_text_format.clone())
144 {
145 set_localized_text_format(commands, label_entity, localization, format);
146 return;
147 }
148 if let (Some(localization), Some(key)) = (localization, option.localized_text) {
149 set_localized_text(commands, label_entity, localization, key);
150 return;
151 }
152 set_plain_text(commands, label_entity, option.text.clone());
153}