#![doc = include_str!("../readme.md")]
use ratatui_core::layout::{Position, Rect};
pub trait RelocatableState {
fn relocate(&mut self, shift: (i16, i16), clip: Rect);
#[allow(unused_variables)]
fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {}
fn relocate_popup_hidden(&mut self) {
self.relocate_popup((0, 0), Rect::default())
}
fn relocate_hidden(&mut self) {
self.relocate((0, 0), Rect::default())
}
}
#[macro_export]
macro_rules! impl_relocatable_state {
($($n:ident),* for $ty:ty) => {
impl $crate::RelocatableState for $ty {
fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
$(self.$n.relocate(shift, clip);)*
}
}
};
}
impl RelocatableState for Rect {
fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
*self = relocate_area(*self, shift, clip);
}
}
impl RelocatableState for [Rect] {
fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
for rect in self {
rect.relocate(shift, clip);
}
}
}
impl RelocatableState for () {
fn relocate(&mut self, _shift: (i16, i16), _clip: Rect) {}
}
pub fn relocate_areas(area: &mut [Rect], shift: (i16, i16), clip: Rect) {
for a in area {
*a = relocate_area(*a, shift, clip)
}
}
pub fn relocate_area(area: Rect, shift: (i16, i16), clip: Rect) -> Rect {
let relocated = relocate(area, shift);
let clipped = clipped(relocated, clip);
if !clipped.is_empty() {
clipped
} else {
Rect::default()
}
}
pub fn relocate_positions(pos: &mut [Position], shift: (i16, i16), clip: Rect) {
for p in pos {
*p = relocate_position(*p, shift, clip).unwrap_or_default()
}
}
pub fn relocate_position(pos: Position, shift: (i16, i16), clip: Rect) -> Option<Position> {
let reloc = relocate(Rect::new(pos.x, pos.y, 0, 0), shift);
let reloc_pos = Position::new(reloc.x, reloc.y);
if clip.contains(reloc_pos) {
Some(reloc_pos)
} else {
None
}
}
pub fn relocate_pos_tuple(pos: (u16, u16), shift: (i16, i16), clip: Rect) -> Option<(u16, u16)> {
let reloc = relocate(Rect::new(pos.0, pos.1, 0, 0), shift);
let reloc_pos = Position::new(reloc.x, reloc.y);
if clip.contains(reloc_pos) {
Some((reloc_pos.x, reloc_pos.y))
} else {
None
}
}
pub fn relocate_pos_tuple_opt(
pos: Option<(u16, u16)>,
shift: (i16, i16),
clip: Rect,
) -> Option<(u16, u16)> {
let Some(pos) = pos else { return None };
relocate_pos_tuple(pos, shift, clip)
}
pub fn relocate_dark_offset(area: Rect, shift: (i16, i16), clip: Rect) -> (u16, u16) {
let relocated = relocate(area, shift);
let clipped = clipped(relocated, clip);
(clipped.x - relocated.x, clipped.y - relocated.y)
}
#[inline]
fn relocate(area: Rect, shift: (i16, i16)) -> Rect {
let x0 = area.left().saturating_add_signed(shift.0);
let x1 = area.right().saturating_add_signed(shift.0);
let y0 = area.top().saturating_add_signed(shift.1);
let y1 = area.bottom().saturating_add_signed(shift.1);
Rect::new(x0, y0, x1 - x0, y1 - y0)
}
#[inline]
fn clipped(area: Rect, clip: Rect) -> Rect {
area.intersection(clip)
}