1#![doc = include_str!("../readme.md")]
2
3use ratatui::layout::{Position, Rect};
4
5pub trait RelocatableState {
17 fn relocate(&mut self, shift: (i16, i16), clip: Rect);
21}
22
23#[macro_export]
26macro_rules! impl_relocatable_state {
27 ($($n:ident),* for $ty:ty) => {
28 impl $crate::RelocatableState for $ty {
29 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
30 $(self.$n.relocate(shift, clip);)*
31 }
32 }
33 };
34}
35
36impl RelocatableState for Rect {
37 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
38 *self = relocate_area(*self, shift, clip);
39 }
40}
41
42impl RelocatableState for [Rect] {
43 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
44 for rect in self {
45 rect.relocate(shift, clip);
46 }
47 }
48}
49
50impl RelocatableState for () {
51 fn relocate(&mut self, _shift: (i16, i16), _clip: Rect) {}
52}
53
54pub fn relocate_areas(area: &mut [Rect], shift: (i16, i16), clip: Rect) {
56 for a in area {
57 *a = relocate_area(*a, shift, clip)
58 }
59}
60
61pub fn relocate_area(area: Rect, shift: (i16, i16), clip: Rect) -> Rect {
63 let relocated = relocate(area, shift);
64 let clipped = clipped(relocated, clip);
65
66 if !clipped.is_empty() {
67 clipped
68 } else {
69 Rect::default()
71 }
72}
73
74pub fn relocate_positions(pos: &mut [Position], shift: (i16, i16), clip: Rect) {
77 for p in pos {
78 *p = relocate_position(*p, shift, clip).unwrap_or_default()
79 }
80}
81
82pub fn relocate_position(pos: Position, shift: (i16, i16), clip: Rect) -> Option<Position> {
85 let reloc = relocate(Rect::new(pos.x, pos.y, 0, 0), shift);
86 let reloc_pos = Position::new(reloc.x, reloc.y);
87
88 if clip.contains(reloc_pos) {
89 Some(reloc_pos)
90 } else {
91 None
92 }
93}
94
95pub fn relocate_pos_tuple(pos: (u16, u16), shift: (i16, i16), clip: Rect) -> Option<(u16, u16)> {
98 let reloc = relocate(Rect::new(pos.0, pos.1, 0, 0), shift);
99 let reloc_pos = Position::new(reloc.x, reloc.y);
100
101 if clip.contains(reloc_pos) {
102 Some((reloc_pos.x, reloc_pos.y))
103 } else {
104 None
105 }
106}
107
108pub fn relocate_dark_offset(area: Rect, shift: (i16, i16), clip: Rect) -> (u16, u16) {
113 let relocated = relocate(area, shift);
114 let clipped = clipped(relocated, clip);
115
116 (clipped.x - relocated.x, clipped.y - relocated.y)
117}
118
119#[inline]
120fn relocate(area: Rect, shift: (i16, i16)) -> Rect {
121 let x0 = area.left().saturating_add_signed(shift.0);
122 let x1 = area.right().saturating_add_signed(shift.0);
123 let y0 = area.top().saturating_add_signed(shift.1);
124 let y1 = area.bottom().saturating_add_signed(shift.1);
125
126 Rect::new(x0, y0, x1 - x0, y1 - y0)
127}
128
129#[inline]
130fn clipped(area: Rect, clip: Rect) -> Rect {
131 area.intersection(clip)
132}