rg3d_ui/
decorator.rs

1use crate::{
2    border::{Border, BorderBuilder},
3    brush::{Brush, GradientPoint},
4    core::{algebra::Vector2, color::Color, pool::Handle},
5    define_constructor,
6    draw::DrawingContext,
7    message::{MessageDirection, UiMessage},
8    widget::{Widget, WidgetMessage},
9    BuildContext, Control, NodeHandleMapping, UiNode, UserInterface, BRUSH_BRIGHT, BRUSH_LIGHT,
10    BRUSH_LIGHTER, BRUSH_LIGHTEST, COLOR_DARKEST, COLOR_LIGHTEST,
11};
12use std::any::{Any, TypeId};
13use std::{
14    ops::{Deref, DerefMut},
15    sync::mpsc::Sender,
16};
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum DecoratorMessage {
20    Select(bool),
21    HoverBrush(Brush),
22    NormalBrush(Brush),
23    PressedBrush(Brush),
24    SelectedBrush(Brush),
25}
26
27impl DecoratorMessage {
28    define_constructor!(DecoratorMessage:Select => fn select(bool), layout: false);
29    define_constructor!(DecoratorMessage:HoverBrush => fn hover_brush(Brush), layout: false);
30    define_constructor!(DecoratorMessage:NormalBrush => fn normal_brush(Brush), layout: false);
31    define_constructor!(DecoratorMessage:PressedBrush => fn pressed_brush(Brush), layout: false);
32    define_constructor!(DecoratorMessage:SelectedBrush => fn selected_brush(Brush), layout: false);
33}
34
35/// A visual element that changes its appearance by listening specific events.
36/// It can has "pressed", "hover", "selected" or normal appearance:
37///
38/// `Pressed` - enables on mouse down message.
39/// `Selected` - whether decorator selected or not.
40/// `Hovered` - mouse is over decorator.
41/// `Normal` - not selected, pressed, hovered.
42///
43/// This element is widely used to provide some generic visual behaviour for various
44/// widgets. For example it used to decorate button, items in items control.
45#[derive(Clone)]
46pub struct Decorator {
47    border: Border,
48    normal_brush: Brush,
49    hover_brush: Brush,
50    pressed_brush: Brush,
51    selected_brush: Brush,
52    disabled_brush: Brush,
53    is_selected: bool,
54    is_pressable: bool,
55}
56
57impl Decorator {
58    pub fn border(&self) -> &Border {
59        &self.border
60    }
61
62    pub fn normal_brush(&self) -> &Brush {
63        &self.normal_brush
64    }
65
66    pub fn hover_brush(&self) -> &Brush {
67        &self.hover_brush
68    }
69
70    pub fn pressed_brush(&self) -> &Brush {
71        &self.pressed_brush
72    }
73
74    pub fn selected_brush(&self) -> &Brush {
75        &self.selected_brush
76    }
77
78    pub fn disabled_brush(&self) -> &Brush {
79        &self.disabled_brush
80    }
81
82    pub fn is_pressable(&self) -> bool {
83        self.is_pressable
84    }
85
86    pub fn is_selected(&self) -> bool {
87        self.is_pressable
88    }
89}
90
91impl Deref for Decorator {
92    type Target = Widget;
93
94    fn deref(&self) -> &Self::Target {
95        &self.border
96    }
97}
98
99impl DerefMut for Decorator {
100    fn deref_mut(&mut self) -> &mut Self::Target {
101        &mut self.border
102    }
103}
104
105impl Control for Decorator {
106    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
107        self.border.query_component(type_id).or_else(|| {
108            if type_id == TypeId::of::<Self>() {
109                Some(self)
110            } else {
111                None
112            }
113        })
114    }
115
116    fn resolve(&mut self, node_map: &NodeHandleMapping) {
117        self.border.resolve(node_map)
118    }
119
120    fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
121        self.border.measure_override(ui, available_size)
122    }
123
124    fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
125        self.border.arrange_override(ui, final_size)
126    }
127
128    fn draw(&self, drawing_context: &mut DrawingContext) {
129        self.border.draw(drawing_context)
130    }
131
132    fn update(&mut self, dt: f32, sender: &Sender<UiMessage>) {
133        self.border.update(dt, sender)
134    }
135
136    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
137        self.border.handle_routed_message(ui, message);
138
139        if let Some(msg) = message.data::<DecoratorMessage>() {
140            match msg {
141                &DecoratorMessage::Select(value) => {
142                    if self.is_selected != value {
143                        self.is_selected = value;
144                        if self.is_selected {
145                            ui.send_message(WidgetMessage::background(
146                                self.handle(),
147                                MessageDirection::ToWidget,
148                                self.selected_brush.clone(),
149                            ));
150                        } else {
151                            ui.send_message(WidgetMessage::background(
152                                self.handle(),
153                                MessageDirection::ToWidget,
154                                self.normal_brush.clone(),
155                            ));
156                        }
157                    }
158                }
159                DecoratorMessage::HoverBrush(brush) => {
160                    self.hover_brush = brush.clone();
161                    if self.is_mouse_directly_over {
162                        ui.send_message(WidgetMessage::background(
163                            self.handle(),
164                            MessageDirection::ToWidget,
165                            self.hover_brush.clone(),
166                        ));
167                    }
168                }
169                DecoratorMessage::NormalBrush(brush) => {
170                    self.normal_brush = brush.clone();
171                    if !self.is_selected && !self.is_mouse_directly_over {
172                        ui.send_message(WidgetMessage::background(
173                            self.handle(),
174                            MessageDirection::ToWidget,
175                            self.normal_brush.clone(),
176                        ));
177                    }
178                }
179                DecoratorMessage::PressedBrush(brush) => {
180                    self.pressed_brush = brush.clone();
181                }
182                DecoratorMessage::SelectedBrush(brush) => {
183                    self.selected_brush = brush.clone();
184                    if self.is_selected {
185                        ui.send_message(WidgetMessage::background(
186                            self.handle(),
187                            MessageDirection::ToWidget,
188                            self.selected_brush.clone(),
189                        ));
190                    }
191                }
192            }
193        } else if let Some(msg) = message.data::<WidgetMessage>() {
194            if message.destination() == self.handle()
195                || self.has_descendant(message.destination(), ui)
196            {
197                match msg {
198                    WidgetMessage::MouseLeave => {
199                        if self.is_selected {
200                            ui.send_message(WidgetMessage::background(
201                                self.handle(),
202                                MessageDirection::ToWidget,
203                                self.selected_brush.clone(),
204                            ));
205                        } else {
206                            ui.send_message(WidgetMessage::background(
207                                self.handle(),
208                                MessageDirection::ToWidget,
209                                self.normal_brush.clone(),
210                            ));
211                        }
212                    }
213                    WidgetMessage::MouseEnter => {
214                        ui.send_message(WidgetMessage::background(
215                            self.handle(),
216                            MessageDirection::ToWidget,
217                            self.hover_brush.clone(),
218                        ));
219                    }
220                    WidgetMessage::MouseDown { .. } if self.is_pressable => {
221                        ui.send_message(WidgetMessage::background(
222                            self.handle(),
223                            MessageDirection::ToWidget,
224                            self.pressed_brush.clone(),
225                        ));
226                    }
227                    WidgetMessage::MouseUp { .. } => {
228                        if self.is_selected {
229                            ui.send_message(WidgetMessage::background(
230                                self.handle(),
231                                MessageDirection::ToWidget,
232                                self.selected_brush.clone(),
233                            ));
234                        } else {
235                            ui.send_message(WidgetMessage::background(
236                                self.handle(),
237                                MessageDirection::ToWidget,
238                                self.normal_brush.clone(),
239                            ));
240                        }
241                    }
242                    _ => {}
243                }
244            }
245        }
246    }
247}
248
249pub struct DecoratorBuilder {
250    border_builder: BorderBuilder,
251    normal_brush: Option<Brush>,
252    hover_brush: Option<Brush>,
253    pressed_brush: Option<Brush>,
254    selected_brush: Option<Brush>,
255    disabled_brush: Option<Brush>,
256    pressable: bool,
257}
258
259impl DecoratorBuilder {
260    pub fn new(border_builder: BorderBuilder) -> Self {
261        Self {
262            border_builder,
263            normal_brush: None,
264            hover_brush: None,
265            pressed_brush: None,
266            selected_brush: None,
267            disabled_brush: None,
268            pressable: true,
269        }
270    }
271
272    pub fn with_normal_brush(mut self, brush: Brush) -> Self {
273        self.normal_brush = Some(brush);
274        self
275    }
276
277    pub fn with_hover_brush(mut self, brush: Brush) -> Self {
278        self.hover_brush = Some(brush);
279        self
280    }
281
282    pub fn with_pressed_brush(mut self, brush: Brush) -> Self {
283        self.pressed_brush = Some(brush);
284        self
285    }
286
287    pub fn with_selected_brush(mut self, brush: Brush) -> Self {
288        self.selected_brush = Some(brush);
289        self
290    }
291
292    pub fn with_disabled_brush(mut self, brush: Brush) -> Self {
293        self.disabled_brush = Some(brush);
294        self
295    }
296
297    pub fn with_pressable(mut self, pressable: bool) -> Self {
298        self.pressable = pressable;
299        self
300    }
301
302    pub fn build(mut self, ui: &mut BuildContext) -> Handle<UiNode> {
303        let normal_brush = self.normal_brush.unwrap_or(BRUSH_LIGHT);
304
305        if self.border_builder.widget_builder.foreground.is_none() {
306            self.border_builder.widget_builder.foreground = Some(Brush::LinearGradient {
307                from: Vector2::new(0.5, 0.0),
308                to: Vector2::new(0.5, 1.0),
309                stops: vec![
310                    GradientPoint {
311                        stop: 0.0,
312                        color: COLOR_LIGHTEST,
313                    },
314                    GradientPoint {
315                        stop: 0.25,
316                        color: COLOR_LIGHTEST,
317                    },
318                    GradientPoint {
319                        stop: 1.0,
320                        color: COLOR_DARKEST,
321                    },
322                ],
323            });
324        }
325
326        let mut border = self.border_builder.build_border();
327
328        border.set_background(normal_brush.clone());
329
330        let node = UiNode::new(Decorator {
331            border,
332            normal_brush,
333            hover_brush: self.hover_brush.unwrap_or(BRUSH_LIGHTER),
334            pressed_brush: self.pressed_brush.unwrap_or(BRUSH_LIGHTEST),
335            selected_brush: self.selected_brush.unwrap_or(BRUSH_BRIGHT),
336            disabled_brush: self
337                .disabled_brush
338                .unwrap_or_else(|| Brush::Solid(Color::opaque(50, 50, 50))),
339            is_selected: false,
340            is_pressable: self.pressable,
341        });
342        ui.add_node(node)
343    }
344}