rg3d_ui/
border.rs

1use crate::{
2    core::{algebra::Vector2, math::Rect, pool::Handle, scope_profile},
3    draw::{CommandTexture, Draw, DrawingContext},
4    message::UiMessage,
5    widget::{Widget, WidgetBuilder},
6    BuildContext, Control, Thickness, UiNode, UserInterface, BRUSH_PRIMARY,
7};
8use std::any::{Any, TypeId};
9use std::ops::{Deref, DerefMut};
10
11#[derive(Clone)]
12pub struct Border {
13    widget: Widget,
14    stroke_thickness: Thickness,
15}
16
17crate::define_widget_deref!(Border);
18
19impl Control for Border {
20    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
21        if type_id == TypeId::of::<Self>() {
22            Some(self)
23        } else {
24            None
25        }
26    }
27
28    fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
29        scope_profile!();
30
31        let margin_x = self.stroke_thickness.left + self.stroke_thickness.right;
32        let margin_y = self.stroke_thickness.top + self.stroke_thickness.bottom;
33
34        let size_for_child = Vector2::new(available_size.x - margin_x, available_size.y - margin_y);
35        let mut desired_size = Vector2::default();
36
37        for child_handle in self.widget.children() {
38            ui.measure_node(*child_handle, size_for_child);
39            let child = ui.nodes.borrow(*child_handle);
40            let child_desired_size = child.desired_size();
41            if child_desired_size.x > desired_size.x {
42                desired_size.x = child_desired_size.x;
43            }
44            if child_desired_size.y > desired_size.y {
45                desired_size.y = child_desired_size.y;
46            }
47        }
48
49        desired_size.x += margin_x;
50        desired_size.y += margin_y;
51
52        desired_size
53    }
54
55    fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
56        scope_profile!();
57
58        let rect_for_child = Rect::new(
59            self.stroke_thickness.left,
60            self.stroke_thickness.top,
61            final_size.x - (self.stroke_thickness.right + self.stroke_thickness.left),
62            final_size.y - (self.stroke_thickness.bottom + self.stroke_thickness.top),
63        );
64
65        for child_handle in self.widget.children() {
66            ui.arrange_node(*child_handle, &rect_for_child);
67        }
68
69        final_size
70    }
71
72    fn draw(&self, drawing_context: &mut DrawingContext) {
73        let bounds = self.widget.screen_bounds();
74        DrawingContext::push_rect_filled(drawing_context, &bounds, None);
75        drawing_context.commit(
76            self.clip_bounds(),
77            self.widget.background(),
78            CommandTexture::None,
79            None,
80        );
81
82        drawing_context.push_rect_vary(&bounds, self.stroke_thickness);
83        drawing_context.commit(
84            self.clip_bounds(),
85            self.widget.foreground(),
86            CommandTexture::None,
87            None,
88        );
89    }
90
91    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
92        self.widget.handle_routed_message(ui, message);
93    }
94}
95
96impl Border {
97    pub fn new(widget: Widget) -> Self {
98        Self {
99            widget,
100            stroke_thickness: Thickness::uniform(1.0),
101        }
102    }
103}
104
105pub struct BorderBuilder {
106    pub widget_builder: WidgetBuilder,
107    pub stroke_thickness: Option<Thickness>,
108}
109
110impl BorderBuilder {
111    pub fn new(widget_builder: WidgetBuilder) -> Self {
112        Self {
113            widget_builder,
114            stroke_thickness: None,
115        }
116    }
117
118    pub fn with_stroke_thickness(mut self, stroke_thickness: Thickness) -> Self {
119        self.stroke_thickness = Some(stroke_thickness);
120        self
121    }
122
123    pub fn build_border(mut self) -> Border {
124        if self.widget_builder.foreground.is_none() {
125            self.widget_builder.foreground = Some(BRUSH_PRIMARY);
126        }
127        Border {
128            widget: self.widget_builder.build(),
129            stroke_thickness: self
130                .stroke_thickness
131                .unwrap_or_else(|| Thickness::uniform(1.0)),
132        }
133    }
134
135    pub fn build(self, ctx: &mut BuildContext<'_>) -> Handle<UiNode> {
136        ctx.add_node(UiNode::new(self.build_border()))
137    }
138}