4
0

uicore.rs 12 KB

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