use ratatui::Frame;
use ratatui::layout::Rect;
use ratatui_image::Image as RatatuiImage;
use crate::tui::message::format::EMOJI_REACTION_IMAGE_WIDTH;
use super::types::EmojiImage;
pub(in crate::tui::ui) fn overlay_emoji_column<S: AsRef<str>>(
frame: &mut Frame,
area: Rect,
x_offset: u16,
row_urls: impl Iterator<Item = Option<S>>,
emoji_images: &[EmojiImage<'_>],
) {
if area.width <= x_offset || area.height == 0 {
return;
}
let width = EMOJI_REACTION_IMAGE_WIDTH.min(area.width.saturating_sub(x_offset));
if width == 0 {
return;
}
for (offset, url) in row_urls.enumerate() {
let Some(url) = url else {
continue;
};
let Some(image) = emoji_images
.iter()
.find(|image| image.url.as_str() == url.as_ref())
else {
continue;
};
let y = area
.y
.saturating_add(u16::try_from(offset).unwrap_or(u16::MAX));
if y >= area.y.saturating_add(area.height) {
continue;
}
frame.render_widget(
RatatuiImage::new(image.protocol),
Rect {
x: area.x.saturating_add(x_offset),
y,
width,
height: 1,
},
);
}
}
pub(in crate::tui::ui) struct EmojiSlot {
pub(in crate::tui::ui) row_in_list: isize,
pub(in crate::tui::ui) col: isize,
pub(in crate::tui::ui) max_width: u16,
pub(in crate::tui::ui) url: String,
}
pub(in crate::tui::ui) fn overlay_emoji_slots(
frame: &mut Frame,
list: Rect,
emoji_images: &[EmojiImage<'_>],
occlusion_areas: &[Rect],
slots: impl Iterator<Item = EmojiSlot>,
) {
if emoji_images.is_empty() || list.height == 0 {
return;
}
let list_right = list.x as isize + list.width as isize;
for slot in slots {
if slot.row_in_list < 0
|| slot.row_in_list >= list.height as isize
|| slot.col < 0
|| slot.col >= list_right
{
continue;
}
let Some(image) = emoji_images.iter().find(|image| image.url == slot.url) else {
continue;
};
let remaining = (list_right - slot.col).max(0) as u16;
let width = EMOJI_REACTION_IMAGE_WIDTH
.min(slot.max_width)
.min(remaining);
if width == 0 {
continue;
}
let image_area = Rect {
x: slot.col as u16,
y: (list.y as isize + slot.row_in_list) as u16,
width,
height: 1,
};
if intersects_any(image_area, occlusion_areas) {
continue;
}
frame.render_widget(RatatuiImage::new(image.protocol), image_area);
}
}
pub(in crate::tui::ui) fn intersects_any(area: Rect, occlusion_areas: &[Rect]) -> bool {
occlusion_areas
.iter()
.any(|occlusion| rects_intersect(area, *occlusion))
}
fn rects_intersect(a: Rect, b: Rect) -> bool {
!a.is_empty()
&& !b.is_empty()
&& a.x < b.x.saturating_add(b.width)
&& b.x < a.x.saturating_add(a.width)
&& a.y < b.y.saturating_add(b.height)
&& b.y < a.y.saturating_add(a.height)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intersects_any_boundaries() {
let a = Rect::new(2, 2, 2, 1);
let right = Rect::new(4, 2, 2, 1);
let below = Rect::new(2, 3, 2, 1);
assert!(!intersects_any(a, &[right, below]));
assert!(intersects_any(a, &[Rect::new(3, 2, 2, 1)]));
assert!(!intersects_any(a, &[Rect::new(2, 2, 0, 1)]));
}
}