fix: Remove unnecessary block usage and handle edge cases in rendering

This commit is contained in:
azy 2025-02-24 20:36:05 +09:00
parent b35304f1c3
commit c6cb971f22
2 changed files with 32 additions and 35 deletions

View File

@ -28,7 +28,7 @@ const FILL_CHAR_EMPTY: char = ' ';
pub enum PreviewWidget<'a> { pub enum PreviewWidget<'a> {
Paragraph(Paragraph<'a>), Paragraph(Paragraph<'a>),
Image(ImagePreviewWidget<'a>), Image(ImagePreviewWidget),
} }
impl Widget for PreviewWidget<'_> { impl Widget for PreviewWidget<'_> {
fn render(self, area: Rect, buf: &mut Buffer) fn render(self, area: Rect, buf: &mut Buffer)
@ -58,7 +58,6 @@ pub fn draw_preview_content_block(
&preview_state.preview.title, &preview_state.preview.title,
use_nerd_font_icons, use_nerd_font_icons,
)?; )?;
// render the preview content // render the preview content
let rp = build_preview_widget( let rp = build_preview_widget(
inner, inner,
@ -117,9 +116,9 @@ pub fn build_preview_widget<'a>(
inner.height, inner.height,
)) ))
} }
PreviewContent::Image(image) => PreviewWidget::Image( PreviewContent::Image(image) => {
image.image_preview_widget(inner, preview_block), PreviewWidget::Image(image.image_preview_widget(inner))
), }
// meta // meta
PreviewContent::Loading => PreviewWidget::Paragraph( PreviewContent::Loading => PreviewWidget::Paragraph(

View File

@ -3,7 +3,7 @@ use image::{DynamicImage, GenericImageView, Pixel, Rgba};
use ratatui::buffer::{Buffer, Cell}; use ratatui::buffer::{Buffer, Cell};
use ratatui::layout::{Position, Rect}; use ratatui::layout::{Position, Rect};
use ratatui::prelude::Color; use ratatui::prelude::Color;
use ratatui::widgets::{Block, Widget}; use ratatui::widgets::Widget;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -18,35 +18,27 @@ const CACHED_HEIGHT: u32 = 128;
const GRAY: Rgba<u8> = Rgba([242, 242, 242, 255]); const GRAY: Rgba<u8> = Rgba([242, 242, 242, 255]);
const WHITE: Rgba<u8> = Rgba([255, 255, 255, 255]); const WHITE: Rgba<u8> = Rgba([255, 255, 255, 255]);
pub struct ImagePreviewWidget<'a> { pub struct ImagePreviewWidget {
block: Block<'a>,
displayed_image: Arc<Mutex<Option<DisplayedImage>>>, displayed_image: Arc<Mutex<Option<DisplayedImage>>>,
} }
impl<'a> ImagePreviewWidget<'a> { impl ImagePreviewWidget {
pub fn new( pub fn new(
displayed_image: Arc<Mutex<Option<DisplayedImage>>>, displayed_image: Arc<Mutex<Option<DisplayedImage>>>,
block: ratatui::widgets::Block<'a>, ) -> ImagePreviewWidget {
) -> ImagePreviewWidget<'a> { ImagePreviewWidget { displayed_image }
ImagePreviewWidget {
block,
displayed_image,
} }
} }
} impl Widget for ImagePreviewWidget {
impl Widget for ImagePreviewWidget<'_> {
fn render(self, area: Rect, buf: &mut Buffer) fn render(self, area: Rect, buf: &mut Buffer)
where where
Self: Sized, Self: Sized,
{ {
// I also put the render of the block to be similar has the preview paragraph, but I believe they are both useless since the block is already rendered
let inner = self.block.inner(area);
self.block.render(area, buf);
self.displayed_image self.displayed_image
.lock() .lock()
.unwrap() .unwrap()
.as_ref() .as_ref()
.unwrap() .unwrap()
.render(inner, buf); .render(area, buf);
} }
} }
@ -64,21 +56,32 @@ impl Widget for &DisplayedImage {
} }
let width = self.cells[0].len(); let width = self.cells[0].len();
// offset of the left top corner where the image is centered // offset of the left top corner where the image is centered
let x_offset = (usize::from(area.width + 2 * area.x) - width) / 2; let total_width = usize::from(area.width) + 2 * usize::from(area.x);
let y_offset = (usize::from(area.height + 2 * area.y) - height) / 2; let x_offset = total_width.saturating_sub(width) / 2;
let total_height = usize::from(area.height) + 2 * usize::from(area.y);
let y_offset = total_height.saturating_sub(height) / 2;
let (area_border_up, area_border_down) =
(area.y, area.y + area.height);
let (area_border_left, area_border_right) =
(area.x, area.x + area.width);
for (y, row) in self.cells.iter().enumerate() { for (y, row) in self.cells.iter().enumerate() {
for (x, cell) in row.iter().enumerate() { for (x, cell) in row.iter().enumerate() {
if let Some(buf_cell) = buf.cell_mut(Position::new( let pos_x = u16::try_from(x_offset + x).unwrap_or(u16::MAX);
u16::try_from(x_offset + x).unwrap_or(u16::MAX), let pos_y = u16::try_from(y_offset + y).unwrap_or(u16::MAX);
u16::try_from(y_offset + y).unwrap_or(u16::MAX), if (pos_y >= area_border_up && pos_y < area_border_down)
)) { && (pos_x >= area_border_left && pos_x < area_border_right)
{
if let Some(buf_cell) =
buf.cell_mut(Position::new(pos_x, pos_y))
{
*buf_cell = cell.clone(); *buf_cell = cell.clone();
} }
} }
} }
} }
} }
}
#[derive(Clone)] #[derive(Clone)]
pub struct CachedImageData { pub struct CachedImageData {
image: DynamicImage, image: DynamicImage,
@ -148,11 +151,7 @@ impl CachedImageData {
}; };
CachedImageData::new(resized_image) CachedImageData::new(resized_image)
} }
pub fn image_preview_widget<'a>( pub fn image_preview_widget(&self, inner: Rect) -> ImagePreviewWidget {
&self,
inner: Rect,
preview_block: Block<'a>,
) -> ImagePreviewWidget<'a> {
// inner has to be the inner of the preview_block given // inner has to be the inner of the preview_block given
// if nothing in the cache of the image, or the area has changed, generate a new image to be displayed and cache it // if nothing in the cache of the image, or the area has changed, generate a new image to be displayed and cache it
@ -161,13 +160,12 @@ impl CachedImageData {
{ {
self.set_cache(inner, self.cells_for_area(inner)); self.set_cache(inner, self.cells_for_area(inner));
} }
ImagePreviewWidget::new(self.inner_cache.clone(), preview_block) ImagePreviewWidget::new(self.inner_cache.clone())
} }
pub fn cells_for_area(&self, inner: Rect) -> Vec<Vec<Cell>> { pub fn cells_for_area(&self, inner: Rect) -> Vec<Vec<Cell>> {
// size of the available area // size of the available area
let preview_width = u32::from(inner.width); let preview_width = u32::from(inner.width);
let preview_height = u32::from(inner.height) * 2; // *2 because 2 pixels per character let preview_height = u32::from(inner.height) * 2; // *2 because 2 pixels per character
// resize if it doesn't fit in // resize if it doesn't fit in
let image_rgba = if self.image.width() > preview_width let image_rgba = if self.image.width() > preview_width
|| self.image.height() > preview_height || self.image.height() > preview_height