xref: /StarryEngine/starry_toolkit/src/layout/list.rs (revision 731cae0674923fcc85c6e683a2eee596eb642796)
1 use std::{
2     any::Any,
3     cell::{Cell, RefCell},
4     collections::BTreeMap,
5     sync::{Arc, Weak},
6 };
7 
8 use starry_client::base::renderer::Renderer;
9 
10 use crate::{
11     base::{panel::Panel, rect::Rect, vector2::Vector2},
12     traits::focus::Focus,
13     widgets::{PivotType, Widget},
14 };
15 
16 #[derive(PartialEq, Copy, Clone)]
17 pub enum ListArrangeType {
18     /// 横向排列
19     Horizontal,
20     /// 纵向排列
21     Vertical,
22 }
23 
24 #[derive(PartialEq, Copy, Clone)]
25 pub enum ListElementPivotType {
26     LeftOrTop,
27     Center,
28     RightOrBottom,
29 }
30 
31 pub struct List {
32     self_ref: RefCell<Weak<List>>,
33     rect: Cell<Rect>,
34     pivot: Cell<PivotType>,
35     pivot_offset: Cell<Vector2>,
36     children: RefCell<Vec<Arc<dyn Widget>>>,
37     parent: RefCell<Option<Arc<dyn Widget>>>,
38     panel: RefCell<Option<Arc<Panel>>>,
39 
40     space: Cell<u32>,
41     current_index: Cell<usize>,
42     elements: RefCell<BTreeMap<usize, Arc<dyn Widget>>>,
43     focused_id: Cell<Option<usize>>,
44     focused_widget: RefCell<Option<Arc<dyn Widget>>>,
45 
46     arrange_type: Cell<ListArrangeType>,
47     element_pivot_type: Cell<ListElementPivotType>,
48 }
49 
50 impl List {
new() -> Arc<Self>51     pub fn new() -> Arc<Self> {
52         let list = Arc::new(List {
53             self_ref: RefCell::new(Weak::default()),
54             rect: Cell::new(Rect::default()),
55             pivot: Cell::new(PivotType::TopLeft),
56             pivot_offset: Cell::new(Vector2::new(0, 0)),
57             children: RefCell::new(vec![]),
58             parent: RefCell::new(None),
59             panel: RefCell::new(None),
60             space: Cell::new(0),
61             current_index: Cell::new(0),
62             elements: RefCell::new(BTreeMap::new()),
63             focused_id: Cell::new(None),
64             focused_widget: RefCell::new(None),
65             arrange_type: Cell::new(ListArrangeType::Vertical),
66             element_pivot_type: Cell::new(ListElementPivotType::Center),
67         });
68 
69         (*list.self_ref.borrow_mut()) = Arc::downgrade(&list);
70 
71         return list;
72     }
73 
set_arrange_type(&self, arrange_type: ListArrangeType) -> &Self74     pub fn set_arrange_type(&self, arrange_type: ListArrangeType) -> &Self {
75         self.arrange_type.set(arrange_type);
76         self
77     }
78 
set_space(&self, space: u32) -> &Self79     pub fn set_space(&self, space: u32) -> &Self {
80         self.space.set(space);
81         self
82     }
83 
add_element<T: Widget>(&self, element: &Arc<T>) -> usize84     pub fn add_element<T: Widget>(&self, element: &Arc<T>) -> usize {
85         self.add_child(element.self_ref());
86 
87         self.elements
88             .borrow_mut()
89             .insert(self.current_index.get(), element.clone());
90 
91         let res = self.current_index.get();
92         self.current_index.set(res + 1);
93         self.arrange_elements();
94         return res;
95     }
96 
arrange_elements(&self)97     pub fn arrange_elements(&self) {
98         if self.elements.borrow().is_empty() {
99             return;
100         }
101 
102         self.arrange_self();
103 
104         // 遍历找到最大的长或宽值
105         let mut max_size: u32 = 0;
106         for (&_index, element) in self.elements.borrow().iter() {
107             match self.arrange_type.get() {
108                 ListArrangeType::Horizontal => {
109                     max_size = u32::max(max_size, element.rect().get().height);
110                 }
111                 ListArrangeType::Vertical => {
112                     max_size = u32::max(max_size, element.rect().get().width);
113                 }
114             }
115         }
116 
117         let mut x_offset: u32 = 0;
118         let mut y_offset: u32 = 0;
119 
120         for (&_index, element) in self.elements.borrow().iter() {
121             let align_vector: Vector2;
122             match self.arrange_type.get() {
123                 ListArrangeType::Horizontal => {
124                     align_vector = match self.element_pivot_type.get() {
125                         ListElementPivotType::LeftOrTop => {
126                             Vector2::new(x_offset as i32, y_offset as i32)
127                         }
128                         ListElementPivotType::Center => Vector2::new(
129                             x_offset as i32,
130                             y_offset as i32 + (max_size - element.rect().get().height) as i32 / 2,
131                         ),
132                         ListElementPivotType::RightOrBottom => Vector2::new(
133                             x_offset as i32,
134                             y_offset as i32 + (max_size - element.rect().get().height) as i32,
135                         ),
136                     };
137                 }
138                 ListArrangeType::Vertical => {
139                     align_vector = match self.element_pivot_type.get() {
140                         ListElementPivotType::LeftOrTop => {
141                             Vector2::new(x_offset as i32, y_offset as i32)
142                         }
143                         ListElementPivotType::Center => Vector2::new(
144                             x_offset as i32 + (max_size - element.rect().get().width) as i32 / 2,
145                             y_offset as i32,
146                         ),
147                         ListElementPivotType::RightOrBottom => Vector2::new(
148                             x_offset as i32 + (max_size - element.rect().get().width) as i32,
149                             y_offset as i32,
150                         ),
151                     }
152                 }
153             }
154 
155             element.set_pivot_type(PivotType::TopLeft);
156             element.set_pivot_offset(align_vector);
157             element.arrange_all();
158 
159             match self.arrange_type.get() {
160                 ListArrangeType::Horizontal => {
161                     x_offset += element.rect().get().width + self.space.get();
162                 }
163                 ListArrangeType::Vertical => {
164                     y_offset += element.rect().get().height + self.space.get();
165                 }
166             }
167         }
168     }
169 }
170 
171 impl Widget for List {
self_ref(&self) -> Arc<dyn Widget>172     fn self_ref(&self) -> Arc<dyn Widget> {
173         self.self_ref.borrow().upgrade().unwrap()
174     }
175 
as_any_ref(&self) -> &dyn Any176     fn as_any_ref(&self) -> &dyn Any {
177         self
178     }
179 
name(&self) -> &str180     fn name(&self) -> &str {
181         "List"
182     }
183 
rect(&self) -> &Cell<Rect>184     fn rect(&self) -> &Cell<Rect> {
185         &self.rect
186     }
187 
pivot(&self) -> &Cell<PivotType>188     fn pivot(&self) -> &Cell<PivotType> {
189         &self.pivot
190     }
191 
pivot_offset(&self) -> &Cell<Vector2>192     fn pivot_offset(&self) -> &Cell<Vector2> {
193         &self.pivot_offset
194     }
195 
parent(&self) -> &RefCell<Option<Arc<dyn Widget>>>196     fn parent(&self) -> &RefCell<Option<Arc<dyn Widget>>> {
197         &self.parent
198     }
199 
children(&self) -> &RefCell<Vec<Arc<dyn Widget>>>200     fn children(&self) -> &RefCell<Vec<Arc<dyn Widget>>> {
201         &self.children
202     }
203 
panel(&self) -> &RefCell<Option<Arc<Panel>>>204     fn panel(&self) -> &RefCell<Option<Arc<Panel>>> {
205         &self.panel
206     }
207 
draw(&self, renderer: &mut dyn Renderer, _focused: bool)208     fn draw(&self, renderer: &mut dyn Renderer, _focused: bool) {
209         for (&_index, widget) in self.elements.borrow().iter() {
210             widget.update();
211             widget.draw(renderer, self.is_focused(widget));
212         }
213     }
214 
handle_event( &self, _event: crate::base::event::Event, _focused: bool, _redraw: &Cell<bool>, _caught: &Cell<bool>, ) -> bool215     fn handle_event(
216         &self,
217         _event: crate::base::event::Event,
218         _focused: bool,
219         _redraw: &Cell<bool>,
220         _caught: &Cell<bool>,
221     ) -> bool {
222         false
223     }
224 }
225 
226 impl Focus for List {
focused_widget(&self) -> RefCell<Option<Arc<dyn Widget>>>227     fn focused_widget(&self) -> RefCell<Option<Arc<dyn Widget>>> {
228         self.focused_widget.clone()
229     }
230 
focus(&self, focused_widget: &Arc<dyn Widget>)231     fn focus(&self, focused_widget: &Arc<dyn Widget>) {
232         // 同时更新focused_id
233         for (&index, widget) in self.elements.borrow().iter() {
234             if Arc::ptr_eq(widget, focused_widget) {
235                 self.focused_id.set(Some(index));
236                 (*self.focused_widget.borrow_mut()) = Some(focused_widget.clone());
237             }
238         }
239     }
240 }
241