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}