1mod iter;
2
3use std::{cell::Cell, fmt::Alignment, sync::Arc};
4
5use crossterm::cursor;
6use duat_core::{
7 cfg::PrintCfg,
8 context::bincode::{Decode, Encode},
9 form::Painter,
10 text::{FwdIter, Item, Part, Point, RevIter, Text, txt},
11 ui::{self, Axis, Caret, Constraint, MutArea, PushSpecs, SpawnSpecs},
12};
13use iter::{print_iter, print_iter_indented, rev_print_iter};
14
15use crate::{
16 AreaId, CStyle, Mutex,
17 layout::{Layout, Rect, transfer_vars},
18 print::Gaps,
19 queue, style,
20};
21
22#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
23pub struct Coord {
24 pub y: u32,
25 pub x: u32,
26}
27
28impl std::fmt::Debug for Coord {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
31 }
32}
33
34impl Coord {
35 pub fn new(x: u32, y: u32) -> Coord {
36 Coord { x, y }
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
41pub struct Coords {
42 pub tl: Coord,
43 pub br: Coord,
44}
45impl Coords {
46 pub fn new(tl: Coord, br: Coord) -> Self {
47 Coords { tl, br }
48 }
49
50 pub fn width(&self) -> u32 {
51 self.br.x - self.tl.x
52 }
53
54 pub fn height(&self) -> u32 {
55 self.br.y - self.tl.y
56 }
57
58 pub fn intersects(&self, other: Self) -> bool {
59 let and_tl = self.tl.max(other.tl);
60 let and_br = self.br.min(other.br);
61 if and_tl.x > and_br.x || and_tl.y > and_br.y {
62 return false;
63 }
64
65 let and_coords = Coords::new(and_tl, and_br);
66 and_coords.width() > 0 && and_coords.height() > 0
67 }
68}
69
70#[derive(Clone)]
71pub struct Area {
72 layouts: Arc<Mutex<Vec<Layout>>>,
73 pub id: AreaId,
74 ansi_codes: Arc<Mutex<micromap::Map<CStyle, String, 16>>>,
75}
76
77impl PartialEq for Area {
78 fn eq(&self, other: &Self) -> bool {
79 self.id == other.id
80 }
81}
82
83impl Area {
84 pub fn new(id: AreaId, layouts: Arc<Mutex<Vec<Layout>>>) -> Self {
85 Self { layouts, id, ansi_codes: Arc::default() }
86 }
87
88 fn print<'a>(
89 &self,
90 text: &mut Text,
91 cfg: PrintCfg,
92 mut painter: Painter,
93 mut f: impl FnMut(&Caret, &Item) + 'a,
94 ) {
95 let layouts = self.layouts.lock().unwrap();
96 let mut ansi_codes = self.ansi_codes.lock().unwrap();
97
98 let layout = get_layout(&layouts, self.id).unwrap();
99 let is_active = layout.active_id() == self.id;
100
101 let (mut lines, iter) = {
102 let rect = layout.get(self.id).unwrap();
103
104 let info = {
105 let mut info = rect.print_info().unwrap().get();
106 info.fix(text);
107 rect.print_info().unwrap().set(info);
108 info
109 };
110
111 let lines = layout.printer.lines(rect.var_points(), info.x_shift, cfg);
112 if lines.coords().width() == 0 || lines.coords().height() == 0 {
113 return;
114 }
115
116 let iter = {
117 let line_start = text.visual_line_start(info.points);
118 let iter = text.iter_fwd(line_start);
119 print_iter(iter, lines.cap(), cfg, info.points)
120 };
121
122 (lines, iter)
123 };
124
125 let mut style_was_set = false;
126 enum Cursor {
127 Main,
128 Extra,
129 }
130
131 let lines_left = {
132 let tl_y = lines.coords().tl.y;
134 let mut y = tl_y;
135 let mut cursor = None;
136
137 for (caret, item) in iter {
138 f(&caret, &item);
139
140 let Caret { x, len, wrap } = caret;
141 let Item { part, .. } = item;
142
143 if wrap {
144 if y > lines.coords().tl.y {
145 lines.end_line(&mut ansi_codes, &painter);
146 }
147 if y == lines.coords().br.y {
148 break;
149 }
150 (0..x).for_each(|_| lines.push_char(' ', 1));
151 style!(lines, &mut ansi_codes, painter.absolute_style());
152 y += 1
153 }
154
155 match part {
156 Part::Char(char) => {
157 if style_was_set {
158 style!(lines, &mut ansi_codes, painter.relative_style());
159 style_was_set = false;
160 }
161 match char {
162 '\t' => (0..len).for_each(|_| lines.push_char(' ', 1)),
163 '\n' => {}
164 char => lines.push_char(char, len),
165 }
166 if let Some(cursor) = cursor.take() {
167 match cursor {
168 Cursor::Main => painter.remove_main_caret(),
169 Cursor::Extra => painter.remove_extra_caret(),
170 }
171 style!(lines, &mut ansi_codes, painter.relative_style())
172 }
173 }
174 Part::PushForm(id) => {
175 painter.apply(id);
176 style_was_set = true;
177 }
178 Part::PopForm(id) => {
179 painter.remove(id);
180 style_was_set = true;
181 }
182 Part::MainCaret => {
183 if let Some(shape) = painter.main_cursor()
184 && is_active
185 {
186 lines.show_real_cursor();
187 queue!(lines, shape, cursor::SavePosition);
188 } else {
189 cursor = Some(Cursor::Main);
190 lines.hide_real_cursor();
191 painter.apply_main_cursor();
192 style_was_set = true;
193 }
194 }
195 Part::ExtraCaret => {
196 cursor = Some(Cursor::Extra);
197 painter.apply_extra_cursor();
198 style_was_set = true;
199 }
200 Part::AlignLeft if !cfg.wrap_method.is_no_wrap() => {
201 lines.realign(Alignment::Left)
202 }
203 Part::AlignCenter if !cfg.wrap_method.is_no_wrap() => {
204 lines.realign(Alignment::Center)
205 }
206 Part::AlignRight if !cfg.wrap_method.is_no_wrap() => {
207 lines.realign(Alignment::Right)
208 }
209 Part::Spacer if !cfg.wrap_method.is_no_wrap() => {
210 lines.add_spacer();
211 }
212 Part::ResetState => {
213 style!(lines, &mut ansi_codes, painter.reset())
214 }
215 Part::ToggleStart(_) | Part::ToggleEnd(_) => todo!(),
216 _ => {}
217 }
218 }
219
220 lines.end_line(&mut ansi_codes, &painter);
221
222 lines.coords().br.y - y
223 };
224
225 for _ in 0..lines_left {
226 lines.end_line(&mut ansi_codes, &painter);
227 }
228
229 layout.printer.send(self.id, lines);
230 }
231}
232
233impl ui::RawArea for Area {
234 type Cache = PrintInfo;
235 type PrintInfo = PrintInfo;
236 type Ui = crate::Ui;
237
238 fn bisect(
241 area: MutArea<Self>,
242 specs: PushSpecs,
243 cluster: bool,
244 on_files: bool,
245 cache: PrintInfo,
246 ) -> (Area, Option<Area>) {
247 let mut layouts = area.layouts.lock().unwrap();
248 let layout = get_layout_mut(&mut layouts, area.id).unwrap();
249
250 let (child, parent) = layout.bisect(area.id, specs, cluster, on_files, cache);
251
252 (
253 Self::new(child, area.layouts.clone()),
254 parent.map(|parent| Self::new(parent, area.layouts.clone())),
255 )
256 }
257
258 fn delete(area: MutArea<Self>) -> Option<Self> {
259 let mut layouts = area.layouts.lock().unwrap();
260 let layout = get_layout_mut(&mut layouts, area.id)?;
263 layout
264 .delete(area.id)
265 .map(|id| Self::new(id, area.layouts.clone()))
266 }
267
268 fn swap(lhs: MutArea<Self>, rhs: &Self) {
269 let mut layouts = lhs.layouts.lock().unwrap();
270 let lhs_lay = get_layout_pos(&layouts, lhs.id).unwrap();
271 let rhs_lay = get_layout_pos(&layouts, rhs.id).unwrap();
272
273 if lhs_lay == rhs_lay {
274 let layout = &mut layouts[lhs_lay];
275 let lhs_id = layout.rects.get_cluster_master(lhs.id).unwrap_or(lhs.id);
276 let rhs_id = layout.rects.get_cluster_master(rhs.id).unwrap_or(rhs.id);
277 if lhs_id == rhs_id {
278 return;
279 }
280 layout.swap(lhs_id, rhs_id);
281 } else {
282 let [lhs_lay, rhs_lay] = layouts.get_disjoint_mut([lhs_lay, rhs_lay]).unwrap();
283 let lhs_id = lhs_lay.rects.get_cluster_master(lhs.id).unwrap_or(lhs.id);
284 let rhs_id = rhs_lay.rects.get_cluster_master(rhs.id).unwrap_or(rhs.id);
285
286 let lhs_rect = lhs_lay.rects.get_mut(lhs_id).unwrap();
287 let rhs_rect = rhs_lay.rects.get_mut(rhs_id).unwrap();
288
289 let lhs_p = &lhs_lay.printer;
290 let rhs_p = &rhs_lay.printer;
291 transfer_vars(lhs_p, rhs_p, lhs_rect);
292 transfer_vars(rhs_p, lhs_p, rhs_rect);
293
294 std::mem::swap(lhs_rect, rhs_rect);
295
296 lhs_lay.reset_eqs(rhs_id);
297 rhs_lay.reset_eqs(lhs_id);
298 }
299
300 for lay in [lhs_lay, rhs_lay] {
301 layouts[lay].printer.update(false);
302 }
303 }
304
305 fn spawn_floating(area: MutArea<Self>, specs: SpawnSpecs) -> Result<Self, Text> {
306 let mut layouts = area.layouts.lock().unwrap();
307 let layout = get_layout_mut(&mut layouts, area.id).unwrap();
308
309 Ok(Self::new(
310 layout.new_floating(area.id, specs),
311 area.layouts.clone(),
312 ))
313 }
314
315 fn spawn_floating_at(
316 _area: MutArea<Self>,
317 _specs: SpawnSpecs,
318 _at: impl duat_core::text::TwoPoints,
319 _text: &Text,
320 _cfg: PrintCfg,
321 ) -> Result<Self, Text> {
322 todo!()
323 }
324
325 fn constrain_hor(&self, cons: impl IntoIterator<Item = Constraint>) -> Result<(), Text> {
326 let cons = {
327 let mut cons: Vec<Constraint> = cons.into_iter().collect();
328 cons.sort_unstable();
329 cons
330 };
331
332 let mut layouts = self.layouts.lock().unwrap();
333 let layout = get_layout_mut(&mut layouts, self.id).unwrap();
334 let old_cons = layout
335 .rects
336 .get_constraints_mut(self.id)
337 .ok_or_else(|| txt!("Area has no parents, so it can't be constrained"))?
338 .clone();
339
340 if old_cons.on(Axis::Horizontal).eq(cons.iter().cloned()) {
341 return Ok(());
342 };
343
344 *layout.rects.get_constraints_mut(self.id).unwrap() = {
345 let (cons, old_eqs) = old_cons.replace(cons.into_iter(), Axis::Horizontal);
346
347 let (_, parent) = layout.get_parent(self.id).unwrap();
348 let rect = layout.get(self.id).unwrap();
349
350 let (cons, new_eqs) = cons.apply(rect, parent.id(), &layout.rects);
351 layout.printer.replace_and_update(old_eqs, new_eqs, false);
352 cons
353 };
354
355 Ok(())
356 }
357
358 fn constrain_ver(&self, cons: impl IntoIterator<Item = Constraint>) -> Result<(), Text> {
359 let cons = {
360 let mut cons: Vec<Constraint> = cons.into_iter().collect();
361 cons.sort_unstable();
362 cons
363 };
364
365 let mut layouts = self.layouts.lock().unwrap();
366 let layout = get_layout_mut(&mut layouts, self.id).unwrap();
367 let old_cons = layout
368 .rects
369 .get_constraints_mut(self.id)
370 .ok_or_else(|| txt!("Area has no parents, so it can't be constrained"))?
371 .clone();
372
373 if old_cons.on(Axis::Vertical).eq(cons.iter().cloned()) {
374 return Ok(());
375 };
376
377 *layout.rects.get_constraints_mut(self.id).unwrap() = {
378 let (cons, old_eqs) = old_cons.replace(cons.into_iter(), Axis::Vertical);
379
380 let (_, parent) = layout.get_parent(self.id).unwrap();
381 let rect = layout.get(self.id).unwrap();
382
383 let (cons, new_eqs) = cons.apply(rect, parent.id(), &layout.rects);
384 layout.printer.replace_and_update(old_eqs, new_eqs, false);
385 cons
386 };
387
388 Ok(())
389 }
390
391 fn restore_constraints(&self) -> Result<(), Text> {
392 todo!();
393 }
394
395 fn request_width_to_fit(&self, _text: &str) -> Result<(), Text> {
396 todo!();
397 }
398
399 fn scroll_around_point(&self, text: &Text, point: Point, cfg: PrintCfg) {
400 let layouts = self.layouts.lock().unwrap();
401 let layout = get_layout(&layouts, self.id).unwrap();
402 let rect = layout.get(self.id).unwrap();
403
404 let (mut info, w, h) = {
405 let coords = layout.printer.coords(rect.var_points(), false);
406 let info = rect.print_info().unwrap().get();
407 (info, coords.width(), coords.height())
408 };
409
410 if w > 0 && h > 0 {
411 scroll_ver_around(&mut info, w, h, point, text, cfg);
412 scroll_hor_around(&mut info, w, point, text, cfg);
413 }
414
415 info.prev_main = point;
416 info.last_points = None;
417
418 rect.print_info().unwrap().set(info);
419 }
420
421 fn set_as_active(&self) {
422 let mut layouts = self.layouts.lock().unwrap();
423 get_layout_mut(&mut layouts, self.id).unwrap().active_id = self.id;
424 }
425
426 fn print(&self, text: &mut Text, cfg: PrintCfg, painter: Painter) {
429 self.print(text, cfg, painter, |_, _| {})
430 }
431
432 fn print_with<'a>(
433 &self,
434 text: &mut Text,
435 cfg: PrintCfg,
436 painter: Painter,
437 f: impl FnMut(&Caret, &Item) + 'a,
438 ) {
439 self.print(text, cfg, painter, f)
440 }
441
442 fn set_print_info(&self, info: Self::PrintInfo) {
443 let layouts = self.layouts.lock().unwrap();
444 let layout = get_layout(&layouts, self.id).unwrap();
445 layout.get(self.id).unwrap().print_info().unwrap().set(info);
446 }
447
448 fn print_iter<'a>(
449 &self,
450 iter: FwdIter<'a>,
451 cfg: PrintCfg,
452 ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a {
453 let points = iter.points();
454 print_iter(iter, cfg.wrap_width(self.width()), cfg, points)
455 }
456
457 fn rev_print_iter<'a>(
458 &self,
459 iter: RevIter<'a>,
460 cfg: PrintCfg,
461 ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a {
462 rev_print_iter(iter, cfg.wrap_width(self.width()), cfg)
463 }
464
465 fn has_changed(&self) -> bool {
466 let layouts = self.layouts.lock().unwrap();
467 if let Some(layout) = get_layout(&layouts, self.id)
468 && let Some(rect) = layout.get(self.id)
469 {
470 rect.has_changed(layout)
471 } else {
472 true
473 }
474 }
475
476 fn is_master_of(&self, other: &Self) -> bool {
479 if other.id == self.id {
480 return true;
481 }
482 let layouts = self.layouts.lock().unwrap();
483 let layout = get_layout(&layouts, self.id).unwrap();
484 let mut parent_id = other.id;
485 while let Some((_, parent)) = layout.get_parent(parent_id) {
486 parent_id = parent.id();
487 if parent.id() == self.id {
488 return true;
489 }
490 }
491
492 parent_id == self.id
493 }
494
495 fn get_cluster_master(&self) -> Option<Self> {
496 let layouts = self.layouts.lock().unwrap();
497 get_layout(&layouts, self.id)
498 .unwrap()
499 .rects
500 .get_cluster_master(self.id)
501 .map(|id| Self::new(id, self.layouts.clone()))
502 }
503
504 fn cache(&self) -> Option<Self::Cache> {
505 let layouts = self.layouts.lock().unwrap();
506 get_rect(&layouts, self.id)
507 .unwrap()
508 .print_info()
509 .map(Cell::get)
510 }
511
512 fn width(&self) -> u32 {
513 let layouts = self.layouts.lock().unwrap();
514 let layout = get_layout(&layouts, self.id).unwrap();
515 let rect = layout.get(self.id).unwrap();
516 let coords = layout.printer.coords(rect.var_points(), false);
517 coords.width()
518 }
519
520 fn height(&self) -> u32 {
521 let layouts = self.layouts.lock().unwrap();
522 let layout = get_layout(&layouts, self.id).unwrap();
523 let rect = layout.get(self.id).unwrap();
524 let coords = layout.printer.coords(rect.var_points(), false);
525 coords.height()
526 }
527
528 fn first_points(&self, _text: &Text, _cfg: PrintCfg) -> (Point, Option<Point>) {
529 let layouts = self.layouts.lock().unwrap();
530 layouted::first_points(self, &layouts)
531 }
532
533 fn last_points(&self, text: &Text, cfg: PrintCfg) -> (Point, Option<Point>) {
534 let layouts = self.layouts.lock().unwrap();
535 layouted::last_points(self, &layouts, text, cfg)
536 }
537
538 fn print_info(&self) -> Self::PrintInfo {
539 let layouts = self.layouts.lock().unwrap();
540 get_rect(&layouts, self.id)
541 .unwrap()
542 .print_info()
543 .unwrap()
544 .get()
545 }
546
547 fn is_active(&self) -> bool {
548 get_layout(&self.layouts.lock().unwrap(), self.id)
549 .unwrap()
550 .active_id
551 == self.id
552 }
553}
554
555#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
559#[bincode(crate = "duat_core::context::bincode")]
560pub struct PrintInfo {
561 points: (Point, Option<Point>),
562 x_shift: u32,
563 prev_main: Point,
564 last_points: Option<(Point, Option<Point>)>,
565 vert_dist: u32,
566}
567
568impl PrintInfo {
569 fn fix(&mut self, text: &Text) {
570 let max = text.len().min(self.points.0);
571 let (_, max_ghost) = text.ghost_max_points_at(max.byte());
572 self.points = (max, self.points.1.min(max_ghost))
573 }
574}
575
576fn scroll_ver_around(
579 info: &mut PrintInfo,
580 width: u32,
581 height: u32,
582 point: Point,
583 text: &Text,
584 cfg: PrintCfg,
585) {
586 if info.prev_main == point {
587 return;
588 }
589
590 let points = text.ghost_max_points_at(point.byte());
591 let after = text
592 .points_after(points)
593 .unwrap_or_else(|| text.len_points());
594
595 let cap = cfg.wrap_width(width);
596
597 let mut below_dist = 0;
598 let mut total_dist = 0;
599 let mut iter = rev_print_iter(text.iter_rev(after), cap, cfg)
600 .filter_map(|(caret, item)| caret.wrap.then_some(item.points()))
601 .inspect(|points| {
602 total_dist += 1;
603 below_dist += (*points >= info.points) as u32;
604 });
605
606 let target = if info.prev_main > point {
607 cfg.scrolloff.y as usize
608 } else {
609 height.saturating_sub(cfg.scrolloff.y as u32 + 1) as usize
610 };
611 let first = iter.nth(target).unwrap_or_default();
612
613 if (info.prev_main > point && first <= info.points)
614 || (info.prev_main < point && first >= info.points)
615 {
616 info.points = first;
617 info.vert_dist = total_dist - 1;
618 } else if info.prev_main > point {
619 iter.take_while(|points| *points >= info.points)
620 .for_each(|_| {});
621
622 info.vert_dist = below_dist - 1;
623 }
624}
625
626fn scroll_hor_around(info: &mut PrintInfo, width: u32, p: Point, text: &Text, cfg: PrintCfg) {
629 let cap = cfg.wrap_width(width);
630 if cap <= width {
632 info.x_shift = 0;
633 return;
634 }
635
636 let (max_shift, start, end) = {
637 let points = text.ghost_max_points_at(p.byte());
638 let after = text
639 .points_after(points)
640 .unwrap_or_else(|| text.len_points());
641
642 let mut iter = rev_print_iter(text.iter_rev(after), cap, cfg);
643
644 let (points, start, end, wrap) = iter
645 .find_map(|(Caret { x, len, wrap }, item)| {
646 let points = item.points();
647 item.part.as_char().and(Some((points, x, x + len, wrap)))
648 })
649 .unwrap_or(((Point::default(), None), 0, 0, true));
650
651 let (line_len, gaps) = {
652 let mut gaps = Gaps::OnRight;
653 let (indent, points) = if wrap {
654 (start, points)
655 } else {
656 iter.find_map(|(caret, item)| caret.wrap.then_some((caret.x, item.points())))
657 .unwrap()
658 };
659
660 let len = print_iter_indented(text.iter_fwd(points), cap, cfg, indent)
661 .inspect(|(_, Item { part, real, .. })| match part {
662 Part::AlignLeft => gaps = Gaps::OnRight,
663 Part::AlignCenter => gaps = Gaps::OnSides,
664 Part::AlignRight => gaps = Gaps::OnLeft,
665 Part::Spacer => gaps.add_spacer(real.byte()),
666 _ => {}
667 })
668 .take_while(|(caret, item)| !caret.wrap || item.points() == points)
669 .last()
670 .map(|(Caret { x, len, .. }, _)| x + len)
671 .unwrap_or(0);
672
673 (len, gaps)
674 };
675
676 let diff = match &gaps {
677 Gaps::OnRight => 0,
678 Gaps::OnLeft => cap - line_len,
679 Gaps::OnSides => (cap - line_len) / 2,
680 Gaps::Spacers(bytes) => {
681 let spaces = gaps.get_spaces(cap - line_len);
682 bytes
683 .iter()
684 .take_while(|b| **b <= p.byte())
685 .zip(spaces)
686 .fold(0, |prev_len, (_, len)| prev_len + len)
687 }
688 };
689
690 (line_len + diff, start + diff, end + diff)
691 };
692
693 info.x_shift = info
694 .x_shift
695 .min(start.saturating_sub(cfg.scrolloff.x as u32))
696 .max(if cfg.force_scrolloff {
697 (end + cfg.scrolloff.x as u32).saturating_sub(width)
698 } else {
699 (end + cfg.scrolloff.x as u32)
700 .min(max_shift)
701 .saturating_sub(width)
702 });
703}
704
705mod layouted {
706 use duat_core::{
707 cfg::PrintCfg,
708 text::{Item, Point, Text},
709 ui::Caret,
710 };
711
712 use super::{Area, get_layout, get_rect, iter::print_iter};
713 use crate::layout::Layout;
714
715 pub fn first_points(area: &Area, layouts: &[Layout]) -> (Point, Option<Point>) {
716 let rect = get_rect(layouts, area.id).unwrap();
717 let info = rect.print_info().unwrap().get();
718 info.points
719 }
720
721 pub fn last_points(
722 area: &Area,
723 layouts: &[Layout],
724 text: &Text,
725 cfg: PrintCfg,
726 ) -> (Point, Option<Point>) {
727 let (mut info, coords) = {
728 let layout = get_layout(layouts, area.id).unwrap();
729 let rect = layout.get(area.id).unwrap();
730 let info = rect.print_info().unwrap();
731 (info.get(), layout.printer.coords(rect.var_points(), false))
732 };
733
734 if let Some(last) = info.last_points {
735 return last;
736 }
737
738 let (line_start, mut y) = if let Some(cursors) = text.selections()
739 && let Some(main) = cursors.get_main()
740 && main.caret() == info.prev_main
741 {
742 (text.visual_line_start(info.prev_main), info.vert_dist)
743 } else {
744 (text.visual_line_start(info.points), 0)
745 };
746
747 let iter = text.iter_fwd(line_start);
748
749 let iter = print_iter(iter, cfg.wrap_width(coords.width()), cfg, info.points);
750 let mut points = info.points;
751
752 for (Caret { wrap, .. }, Item { part, real, ghost }) in iter {
753 if wrap {
754 if y == coords.height() {
755 break;
756 }
757 if part.is_char() {
758 y += 1
759 }
760 } else {
761 points = (real, ghost);
762 }
763 }
764
765 info.last_points = Some(points);
766 let layout = get_layout(layouts, area.id).unwrap();
767 let rect = layout.get(area.id).unwrap();
768 rect.print_info().unwrap().set(info);
769
770 points
771 }
772}
773
774fn get_rect(layouts: &[Layout], id: AreaId) -> Option<&Rect> {
775 layouts.iter().find_map(|l| l.get(id))
776}
777
778fn get_layout(layouts: &[Layout], id: AreaId) -> Option<&Layout> {
779 layouts.iter().find(|l| l.get(id).is_some())
780}
781
782fn get_layout_mut(layouts: &mut [Layout], id: AreaId) -> Option<&mut Layout> {
783 layouts.iter_mut().find(|l| l.get(id).is_some())
784}
785
786fn get_layout_pos(layouts: &[Layout], id: AreaId) -> Option<usize> {
787 layouts.iter().position(|l| l.get(id).is_some())
788}