fyrox_ui/
selector.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use 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        /// Creates [`SelectorMessage::AddItem`] message.
53        SelectorMessage:AddItem => fn add_item(Handle<UiNode>), layout: false
54    );
55    define_constructor!(
56        /// Creates [`SelectorMessage::RemoveItem`] message.
57        SelectorMessage:RemoveItem => fn remove_item(Handle<UiNode>), layout: false
58    );
59    define_constructor!(
60        /// Creates [`SelectorMessage::SetItems`] message.
61        SelectorMessage:SetItems => fn set_items(items: Vec<Handle<UiNode>>, remove_previous: bool), layout: false
62    );
63    define_constructor!(
64        /// Creates [`SelectorMessage::Current`] message.
65        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}