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