mod.rs - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
HTML git clone https://git.drkhsh.at/icy_draw.git
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
mod.rs (32114B)
---
1 use std::{
2 cmp::{max, min},
3 ffi::OsStr,
4 fs::File,
5 io::Write,
6 path::{Path, PathBuf},
7 sync::Arc,
8 };
9
10 use eframe::{
11 egui::{self, Id, Key, Response},
12 epaint::{mutex::Mutex, Vec2},
13 };
14 use i18n_embed_fl::fl;
15 use icy_engine::{
16 attribute,
17 editor::{AtomicUndoGuard, UndoState},
18 util::{pop_data, pop_sixel_image, push_data, BUFFER_DATA},
19 AttributedChar, Buffer, EngineResult, Line, Position, Rectangle, SaveOptions, TextAttribute, TextPane,
20 };
21
22 use icy_engine_egui::{show_terminal_area, BufferView, CaretShape, TerminalCalc};
23
24 use crate::{
25 model::{DragPos, MKey, MModifiers, Tool},
26 paint::ColorMode,
27 ClipboardHandler, Commands, Document, DocumentOptions, Message, SavingError, TerminalResult, UndoHandler, SETTINGS,
28 };
29
30 pub enum Event {
31 None,
32 CursorPositionChange(Position, Position),
33 }
34
35 pub struct AnsiEditor {
36 pub id: usize,
37 pub drag_pos: DragPos,
38 pub half_block_click_pos: Position,
39 drag_started: bool,
40 pub buffer_view: Arc<eframe::epaint::mutex::Mutex<BufferView>>,
41 pub is_inactive: bool,
42
43 pub outline_font_mode: bool,
44 last_selected_tool: usize,
45
46 pub reference_image: Option<PathBuf>,
47 // pub outline_changed: std::boxed::Box<dyn Fn(&Editor)>,
48 //pub request_refresh: Box<dyn Fn ()>,
49 pub egui_id: Id,
50 pub next_scroll_x_position: Option<f32>,
51 pub next_scroll_y_position: Option<f32>,
52 pub guide: Option<Vec2>,
53 pub raster: Option<Vec2>, //pub pos_changed: std::boxed::Box<dyn Fn(&Editor, Position)>,
54 //pub attr_changed: std::boxed::Box<dyn Fn(TextAttribute)>
55 pub request_focus: bool,
56 pub color_mode: ColorMode,
57 }
58
59 impl UndoHandler for AnsiEditor {
60 fn undo_description(&self) -> Option<String> {
61 self.buffer_view.lock().get_edit_state().undo_description()
62 }
63
64 fn can_undo(&self) -> bool {
65 self.buffer_view.lock().get_edit_state().can_undo()
66 }
67
68 fn undo(&mut self) -> EngineResult<Option<Message>> {
69 self.buffer_view.lock().get_edit_state_mut().undo()?;
70 Ok(None)
71 }
72
73 fn redo_description(&self) -> Option<String> {
74 self.buffer_view.lock().get_edit_state().redo_description()
75 }
76
77 fn can_redo(&self) -> bool {
78 self.buffer_view.lock().get_edit_state().can_redo()
79 }
80
81 fn redo(&mut self) -> EngineResult<Option<Message>> {
82 self.buffer_view.lock().get_edit_state_mut().redo()?;
83 Ok(None)
84 }
85 }
86
87 impl ClipboardHandler for AnsiEditor {
88 fn can_cut(&self) -> bool {
89 self.buffer_view.lock().get_selection().is_some()
90 }
91 fn cut(&mut self) -> EngineResult<()> {
92 let _cut = self.begin_atomic_undo(fl!(crate::LANGUAGE_LOADER, "undo-cut"));
93 self.copy()?;
94 self.buffer_view.lock().get_edit_state_mut().erase_selection()?;
95 Ok(())
96 }
97
98 fn can_copy(&self) -> bool {
99 self.buffer_view.lock().get_selection().is_some()
100 }
101
102 fn copy(&mut self) -> EngineResult<()> {
103 if let Some(data) = self.buffer_view.lock().get_edit_state_mut().get_clipboard_data() {
104 push_data(BUFFER_DATA, &data)?;
105 } else {
106 log::error!("can't get clipboard data!");
107 }
108 Ok(())
109 }
110
111 fn can_paste(&self) -> bool {
112 pop_data(BUFFER_DATA).is_some() || pop_sixel_image().is_some()
113 }
114
115 fn paste(&mut self) -> EngineResult<()> {
116 if self.buffer_view.lock().get_edit_state_mut().has_floating_layer() {
117 return Ok(());
118 }
119
120 if let Some(data) = pop_data(BUFFER_DATA) {
121 self.buffer_view.lock().get_edit_state_mut().paste_clipboard_data(&data)?;
122 } else if let Some(sixel) = pop_sixel_image() {
123 self.buffer_view.lock().get_edit_state_mut().paste_sixel(sixel)?;
124 }
125 Ok(())
126 }
127 }
128 const ICED_EXT: &str = "icy";
129
130 impl Document for AnsiEditor {
131 fn default_extension(&self) -> &'static str {
132 ICED_EXT
133 }
134
135 fn undo_stack_len(&self) -> usize {
136 if let Ok(stack) = self.buffer_view.lock().get_edit_state().get_undo_stack().lock() {
137 for i in (0..stack.len()).rev() {
138 if stack[i].changes_data() {
139 return i + 1;
140 }
141 }
142 }
143 0
144 }
145
146 fn get_bytes(&mut self, path: &Path) -> TerminalResult<Vec<u8>> {
147 let ext = if let Some(ext) = path.extension() {
148 OsStr::to_str(ext).unwrap_or(ICED_EXT).to_lowercase()
149 } else {
150 ICED_EXT.to_string()
151 };
152 let mut options = SaveOptions::new();
153 options.compress = false;
154 options.lossles_output = true;
155 let bytes = self.buffer_view.lock().get_buffer().to_bytes(&ext, &options)?;
156 Ok(bytes)
157 }
158
159 fn show_ui(&mut self, ui: &mut egui::Ui, cur_tool: &mut Box<dyn Tool>, selected_tool: usize, options: &DocumentOptions) -> Option<Message> {
160 let mut message = None;
161
162 // clear tool overlays on tool change
163 if self.last_selected_tool != selected_tool {
164 self.last_selected_tool = selected_tool;
165 self.buffer_view.lock().get_edit_state_mut().get_tool_overlay_mask_mut().clear();
166 self.buffer_view.lock().get_edit_state_mut().set_is_buffer_dirty();
167 }
168
169 let mut scale = unsafe { SETTINGS.get_scale() };
170 let is_visible = cur_tool.use_caret(self);
171 self.buffer_view.lock().get_caret_mut().set_is_visible(is_visible);
172 if self.buffer_view.lock().get_buffer().use_aspect_ratio() {
173 if self.buffer_view.lock().get_buffer().use_letter_spacing() {
174 scale.y *= 1.2;
175 } else {
176 scale.y *= 1.35;
177 }
178 }
179 let opt = icy_engine_egui::TerminalOptions {
180 stick_to_bottom: false,
181 scale: Some(scale),
182 fit_width: options.fit_width,
183 monitor_settings: unsafe { SETTINGS.monitor_settings.clone() },
184 marker_settings: unsafe { SETTINGS.marker_settings.clone() },
185 id: Some(Id::new(self.id + 10000)),
186 raster: self.raster,
187 guide: self.guide,
188 scroll_offset_y: self.next_scroll_y_position.take(),
189 scroll_offset_x: self.next_scroll_x_position.take(),
190 show_layer_borders: unsafe { SETTINGS.show_layer_borders },
191 show_line_numbers: unsafe { SETTINGS.show_line_numbers },
192 request_focus: self.request_focus,
193 caret_shape: CaretShape::Block,
194 ..Default::default()
195 };
196 let (mut response, calc) = show_terminal_area(ui, self.buffer_view.clone(), opt);
197
198 if calc.has_focus {
199 self.request_focus = false;
200 }
201 let response_opt = response.context_menu(|ui| {
202 message = terminal_context_menu(self, &options.commands, ui);
203 });
204 if let Some(response_opt) = response_opt {
205 response = response_opt.response;
206 }
207 self.handle_response(ui, response, calc, cur_tool, &mut message);
208
209 message
210 }
211
212 fn get_ansi_editor_mut(&mut self) -> Option<&mut AnsiEditor> {
213 Some(self)
214 }
215
216 fn get_ansi_editor(&self) -> Option<&AnsiEditor> {
217 Some(self)
218 }
219
220 fn destroy(&self, gl: &glow::Context) -> Option<Message> {
221 self.buffer_view.lock().destroy(gl);
222 None
223 }
224 }
225
226 impl AnsiEditor {
227 pub fn new(gl: &Arc<glow::Context>, id: usize, buf: Buffer) -> Self {
228 let buffer_view = Arc::new(Mutex::new(BufferView::from_buffer(gl, buf)));
229 // let buffer_parser = ansi::Parser::default();
230 AnsiEditor {
231 id,
232 buffer_view,
233 is_inactive: false,
234 reference_image: None,
235 drag_started: false,
236 drag_pos: DragPos::default(),
237 egui_id: Id::new(id),
238 guide: None,
239 raster: None,
240 outline_font_mode: false,
241 next_scroll_x_position: Some(0.0),
242 next_scroll_y_position: Some(0.0),
243 half_block_click_pos: Position::default(),
244 last_selected_tool: 0,
245 request_focus: false,
246 color_mode: ColorMode::Both,
247 }
248 }
249
250 pub fn get_cur_layer_index(&self) -> TerminalResult<usize> {
251 self.buffer_view.lock().get_edit_state_mut().get_current_layer()
252 }
253
254 pub fn set_cur_layer_index(&self, layer: usize) {
255 self.buffer_view.lock().get_edit_state_mut().set_current_layer(layer);
256 }
257
258 pub fn output_string(&mut self, str: &str) {
259 for ch in str.chars() {
260 self.type_key(ch);
261 }
262 }
263
264 pub fn get_caret_position(&self) -> Position {
265 self.buffer_view.lock().get_caret().get_position()
266 }
267
268 pub fn set_caret_position(&mut self, pos: Position) {
269 let buffer_view = &mut self.buffer_view.lock();
270 let pos = Position::new(
271 min(buffer_view.get_width() - 1, max(0, pos.x)),
272 min(buffer_view.get_buffer().get_height() - 1, max(0, pos.y)),
273 );
274 buffer_view.get_caret_mut().set_position(pos);
275 buffer_view.reset_caret_blink();
276 //(self.pos_changed)(self, pos);
277 }
278
279 pub fn set_caret_attribute(&mut self, attr: TextAttribute) {
280 if attr == self.buffer_view.lock().get_caret().get_attribute() {
281 return;
282 }
283
284 self.buffer_view.lock().get_caret_mut().set_attr(attr);
285 // (self.attr_changed)(attr);
286 }
287
288 pub fn join_overlay(&mut self, description: impl Into<String>) {
289 let _undo = self.begin_atomic_undo(description.into());
290 let opt_layer = self.buffer_view.lock().get_buffer_mut().remove_overlay();
291 let use_selection = self.buffer_view.lock().get_edit_state().is_something_selected();
292
293 if let Some(layer) = &opt_layer {
294 for y in 0..layer.lines.len() {
295 let line = &layer.lines[y];
296 for x in 0..line.chars.len() {
297 let ch = line.chars[x];
298 let pos = Position::new(x as i32, y as i32);
299 if ch.is_visible() && (!use_selection || self.buffer_view.lock().get_edit_state().get_is_selected(pos + layer.get_offset())) {
300 self.set_char(pos, ch);
301 }
302 }
303 }
304 }
305 }
306
307 pub fn delete_line(&mut self, line: i32) {
308 // TODO: Undo
309 let mut lock = self.buffer_view.lock();
310 if let Ok(cur_layer) = self.get_cur_layer_index() {
311 let layer = &mut lock.get_buffer_mut().layers[cur_layer];
312 layer.remove_line(line);
313 } else {
314 log::error!("can't get current layer!");
315 }
316 }
317
318 pub fn insert_line(&mut self, line: i32) {
319 // TODO: Undo
320 let mut binding = self.buffer_view.lock();
321 if let Ok(cur_layer) = self.get_cur_layer_index() {
322 let layer = &mut binding.get_buffer_mut().layers[cur_layer];
323 layer.insert_line(line, Line::new());
324 } else {
325 log::error!("can't get current layer!");
326 }
327 }
328
329 pub fn pickup_color(&mut self, pos: Position) {
330 let ch = self.buffer_view.lock().get_buffer().get_char(pos);
331 if ch.is_visible() {
332 self.buffer_view.lock().get_caret_mut().set_attr(ch.attribute);
333 }
334 }
335
336 pub fn set_caret(&mut self, x: i32, y: i32) -> Event {
337 let old = self.buffer_view.lock().get_caret().get_position();
338 let mut w = self.buffer_view.lock().get_buffer().get_width() - 1;
339 let mut h = self.buffer_view.lock().get_buffer().get_height() - 1;
340 let offset: Position = if let Some(layer) = self.buffer_view.lock().get_edit_state().get_cur_layer() {
341 w = layer.get_width() - 1;
342 h = layer.get_height() - 1;
343 layer.get_offset()
344 } else {
345 Position::default()
346 };
347
348 let char_scroll_position = self.buffer_view.lock().calc.char_scroll_position;
349 let terminal_rect = self.buffer_view.lock().calc.terminal_rect;
350 let terminal_width = terminal_rect.width();
351 let terminal_height = terminal_rect.height();
352 let rect = self.buffer_view.lock().calc.buffer_rect;
353 let buffer_width = rect.width().min(terminal_width);
354 let buffer_height = rect.height().min(terminal_height);
355 let buffer_char_width = buffer_width / self.buffer_view.lock().calc.scale.x;
356 let buffer_char_height = buffer_height / self.buffer_view.lock().calc.scale.y;
357
358 let y = min(max(0, y), h);
359 self.set_caret_position(Position::new(min(max(0, x), w), y));
360
361 let font_dim = self.buffer_view.lock().get_buffer().get_font_dimensions();
362
363 let pos = self.buffer_view.lock().get_caret().get_position();
364 let x = (offset.x + pos.x) as f32 * font_dim.width as f32;
365 let y = (offset.y + pos.y) as f32 * font_dim.height as f32;
366 let mut next_y = None;
367 let mut next_x = None;
368 if y < char_scroll_position.y {
369 next_y = Some(y);
370 }
371 if x < char_scroll_position.x {
372 next_x = Some(x);
373 }
374 if y > (char_scroll_position.y + buffer_char_height - font_dim.height as f32) {
375 next_y = Some((y - buffer_char_height + font_dim.height as f32).max(0.0));
376 }
377 if x > (char_scroll_position.x + buffer_char_width - font_dim.width as f32) {
378 next_x = Some((x - buffer_char_width + font_dim.width as f32).max(0.0));
379 }
380 self.next_scroll_x_position = next_x;
381 self.next_scroll_y_position = next_y;
382 Event::CursorPositionChange(old, self.buffer_view.lock().get_caret().get_position())
383 }
384
385 pub fn save_content(&self, file_name: &Path, options: &SaveOptions) -> EngineResult<bool> {
386 match File::create(file_name) {
387 Ok(mut f) => {
388 let content = if let Some(ext) = file_name.extension() {
389 let ext = OsStr::to_string_lossy(ext).to_lowercase();
390 self.buffer_view.lock().get_buffer().to_bytes(ext.as_str(), options)?
391 } else {
392 self.buffer_view.lock().get_buffer().to_bytes(ICED_EXT, options)?
393 };
394 if let Err(err) = f.write_all(&content) {
395 return Err(SavingError::ErrorWritingFile(format!("{err}")).into());
396 }
397 }
398 Err(err) => {
399 return Err(SavingError::ErrorCreatingFile(format!("{err}")).into());
400 }
401 }
402
403 Ok(true)
404 }
405
406 pub fn get_char_set_key(&self, i: usize) -> char {
407 unsafe {
408 let lock = self.buffer_view.lock();
409 let font_page = lock.get_caret().get_font_page();
410
411 let checksum = if let Some(font) = lock.get_buffer().get_font(font_page) {
412 font.get_checksum()
413 } else {
414 0
415 };
416
417 SETTINGS.get_character_set_char(checksum, i)
418 }
419 }
420
421 pub fn type_char_set_key(&mut self, character_set: usize) {
422 self.buffer_view.lock().clear_selection();
423 let ch = self.get_char_set_key(character_set);
424 self.type_key(unsafe { char::from_u32_unchecked(ch as u32) });
425 }
426
427 pub fn get_char(&self, pos: Position) -> AttributedChar {
428 self.buffer_view.lock().get_buffer().get_char(pos)
429 }
430
431 pub fn get_char_from_cur_layer(&self, pos: Position) -> AttributedChar {
432 if let Ok(cur_layer) = self.get_cur_layer_index() {
433 if cur_layer >= self.buffer_view.lock().get_buffer().layers.len() {
434 return AttributedChar::invisible();
435 }
436 self.buffer_view.lock().get_buffer().layers[cur_layer].get_char(pos)
437 } else {
438 log::error!("can't get current layer!");
439 AttributedChar::invisible()
440 }
441 }
442
443 pub fn set_char(&mut self, pos: impl Into<Position>, attributed_char: AttributedChar) {
444 let _ = self.buffer_view.lock().get_edit_state_mut().set_char(pos, attributed_char);
445 }
446
447 #[must_use]
448 pub fn begin_atomic_undo(&mut self, description: impl Into<String>) -> AtomicUndoGuard {
449 self.buffer_view.lock().get_edit_state_mut().begin_atomic_undo(description.into())
450 }
451
452 pub fn fill(&mut self, rect: Rectangle, dos_char: AttributedChar) {
453 let mut pos = rect.start;
454 let _undo = self.begin_atomic_undo("Fill");
455 for _ in 0..rect.size.height {
456 for _ in 0..rect.size.width {
457 self.set_char(pos, dos_char);
458 pos.x += 1;
459 }
460 pos.y += 1;
461 pos.x = rect.start.x;
462 }
463 }
464
465 pub fn type_key(&mut self, char_code: char) {
466 let pos = self.buffer_view.lock().get_caret().get_position();
467 self.buffer_view.lock().clear_selection();
468 if self.buffer_view.lock().get_caret().insert_mode {
469 let end = self.buffer_view.lock().get_buffer().get_width();
470 for i in (pos.x..end).rev() {
471 let next = self.get_char_from_cur_layer(Position::new(i - 1, pos.y));
472 self.set_char(Position::new(i, pos.y), next);
473 }
474 }
475 let mut attr = self.get_char(pos).attribute;
476 let caret_attr = self.buffer_view.lock().get_caret().get_attribute();
477 attr.set_font_page(caret_attr.get_font_page());
478 attr.attr = caret_attr.attr & !attribute::INVISIBLE;
479 if self.color_mode.use_fore() {
480 attr.set_foreground(caret_attr.get_foreground());
481 }
482 if self.color_mode.use_back() {
483 attr.set_background(caret_attr.get_background());
484 }
485
486 self.set_char(pos, AttributedChar::new(char_code, attr));
487 self.set_caret(pos.x + 1, pos.y);
488 self.request_focus = true;
489 }
490
491 pub fn switch_fg_bg_color(&mut self) {
492 let mut attr = self.buffer_view.lock().get_caret().get_attribute();
493 let bg = attr.get_background();
494 attr.set_background(attr.get_foreground());
495 attr.set_foreground(bg);
496 self.set_caret_attribute(attr);
497 }
498
499 pub fn erase_line(&mut self) {
500 let _undo = self.begin_atomic_undo("Erase line");
501 // TODO
502 }
503
504 pub fn erase_line_to_start(&mut self) {
505 let _undo = self.begin_atomic_undo("Erase line to start");
506 // TODO
507 }
508
509 pub fn erase_line_to_end(&mut self) {
510 let _undo = self.begin_atomic_undo("Erase line to end");
511 // TODO
512 }
513
514 pub fn erase_column(&mut self) {
515 let _undo = self.begin_atomic_undo("Erase column");
516 // TODO
517 }
518
519 pub fn erase_column_to_start(&mut self) {
520 let _undo = self.begin_atomic_undo("Erase column to start");
521 // TODO
522 }
523
524 pub fn erase_column_to_end(&mut self) {
525 let _undo = self.begin_atomic_undo("Erase column to end");
526 // TODO
527 }
528
529 pub fn delete_row(&mut self) {
530 let _undo = self.begin_atomic_undo("Delete row");
531 // TODO
532 }
533
534 pub fn insert_row(&mut self) {
535 let _undo = self.begin_atomic_undo("Insert row");
536 // TODO
537 }
538
539 pub fn delete_column(&mut self) {
540 let _undo = self.begin_atomic_undo("Delete column");
541 // TODO
542 }
543
544 pub fn insert_column(&mut self) {
545 let _undo = self.begin_atomic_undo("Insert column");
546 // TODO
547 }
548
549 fn handle_response(
550 &mut self,
551 ui: &egui::Ui,
552 mut response: Response,
553 calc: TerminalCalc,
554 cur_tool: &mut Box<dyn Tool>,
555 message: &mut Option<Message>,
556 ) -> Response {
557 if response.has_focus() {
558 let events = ui.input(|i| i.events.clone());
559 for e in &events {
560 match e {
561 egui::Event::Copy => {}
562 egui::Event::Cut => {}
563 egui::Event::Paste(text) => {
564 self.output_string(text);
565 }
566
567 egui::Event::CompositionEnd(text) | egui::Event::Text(text) => {
568 if !ui.input(|i| i.modifiers.ctrl || i.modifiers.command || i.modifiers.alt) {
569 for c in text.chars() {
570 cur_tool.handle_key(self, MKey::Character(c as u16), MModifiers::None);
571 }
572 }
573 }
574
575 egui::Event::Key {
576 key, pressed: true, modifiers, ..
577 } => {
578 let mut key_code = *key as u32;
579 if modifiers.shift {
580 key_code |= SHIFT_MOD;
581 }
582
583 let mut modifier: MModifiers = MModifiers::None;
584 if modifiers.ctrl || modifiers.command {
585 modifier = MModifiers::Control;
586 }
587
588 if modifiers.shift {
589 modifier = MModifiers::Shift;
590 }
591
592 if modifiers.alt {
593 modifier = MModifiers::Alt;
594 }
595 for (k, m) in EDITOR_KEY_MAP {
596 if *k == key_code {
597 cur_tool.handle_key(self, *m, modifier);
598 break;
599 }
600 }
601 }
602 _ => {}
603 }
604 }
605 }
606
607 if response.clicked_by(egui::PointerButton::Primary) {
608 if let Some(mouse_pos) = response.hover_pos() {
609 if calc.buffer_rect.contains(mouse_pos) && !calc.vert_scrollbar_rect.contains(mouse_pos) && !calc.horiz_scrollbar_rect.contains(mouse_pos) {
610 let click_pos = calc.calc_click_pos(mouse_pos);
611 let cp_abs = Position::new(click_pos.x as i32, click_pos.y as i32);
612 let layer_offset = self.get_cur_click_offset();
613 let cp = cp_abs - layer_offset;
614 let click_pos2 = calc.calc_click_pos_half_block(mouse_pos);
615 self.half_block_click_pos = Position::new(click_pos2.x as i32 - layer_offset.x, click_pos2.y as i32 - layer_offset.y);
616
617 /*
618 let b: i32 = match responsee.b {
619 PointerButton::Primary => 1,
620 PointerButton::Secondary => 2,
621 PointerButton::Middle => 3,
622 PointerButton::Extra1 => 4,
623 PointerButton::Extra2 => 5,
624 }; */
625 let msg = cur_tool.handle_click(self, 1, cp, cp_abs, &response);
626 if message.is_none() {
627 *message = msg;
628 }
629 }
630 }
631 }
632
633 if response.drag_started_by(egui::PointerButton::Primary) {
634 if let Some(mouse_pos) = response.hover_pos() {
635 if calc.buffer_rect.contains(mouse_pos) && !calc.vert_scrollbar_rect.contains(mouse_pos) && !calc.horiz_scrollbar_rect.contains(mouse_pos) {
636 let click_pos = calc.calc_click_pos(mouse_pos);
637 let cp_abs = Position::new(click_pos.x as i32, click_pos.y as i32);
638
639 let layer_offset = self.get_cur_click_offset();
640 let cp = cp_abs - layer_offset;
641 let click_pos2 = calc.calc_click_pos_half_block(mouse_pos);
642 let click_pos2 = Position::new(click_pos2.x as i32, click_pos2.y as i32);
643 let half_block_layer_offset = Position::new(layer_offset.x, layer_offset.y * 2);
644 let half_block_click_pos = click_pos2 - half_block_layer_offset;
645 self.half_block_click_pos = half_block_click_pos;
646
647 self.drag_pos.start_abs = cp_abs;
648 self.drag_pos.start = cp;
649
650 self.drag_pos.cur_abs = cp_abs;
651 self.drag_pos.cur = cp;
652 self.drag_pos.start_half_block = half_block_click_pos;
653 self.drag_started = true;
654
655 cur_tool.handle_drag_begin(self, &response);
656 }
657 }
658 }
659
660 if response.dragged_by(egui::PointerButton::Primary) && self.drag_started {
661 if let Some(mouse_pos) = response.hover_pos() {
662 let layer_offset = self.get_cur_click_offset();
663 let click_pos2 = calc.calc_click_pos_half_block(mouse_pos);
664 let click_pos2 = Position::new(click_pos2.x as i32, click_pos2.y as i32);
665
666 let half_block_layer_offset = Position::new(layer_offset.x, layer_offset.y * 2);
667 let half_block_click_pos = click_pos2 - half_block_layer_offset;
668
669 let mut c_abs = self.half_block_click_pos;
670 while c_abs != half_block_click_pos {
671 let s = (half_block_click_pos - c_abs).signum();
672 c_abs += s;
673 self.half_block_click_pos = c_abs;
674
675 self.drag_pos.cur_abs = Position::new(c_abs.x, c_abs.y / 2) + layer_offset;
676 self.drag_pos.cur = self.drag_pos.cur_abs - layer_offset;
677 response = cur_tool.handle_drag(ui, response, self, &calc);
678 }
679 }
680 }
681 if response.hovered() {
682 if let Some(mouse_pos) = response.hover_pos() {
683 if calc.buffer_rect.contains(mouse_pos) && !calc.vert_scrollbar_rect.contains(mouse_pos) && !calc.horiz_scrollbar_rect.contains(mouse_pos) {
684 let click_pos = calc.calc_click_pos(mouse_pos);
685 let cp_abs = Position::new(click_pos.x as i32, click_pos.y as i32);
686 let cp = cp_abs - self.get_cur_click_offset();
687 response = cur_tool.handle_hover(ui, response, self, cp, cp_abs);
688 } else {
689 cur_tool.handle_no_hover(self);
690 }
691 } else {
692 cur_tool.handle_no_hover(self);
693 }
694 } else {
695 cur_tool.handle_no_hover(self);
696 }
697
698 if response.drag_released_by(egui::PointerButton::Primary) {
699 let msg = cur_tool.handle_drag_end(self);
700 if msg.is_some() {
701 *message = msg;
702 }
703
704 self.drag_started = false;
705 }
706
707 response
708 }
709
710 fn get_cur_click_offset(&mut self) -> Position {
711 if let Some(layer) = self.buffer_view.lock().get_edit_state().get_cur_layer() {
712 return layer.get_offset();
713 }
714 Position::default()
715 }
716
717 pub(crate) fn clear_overlay_layer(&self) {
718 let cur_offset = self.buffer_view.lock().get_edit_state().get_cur_layer().unwrap().get_offset();
719
720 if let Some(layer) = self.buffer_view.lock().get_edit_state_mut().get_overlay_layer() {
721 layer.set_offset(cur_offset);
722 layer.clear();
723 }
724 self.buffer_view.lock().get_edit_state_mut().set_is_buffer_dirty();
725 }
726
727 pub fn backspace(&mut self) {
728 let _op: AtomicUndoGuard = self
729 .buffer_view
730 .lock()
731 .get_edit_state_mut()
732 .begin_atomic_undo(fl!(crate::LANGUAGE_LOADER, "undo-backspace"));
733 self.buffer_view.lock().clear_selection();
734 let pos = self.get_caret_position();
735 if pos.x > 0 {
736 self.set_caret_position(pos + Position::new(-1, 0));
737 if self.buffer_view.lock().get_caret().insert_mode {
738 let end = self.buffer_view.lock().get_width() - 1;
739 for i in pos.x..end {
740 let next = self.get_char_from_cur_layer(Position::new(i + 1, pos.y));
741 self.set_char(Position::new(i, pos.y), next);
742 }
743 let last_pos = Position::new(self.buffer_view.lock().get_width() - 1, pos.y);
744 self.set_char(last_pos, AttributedChar::invisible());
745 } else {
746 let pos = self.get_caret_position();
747 self.set_char(pos, AttributedChar::invisible());
748 }
749 }
750 }
751
752 pub fn delete(&mut self) {
753 let _op: AtomicUndoGuard = self
754 .buffer_view
755 .lock()
756 .get_edit_state_mut()
757 .begin_atomic_undo(fl!(crate::LANGUAGE_LOADER, "undo-delete"));
758 self.buffer_view.lock().clear_selection();
759 let pos = self.get_caret_position();
760 if pos.x >= 0 {
761 let end = self.buffer_view.lock().get_width() - 1;
762 for i in pos.x..end {
763 let next = self.get_char_from_cur_layer(Position::new(i + 1, pos.y));
764 self.set_char(Position::new(i, pos.y), next);
765 }
766 let last_pos = Position::new(self.buffer_view.lock().get_width() - 1, pos.y);
767 self.set_char(last_pos, AttributedChar::invisible());
768 }
769 }
770 }
771
772 pub const DEFAULT_CHAR_SET_TABLE: [[u8; 10]; 15] = [
773 [218, 191, 192, 217, 196, 179, 195, 180, 193, 194],
774 [201, 187, 200, 188, 205, 186, 204, 185, 202, 203],
775 [213, 184, 212, 190, 205, 179, 198, 181, 207, 209],
776 [214, 183, 211, 189, 196, 186, 199, 182, 208, 210],
777 [197, 206, 216, 215, 232, 233, 155, 156, 153, 239],
778 [176, 177, 178, 219, 223, 220, 221, 222, 254, 250],
779 [1, 2, 3, 4, 5, 6, 240, 127, 14, 15],
780 [24, 25, 30, 31, 16, 17, 18, 29, 20, 21],
781 [174, 175, 242, 243, 169, 170, 253, 246, 171, 172],
782 [227, 241, 244, 245, 234, 157, 228, 248, 251, 252],
783 [224, 225, 226, 229, 230, 231, 235, 236, 237, 238],
784 [128, 135, 165, 164, 152, 159, 247, 249, 173, 168],
785 [131, 132, 133, 160, 166, 134, 142, 143, 145, 146],
786 [136, 137, 138, 130, 144, 140, 139, 141, 161, 158],
787 [147, 148, 149, 162, 167, 150, 129, 151, 163, 154],
788 ];
789
790 pub fn terminal_context_menu(editor: &AnsiEditor, commands: &Commands, ui: &mut egui::Ui) -> Option<Message> {
791 ui.style_mut().wrap = Some(false);
792 let mut result = None;
793 ui.input_mut(|i| i.events.clear());
794
795 commands.cut.ui(ui, &mut result);
796 commands.copy.ui(ui, &mut result);
797 commands.paste.ui(ui, &mut result);
798
799 let sel = editor.buffer_view.lock().get_selection();
800
801 if let Some(_sel) = sel {
802 commands.erase_selection.ui(ui, &mut result);
803 commands.flip_x.ui(ui, &mut result);
804 commands.flip_y.ui(ui, &mut result);
805 commands.justifycenter.ui(ui, &mut result);
806 commands.justifyleft.ui(ui, &mut result);
807 commands.justifyright.ui(ui, &mut result);
808 commands.crop.ui(ui, &mut result);
809 }
810 result
811 }
812
813 pub const CTRL_MOD: u32 = 0b1000_0000_0000_0000_0000;
814 pub const SHIFT_MOD: u32 = 0b0100_0000_0000_0000_0000;
815
816 pub static EDITOR_KEY_MAP: &[(u32, MKey)] = &[
817 (Key::Escape as u32, MKey::Escape),
818 (Key::Home as u32, MKey::Home),
819 (Key::End as u32, MKey::End),
820 (Key::Home as u32 | CTRL_MOD, MKey::Home),
821 (Key::End as u32 | CTRL_MOD, MKey::End),
822 (Key::Insert as u32, MKey::Insert),
823 (Key::Backspace as u32, MKey::Backspace),
824 (Key::Enter as u32, MKey::Return),
825 (Key::Tab as u32, MKey::Tab),
826 (Key::Tab as u32 | SHIFT_MOD, MKey::Tab),
827 (Key::Delete as u32, MKey::Delete),
828 (Key::PageUp as u32, MKey::PageUp),
829 (Key::PageDown as u32, MKey::PageDown),
830 (Key::ArrowUp as u32, MKey::Up),
831 (Key::ArrowDown as u32, MKey::Down),
832 (Key::ArrowRight as u32, MKey::Right),
833 (Key::ArrowLeft as u32, MKey::Left),
834 (Key::ArrowUp as u32 | SHIFT_MOD, MKey::Up),
835 (Key::ArrowDown as u32 | SHIFT_MOD, MKey::Down),
836 (Key::ArrowRight as u32 | SHIFT_MOD, MKey::Right),
837 (Key::ArrowLeft as u32 | SHIFT_MOD, MKey::Left),
838 (Key::F1 as u32, MKey::F1),
839 (Key::F2 as u32, MKey::F2),
840 (Key::F3 as u32, MKey::F3),
841 (Key::F4 as u32, MKey::F4),
842 (Key::F5 as u32, MKey::F5),
843 (Key::F6 as u32, MKey::F6),
844 (Key::F7 as u32, MKey::F7),
845 (Key::F8 as u32, MKey::F8),
846 (Key::F9 as u32, MKey::F9),
847 (Key::F10 as u32, MKey::F10),
848 (Key::F11 as u32, MKey::F11),
849 (Key::F12 as u32, MKey::F12),
850 ];