xref: /StarryEngine/starry_toolkit/src/widgets/mod.rs (revision 1bee64b64bc410ee78964a11a40a0fff69945480)
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::{
11     base::{event::Event, panel::Panel, rect::Rect, vector2::Vector2},
12     util::{align_rect, widget_set_panel},
13 };
14 
15 pub mod image;
16 pub mod label;
17 
18 #[derive(PartialEq, Copy, Clone, Debug)]
19 pub enum PivotType {
20     /// 不进行对齐 pivot_offset即为世界坐标
21     None,
22     /// 对齐左上角(默认对齐方式,这是由于矩形位置通过左上角顶点坐标来表示)
23     TopLeft,
24     /// 对齐正上方
25     Top,
26     /// 对齐右上角
27     TopRight,
28     /// 对齐正左方
29     Left,
30     /// 对齐中心
31     Center,
32     /// 对齐正右方
33     Right,
34     /// 对齐左下角
35     BottomLeft,
36     /// 对齐正下方
37     Bottom,
38     /// 对齐右下角
39     BottomRight,
40 }
41 
42 ///  UI组件需要实现的特性
43 pub trait Widget: Any {
44     /// 返回自身指针
45     fn self_ref(&self) -> Arc<dyn Widget>;
46 
47     /// 返回Any引用
48     fn as_any_ref(&self) -> &dyn Any;
49 
50     /// 返回渲染的矩形区域
51     fn rect(&self) -> &Cell<Rect>;
52 
53     /// 对齐方式
54     fn pivot(&self) -> &Cell<PivotType>;
55 
56     /// 基于基准点的偏移量
57     fn pivot_offset(&self) -> &Cell<Vector2>;
58 
59     /// 所属面板
60     fn panel(&self) -> &RefCell<Option<Arc<Panel>>>;
61 
62     /// 返回组件的名字
63     fn name(&self) -> &str;
64 
65     /// 返回子组件数组
66     fn children(&self) -> &RefCell<Vec<Arc<dyn Widget>>>;
67 
68     /// 父物体
69     fn parent(&self) -> &RefCell<Option<Arc<dyn Widget>>>;
70 
71     /// 添加子物体
72     fn add_child(&self, widget: Arc<dyn Widget>) {
73         self.children().borrow_mut().push(widget.clone());
74 
75         // 赋值父物体
76         (*widget.parent().borrow_mut()) = Some(self.self_ref());
77 
78         // 赋值所属的面板
79         if self.panel().borrow().is_some() {
80             widget_set_panel(&widget, &self.panel().borrow().clone().unwrap());
81         }
82     }
83 
84     /// 渲染组件
85     fn draw(&self, renderer: &mut dyn Renderer, focused: bool);
86 
87     /// 更新组件状态
88     fn update(&self) {}
89 
90     /// 处理输入事件
91     fn handle_event(
92         &self,
93         _event: Event,
94         _focused: bool,
95         _redraw: &Cell<bool>,
96         _caught: &Cell<bool>,
97     ) -> bool {
98         false
99     }
100 
101     fn set_pivot_type(&self, pivot_type: PivotType) {
102         self.set_pivot_type_base(pivot_type);
103     }
104 
105     /// 修改对齐方式的统一处理 方便覆写
106     fn set_pivot_type_base(&self, pivot_type: PivotType) {
107         self.pivot().set(pivot_type);
108         self.arrange_all();
109     }
110 
111     fn set_pivot_offset(&self, pivot_offset: Vector2) {
112         self.set_pivot_offset_base(pivot_offset);
113     }
114 
115     /// 修改对齐偏移量的统一处理 方便覆写
116     fn set_pivot_offset_base(&self, pivot_offset: Vector2) {
117         self.pivot_offset().set(pivot_offset);
118         self.arrange_all();
119     }
120 
121     fn resize(&self, width: u32, height: u32) {
122         self.resize_base(width, height);
123     }
124 
125     /// 修改大小时的统一处理 方便覆写
126     fn resize_base(&self, width: u32, height: u32) {
127         let mut rect = self.rect().get();
128         rect.width = width;
129         rect.height = height;
130         self.rect().set(rect);
131         self.arrange_all();
132     }
133 
134     /// 重新排布自身和子对象的位置
135     fn arrange_all(&self) {
136         self.arrange_self();
137 
138         for child in self.children().borrow_mut().iter() {
139             child.arrange_all();
140         }
141     }
142 
143     fn arrange_self(&self) {
144         self.arrange_self_base();
145     }
146 
147     /// 根据参考的矩形和pivot值来调整自身位置(默认为父物体,也可以自定义为其他矩形)
148     /// 统一处理 方便覆写
149     fn arrange_self_base(&self) {
150         let relative_rect: Rect = if self.parent().borrow().is_some() {
151             // 优先以父物体作为参考
152             self.parent().borrow().clone().unwrap().rect().get()
153         } else if self.panel().borrow().is_some() {
154             // 没有父物体 则以所属面板作为参考
155             self.panel().borrow().clone().unwrap().rect()
156         } else {
157             // 否则以整个屏幕作为参考
158             Rect::new(0, 0, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32)
159         };
160 
161         let target_rect = align_rect(
162             self.rect().get(),
163             relative_rect,
164             self.pivot().get(),
165             self.pivot_offset().get(),
166         );
167 
168         self.rect().set(target_rect);
169     }
170 }
171