Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_tui/src/lib_tui/text/view.rs

7.7 KiB, 23 runs

created by r1870400018:1215, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::lib_tui::{
2 text::{
3 nav::PositionCursor,
4 typ::{
5 ContentType,
6 HighlightType,
7 TextType,
8 TextViewType,
9 },
10 },
11};
12
13use oxedyne_fe2o3_core::prelude::*;
14use oxedyne_fe2o3_geom::{
15 dim::Coord,
16 rect::{
17 AbsRect,
18 AbsSize,
19 },
20};
21use oxedyne_fe2o3_text::{
22 Text,
23 access::AccessibleText,
24};
25
26
27/// Contains access to the source text and keeps track of view metrics. The text extent is
28/// represented by a rectangle of `AbsSize` where the height is the current number of lines and the
29/// width is the character length of the longest line.
30#[derive(Clone, Debug, Default)]
31pub struct TextView {
32 pub ctyp: ContentType,
33 pub vtyp: TextViewType,
34 pub atext: AccessibleText<TextType, HighlightType>,
35 pub extent: AbsSize, // A dynamic rectangle representing the extent of the text.
36 pub term_view: AbsRect, // Current terminal view.
37 pub text_view: AbsRect, // Current text view.
38 pub term_cursor: Coord, // Cursor position in terminal coordinates.
39}
40
41impl TextView {
42
43 pub fn new(
44 ctyp: ContentType,
45 vtyp: TextViewType,
46 //atext: Option<AccessibleText<TextType, HighlightType>>,
47 atext: AccessibleText<TextType, HighlightType>,
48 )
49 -> Outcome<Self>
50 {
51 let text_lines = res!(atext.get_text_lines());
52 //if let Some(highlighter) = text_lines.get_highlighter_mut() {
53 // highlighter.build(text_lines);
54 //}
55 let extent = AbsSize::from((text_lines.max_width(), text_lines.len()));
56 //match vtyp {
57 // TextViewType::FileTree(ftree, _nav) => {
58 // if atext.is_none() {
59 // let txt = ftree.display(true)
60 // AccessibleText::Shared(Rc::new(RwLock::new(
61 // TextLines::new(
62 // vec![],
63 // Some(Highlighter::default()),
64 // ),
65 // ))),
66 // }
67 // }
68 // _ => {}
69 //}
70 Ok(Self {
71 ctyp,
72 vtyp,
73 atext: atext.clone(),
74 extent,
75 term_view: AbsRect::default(),
76 text_view: AbsRect::default(),
77 term_cursor: Coord::default(),
78 //editor,
79 //line_focus_mgr,
80 })
81 }
82
83 //pub fn get_highlighter<'a>(&'a self) -> Outcome<Option<&'a Highlighter<HighlightType>>> {
84 // let text_lines = res!(self.atext.get_text_lines());
85 // let result = text_lines.get_highlighter();
86 // Ok(text_lines.get_highlighter())
87 // //match self.atext {
88 // // AccessibleText::ThreadShared(ref locked) => {
89 // // let text_lines = lock_read!(locked);
90 // // Ok(text_lines.get_highlighter())
91 // // }
92 // // AccessibleText::Shared(ref locked) => {
93 // // let text_lines = lock_read!(locked);
94 // // Ok(text_lines.get_highlighter())
95 // // }
96 // //}
97 //}
98
99 //pub fn get_highlighter_mut(&mut self) -> Outcome<Option<&mut Highlighter<HighlightType>>> {
100 // let text_lines = res!(self.atext.get_text_lines_mut());
101 // Ok(text_lines.get_highlighter_mut())
102 // //match self.atext {
103 // // AccessibleText::ThreadShared(ref locked) => {
104 // // let text_lines = lock_write!(locked);
105 // // Ok(text_lines.get_highlighter_mut())
106 // // }
107 // // AccessibleText::Shared(ref locked) => {
108 // // let text_lines = lock_write!(locked);
109 // // Ok(text_lines.get_highlighter_mut())
110 // // }
111 // //}
112 //}
113
114 /// Extract the text view from the text.
115 ///
116 /// +----------------------------+
117 /// | | text
118 /// | |
119 /// | |
120 /// | x w x+w |
121 /// | +--------------+ |
122 /// | len | | |
123 /// |<-------|--->| line | |
124 /// | | | |
125 /// | | | |
126 /// | +--------------+ |
127 /// | text |
128 /// | view |
129 /// | |
130 /// | |
131 /// +----------------------------+
132 ///
133 pub fn extract_view<'a>(
134 lines: &'a [Text<TextType>],
135 text_view: &AbsRect,
136 )
137 -> Vec<&'a str>
138 {
139 let (x, y, w, h) = text_view.tup();
140 let mut result = Vec::new();
141 let len = lines.len();
142 let rng = if len < y {
143 return result;
144 } else if len > y + h {
145 y.as_index()..(y + h).as_index()
146 } else {
147 y.as_index()..len
148 };
149
150 for line in &lines[rng] {
151 let chars: Vec<char> = line.txt.chars().collect();
152 let len = chars.len();
153 if len <= x {
154 result.push("");
155 } else if len > x + w {
156 let start = line.txt.char_indices().nth(x.as_index()).map(|(i, _)| i).unwrap_or(0);
157 let end = line.txt.char_indices().nth((x + w).as_index())
158 .map(|(i, _)| i).unwrap_or(line.txt.len());
159 result.push(&line.txt[start..end]);
160 } else {
161 let start = line.txt.char_indices().nth(x.as_index()).map(|(i, _)| i).unwrap_or(0);
162 result.push(&line.txt[start..]);
163 }
164 }
165
166 result
167 }
168
169 pub fn update_view(
170 &mut self,
171 outer: &AbsRect,
172 position_cursor: &PositionCursor,
173 )
174 -> Outcome<()>
175 {
176 // Update the extent.
177 {
178 let result = self.atext.get_text_lines_mut();
179 let text_lines = res!(result);
180 self.extent = AbsSize::new((text_lines.max_width(), text_lines.len()));
181 }
182 // The terminal view may have resized.
183 self.term_view = outer.clone();
184 self.text_view = AbsRect::new(self.text_view.top_left, self.term_view.size);
185 match position_cursor {
186 PositionCursor::LatestLine(_) => {
187 if let Some(cursor) = self.vtyp.get_cursor_mut() {
188 cursor.y = self.extent.y;
189 }
190 }
191 _ => {}
192 }
193 self.keep_cursor_in_view();
194 Ok(())
195 }
196
197 /// If for some reason, such as a large text insertion, the cursor finds itself outside the
198 /// text view rectangle, this method normalises the situation by moving the text view.
199 pub fn keep_cursor_in_view(&mut self) {
200 let (mut xt, mut yt, wt, ht) = self.text_view.tup();
201 if let Some(cursor) = self.vtyp.get_cursor() {
202 if cursor.y < yt {
203 yt = cursor.y;
204 } else if cursor.y > yt + ht {
205 yt = cursor.y - ht + 1;
206 }
207 if cursor.x < xt {
208 xt = cursor.x;
209 } else if cursor.x > xt + wt {
210 xt = cursor.x - wt;
211 }
212 self.text_view.top_left = Coord::new((xt, yt));
213 self.term_cursor = self.term_view.top_left
214 + (*cursor - self.text_view.top_left);
215 }
216 }
217
218 pub fn update_cursor(&mut self) {
219 if let Some(cursor) = self.vtyp.get_cursor() {
220 self.term_cursor = self.term_view.top_left
221 + (*cursor - self.text_view.top_left);
222 }
223 }
224
225 pub fn max_width(&self) -> Outcome<usize> {
226 let text_lines = res!(self.atext.get_text_lines());
227 Ok(text_lines.max_width())
228 }
229}