xref: /Held/src/utils/ui/uicore.rs (revision fcc6ced0d18453415ce4b88b047a4e8e424fc693)
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