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