xref: /StarryEngine/starry_toolkit/src/widgets/label.rs (revision 731cae0674923fcc85c6e683a2eee596eb642796)
1 use std::{
2     any::Any,
3     cell::{Cell, RefCell},
4     cmp::max,
5     sync::{Arc, Weak},
6 };
7 
8 use starry_client::base::{color::Color, renderer::Renderer};
9 
10 use crate::{
11     base::{panel::Panel, rect::Rect, vector2::Vector2},
12     traits::text::Text,
13     util::{align_rect, get_local_rect},
14 };
15 
16 use super::{PivotType, Widget};
17 
18 // TODO 支持"调整字体大小以适配"的选项
19 #[derive(PartialEq, Copy, Clone)]
20 pub enum LabelOverflowType {
21     /// 不适配 溢出部分不显示
22     None,
23     /// 根据字数调整大小
24     ShinkToFit,
25     /// 省略多余内容
26     Omit,
27 }
28 
29 // TODO 暂不支持自动换行
30 pub struct Label {
31     self_ref: RefCell<Weak<Label>>,
32     rect: Cell<Rect>,
33     pivot: Cell<PivotType>,
34     pivot_offset: Cell<Vector2>,
35     children: RefCell<Vec<Arc<dyn Widget>>>,
36     parent: RefCell<Option<Arc<dyn Widget>>>,
37     panel: RefCell<Option<Arc<Panel>>>,
38     /// 实际上的文本
39     real_text: RefCell<String>,
40     /// 用于显示的文本
41     show_text: RefCell<String>,
42     text_color: Cell<Color>,
43     adapt_type: Cell<LabelOverflowType>,
44     /// 渲染文本时的矩形区域
45     text_rect: Cell<Rect>,
46     /// 文本在矩形框内的对齐方式
47     text_pivot: Cell<PivotType>,
48 }
49 
50 impl Label {
new() -> Arc<Self>51     pub fn new() -> Arc<Self> {
52         let label = Arc::new(Label {
53             rect: Cell::new(Rect::default()),
54             pivot: Cell::new(PivotType::TopLeft),
55             pivot_offset: Cell::new(Vector2::new(0, 0)),
56             children: RefCell::new(vec![]),
57             parent: RefCell::new(None),
58             panel: RefCell::new(None),
59             real_text: RefCell::new(String::new()),
60             show_text: RefCell::new(String::new()),
61             text_color: Cell::new(Color::rgb(0, 0, 0)), // 默认黑色字体
62             adapt_type: Cell::new(LabelOverflowType::None),
63             text_rect: Cell::new(Rect::default()),
64             text_pivot: Cell::new(PivotType::Center),
65             self_ref: RefCell::new(Weak::new()),
66         });
67 
68         (*label.self_ref.borrow_mut()) = Arc::downgrade(&label);
69 
70         return label;
71     }
72 
73     /// 处理文本溢出的情况
74     /// 在文本内容改变或大小改变时调用
handle_overflow(&self)75     fn handle_overflow(&self) {
76         let text = self.real_text.borrow();
77 
78         match self.adapt_type.get() {
79             // 不适配 溢出部分不显示
80             LabelOverflowType::None => {}
81             // 根据字数调整大小
82             LabelOverflowType::ShinkToFit => {
83                 self.resize_base(text.len() as u32 * 8 as u32, 16);
84             }
85             // 省略溢出的部分
86             LabelOverflowType::Omit => {
87                 let rect = self.rect.get();
88 
89                 if text.len() as u32 * 8 > rect.width {
90                     let max_count = max(0, (rect.width as i32 - 3 * 8) / 8);
91                     let mut omit_str = self.real_text.borrow().clone();
92                     let _ = omit_str.split_off(max_count as usize);
93                     omit_str.push_str("..."); // 溢出字符用省略号取代
94                     (*self.show_text.borrow_mut()) = omit_str;
95                 }
96             }
97         }
98 
99         self.text_rect.set(Rect::new(
100             0,
101             0,
102             self.show_text.borrow().len() as u32 * 8,
103             16,
104         ));
105 
106         self.text_rect.set(align_rect(
107             self.text_rect.get(),
108             self.rect.get(),
109             self.text_pivot.get(),
110             Vector2::new(0, 0),
111         ));
112     }
113 
set_adapt_type(&self, adapt_type: LabelOverflowType)114     pub fn set_adapt_type(&self, adapt_type: LabelOverflowType) {
115         self.adapt_type.set(adapt_type);
116         self.handle_overflow();
117         self.arrange_all();
118     }
119 
set_text_pivot_type(&self, pivot: PivotType)120     pub fn set_text_pivot_type(&self, pivot: PivotType) {
121         self.text_pivot.set(pivot);
122     }
123 }
124 
125 impl Widget for Label {
self_ref(&self) -> Arc<dyn Widget>126     fn self_ref(&self) -> Arc<dyn Widget> {
127         self.self_ref.borrow().upgrade().unwrap() as Arc<dyn Widget>
128     }
129 
as_any_ref(&self) -> &dyn Any130     fn as_any_ref(&self) -> &dyn Any {
131         self
132     }
133 
name(&self) -> &str134     fn name(&self) -> &str {
135         "Label"
136     }
137 
rect(&self) -> &Cell<Rect>138     fn rect(&self) -> &Cell<Rect> {
139         &self.rect
140     }
141 
pivot(&self) -> &Cell<PivotType>142     fn pivot(&self) -> &Cell<PivotType> {
143         &self.pivot
144     }
145 
pivot_offset(&self) -> &Cell<Vector2>146     fn pivot_offset(&self) -> &Cell<Vector2> {
147         &self.pivot_offset
148     }
149 
parent(&self) -> &RefCell<Option<Arc<dyn Widget>>>150     fn parent(&self) -> &RefCell<Option<Arc<dyn Widget>>> {
151         &self.parent
152     }
153 
children(&self) -> &RefCell<Vec<Arc<dyn Widget>>>154     fn children(&self) -> &RefCell<Vec<Arc<dyn Widget>>> {
155         &self.children
156     }
157 
panel(&self) -> &RefCell<Option<Arc<crate::base::panel::Panel>>>158     fn panel(&self) -> &RefCell<Option<Arc<crate::base::panel::Panel>>> {
159         &self.panel
160     }
161 
draw(&self, renderer: &mut dyn Renderer, _focused: bool)162     fn draw(&self, renderer: &mut dyn Renderer, _focused: bool) {
163         let origin_rect = self.text_rect.get();
164         let mut current_rect = self.text_rect.get(); // 当前字符渲染矩形
165         let origin_x = origin_rect.x;
166         let text = self.show_text.borrow().clone();
167 
168         // 矩形高度不满足
169         if origin_rect.height < 16 {
170             return;
171         }
172 
173         for char in text.chars() {
174             if char == '\n' {
175                 // 换行 退格到起始位置
176                 current_rect.x = origin_x;
177                 current_rect.y += 16;
178             } else {
179                 // 避免超出矩形范围
180                 if current_rect.x + 8 <= origin_rect.x + origin_rect.width as i32
181                     && current_rect.y + 16 <= origin_rect.y + origin_rect.height as i32
182                 {
183                     if self.panel().borrow().is_some() {
184                         let local_rect = get_local_rect(
185                             current_rect,
186                             self.panel().borrow().clone().unwrap().rect(),
187                         );
188                         renderer.char(local_rect.x, local_rect.y, char, self.text_color.get());
189                     } else {
190                         println!("[Error] Label do not belong to any panel!");
191                     }
192                 }
193                 current_rect.x += 8;
194             }
195         }
196     }
197 
set_pivot_type(&self, pivot_type: PivotType)198     fn set_pivot_type(&self, pivot_type: PivotType) {
199         self.set_pivot_type_base(pivot_type);
200 
201         self.text_rect.set(align_rect(
202             self.text_rect.get(),
203             self.rect.get(),
204             self.text_pivot.get(),
205             Vector2::new(0, 0),
206         ));
207     }
208 
set_pivot_offset(&self, pivot_offset: Vector2)209     fn set_pivot_offset(&self, pivot_offset: Vector2) {
210         self.set_pivot_offset_base(pivot_offset);
211 
212         self.text_rect.set(align_rect(
213             self.text_rect.get(),
214             self.rect.get(),
215             self.text_pivot.get(),
216             Vector2::new(0, 0),
217         ));
218     }
219 
resize(&self, width: u32, height: u32)220     fn resize(&self, width: u32, height: u32) {
221         self.resize_base(width, height);
222         self.handle_overflow();
223         self.arrange_all();
224     }
225 
arrange_self(&self)226     fn arrange_self(&self) {
227         self.arrange_self_base();
228 
229         self.text_rect.set(align_rect(
230             self.text_rect.get(),
231             self.rect.get(),
232             self.text_pivot.get(),
233             Vector2::new(0, 0),
234         ));
235     }
236 }
237 
238 impl Text for Label {
set_text<S: Into<String>>(&self, text: S) -> &Self239     fn set_text<S: Into<String>>(&self, text: S) -> &Self {
240         let text = text.into();
241         (*self.real_text.borrow_mut()) = text.clone();
242         (*self.show_text.borrow_mut()) = text;
243         self.handle_overflow(); //处理文本溢出的情况
244         self.arrange_all();
245         self
246     }
247 
set_text_color(&self, color: Color) -> &Self248     fn set_text_color(&self, color: Color) -> &Self {
249         self.text_color.set(color);
250         self
251     }
252 }
253