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}