xref: /StarryEngine/starry_toolkit/src/widgets/mod.rs (revision b0262857c524418d6bf547b3893cb67126e5df18)
145e17157SR0ronoa use std::{
245e17157SR0ronoa     any::Any,
345e17157SR0ronoa     cell::{Cell, RefCell},
445e17157SR0ronoa     sync::Arc,
545e17157SR0ronoa };
645e17157SR0ronoa 
745e17157SR0ronoa use starry_client::base::renderer::Renderer;
8*b0262857SR0ronoa use starry_server::core::{SCREEN_HEIGHT, SCREEN_WIDTH};
945e17157SR0ronoa 
10*b0262857SR0ronoa use crate::base::{rect::Rect, vector2::Vector2};
1145e17157SR0ronoa 
1245e17157SR0ronoa pub mod image;
1345e17157SR0ronoa pub mod label;
1445e17157SR0ronoa 
15*b0262857SR0ronoa pub fn widget_add_child(parent: Arc<dyn Widget>, child: Arc<dyn Widget>) {
16*b0262857SR0ronoa     parent.children().borrow_mut().push(child.clone());
17*b0262857SR0ronoa     (*child.parent().borrow_mut()) = Some(parent.clone());
18*b0262857SR0ronoa     parent.arrange_all();
1945e17157SR0ronoa }
2045e17157SR0ronoa 
21*b0262857SR0ronoa /// # 函数功能
22*b0262857SR0ronoa /// 工具类 根据pivot和offset来进行矩形位置的对齐
23*b0262857SR0ronoa ///
24*b0262857SR0ronoa /// ## 参数
25*b0262857SR0ronoa /// - origin_rect: 待对齐的矩形
26*b0262857SR0ronoa /// - relative_rect: 作为对齐参考的矩形
27*b0262857SR0ronoa /// - pivot: 对齐方式
28*b0262857SR0ronoa /// - pivot_offset: 偏移量
29*b0262857SR0ronoa ///
30*b0262857SR0ronoa /// ## 返回值
31*b0262857SR0ronoa /// 对齐后的矩形
32*b0262857SR0ronoa pub fn align_rect(
33*b0262857SR0ronoa     origin_rect: Rect,
34*b0262857SR0ronoa     relative_rect: Rect,
35*b0262857SR0ronoa     pivot: PivotType,
36*b0262857SR0ronoa     pivot_offset: Vector2,
37*b0262857SR0ronoa ) -> Rect {
38*b0262857SR0ronoa     let relative_pos = match pivot {
39*b0262857SR0ronoa         PivotType::None => Vector2::new(0, 0),
40*b0262857SR0ronoa         PivotType::Bottom => relative_rect.bottom_pos(),
41*b0262857SR0ronoa         PivotType::BottomLeft => relative_rect.bottom_left_pos(),
42*b0262857SR0ronoa         PivotType::BottomRight => relative_rect.bottom_right_pos(),
43*b0262857SR0ronoa         PivotType::Center => relative_rect.center_pos(),
44*b0262857SR0ronoa         PivotType::Top => relative_rect.top_pos(),
45*b0262857SR0ronoa         PivotType::TopLeft => relative_rect.top_left_pos(),
46*b0262857SR0ronoa         PivotType::TopRight => relative_rect.top_right_pos(),
47*b0262857SR0ronoa         PivotType::Left => relative_rect.left_pos(),
48*b0262857SR0ronoa         PivotType::Right => relative_rect.right_pos(),
49*b0262857SR0ronoa     };
50*b0262857SR0ronoa 
51*b0262857SR0ronoa     let mut target_pos = relative_pos + pivot_offset;
52*b0262857SR0ronoa 
53*b0262857SR0ronoa     let negative_width = -(origin_rect.width as i32);
54*b0262857SR0ronoa     let negative_height = -(origin_rect.height as i32);
55*b0262857SR0ronoa     let offset_vec = match pivot {
56*b0262857SR0ronoa         PivotType::None => Vector2::new(0, 0),
57*b0262857SR0ronoa         PivotType::Bottom => Vector2::new(negative_width / 2, negative_height),
58*b0262857SR0ronoa         PivotType::BottomLeft => Vector2::new(0, negative_height),
59*b0262857SR0ronoa         PivotType::BottomRight => Vector2::new(negative_width, negative_height),
60*b0262857SR0ronoa         PivotType::Center => Vector2::new(negative_width / 2, negative_height / 2),
61*b0262857SR0ronoa         PivotType::Top => Vector2::new(negative_width / 2, 0),
62*b0262857SR0ronoa         PivotType::TopLeft => Vector2::new(0, 0),
63*b0262857SR0ronoa         PivotType::TopRight => Vector2::new(negative_width, 0),
64*b0262857SR0ronoa         PivotType::Left => Vector2::new(0, negative_height / 2),
65*b0262857SR0ronoa         PivotType::Right => Vector2::new(negative_width, negative_height / 2),
66*b0262857SR0ronoa     };
67*b0262857SR0ronoa 
68*b0262857SR0ronoa     target_pos = target_pos + offset_vec;
69*b0262857SR0ronoa     Rect::new(
70*b0262857SR0ronoa         target_pos.x,
71*b0262857SR0ronoa         target_pos.y,
72*b0262857SR0ronoa         origin_rect.width,
73*b0262857SR0ronoa         origin_rect.height,
74*b0262857SR0ronoa     )
75*b0262857SR0ronoa }
76*b0262857SR0ronoa 
7745e17157SR0ronoa #[derive(PartialEq, Copy, Clone)]
78*b0262857SR0ronoa pub enum PivotType {
79*b0262857SR0ronoa     /// 不进行对齐 pivot_offset即为世界坐标
80*b0262857SR0ronoa     None,
81*b0262857SR0ronoa     /// 对齐左上角(默认对齐方式,这是由于矩形位置通过左上角顶点坐标来表示)
82*b0262857SR0ronoa     TopLeft,
83*b0262857SR0ronoa     /// 对齐正上方
84*b0262857SR0ronoa     Top,
85*b0262857SR0ronoa     /// 对齐右上角
86*b0262857SR0ronoa     TopRight,
87*b0262857SR0ronoa     /// 对齐正左方
8845e17157SR0ronoa     Left,
89*b0262857SR0ronoa     /// 对齐中心
9045e17157SR0ronoa     Center,
91*b0262857SR0ronoa     /// 对齐正右方
9245e17157SR0ronoa     Right,
93*b0262857SR0ronoa     /// 对齐左下角
94*b0262857SR0ronoa     BottomLeft,
95*b0262857SR0ronoa     /// 对齐正下方
96*b0262857SR0ronoa     Bottom,
97*b0262857SR0ronoa     /// 对齐右下角
98*b0262857SR0ronoa     BottomRight,
9945e17157SR0ronoa }
10045e17157SR0ronoa 
10145e17157SR0ronoa ///  UI组件需要实现的特性
10245e17157SR0ronoa pub trait Widget: Any {
10345e17157SR0ronoa     /// 返回渲染的矩形区域
10445e17157SR0ronoa     fn rect(&self) -> &Cell<Rect>;
10545e17157SR0ronoa 
106*b0262857SR0ronoa     /// 对齐方式
107*b0262857SR0ronoa     fn pivot(&self) -> &Cell<PivotType>;
10845e17157SR0ronoa 
109*b0262857SR0ronoa     /// 基于基准点的偏移量
110*b0262857SR0ronoa     fn pivot_offset(&self) -> &Cell<Vector2>;
11145e17157SR0ronoa 
11245e17157SR0ronoa     /// 返回组件的名字
11345e17157SR0ronoa     fn name(&self) -> &str;
11445e17157SR0ronoa 
115*b0262857SR0ronoa     /// 返回父物体
116*b0262857SR0ronoa     fn parent(&self) -> &RefCell<Option<Arc<dyn Widget>>>;
117*b0262857SR0ronoa 
11845e17157SR0ronoa     /// 返回子组件数组
11945e17157SR0ronoa     fn children(&self) -> &RefCell<Vec<Arc<dyn Widget>>>;
12045e17157SR0ronoa 
12145e17157SR0ronoa     /// 渲染组件
1226f3c1837SR0ronoa     fn draw(&self, renderer: &mut dyn Renderer, focused: bool);
12345e17157SR0ronoa 
12445e17157SR0ronoa     /// 更新组件状态
12545e17157SR0ronoa     fn update(&self) {}
12645e17157SR0ronoa 
127*b0262857SR0ronoa     fn set_pivot_type(&self, pivot_type: PivotType) {
128*b0262857SR0ronoa         self.set_pivot_type_base(pivot_type);
129*b0262857SR0ronoa     }
130*b0262857SR0ronoa 
131*b0262857SR0ronoa     /// 修改对齐方式的统一处理 方便覆写
132*b0262857SR0ronoa     fn set_pivot_type_base(&self, pivot_type: PivotType) {
133*b0262857SR0ronoa         self.pivot().set(pivot_type);
134*b0262857SR0ronoa         self.arrange_all();
135*b0262857SR0ronoa     }
136*b0262857SR0ronoa 
137*b0262857SR0ronoa     fn set_pivot_offset(&self, pivot_offset: Vector2) {
138*b0262857SR0ronoa         self.set_pivot_offset_base(pivot_offset);
139*b0262857SR0ronoa     }
140*b0262857SR0ronoa 
141*b0262857SR0ronoa     /// 修改对齐偏移量的统一处理 方便覆写
142*b0262857SR0ronoa     fn set_pivot_offset_base(&self, pivot_offset: Vector2) {
143*b0262857SR0ronoa         self.pivot_offset().set(pivot_offset);
144*b0262857SR0ronoa         self.arrange_all();
145*b0262857SR0ronoa     }
146*b0262857SR0ronoa 
147*b0262857SR0ronoa     fn resize(&self, width: u32, height: u32) {
148*b0262857SR0ronoa         self.resize_base(width, height);
149*b0262857SR0ronoa     }
150*b0262857SR0ronoa 
151*b0262857SR0ronoa     /// 修改大小时的统一处理 方便覆写
152*b0262857SR0ronoa     fn resize_base(&self, width: u32, height: u32) {
153*b0262857SR0ronoa         let mut rect = self.rect().get();
154*b0262857SR0ronoa         rect.width = width;
155*b0262857SR0ronoa         rect.height = height;
156*b0262857SR0ronoa         self.rect().set(rect);
157*b0262857SR0ronoa         self.arrange_all();
158*b0262857SR0ronoa     }
159*b0262857SR0ronoa 
160*b0262857SR0ronoa     /// 重新排布自身和子对象的位置
161*b0262857SR0ronoa     fn arrange_all(&self) {
162*b0262857SR0ronoa         self.arrange_self();
16349182ea7SR0ronoa 
16449182ea7SR0ronoa         for child in self.children().borrow_mut().iter() {
165*b0262857SR0ronoa             child.arrange_all();
16649182ea7SR0ronoa         }
16749182ea7SR0ronoa     }
16849182ea7SR0ronoa 
169*b0262857SR0ronoa     fn arrange_self(&self) {
170*b0262857SR0ronoa         self.arrange_self_base();
17149182ea7SR0ronoa     }
17249182ea7SR0ronoa 
173*b0262857SR0ronoa     /// 根据父物体和pivot值来调整自身位置 统一处理 方便覆写
174*b0262857SR0ronoa     fn arrange_self_base(&self) {
175*b0262857SR0ronoa         let relative_rect: Rect = if self.parent().borrow().is_some() {
176*b0262857SR0ronoa             self.parent().borrow().as_ref().unwrap().rect().get()
177*b0262857SR0ronoa         } else {
178*b0262857SR0ronoa             // 没有父物体 则以整个屏幕作为参考
179*b0262857SR0ronoa             Rect::new(0, 0, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32)
180*b0262857SR0ronoa         };
181*b0262857SR0ronoa 
182*b0262857SR0ronoa         let target_rect = align_rect(
183*b0262857SR0ronoa             self.rect().get(),
184*b0262857SR0ronoa             relative_rect,
185*b0262857SR0ronoa             self.pivot().get(),
186*b0262857SR0ronoa             self.pivot_offset().get(),
187*b0262857SR0ronoa         );
188*b0262857SR0ronoa 
189*b0262857SR0ronoa         self.rect().set(target_rect);
19049182ea7SR0ronoa     }
19145e17157SR0ronoa }
192