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