xref: /StarryEngine/starry_toolkit/src/widgets/mod.rs (revision 8a41b76c5717dfb7ea5d2beaed20030c10ffce86)
1 use std::{
2     any::Any,
3     cell::{Cell, RefCell},
4     sync::Arc,
5 };
6 
7 use starry_client::base::renderer::Renderer;
8 use starry_server::core::{SCREEN_HEIGHT, SCREEN_WIDTH};
9 
10 use crate::base::{event::Event, rect::Rect, vector2::Vector2};
11 
12 pub mod image;
13 pub mod label;
14 
15 pub fn widget_add_child(parent: Arc<dyn Widget>, child: Arc<dyn Widget>) {
16     parent.children().borrow_mut().push(child.clone());
17     (*child.parent().borrow_mut()) = Some(parent.clone());
18     parent.arrange_all();
19 }
20 
21 /// # 函数功能
22 /// 工具类 根据pivot和offset来进行矩形位置的对齐
23 ///
24 /// ## 参数
25 /// - origin_rect: 待对齐的矩形
26 /// - relative_rect: 作为对齐参考的矩形
27 /// - pivot: 对齐方式
28 /// - pivot_offset: 偏移量
29 ///
30 /// ## 返回值
31 /// 对齐后的矩形
32 pub fn align_rect(
33     origin_rect: Rect,
34     relative_rect: Rect,
35     pivot: PivotType,
36     pivot_offset: Vector2,
37 ) -> Rect {
38     let relative_pos = match pivot {
39         PivotType::None => Vector2::new(0, 0),
40         PivotType::Bottom => relative_rect.bottom_pos(),
41         PivotType::BottomLeft => relative_rect.bottom_left_pos(),
42         PivotType::BottomRight => relative_rect.bottom_right_pos(),
43         PivotType::Center => relative_rect.center_pos(),
44         PivotType::Top => relative_rect.top_pos(),
45         PivotType::TopLeft => relative_rect.top_left_pos(),
46         PivotType::TopRight => relative_rect.top_right_pos(),
47         PivotType::Left => relative_rect.left_pos(),
48         PivotType::Right => relative_rect.right_pos(),
49     };
50 
51     let mut target_pos = relative_pos + pivot_offset;
52 
53     let negative_width = -(origin_rect.width as i32);
54     let negative_height = -(origin_rect.height as i32);
55     let offset_vec = match pivot {
56         PivotType::None => Vector2::new(0, 0),
57         PivotType::Bottom => Vector2::new(negative_width / 2, negative_height),
58         PivotType::BottomLeft => Vector2::new(0, negative_height),
59         PivotType::BottomRight => Vector2::new(negative_width, negative_height),
60         PivotType::Center => Vector2::new(negative_width / 2, negative_height / 2),
61         PivotType::Top => Vector2::new(negative_width / 2, 0),
62         PivotType::TopLeft => Vector2::new(0, 0),
63         PivotType::TopRight => Vector2::new(negative_width, 0),
64         PivotType::Left => Vector2::new(0, negative_height / 2),
65         PivotType::Right => Vector2::new(negative_width, negative_height / 2),
66     };
67 
68     target_pos = target_pos + offset_vec;
69     Rect::new(
70         target_pos.x,
71         target_pos.y,
72         origin_rect.width,
73         origin_rect.height,
74     )
75 }
76 
77 #[derive(PartialEq, Copy, Clone)]
78 pub enum PivotType {
79     /// 不进行对齐 pivot_offset即为世界坐标
80     None,
81     /// 对齐左上角(默认对齐方式,这是由于矩形位置通过左上角顶点坐标来表示)
82     TopLeft,
83     /// 对齐正上方
84     Top,
85     /// 对齐右上角
86     TopRight,
87     /// 对齐正左方
88     Left,
89     /// 对齐中心
90     Center,
91     /// 对齐正右方
92     Right,
93     /// 对齐左下角
94     BottomLeft,
95     /// 对齐正下方
96     Bottom,
97     /// 对齐右下角
98     BottomRight,
99 }
100 
101 ///  UI组件需要实现的特性
102 pub trait Widget: Any {
103     /// 返回渲染的矩形区域
104     fn rect(&self) -> &Cell<Rect>;
105 
106     /// 对齐方式
107     fn pivot(&self) -> &Cell<PivotType>;
108 
109     /// 基于基准点的偏移量
110     fn pivot_offset(&self) -> &Cell<Vector2>;
111 
112     /// 返回组件的名字
113     fn name(&self) -> &str;
114 
115     /// 返回父物体
116     fn parent(&self) -> &RefCell<Option<Arc<dyn Widget>>>;
117 
118     /// 返回子组件数组
119     fn children(&self) -> &RefCell<Vec<Arc<dyn Widget>>>;
120 
121     /// 渲染组件
122     fn draw(&self, renderer: &mut dyn Renderer, focused: bool);
123 
124     /// 更新组件状态
125     fn update(&self) {}
126 
127     /// 处理输入事件
128     fn handle_event(
129         &self,
130         _event: Event,
131         _focused: bool,
132         _redraw: &mut bool,
133         _caught: &mut bool,
134     ) -> bool {
135         false
136     }
137 
138     fn set_pivot_type(&self, pivot_type: PivotType) {
139         self.set_pivot_type_base(pivot_type);
140     }
141 
142     /// 修改对齐方式的统一处理 方便覆写
143     fn set_pivot_type_base(&self, pivot_type: PivotType) {
144         self.pivot().set(pivot_type);
145         self.arrange_all();
146     }
147 
148     fn set_pivot_offset(&self, pivot_offset: Vector2) {
149         self.set_pivot_offset_base(pivot_offset);
150     }
151 
152     /// 修改对齐偏移量的统一处理 方便覆写
153     fn set_pivot_offset_base(&self, pivot_offset: Vector2) {
154         self.pivot_offset().set(pivot_offset);
155         self.arrange_all();
156     }
157 
158     fn resize(&self, width: u32, height: u32) {
159         self.resize_base(width, height);
160     }
161 
162     /// 修改大小时的统一处理 方便覆写
163     fn resize_base(&self, width: u32, height: u32) {
164         let mut rect = self.rect().get();
165         rect.width = width;
166         rect.height = height;
167         self.rect().set(rect);
168         self.arrange_all();
169     }
170 
171     /// 重新排布自身和子对象的位置
172     fn arrange_all(&self) {
173         self.arrange_self();
174 
175         for child in self.children().borrow_mut().iter() {
176             child.arrange_all();
177         }
178     }
179 
180     fn arrange_self(&self) {
181         self.arrange_self_base();
182     }
183 
184     /// 根据父物体和pivot值来调整自身位置 统一处理 方便覆写
185     fn arrange_self_base(&self) {
186         let relative_rect: Rect = if self.parent().borrow().is_some() {
187             self.parent().borrow().as_ref().unwrap().rect().get()
188         } else {
189             // 没有父物体 则以整个屏幕作为参考
190             Rect::new(0, 0, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32)
191         };
192 
193         let target_rect = align_rect(
194             self.rect().get(),
195             relative_rect,
196             self.pivot().get(),
197             self.pivot_offset().get(),
198         );
199 
200         self.rect().set(target_rect);
201     }
202 }
203