1mod iter;
2mod print_info;
3
4use std::{io::Write, sync::Arc};
5
6use crossterm::{
7 cursor,
8 style::{Attribute, Attributes},
9};
10use duat_core::{
11 context::{self, Decode, Encode},
12 form::{CONTROL_CHAR_ID, Painter},
13 opts::PrintOpts,
14 text::{Item, Part, SpawnId, Text, TwoPoints, txt},
15 ui::{
16 Caret, PushSpecs, SpawnSpecs,
17 traits::{CoreAccess, RawArea},
18 },
19};
20use iter::{print_iter, rev_print_iter};
21
22pub use self::print_info::PrintInfo;
23use crate::{AreaId, CStyle, Mutex, layout::Layouts, print_style, printer::Lines, queue};
24
25#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
26#[bincode(crate = "duat_core::context::bincode")]
27pub struct Coord {
28 pub x: u32,
29 pub y: u32,
30}
31
32impl std::fmt::Debug for Coord {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
35 }
36}
37
38impl Coord {
39 pub fn new(x: u32, y: u32) -> Coord {
40 Coord { x, y }
41 }
42}
43
44#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
45#[bincode(crate = "duat_core::context::bincode")]
46pub struct Coords {
47 pub tl: Coord,
48 pub br: Coord,
49}
50
51impl Coords {
52 pub fn new(tl: Coord, br: Coord) -> Self {
53 Coords { tl, br }
54 }
55
56 pub fn width(&self) -> u32 {
57 self.br.x - self.tl.x
58 }
59
60 pub fn height(&self) -> u32 {
61 self.br.y - self.tl.y
62 }
63
64 pub fn area(&self) -> u32 {
65 self.width() * self.height()
66 }
67
68 pub fn intersects(&self, other: Self) -> bool {
69 self.tl.x < other.br.x
70 && self.br.x > other.tl.x
71 && self.tl.y < other.br.y
72 && self.br.y > other.tl.y
73 }
74}
75
76#[derive(Clone)]
77pub struct Area {
78 layouts: Layouts,
79 id: AreaId,
80 ansi_codes: Arc<Mutex<micromap::Map<CStyle, String, 16>>>,
81}
82
83impl PartialEq for Area {
84 fn eq(&self, other: &Self) -> bool {
85 self.id == other.id
86 }
87}
88
89impl Area {
90 pub(crate) fn new(id: AreaId, layouts: Layouts) -> Self {
91 Self { layouts, id, ansi_codes: Arc::default() }
92 }
93
94 fn print<'a>(
95 &self,
96 text: &Text,
97 opts: PrintOpts,
98 mut painter: Painter,
99 mut f: impl FnMut(&Caret, &Item) + 'a,
100 ) {
101 const SPACES: &[u8] = &[b' '; 3000];
102
103 fn end_line(
104 lines: &mut Lines,
105 painter: &Painter,
106 ansi_codes: &mut micromap::Map<CStyle, String, 16>,
107 len: u32,
108 max_x: u32,
109 ) {
110 let mut default_style = painter.get_default();
111 default_style.style.foreground_color = None;
112 default_style.style.underline_color = None;
113 default_style.style.attributes = Attributes::from(Attribute::Reset);
114
115 print_style(lines, default_style.style, ansi_codes);
116 if lines.coords().br.x == max_x {
117 lines.write_all(b"\x1b[0K").unwrap();
118 } else {
119 lines
120 .write_all(&SPACES[..(lines.coords().width() - len) as usize])
121 .unwrap();
122 }
123 lines.flush().unwrap();
124 }
125
126 let (mut lines, iter, x_shift, max_x) = {
127 let Some(coords) = self.layouts.coords_of(self.id, true) else {
128 context::warn!("This Area was already deleted");
129 return;
130 };
131
132 let max = self
133 .layouts
134 .inspect(self.id, |_, layout| layout.max_value())
135 .unwrap();
136
137 if coords.width() == 0 || coords.height() == 0 {
138 return;
139 }
140
141 let (s_points, x_shift) = {
142 let mut info = self.layouts.get_info_of(self.id).unwrap();
143 let s_points = info.start_points(coords, text, opts);
144 self.layouts.set_info_of(self.id, info);
145 (s_points, info.x_shift())
146 };
147
148 let lines = Lines::new(coords);
149 let width = opts.wrap_width(coords.width()).unwrap_or(coords.width());
150 let iter = print_iter(text, s_points, width, opts);
151
152 (lines, iter, x_shift, max.x)
153 };
154
155 let mut observed_spawns = Vec::new();
156 let spawn_id = self
157 .layouts
158 .inspect(self.id, |rect, _| rect.spawn_id())
159 .unwrap();
160 let is_active = self.id == self.layouts.get_active_id();
161
162 let mut ansi_codes = self.ansi_codes.lock().unwrap();
163 let mut style_was_set = false;
164
165 enum Cursor {
166 Main,
167 Extra,
168 }
169
170 let tl_y = lines.coords().tl.y;
172 let mut y = tl_y;
173 let mut cursor = None;
174 let mut spawns_for_next: Vec<SpawnId> = Vec::new();
175 let mut last_len = 0;
176
177 for (caret, item) in iter {
178 let (painter, lines, ansi_codes) = (&mut painter, &mut lines, &mut ansi_codes);
179 f(&caret, &item);
180
181 let Caret { x, len, wrap } = caret;
182 let Item { part, .. } = item;
183
184 if wrap {
185 if y == lines.coords().br.y {
186 break;
187 }
188 if y > lines.coords().tl.y {
189 end_line(lines, painter, ansi_codes, last_len, max_x);
190 }
191 let initial_space = x.saturating_sub(x_shift).min(lines.coords().width());
192 if initial_space > 0 {
193 let mut default_style = painter.get_default().style;
194 default_style.attributes.set(Attribute::Reset);
195 print_style(lines, default_style, ansi_codes);
196 lines.write_all(&SPACES[..initial_space as usize]).unwrap();
197 }
198 y += 1;
199
200 painter.reset_prev_style();
202 style_was_set = true;
203 last_len = initial_space;
204 }
205
206 let is_contained = x + len > x_shift && x < x_shift + lines.coords().width();
207
208 match part {
209 Part::Char(char) if is_contained => {
210 if let Some(str) = get_control_str(char) {
211 painter.apply(CONTROL_CHAR_ID, 100);
212 if style_was_set && let Some(style) = painter.relative_style() {
213 print_style(lines, style, ansi_codes);
214 }
215 lines.write_all(str.as_bytes()).unwrap();
216 painter.remove(CONTROL_CHAR_ID)
217 } else {
218 if style_was_set && let Some(style) = painter.relative_style() {
219 print_style(lines, style, ansi_codes);
220 }
221 match char {
222 '\t' => {
223 let truncated_start = x_shift.saturating_sub(x);
224 let truncated_end =
225 (x + len).saturating_sub(lines.coords().width() + x_shift);
226 let tab_len = len - (truncated_start + truncated_end);
227 lines.write_all(&SPACES[..tab_len as usize]).unwrap()
228 }
229 '\n' if opts.print_new_line => lines.write_all(b" ").unwrap(),
230 '\n' | '\r' => {}
231 char => {
232 let mut bytes = [0; 4];
233 char.encode_utf8(&mut bytes);
234 lines.write_all(&bytes[..char.len_utf8()]).unwrap();
235 }
236 }
237 }
238
239 if let Some(cursor) = cursor.take() {
240 match cursor {
241 Cursor::Main => painter.remove_main_caret(),
242 Cursor::Extra => painter.remove_extra_caret(),
243 }
244 if let Some(style) = painter.relative_style() {
245 print_style(lines, style, ansi_codes)
246 }
247 }
248 for id in spawns_for_next.drain(..) {
249 observed_spawns.push(id);
250 self.layouts.move_spawn_to(
251 id,
252 Coord::new(lines.coords().tl.x + x, y - 1),
253 len,
254 );
255 }
256
257 last_len = x + len - x_shift;
258 style_was_set = false;
259 }
260 Part::Char(_) => {
261 match cursor.take() {
262 Some(Cursor::Main) => painter.remove_main_caret(),
263 Some(Cursor::Extra) => painter.remove_extra_caret(),
264 None => {}
265 }
266 spawns_for_next.clear();
267 }
268 Part::PushForm(id, prio) => {
269 painter.apply(id, prio);
270 style_was_set = true;
271 }
272 Part::PopForm(id) => {
273 painter.remove(id);
274 style_was_set = true;
275 }
276 Part::MainCaret => {
277 if let Some(shape) = painter.main_cursor()
278 && is_active
279 {
280 lines.show_real_cursor();
281 queue!(lines, shape, cursor::SavePosition);
282 } else {
283 cursor = Some(Cursor::Main);
284 lines.hide_real_cursor();
285 painter.apply_main_cursor();
286 style_was_set = true;
287 }
288 }
289 Part::ExtraCaret => {
290 cursor = Some(Cursor::Extra);
291 painter.apply_extra_cursor();
292 style_was_set = true;
293 }
294 Part::Spacer => {
295 let truncated_start = x_shift.saturating_sub(x).min(len);
296 let truncated_end = (x + len)
297 .saturating_sub(lines.coords().width().saturating_sub(x_shift))
298 .min(len);
299 let spacer_len = len - (truncated_start + truncated_end);
300 lines.write_all(&SPACES[0..spacer_len as usize]).unwrap();
301 last_len = (x + len)
302 .saturating_sub(x_shift)
303 .min(lines.coords().width());
304 }
305 Part::ResetState => print_style(lines, painter.reset(), ansi_codes),
306 Part::SpawnedWidget(id) => spawns_for_next.push(id),
307 Part::ToggleStart(_) | Part::ToggleEnd(_) => {
308 todo!("Toggles have not been implemented yet.")
309 }
310 Part::AlignLeft | Part::AlignCenter | Part::AlignRight => {}
311 }
312 }
313
314 end_line(&mut lines, &painter, &mut ansi_codes, last_len, max_x);
315
316 for _ in 0..lines.coords().br.y - y {
317 end_line(&mut lines, &painter, &mut ansi_codes, 0, max_x);
318 }
319
320 let spawns = text.get_spawned_ids();
321
322 self.layouts
323 .send_lines(self.id, spawn_id, lines, spawns, &observed_spawns);
324 }
325}
326
327impl RawArea for Area {
328 type Cache = PrintInfo;
329 type PrintInfo = PrintInfo;
330
331 fn push(
334 self: CoreAccess<Self>,
335 specs: PushSpecs,
336 on_files: bool,
337 cache: PrintInfo,
338 ) -> Option<(Area, Option<Area>)> {
339 let (child, parent) = self.layouts.push(self.id, specs, on_files, cache)?;
340
341 Some((
342 Self::new(child, self.layouts.clone()),
343 parent.map(|parent| Self::new(parent, self.layouts.clone())),
344 ))
345 }
346
347 fn delete(self: CoreAccess<Self>) -> (bool, Vec<Self>) {
348 let (do_rm_window, rm_areas) = self.layouts.delete(self.id);
349 (
350 do_rm_window,
351 rm_areas
352 .into_iter()
353 .map(|id| Self::new(id, self.layouts.clone()))
354 .collect(),
355 )
356 }
357
358 fn swap(self: CoreAccess<Self>, rhs: &Self) -> bool {
359 self.layouts.swap(self.id, rhs.id)
360 }
361
362 fn spawn(
363 self: CoreAccess<Self>,
364 spawn_id: SpawnId,
365 specs: SpawnSpecs,
366 cache: Self::Cache,
367 ) -> Option<Self> {
368 Some(Self::new(
369 self.layouts
370 .spawn_on_widget(self.id, spawn_id, specs, cache)?,
371 self.layouts.clone(),
372 ))
373 }
374
375 fn set_width(self: CoreAccess<Self>, width: f32) -> Result<(), Text> {
376 if self
377 .layouts
378 .set_constraints(self.id, Some(width), None, None)
379 {
380 Ok(())
381 } else {
382 Err(txt!("Couldn't set the width to [a]{width}"))
383 }
384 }
385
386 fn set_height(self: CoreAccess<Self>, height: f32) -> Result<(), Text> {
387 if self
388 .layouts
389 .set_constraints(self.id, None, Some(height), None)
390 {
391 Ok(())
392 } else {
393 Err(txt!("Couldn't set the height to [a]{height}"))
394 }
395 }
396
397 fn hide(self: CoreAccess<Self>) -> Result<(), Text> {
398 if self
399 .layouts
400 .set_constraints(self.id, None, None, Some(true))
401 {
402 Ok(())
403 } else {
404 Err(txt!("Couldn't hide the Area"))
405 }
406 }
407
408 fn reveal(self: CoreAccess<Self>) -> Result<(), Text> {
409 if self
410 .layouts
411 .set_constraints(self.id, None, None, Some(false))
412 {
413 Ok(())
414 } else {
415 Err(txt!("Couldn't reveal the Area"))
416 }
417 }
418
419 fn width_of_text(self: CoreAccess<Self>, opts: PrintOpts, text: &Text) -> Result<f32, Text> {
420 let max = self
421 .layouts
422 .inspect(self.id, |_, layout| layout.max_value())
423 .ok_or_else(|| txt!("This Area was already deleted"))?;
424
425 let iter = iter::print_iter(text, TwoPoints::default(), max.x, opts);
426
427 Ok(iter.map(|(c, _)| c.x + c.len).max().unwrap_or(0) as f32)
429 }
430
431 fn scroll_ver(self: CoreAccess<Self>, text: &Text, by: i32, opts: PrintOpts) {
432 if by == 0 {
433 return;
434 }
435
436 let Some(coords) = self.layouts.coords_of(self.id, false) else {
437 context::warn!("This Area was already deleted");
438 return;
439 };
440
441 if coords.width() == 0 || coords.height() == 0 {
442 return;
443 }
444
445 let mut info = self.layouts.get_info_of(self.id).unwrap();
446 info.scroll_ver(by, coords, text, opts);
447 self.layouts.set_info_of(self.id, info);
448 }
449
450 fn scroll_around_points(
453 self: CoreAccess<Self>,
454 text: &Text,
455 points: TwoPoints,
456 opts: PrintOpts,
457 ) {
458 let Some(coords) = self.layouts.coords_of(self.id, false) else {
459 context::warn!("This Area was already deleted");
460 return;
461 };
462
463 if coords.width() == 0 || coords.height() == 0 {
464 return;
465 }
466
467 let mut info = self.layouts.get_info_of(self.id).unwrap();
468 info.scroll_around(points.real, coords, text, opts);
469 self.layouts.set_info_of(self.id, info);
470 }
471
472 fn scroll_to_points(self: CoreAccess<Self>, text: &Text, points: TwoPoints, opts: PrintOpts) {
473 let Some(coords) = self.layouts.coords_of(self.id, false) else {
474 context::warn!("This Area was already deleted");
475 return;
476 };
477
478 if coords.width() == 0 || coords.height() == 0 {
479 return;
480 }
481
482 let mut info = self.layouts.get_info_of(self.id).unwrap();
483 info.scroll_to_points(points, coords, text, opts);
484 self.layouts.set_info_of(self.id, info);
485 }
486
487 fn set_as_active(self: CoreAccess<Self>) {
488 self.layouts.set_active_id(self.id);
489 }
490
491 fn print(self: CoreAccess<Self>, text: &Text, opts: PrintOpts, painter: Painter) {
492 Area::print(&self, text, opts, painter, |_, _| {})
493 }
494
495 fn print_with<'a>(
496 self: CoreAccess<Self>,
497 text: &Text,
498 opts: PrintOpts,
499 painter: Painter,
500 f: impl FnMut(&Caret, &Item) + 'a,
501 ) {
502 Area::print(&self, text, opts, painter, f)
503 }
504
505 fn set_print_info(self: CoreAccess<Self>, info: Self::PrintInfo) {
508 self.layouts.set_info_of(self.id, info);
509 }
510
511 fn print_iter<'a>(
512 self: CoreAccess<Self>,
513 text: &'a Text,
514 points: TwoPoints,
515 opts: PrintOpts,
516 ) -> impl Iterator<Item = (Caret, Item)> + 'a {
517 let width = self.width() as u32;
518 print_iter(text, points, width, opts)
519 }
520
521 fn rev_print_iter<'a>(
522 self: CoreAccess<Self>,
523 text: &'a Text,
524 points: TwoPoints,
525 opts: PrintOpts,
526 ) -> impl Iterator<Item = (Caret, Item)> + 'a {
527 let width = self.width() as u32;
528 rev_print_iter(text, points, opts.wrap_width(width).unwrap_or(width), opts)
529 }
530
531 fn has_changed(self: CoreAccess<Self>) -> bool {
532 self.layouts
533 .inspect(self.id, |rect, layout| rect.has_changed(layout))
534 .unwrap_or(false)
535 }
536
537 fn is_master_of(self: CoreAccess<Self>, other: &Self) -> bool {
538 if other.id == self.id {
539 return true;
540 }
541
542 let mut parent_id = other.id;
543
544 self.layouts.inspect(self.id, |_, layout| {
545 while let Some((_, parent)) = layout.get_parent(parent_id) {
546 parent_id = parent.id();
547 if parent.id() == self.id {
548 break;
549 }
550 }
551 });
552
553 parent_id == self.id
554 }
555
556 fn get_cluster_master(self: CoreAccess<Self>) -> Option<Self> {
557 let id = self
558 .layouts
559 .inspect(self.id, |_, layout| layout.get_cluster_master(self.id))??;
560
561 Some(Self {
562 layouts: self.layouts.clone(),
563 id,
564 ansi_codes: Arc::default(),
565 })
566 }
567
568 fn cache(self: CoreAccess<Self>) -> Option<Self::Cache> {
569 let info = self.layouts.get_info_of(self.id)?.for_caching();
570 Some(info)
571 }
572
573 fn width(self: CoreAccess<Self>) -> f32 {
574 self.layouts
575 .coords_of(self.id, false)
576 .map(|coords| coords.width() as f32)
577 .unwrap_or(0.0)
578 }
579
580 fn height(self: CoreAccess<Self>) -> f32 {
581 self.layouts
582 .coords_of(self.id, false)
583 .map(|coords| coords.height() as f32)
584 .unwrap_or(0.0)
585 }
586
587 fn start_points(self: CoreAccess<Self>, text: &Text, opts: PrintOpts) -> TwoPoints {
588 let Some(coords) = self.layouts.coords_of(self.id, false) else {
589 context::warn!("This Area was already deleted");
590 return Default::default();
591 };
592
593 let mut info = self.layouts.get_info_of(self.id).unwrap();
594 let start_points = info.start_points(coords, text, opts);
595 self.layouts.set_info_of(self.id, info);
596
597 start_points
598 }
599
600 fn end_points(self: CoreAccess<Self>, text: &Text, opts: PrintOpts) -> TwoPoints {
601 let Some(coords) = self.layouts.coords_of(self.id, false) else {
602 context::warn!("This Area was already deleted");
603 return Default::default();
604 };
605
606 let mut info = self.layouts.get_info_of(self.id).unwrap();
607 let end_points = info.end_points(coords, text, opts);
608 self.layouts.set_info_of(self.id, info);
609
610 end_points
611 }
612
613 fn get_print_info(self: CoreAccess<Self>) -> Self::PrintInfo {
614 self.layouts.get_info_of(self.id).unwrap_or_default()
615 }
616
617 fn is_active(self: CoreAccess<Self>) -> bool {
618 self.layouts.get_active_id() == self.id
619 }
620}
621
622const fn get_control_str(char: char) -> Option<&'static str> {
623 match char {
624 '\0' => Some("^@"),
625 '\u{01}' => Some("^A"),
626 '\u{02}' => Some("^B"),
627 '\u{03}' => Some("^C"),
628 '\u{04}' => Some("^D"),
629 '\u{05}' => Some("^E"),
630 '\u{06}' => Some("^F"),
631 '\u{07}' => Some("^G"),
632 '\u{08}' => Some("^H"),
633 '\u{0b}' => Some("^K"),
634 '\u{0c}' => Some("^L"),
635 '\u{0e}' => Some("^N"),
636 '\u{0f}' => Some("^O"),
637 '\u{10}' => Some("^P"),
638 '\u{11}' => Some("^Q"),
639 '\u{12}' => Some("^R"),
640 '\u{13}' => Some("^S"),
641 '\u{14}' => Some("^T"),
642 '\u{15}' => Some("^U"),
643 '\u{16}' => Some("^V"),
644 '\u{17}' => Some("^W"),
645 '\u{18}' => Some("^X"),
646 '\u{19}' => Some("^Y"),
647 '\u{1a}' => Some("^Z"),
648 '\u{1b}' => Some("^["),
649 '\u{1c}' => Some("^\\"),
650 '\u{1d}' => Some("^]"),
651 '\u{1e}' => Some("^^"),
652 '\u{1f}' => Some("^_"),
653 '\u{80}' => Some("<80>"),
654 '\u{81}' => Some("<81>"),
655 '\u{82}' => Some("<82>"),
656 '\u{83}' => Some("<83>"),
657 '\u{84}' => Some("<84>"),
658 '\u{85}' => Some("<85>"),
659 '\u{86}' => Some("<86>"),
660 '\u{87}' => Some("<87>"),
661 '\u{88}' => Some("<88>"),
662 '\u{89}' => Some("<89>"),
663 '\u{8a}' => Some("<8a>"),
664 '\u{8b}' => Some("<8b>"),
665 '\u{8c}' => Some("<8c>"),
666 '\u{8d}' => Some("<8d>"),
667 '\u{8e}' => Some("<8e>"),
668 '\u{8f}' => Some("<8f>"),
669 '\u{90}' => Some("<90>"),
670 '\u{91}' => Some("<91>"),
671 '\u{92}' => Some("<92>"),
672 '\u{93}' => Some("<93>"),
673 '\u{94}' => Some("<94>"),
674 '\u{95}' => Some("<95>"),
675 '\u{96}' => Some("<96>"),
676 '\u{97}' => Some("<97>"),
677 '\u{98}' => Some("<98>"),
678 '\u{99}' => Some("<99>"),
679 '\u{9a}' => Some("<9a>"),
680 '\u{9b}' => Some("<9b>"),
681 '\u{9c}' => Some("<9c>"),
682 '\u{9d}' => Some("<9d>"),
683 '\u{9e}' => Some("<9e>"),
684 '\u{9f}' => Some("<9f>"),
685 _ => None,
686 }
687}