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