1pub use crate::Selection;
2use crate::{BaseOptions, SelectionMode, TextBuffer, TextEdit};
3use nalgebra::Point2;
4use std::marker::PhantomData;
5use std::sync::Arc;
6pub use ultron_syntaxes_themes::{Style, TextHighlighter};
7
8pub struct BaseEditor<XMSG> {
10 options: BaseOptions,
11 text_edit: TextEdit,
12 #[cfg(feature = "callback")]
15 change_listeners: Vec<Callback<String, XMSG>>,
16 #[cfg(feature = "callback")]
19 change_notify_listeners: Vec<Callback<(), XMSG>>,
20 _phantom: PhantomData<XMSG>,
21}
22
23impl<XMSG> AsRef<TextEdit> for BaseEditor<XMSG> {
24 fn as_ref(&self) -> &TextEdit {
25 &self.text_edit
26 }
27}
28
29impl<XMSG> AsMut<TextEdit> for BaseEditor<XMSG> {
30 fn as_mut(&mut self) -> &mut TextEdit {
31 &mut self.text_edit
32 }
33}
34
35impl<XMSG> Default for BaseEditor<XMSG> {
36 fn default() -> Self {
37 Self {
38 options: BaseOptions::default(),
39 text_edit: TextEdit::default(),
40 #[cfg(feature = "callback")]
41 change_listeners: vec![],
42 #[cfg(feature = "callback")]
43 change_notify_listeners: vec![],
44 _phantom: PhantomData,
45 }
46 }
47}
48
49impl<XMSG> Clone for BaseEditor<XMSG> {
50 fn clone(&self) -> Self {
51 Self {
52 options: self.options.clone(),
53 text_edit: self.text_edit.clone(),
54 #[cfg(feature = "callback")]
55 change_listeners: self.change_listeners.clone(),
56 #[cfg(feature = "callback")]
57 change_notify_listeners: self.change_notify_listeners.clone(),
58 _phantom: self._phantom,
59 }
60 }
61}
62
63#[derive(Debug)]
64pub enum Command {
65 IndentForward,
66 IndentBackward,
67 BreakLine,
68 DeleteBack,
69 DeleteForward,
70 MoveUp,
71 MoveDown,
72 MoveLeft,
73 MoveLeftStart,
74 MoveRight,
75 MoveRightEnd,
76 InsertChar(char),
77 ReplaceChar(char),
78 InsertText(String),
79 PasteTextBlock(String),
80 MergeText(String),
81 SetContent(String),
83 Undo,
84 Redo,
85 BumpHistory,
86 SetSelection(Point2<i32>, Point2<i32>),
87 SelectAll,
88 ClearSelection,
89 SetPosition(Point2<i32>),
90}
91
92pub struct Callback<IN, OUT> {
93 func: Arc<dyn Fn(IN) -> OUT>,
94}
95
96impl<IN, F, OUT> From<F> for Callback<IN, OUT>
97where
98 F: Fn(IN) -> OUT + 'static,
99{
100 fn from(func: F) -> Self {
101 Self {
102 func: Arc::new(func),
103 }
104 }
105}
106
107impl<IN, OUT> Clone for Callback<IN, OUT> {
108 fn clone(&self) -> Self {
109 Self {
110 func: Arc::clone(&self.func),
111 }
112 }
113}
114
115impl<IN, OUT> Callback<IN, OUT> {
116 pub fn emit(&self, input: IN) -> OUT {
118 (self.func)(input)
119 }
120}
121
122impl<XMSG> BaseEditor<XMSG> {
123 pub fn from_str(options: &BaseOptions, content: &str) -> Self {
124 let text_edit = TextEdit::new_from_str(content);
125
126 BaseEditor {
127 options: options.clone(),
128 text_edit,
129 #[cfg(feature = "callback")]
130 change_listeners: vec![],
131 #[cfg(feature = "callback")]
132 change_notify_listeners: vec![],
133 _phantom: PhantomData,
134 }
135 }
136
137 pub fn text_buffer(&self) -> &TextBuffer {
138 self.text_edit.text_buffer()
139 }
140
141 pub fn set_selection(&mut self, start: Point2<i32>, end: Point2<i32>) {
142 self.text_edit.set_selection(start, end);
143 }
144
145 pub fn selection(&self) -> &Selection {
146 self.text_edit.selection()
147 }
148
149 pub fn selected_text(&self) -> Option<String> {
150 match self.options.selection_mode {
151 SelectionMode::Linear => self.text_edit.selected_text_in_linear_mode(),
152 SelectionMode::Block => self.text_edit.selected_text_in_block_mode(),
153 }
154 }
155
156 pub fn cut_selected_text(&mut self) -> Option<String> {
157 match self.options.selection_mode {
158 SelectionMode::Linear => self.text_edit.cut_selected_text_in_linear_mode(),
159 SelectionMode::Block => self.text_edit.cut_selected_text_in_block_mode(),
160 }
161 }
162
163 pub fn is_selected(&self, loc: Point2<i32>) -> bool {
164 match self.options.selection_mode {
165 SelectionMode::Linear => self.text_edit.is_selected_in_linear_mode(loc),
166 SelectionMode::Block => self.text_edit.is_selected_in_block_mode(loc),
167 }
168 }
169
170 pub fn clear_selection(&mut self) {
171 self.text_edit.clear_selection()
172 }
173
174 pub fn set_selection_start(&mut self, start: Point2<i32>) {
175 self.text_edit.set_selection_start(start);
176 }
177
178 pub fn set_selection_end(&mut self, end: Point2<i32>) {
179 self.text_edit.set_selection_end(end);
180 }
181
182 pub fn get_char(&self, loc: Point2<usize>) -> Option<char> {
183 self.text_edit.get_char(loc)
184 }
185
186 pub fn get_position(&self) -> Point2<usize> {
187 self.text_edit.get_position()
188 }
189
190 pub fn get_content(&self) -> String {
191 self.text_edit.get_content()
192 }
193
194 pub fn total_lines(&self) -> usize {
195 self.text_edit.total_lines()
196 }
197}
198
199impl<XMSG> BaseEditor<XMSG> {
200 #[cfg(feature = "callback")]
201 pub fn process_commands(&mut self, commands: impl IntoIterator<Item = Command>) -> Vec<XMSG> {
202 let results: Vec<bool> = commands
203 .into_iter()
204 .map(|command| self.process_command(command))
205 .collect();
206
207 if results.into_iter().any(|v| v) {
208 self.emit_on_change_listeners()
209 } else {
210 vec![]
211 }
212 }
213
214 #[cfg(not(feature = "callback"))]
215 pub fn process_commands(&mut self, _commands: impl IntoIterator<Item = Command>) -> Vec<XMSG> {
216 vec![]
217 }
218
219 pub fn process_command(&mut self, command: Command) -> bool {
221 match command {
222 Command::IndentForward => {
223 let indent = " ";
224 self.text_edit.command_insert_text(indent);
225 true
226 }
227 Command::IndentBackward => true,
228 Command::BreakLine => {
229 self.text_edit.command_break_line();
230 true
231 }
232 Command::DeleteBack => {
233 self.text_edit.command_delete_back();
234 true
235 }
236 Command::DeleteForward => {
237 self.text_edit.command_delete_forward();
238 true
239 }
240 Command::MoveUp => {
241 self.command_move_up();
242 false
243 }
244 Command::MoveDown => {
245 self.command_move_down();
246 false
247 }
248 Command::PasteTextBlock(text) => {
249 self.text_edit.paste_text_in_block_mode(text);
250 true
251 }
252 Command::MergeText(text) => {
253 self.text_edit.command_merge_text(text);
254 true
255 }
256 Command::MoveLeft => {
257 self.command_move_left();
258 false
259 }
260 Command::MoveLeftStart => {
261 self.text_edit.command_move_left_start();
262 false
263 }
264 Command::MoveRightEnd => {
265 self.text_edit.command_move_right_end();
266 false
267 }
268 Command::MoveRight => {
269 self.command_move_right();
270 false
271 }
272 Command::InsertChar(c) => {
273 self.text_edit.command_insert_char(c);
274 true
275 }
276 Command::ReplaceChar(c) => {
277 self.text_edit.command_replace_char(c);
278 true
279 }
280 Command::InsertText(text) => {
281 self.text_edit.command_insert_text(&text);
282 true
283 }
284 Command::SetContent(content) => {
285 self.text_edit = TextEdit::new_from_str(&content);
286 true
287 }
288 Command::Undo => {
289 self.text_edit.command_undo();
290 true
291 }
292 Command::Redo => {
293 self.text_edit.command_redo();
294 true
295 }
296 Command::BumpHistory => {
297 self.text_edit.bump_history();
298 false
299 }
300 Command::SetSelection(start, end) => {
301 self.text_edit.command_set_selection(start, end);
302 false
303 }
304 Command::SelectAll => {
305 self.text_edit.command_select_all();
306 false
307 }
308 Command::ClearSelection => {
309 self.text_edit.clear_selection();
310 false
311 }
312 Command::SetPosition(pos) => {
313 self.command_set_position(pos);
314 false
315 }
316 }
317 }
318
319 fn command_move_up(&mut self) {
320 if self.options.use_virtual_edit {
321 self.text_edit.command_move_up();
322 } else {
323 self.text_edit.command_move_up_clamped();
324 }
325 }
326
327 fn command_move_down(&mut self) {
328 if self.options.use_virtual_edit {
329 self.text_edit.command_move_down();
330 } else {
331 self.text_edit.command_move_down_clamped();
332 }
333 }
334
335 fn command_move_left(&mut self) {
336 self.text_edit.command_move_left();
337 }
338
339 fn command_move_right(&mut self) {
340 if self.options.use_virtual_edit {
341 self.text_edit.command_move_right();
342 } else {
343 self.text_edit.command_move_right_clamped();
344 }
345 }
346
347 fn command_set_position(&mut self, loc: Point2<i32>) {
348 let cursor = Point2::new(loc.x as usize, loc.y as usize);
349 if self.options.use_virtual_edit {
350 self.text_edit.command_set_position(cursor);
351 } else {
352 self.text_edit.command_set_position_clamped(cursor);
353 }
354 }
355
356 pub fn clear(&mut self) {
357 self.text_edit.clear();
358 }
359
360 #[cfg(feature = "callback")]
365 pub fn on_change<F>(mut self, f: F) -> Self
366 where
367 F: Fn(String) -> XMSG + 'static,
368 {
369 let cb = Callback::from(f);
370 self.change_listeners.push(cb);
371 self
372 }
373
374 #[cfg(feature = "callback")]
375 pub fn add_on_change_listener<F>(&mut self, f: F)
376 where
377 F: Fn(String) -> XMSG + 'static,
378 {
379 let cb = Callback::from(f);
380 self.change_listeners.push(cb);
381 }
382
383 #[cfg(feature = "callback")]
392 pub fn on_change_notify<F>(mut self, f: F) -> Self
393 where
394 F: Fn(()) -> XMSG + 'static,
395 {
396 let cb = Callback::from(f);
397 self.change_notify_listeners.push(cb);
398 self
399 }
400
401 #[cfg(feature = "callback")]
402 pub fn add_on_change_notify<F>(&mut self, f: F)
403 where
404 F: Fn(()) -> XMSG + 'static,
405 {
406 let cb = Callback::from(f);
407 self.change_notify_listeners.push(cb);
408 }
409
410 #[cfg(feature = "callback")]
411 pub fn emit_on_change_listeners(&self) -> Vec<XMSG> {
412 let mut extern_msgs: Vec<XMSG> = vec![];
413 if !self.change_listeners.is_empty() {
414 let content = self.text_edit.get_content();
415 let xmsgs: Vec<XMSG> = self
416 .change_listeners
417 .iter()
418 .map(|listener| listener.emit(content.clone()))
419 .collect();
420 extern_msgs.extend(xmsgs);
421 }
422
423 if !self.change_notify_listeners.is_empty() {
424 let xmsgs: Vec<XMSG> = self
425 .change_notify_listeners
426 .iter()
427 .map(|notify| notify.emit(()))
428 .collect();
429 extern_msgs.extend(xmsgs);
430 }
431
432 extern_msgs
433 }
434
435 pub fn numberline_wide(&self) -> usize {
436 self.text_edit.numberline_wide()
437 }
438}