1 use std::{ 2 io, 3 sync::{atomic::AtomicU16, Arc, Mutex, MutexGuard, Once, RwLock, Weak}, 4 }; 5 6 use crossterm::{ 7 style::Color, 8 terminal::{self}, 9 }; 10 use lazy_static::lazy_static; 11 12 use crate::{ 13 config::appconfig::AppSetting, 14 utils::{ 15 buffer::EditBuffer, cursor::CursorCrtl, style::StyleManager, terminal::TermManager, 16 ui::InfoLevel, 17 }, 18 }; 19 20 #[cfg(feature = "dragonos")] 21 use crate::utils::input::Input; 22 23 use super::{ 24 mode::mode::{Command, InputMode, Insert, LastLine, ModeType}, 25 mode::normal::Normal, 26 AppInfo, 27 }; 28 29 lazy_static! { 30 static ref COMMAND: Arc<Command> = Arc::new(Command); 31 static ref INSERT: Arc<Insert> = Arc::new(Insert); 32 static ref LASTLINE: Arc<LastLine> = Arc::new(LastLine::new()); 33 static ref NORMAL: Arc<Normal> = Arc::new(Normal::new()); 34 pub static ref APP_INFO: Mutex<AppInfo> = Mutex::new(AppInfo { 35 level: InfoLevel::Info, 36 info: String::new() 37 }); 38 } 39 40 pub static TAB_SIZE: AtomicU16 = AtomicU16::new(4); 41 42 #[derive(Debug, Clone, Copy)] 43 pub struct WinSize { 44 pub cols: u16, 45 pub rows: u16, 46 } 47 48 #[derive(Debug)] 49 pub struct UiCore { 50 pub buffer: Arc<EditBuffer>, 51 pub cursor: CursorCrtl, 52 53 #[allow(dead_code)] 54 setting: AppSetting, 55 container: Weak<Ui>, 56 57 edited: bool, 58 edited_once: Once, 59 } 60 61 impl UiCore { new(buf: Arc<EditBuffer>, cursor: CursorCrtl, setting: AppSetting) -> Self62 pub fn new(buf: Arc<EditBuffer>, cursor: CursorCrtl, setting: AppSetting) -> Self { 63 Self { 64 buffer: buf, 65 cursor, 66 container: Weak::new(), 67 setting, 68 edited: false, 69 edited_once: Once::new(), 70 } 71 } 72 edited(&self) -> bool73 pub fn edited(&self) -> bool { 74 self.edited 75 } 76 set_edited(&mut self)77 pub fn set_edited(&mut self) { 78 self.edited_once.call_once(|| self.edited = true) 79 } 80 update_bottom_state_bar(&mut self) -> io::Result<()>81 pub fn update_bottom_state_bar(&mut self) -> io::Result<()> { 82 let container = self.container.upgrade().unwrap(); 83 let mode = container.mode.read().unwrap().mode_type(); 84 if mode == ModeType::LastLine { 85 return Ok(()); 86 } 87 88 let size = *WINSIZE.read().unwrap(); 89 90 let store_x = self.cursor.x(); 91 let store_y = self.cursor.y(); 92 93 self.cursor.set_prefix_mode(false); 94 95 DEF_STYLE.read().unwrap().set_cmd_style()?; 96 let cmd_y = size.rows - 1; 97 self.cursor.move_to_row(cmd_y)?; 98 self.cursor.clear_current_line()?; 99 self.cursor 100 .write_with_pos(format!("{mode:?}"), 0, cmd_y, false)?; 101 102 let (buf_x, buf_y) = (store_x, store_y + 1 + self.buffer.offset() as u16); 103 let index_info = format!("row:{buf_y} col:{buf_x}"); 104 let len = index_info.len() as u16; 105 self.cursor 106 .write_with_pos(index_info, size.cols - len, cmd_y, false)?; 107 108 self.cursor.set_prefix_mode(true); 109 self.cursor.move_to(store_x, store_y)?; 110 111 let mut info = APP_INFO.lock().unwrap(); 112 info.level.set_style()?; 113 self.cursor 114 .write_with_pos(&info.info, size.cols / 3, cmd_y, false)?; 115 116 info.reset(); 117 self.cursor.move_to(store_x, store_y)?; 118 119 StyleManager::reset_color()?; 120 121 Ok(()) 122 } 123 124 /// 渲染部分文件内容,从y行开始渲染count行 125 /// 返回实际渲染行数 render_content(&mut self, mut y: u16, mut count: usize) -> io::Result<usize>126 pub fn render_content(&mut self, mut y: u16, mut count: usize) -> io::Result<usize> { 127 y += UI_HEAD_OFFSET; 128 let content_winsize = *CONTENT_WINSIZE.read().unwrap(); 129 130 // 超出正文范围 131 if y + count as u16 > content_winsize.rows { 132 count = (content_winsize.rows - y) as usize; 133 } 134 135 let def_style = *DEF_STYLE.read().unwrap(); 136 137 let content = self.buffer.get_content(y as usize, count); 138 139 if content.is_none() { 140 return Ok(0); 141 } 142 let content = content.unwrap(); 143 144 // 保存光标 145 let pos = self.cursor.store_tmp_pos(); 146 147 let tmp = y; 148 149 let num_len = (tmp + content_winsize.rows).to_string().len(); 150 151 self.cursor.set_prefix_mode(false); 152 for line in content.iter() { 153 let str = String::from_utf8_lossy(&line.data).to_string(); 154 def_style.set_content_style()?; 155 156 // 移动 157 self.cursor 158 .move_to(num_len as u16 + 2 + CursorCrtl::PREFIX_COL, y)?; 159 self.cursor.clear_current_line()?; 160 self.cursor.write(str)?; 161 y += 1; 162 StyleManager::reset_color()?; 163 } 164 165 self.cursor.update_line_prefix(&content, tmp, num_len)?; 166 self.cursor.set_prefix_mode(true); 167 168 self.cursor.restore_tmp_pos(pos)?; 169 170 self.cursor.highlight(None)?; 171 172 Ok(content.len()) 173 } 174 175 // 将正文向上滚动count行 scroll_up(&mut self, mut count: u16) -> io::Result<()>176 pub fn scroll_up(&mut self, mut count: u16) -> io::Result<()> { 177 let winsize = *CONTENT_WINSIZE.read().unwrap(); 178 179 let pos = self.cursor.store_tmp_pos(); 180 181 // 计算最多还能滚动多少行 182 let offset = self.buffer.offset(); 183 184 // 最多出两行 185 let linecount = self.buffer.line_count(); 186 if offset + winsize.rows as usize + count as usize >= linecount { 187 count = linecount as u16 - offset as u16 - winsize.rows; 188 } 189 self.buffer.set_offset(offset + count as usize); 190 // 将光标移动到滚动后的位置 191 self.cursor.move_to_row(winsize.rows - count)?; 192 193 // 执行滚动 194 TermManager::scroll_up(count)?; 195 196 // 清除光标以下的内容 197 TermManager::clear_under_cursor()?; 198 199 // 渲染count行数据 200 self.render_content(self.cursor.y(), count as usize)?; 201 202 self.cursor.restore_tmp_pos(pos)?; 203 204 self.cursor.highlight(Some(self.cursor.y() - count))?; 205 Ok(()) 206 } 207 scroll_down(&mut self, mut count: u16) -> io::Result<()>208 pub fn scroll_down(&mut self, mut count: u16) -> io::Result<()> { 209 let pos = self.cursor.store_tmp_pos(); 210 211 // 计算最多还能滚动多少行 212 let offset = self.buffer.offset(); 213 if offset < count as usize { 214 count = offset as u16; 215 } 216 217 self.buffer.set_offset(offset - count as usize); 218 219 // 执行滚动 220 TermManager::scroll_down(count)?; 221 222 // 将光标移动第count行 223 self.cursor.move_to_row(count - 1)?; 224 // 清除光标以上的内容 225 TermManager::clear_up_cursor()?; 226 227 // 渲染count行数据 228 self.render_content(0, count as usize)?; 229 230 self.cursor.restore_tmp_pos(pos)?; 231 232 self.cursor.highlight(Some(self.cursor.y() + count))?; 233 234 Ok(()) 235 } 236 } 237 238 #[derive(Debug)] 239 pub struct Ui { 240 pub core: Arc<Mutex<UiCore>>, 241 pub mode: RwLock<Arc<dyn InputMode>>, 242 } 243 244 lazy_static! { 245 pub static ref WINSIZE: RwLock<WinSize> = { 246 let size = terminal::size().unwrap(); 247 RwLock::new(WinSize { 248 cols: size.0, 249 rows: size.1, 250 }) 251 }; 252 pub static ref CONTENT_WINSIZE: RwLock<WinSize> = { 253 let size = *WINSIZE.read().unwrap(); 254 RwLock::new(WinSize { 255 cols: size.cols, 256 rows: size.rows - UI_CMD_HEIGHT - UI_HEAD_OFFSET, 257 }) 258 }; 259 pub static ref DEF_STYLE: RwLock<UiStyle> = { 260 let style = UiStyle { 261 content_fg: Some(Color::White), 262 content_bg: None, 263 cmd_line_fg: Some(Color::White), 264 cmd_line_bg: Some(Color::DarkCyan), 265 }; 266 267 RwLock::new(style) 268 }; 269 } 270 271 pub static UI_HEAD_OFFSET: u16 = 0; 272 pub const UI_CMD_HEIGHT: u16 = 1; 273 274 impl Ui { new(buf: Arc<EditBuffer>, setting: AppSetting) -> Arc<Self>275 pub fn new(buf: Arc<EditBuffer>, setting: AppSetting) -> Arc<Self> { 276 let mut cursor = CursorCrtl::new(buf.clone(), setting.line); 277 cursor.move_to(0, 0).unwrap(); 278 279 let core = Arc::new(Mutex::new(UiCore::new(buf, cursor, setting))); 280 let ret = Arc::new(Self { 281 mode: RwLock::new(Arc::new(Command)), 282 core: core.clone(), 283 }); 284 285 core.lock().unwrap().container = Arc::downgrade(&ret); 286 287 ret 288 } init_ui() -> io::Result<()>289 pub fn init_ui() -> io::Result<()> { 290 TermManager::init_term()?; 291 Ok(()) 292 } 293 start_page_ui(&self) -> io::Result<()>294 pub fn start_page_ui(&self) -> io::Result<()> { 295 StyleManager::set_foreground_color(Color::Cyan)?; 296 let mut core = self.core.lock().unwrap(); 297 core.cursor 298 .write_with_pos("Held - DragonOS/Linux Term Editor\n", 5, 0, false)?; 299 StyleManager::set_foreground_color(Color::Green)?; 300 core.cursor 301 .write_with_pos("Author: heyicong@dragonos.org\n", 7, 1, false)?; 302 StyleManager::set_foreground_color(Color::DarkMagenta)?; 303 core.cursor 304 .write_with_pos("Type any key to continue ><\n", 8, 2, false)?; 305 StyleManager::reset_color()?; 306 307 core.cursor.move_to(0, 0)?; 308 309 #[cfg(feature = "dragonos")] 310 let _ = Input::wait_keydown(); 311 312 #[cfg(not(feature = "dragonos"))] 313 loop { 314 let ev = crossterm::event::read()?; 315 if let crossterm::event::Event::Key(_) = ev { 316 break; 317 } 318 } 319 320 TermManager::clear_all()?; 321 322 Ok(()) 323 } 324 ui_loop(&self) -> io::Result<bool>325 pub fn ui_loop(&self) -> io::Result<bool> { 326 let mut core = self.core.lock().unwrap(); 327 core.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?; 328 core.update_bottom_state_bar()?; 329 core.cursor.move_to(0, 0)?; 330 core.cursor.highlight(None)?; 331 loop { 332 #[cfg(feature = "dragonos")] 333 let callback = { 334 let key = Input::wait_keydown()?; 335 self.mode.read().unwrap().key_event_route(&mut core, key)? 336 }; 337 338 #[cfg(not(feature = "dragonos"))] 339 let callback = { 340 let ev = crossterm::event::read()?; 341 self.mode.read().unwrap().event_route(&mut core, ev)? 342 }; 343 344 match callback { 345 super::event::WarpUiCallBackType::ChangMode(mode) => { 346 self.set_mode(mode, &mut core)? 347 } 348 super::event::WarpUiCallBackType::None => {} 349 super::event::WarpUiCallBackType::Exit(store) => { 350 self.ui_exit(); 351 return Ok(store); 352 } 353 } 354 355 if self.mode.read().unwrap().mode_type() != ModeType::LastLine { 356 core.update_bottom_state_bar()?; 357 } 358 } 359 } 360 set_mode(&self, mode: ModeType, ui: &mut MutexGuard<UiCore>) -> io::Result<()>361 fn set_mode(&self, mode: ModeType, ui: &mut MutexGuard<UiCore>) -> io::Result<()> { 362 if mode != ModeType::LastLine { 363 ui.cursor.set_prefix_mode(true); 364 365 ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?; 366 } 367 match mode { 368 ModeType::Command => *self.mode.write().unwrap() = COMMAND.clone(), 369 ModeType::LastLine => { 370 ui.cursor.set_prefix_mode(false); 371 let lastline = LASTLINE.clone(); 372 lastline.reset(); 373 *self.mode.write().unwrap() = lastline; 374 375 ui.cursor.move_to(0, u16::MAX - 1)?; 376 DEF_STYLE.read().unwrap().set_cmd_style()?; 377 // 写一个空行 378 ui.cursor.clear_current_line()?; 379 ui.cursor.move_to_columu(0)?; 380 ui.cursor.write(':')?; 381 } 382 ModeType::Insert => *self.mode.write().unwrap() = INSERT.clone(), 383 ModeType::Normal => *self.mode.write().unwrap() = NORMAL.clone(), 384 } 385 386 Ok(()) 387 } 388 ui_exit(&self)389 fn ui_exit(&self) { 390 // 处理未保存退出时的提醒 391 392 // 解决退出程序后颜色没改变的问题 393 StyleManager::reset_color().unwrap(); 394 } 395 } 396 397 #[derive(Debug, Clone, Copy)] 398 pub struct UiStyle { 399 pub content_fg: Option<Color>, 400 pub content_bg: Option<Color>, 401 pub cmd_line_fg: Option<Color>, 402 pub cmd_line_bg: Option<Color>, 403 } 404 405 impl UiStyle { set_cmd_style(&self) -> io::Result<()>406 pub fn set_cmd_style(&self) -> io::Result<()> { 407 StyleManager::reset_color()?; 408 if self.cmd_line_bg.is_some() { 409 StyleManager::set_background_color(self.cmd_line_bg.unwrap())?; 410 } 411 if self.cmd_line_fg.is_some() { 412 StyleManager::set_foreground_color(self.cmd_line_fg.unwrap())?; 413 } 414 415 Ok(()) 416 } 417 set_content_style(&self) -> io::Result<()>418 pub fn set_content_style(&self) -> io::Result<()> { 419 StyleManager::reset_color()?; 420 if self.content_bg.is_some() { 421 StyleManager::set_background_color(self.content_bg.unwrap())?; 422 } 423 if self.content_fg.is_some() { 424 StyleManager::set_foreground_color(self.content_fg.unwrap())?; 425 } 426 427 Ok(()) 428 } 429 } 430