1use crate::{
22 border::BorderBuilder,
23 button::{ButtonBuilder, ButtonMessage},
24 core::{
25 pool::Handle, reflect::prelude::*, type_traits::prelude::*, variable::InheritableVariable,
26 visitor::prelude::*,
27 },
28 define_constructor, define_widget_deref,
29 grid::{Column, GridBuilder, Row},
30 message::{MessageDirection, UiMessage},
31 utils::{make_arrow, ArrowDirection},
32 widget::{Widget, WidgetBuilder, WidgetMessage},
33 BuildContext, Control, Thickness, UiNode, UserInterface,
34};
35
36use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
37use std::ops::{Deref, DerefMut};
38
39#[derive(Debug, PartialEq, Clone)]
40pub enum SelectorMessage {
41 AddItem(Handle<UiNode>),
42 RemoveItem(Handle<UiNode>),
43 SetItems {
44 items: Vec<Handle<UiNode>>,
45 remove_previous: bool,
46 },
47 Current(Option<usize>),
48}
49
50impl SelectorMessage {
51 define_constructor!(
52 SelectorMessage:AddItem => fn add_item(Handle<UiNode>), layout: false
54 );
55 define_constructor!(
56 SelectorMessage:RemoveItem => fn remove_item(Handle<UiNode>), layout: false
58 );
59 define_constructor!(
60 SelectorMessage:SetItems => fn set_items(items: Vec<Handle<UiNode>>, remove_previous: bool), layout: false
62 );
63 define_constructor!(
64 SelectorMessage:Current => fn current(Option<usize>), layout: false
66 );
67}
68
69#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider, TypeUuidProvider)]
70#[reflect(derived_type = "UiNode")]
71#[type_uuid(id = "25118853-5c3c-4197-9e4b-2e3b9d92f4d2")]
72pub struct Selector {
73 widget: Widget,
74 items: InheritableVariable<Vec<Handle<UiNode>>>,
75 items_panel: InheritableVariable<Handle<UiNode>>,
76 current: InheritableVariable<Option<usize>>,
77 prev: InheritableVariable<Handle<UiNode>>,
78 next: InheritableVariable<Handle<UiNode>>,
79}
80
81impl ConstructorProvider<UiNode, UserInterface> for Selector {
82 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
83 GraphNodeConstructor::new::<Self>()
84 .with_variant("Selector", |ui| {
85 SelectorBuilder::new(WidgetBuilder::new().with_name("Selector"))
86 .build(&mut ui.build_ctx())
87 .into()
88 })
89 .with_group("Input")
90 }
91}
92
93define_widget_deref!(Selector);
94
95impl Control for Selector {
96 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
97 self.widget.handle_routed_message(ui, message);
98
99 if let Some(msg) = message.data::<SelectorMessage>() {
100 match msg {
101 SelectorMessage::AddItem(item) => {
102 ui.send_message(WidgetMessage::link(
103 *item,
104 MessageDirection::ToWidget,
105 *self.items_panel,
106 ));
107 self.items.push(*item);
108 }
109 SelectorMessage::RemoveItem(item) => {
110 if let Some(position) = self.items.iter().position(|i| i == item) {
111 ui.send_message(WidgetMessage::remove(*item, MessageDirection::ToWidget));
112
113 self.items.remove(position);
114 }
115 }
116 SelectorMessage::SetItems {
117 items,
118 remove_previous,
119 } => {
120 if *remove_previous {
121 for &item in &*self.items {
122 ui.send_message(WidgetMessage::remove(
123 item,
124 MessageDirection::ToWidget,
125 ));
126 }
127 }
128
129 for &item in items {
130 ui.send_message(WidgetMessage::link(
131 item,
132 MessageDirection::ToWidget,
133 *self.items_panel,
134 ));
135 }
136
137 self.items.set_value_and_mark_modified(items.clone());
138
139 for (i, item) in self.items.iter().enumerate() {
140 ui.send_message(WidgetMessage::visibility(
141 *item,
142 MessageDirection::ToWidget,
143 *self.current == Some(i),
144 ));
145 }
146 }
147 SelectorMessage::Current(current) => {
148 if &*self.current != current
149 && message.direction() == MessageDirection::ToWidget
150 {
151 if let Some(current) = *self.current {
152 if let Some(current_item) = self.items.get(current) {
153 ui.send_message(WidgetMessage::visibility(
154 *current_item,
155 MessageDirection::ToWidget,
156 false,
157 ));
158 }
159 }
160
161 self.current.set_value_and_mark_modified(*current);
162
163 if let Some(new_current) = *self.current {
164 if let Some(new_current_item) = self.items.get(new_current) {
165 ui.send_message(WidgetMessage::visibility(
166 *new_current_item,
167 MessageDirection::ToWidget,
168 true,
169 ));
170 }
171 }
172
173 ui.send_message(message.reverse());
174 }
175 }
176 }
177 } else if let Some(ButtonMessage::Click) = message.data() {
178 if message.destination() == *self.prev {
179 if let Some(current) = *self.current {
180 let new_current = current.saturating_sub(1);
181 ui.send_message(SelectorMessage::current(
182 self.handle,
183 MessageDirection::ToWidget,
184 Some(new_current),
185 ));
186 }
187 } else if message.destination() == *self.next {
188 if let Some(current) = *self.current {
189 let new_current = current
190 .saturating_add(1)
191 .min(self.items.len().saturating_sub(1));
192 ui.send_message(SelectorMessage::current(
193 self.handle,
194 MessageDirection::ToWidget,
195 Some(new_current),
196 ));
197 }
198 }
199 }
200 }
201}
202
203pub struct SelectorBuilder {
204 widget_builder: WidgetBuilder,
205 items: Vec<Handle<UiNode>>,
206 current: Option<usize>,
207}
208
209impl SelectorBuilder {
210 pub fn new(widget_builder: WidgetBuilder) -> Self {
211 Self {
212 widget_builder,
213 items: Default::default(),
214 current: None,
215 }
216 }
217
218 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
219 for (i, item) in self.items.iter().enumerate() {
220 ctx[*item].set_visibility(self.current == Some(i));
221 }
222
223 let prev;
224 let next;
225 let items_panel;
226 let grid = GridBuilder::new(
227 WidgetBuilder::new()
228 .with_child({
229 prev = ButtonBuilder::new(WidgetBuilder::new().on_column(0))
230 .with_content(make_arrow(ctx, ArrowDirection::Left, 24.0))
231 .build(ctx);
232 prev
233 })
234 .with_child({
235 items_panel = BorderBuilder::new(
236 WidgetBuilder::new()
237 .with_children(self.items.clone())
238 .on_column(1),
239 )
240 .with_stroke_thickness(Thickness::uniform(0.0).into())
241 .build(ctx);
242 items_panel
243 })
244 .with_child({
245 next = ButtonBuilder::new(WidgetBuilder::new().on_column(2))
246 .with_content(make_arrow(ctx, ArrowDirection::Right, 24.0))
247 .build(ctx);
248 next
249 }),
250 )
251 .add_row(Row::auto())
252 .add_column(Column::auto())
253 .add_column(Column::stretch())
254 .add_column(Column::auto())
255 .build(ctx);
256
257 let selector = Selector {
258 widget: self.widget_builder.with_child(grid).build(ctx),
259 items: self.items.into(),
260 items_panel: items_panel.into(),
261 prev: prev.into(),
262 next: next.into(),
263 current: self.current.into(),
264 };
265
266 ctx.add_node(UiNode::new(selector))
267 }
268}
269
270#[cfg(test)]
271mod test {
272 use crate::selector::SelectorBuilder;
273 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
274
275 #[test]
276 fn test_deletion() {
277 test_widget_deletion(|ctx| SelectorBuilder::new(WidgetBuilder::new()).build(ctx));
278 }
279}