rg3d_ui/
range.rs

1use crate::{
2    core::pool::Handle,
3    define_constructor,
4    grid::{Column, GridBuilder, Row},
5    message::{MessageDirection, UiMessage},
6    numeric::{NumericType, NumericUpDownBuilder, NumericUpDownMessage},
7    text::TextBuilder,
8    widget::{Widget, WidgetBuilder},
9    BuildContext, Control, Thickness, UiNode, UserInterface, VerticalAlignment,
10};
11use std::{
12    any::{Any, TypeId},
13    ops::{Deref, DerefMut, Range},
14};
15
16#[derive(Debug, PartialEq)]
17pub enum RangeEditorMessage<T>
18where
19    T: NumericType,
20{
21    Value(Range<T>),
22}
23
24impl<T: NumericType> RangeEditorMessage<T> {
25    define_constructor!(RangeEditorMessage:Value => fn value(Range<T>), layout: false);
26}
27
28#[derive(Debug, Clone)]
29pub struct RangeEditor<T>
30where
31    T: NumericType,
32{
33    widget: Widget,
34    value: Range<T>,
35    start: Handle<UiNode>,
36    end: Handle<UiNode>,
37}
38
39impl<T> Deref for RangeEditor<T>
40where
41    T: NumericType,
42{
43    type Target = Widget;
44
45    fn deref(&self) -> &Self::Target {
46        &self.widget
47    }
48}
49
50impl<T> DerefMut for RangeEditor<T>
51where
52    T: NumericType,
53{
54    fn deref_mut(&mut self) -> &mut Self::Target {
55        &mut self.widget
56    }
57}
58
59const SYNC_FLAG: u64 = 1;
60
61impl<T> Control for RangeEditor<T>
62where
63    T: NumericType,
64{
65    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
66        if type_id == TypeId::of::<Self>() {
67            Some(self)
68        } else {
69            None
70        }
71    }
72
73    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
74        self.widget.handle_routed_message(ui, message);
75
76        if message.direction() == MessageDirection::ToWidget && message.flags != SYNC_FLAG {
77            if let Some(RangeEditorMessage::Value(range)) = message.data::<RangeEditorMessage<T>>()
78            {
79                if message.destination() == self.handle && self.value != *range {
80                    self.value = range.clone();
81
82                    ui.send_message(NumericUpDownMessage::value(
83                        self.start,
84                        MessageDirection::ToWidget,
85                        range.start,
86                    ));
87                    ui.send_message(NumericUpDownMessage::value(
88                        self.end,
89                        MessageDirection::ToWidget,
90                        range.end,
91                    ));
92
93                    ui.send_message(message.reverse());
94                }
95            } else if let Some(NumericUpDownMessage::Value(value)) =
96                message.data::<NumericUpDownMessage<T>>()
97            {
98                if message.destination() == self.start {
99                    if *value < self.value.end {
100                        ui.send_message(RangeEditorMessage::value(
101                            self.handle,
102                            MessageDirection::ToWidget,
103                            Range {
104                                start: *value,
105                                end: self.value.end,
106                            },
107                        ));
108                    } else {
109                        let mut msg = NumericUpDownMessage::value(
110                            self.start,
111                            MessageDirection::ToWidget,
112                            self.value.end,
113                        );
114                        msg.flags = SYNC_FLAG;
115                        ui.send_message(msg);
116                    }
117                } else if message.destination() == self.end {
118                    if *value > self.value.start {
119                        ui.send_message(RangeEditorMessage::value(
120                            self.handle,
121                            MessageDirection::ToWidget,
122                            Range {
123                                start: self.value.start,
124                                end: *value,
125                            },
126                        ));
127                    } else {
128                        let mut msg = NumericUpDownMessage::value(
129                            self.end,
130                            MessageDirection::ToWidget,
131                            self.value.start,
132                        );
133                        msg.flags = SYNC_FLAG;
134                        ui.send_message(msg);
135                    }
136                }
137            }
138        }
139    }
140}
141
142pub struct RangeEditorBuilder<T>
143where
144    T: NumericType,
145{
146    widget_builder: WidgetBuilder,
147    value: Range<T>,
148}
149
150impl<T> RangeEditorBuilder<T>
151where
152    T: NumericType,
153{
154    pub fn new(widget_builder: WidgetBuilder) -> Self {
155        Self {
156            widget_builder,
157            value: Range::default(),
158        }
159    }
160
161    pub fn with_value(mut self, value: Range<T>) -> Self {
162        self.value = value;
163        self
164    }
165
166    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
167        let start = NumericUpDownBuilder::new(
168            WidgetBuilder::new()
169                .with_margin(Thickness::uniform(1.0))
170                .on_column(1),
171        )
172        .with_value(self.value.start)
173        .build(ctx);
174        let end = NumericUpDownBuilder::new(
175            WidgetBuilder::new()
176                .with_margin(Thickness::uniform(1.0))
177                .on_column(3),
178        )
179        .with_value(self.value.end)
180        .build(ctx);
181        let editor = RangeEditor {
182            widget: self
183                .widget_builder
184                .with_child(
185                    GridBuilder::new(
186                        WidgetBuilder::new()
187                            .with_child(
188                                TextBuilder::new(WidgetBuilder::new().on_column(0))
189                                    .with_text("Start")
190                                    .with_vertical_text_alignment(VerticalAlignment::Center)
191                                    .build(ctx),
192                            )
193                            .with_child(start)
194                            .with_child(
195                                TextBuilder::new(WidgetBuilder::new().on_column(2))
196                                    .with_vertical_text_alignment(VerticalAlignment::Center)
197                                    .with_text("End")
198                                    .build(ctx),
199                            )
200                            .with_child(end),
201                    )
202                    .add_column(Column::strict(30.0))
203                    .add_column(Column::stretch())
204                    .add_column(Column::strict(30.0))
205                    .add_column(Column::stretch())
206                    .add_row(Row::stretch())
207                    .build(ctx),
208                )
209                .build(),
210            value: self.value,
211            start,
212            end,
213        };
214
215        ctx.add_node(UiNode::new(editor))
216    }
217}