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