Skip to content

Commit

Permalink
feat: support customizable alignment for image display
Browse files Browse the repository at this point in the history
  • Loading branch information
gaesa committed Nov 12, 2024
1 parent fd8871d commit 2fad7d8
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 55 deletions.
16 changes: 8 additions & 8 deletions yazi-adapter/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tracing::warn;
use yazi_shared::env_exists;

use super::{Iip, Kgp, KgpOld};
use crate::{Chafa, Emulator, SHOWN, Sixel, TMUX, Ueberzug, WSL};
use crate::{Chafa, Emulator, Offset, SHOWN, Sixel, TMUX, Ueberzug, WSL};

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Adapter {
Expand Down Expand Up @@ -36,18 +36,18 @@ impl Display for Adapter {
}

impl Adapter {
pub async fn image_show(self, path: &Path, max: Rect) -> Result<Rect> {
pub async fn image_show(self, path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
if max.is_empty() {
return Ok(Rect::default());
}

match self {
Self::Kgp => Kgp::image_show(path, max).await,
Self::KgpOld => KgpOld::image_show(path, max).await,
Self::Iip => Iip::image_show(path, max).await,
Self::Sixel => Sixel::image_show(path, max).await,
Self::X11 | Self::Wayland => Ueberzug::image_show(path, max).await,
Self::Chafa => Chafa::image_show(path, max).await,
Self::Kgp => Kgp::image_show(path, max, offset).await,
Self::KgpOld => KgpOld::image_show(path, max, offset).await,
Self::Iip => Iip::image_show(path, max, offset).await,
Self::Sixel => Sixel::image_show(path, max, offset).await,
Self::X11 | Self::Wayland => Ueberzug::image_show(path, max, offset).await,
Self::Chafa => Chafa::image_show(path, max, offset).await,
}
}

Expand Down
18 changes: 10 additions & 8 deletions yazi-adapter/src/chafa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use std::{io::Write, path::Path, process::Stdio};
use ansi_to_tui::IntoText;
use anyhow::{Result, bail};
use crossterm::{cursor::MoveTo, queue};
use ratatui::layout::Rect;
use ratatui::layout::{Rect, Size};
use tokio::process::Command;

use crate::{Adapter, Emulator};
use crate::{Adapter, Emulator, Offset};

pub(super) struct Chafa;

impl Chafa {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
let output = Command::new("chafa")
.args([
"-f",
Expand Down Expand Up @@ -46,11 +46,13 @@ impl Chafa {
bail!("failed to parse chafa output");
};

let area = Rect {
x: max.x,
y: max.y,
width: first.width() as u16,
height: lines.len() as u16,
let area = {
let width = first.width() as u16;
let height = lines.len() as u16;
let offset = offset.unwrap_or_else(|| {
Offset::from((Size { width, height }, Size { width: max.width, height: max.height }))
});
Rect { x: max.x + offset.x, y: max.y + offset.y, width, height }
};

Adapter::Chafa.image_hide()?;
Expand Down
6 changes: 3 additions & 3 deletions yazi-adapter/src/iip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use ratatui::layout::Rect;
use yazi_config::PREVIEW;

use super::image::Image;
use crate::{CLOSE, Emulator, START, adapter::Adapter};
use crate::{CLOSE, Emulator, Offset, START, adapter::Adapter};

pub(super) struct Iip;

impl Iip {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let area = Image::pixel_area((img.width(), img.height()), max, offset);
let b = Self::encode(img).await?;

Adapter::Iip.image_hide()?;
Expand Down
18 changes: 10 additions & 8 deletions yazi-adapter/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use std::path::{Path, PathBuf};

use anyhow::Result;
use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation};
use ratatui::layout::Rect;
use ratatui::layout::{Rect, Size};
use yazi_config::{PREVIEW, TASKS};

use crate::Dimension;
use crate::{Dimension, Offset};

pub struct Image;

Expand Down Expand Up @@ -73,13 +73,15 @@ impl Image {
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height))
}

pub(super) fn pixel_area(size: (u32, u32), rect: Rect) -> Rect {
pub(super) fn pixel_area(size: (u32, u32), rect: Rect, offset: Option<Offset>) -> Rect {
Dimension::ratio()
.map(|(r1, r2)| Rect {
x: rect.x,
y: rect.y,
width: (size.0 as f64 / r1).ceil() as u16,
height: (size.1 as f64 / r2).ceil() as u16,
.map(|(r1, r2)| {
let width = (size.0 as f64 / r1).ceil() as u16;
let height = (size.1 as f64 / r2).ceil() as u16;
let offset = offset.unwrap_or_else(|| {
Offset::from((Size { width, height }, Size { width: rect.width, height: rect.height }))
});
Rect { x: rect.x + offset.x, y: rect.y + offset.y, width, height }
})
.unwrap_or(rect)
}
Expand Down
6 changes: 3 additions & 3 deletions yazi-adapter/src/kgp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use image::DynamicImage;
use ratatui::layout::Rect;

use super::image::Image;
use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter};
use crate::{CLOSE, ESCAPE, Emulator, Offset, START, adapter::Adapter};

static DIACRITICS: [char; 297] = [
'\u{0305}',
Expand Down Expand Up @@ -313,9 +313,9 @@ static DIACRITICS: [char; 297] = [
pub(super) struct Kgp;

impl Kgp {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let area = Image::pixel_area((img.width(), img.height()), max, offset);

let b1 = Self::encode(img).await?;
let b2 = Self::place(&area)?;
Expand Down
6 changes: 3 additions & 3 deletions yazi-adapter/src/kgp_old.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use image::DynamicImage;
use ratatui::layout::Rect;

use super::image::Image;
use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter};
use crate::{CLOSE, ESCAPE, Emulator, Offset, START, adapter::Adapter};

pub(super) struct KgpOld;

impl KgpOld {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let area = Image::pixel_area((img.width(), img.height()), max, offset);
let b = Self::encode(img).await?;

Adapter::KgpOld.image_hide()?;
Expand Down
2 changes: 1 addition & 1 deletion yazi-adapter/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(clippy::unit_arg)]

yazi_macro::mod_flat!(
adapter chafa dimension emulator iip image kgp kgp_old mux sixel ueberzug
adapter chafa dimension emulator iip image kgp kgp_old mux offset sixel ueberzug
);

use yazi_shared::{RoCell, SyncCell, env_exists, in_wsl};
Expand Down
27 changes: 27 additions & 0 deletions yazi-adapter/src/offset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use ratatui::layout::Size;
use yazi_config::PREVIEW;
use yazi_shared::alignment::{HorizontalAlignment, VerticalAlignment};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Offset {
pub x: u16,
pub y: u16,
}

impl From<(Size, Size)> for Offset {
fn from(value: (Size, Size)) -> Self {
let inner = value.0;
let outer = value.1;
let offset_x = match PREVIEW.alignment.horizontal {
HorizontalAlignment::Left => 0,
HorizontalAlignment::Center => (outer.width - inner.width) / 2,
HorizontalAlignment::Right => outer.width - inner.width,
};
let offset_y = match PREVIEW.alignment.vertical {
VerticalAlignment::Top => 0,
VerticalAlignment::Center => (outer.height - inner.height) / 2,
VerticalAlignment::Bottom => outer.height - inner.height,
};
Self { x: offset_x, y: offset_y }
}
}
6 changes: 3 additions & 3 deletions yazi-adapter/src/sixel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use image::DynamicImage;
use ratatui::layout::Rect;
use yazi_config::PREVIEW;

use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};
use crate::{CLOSE, ESCAPE, Emulator, Image, Offset, START, adapter::Adapter};

pub(super) struct Sixel;

impl Sixel {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let area = Image::pixel_area((img.width(), img.height()), max, offset);
let b = Self::encode(img).await?;

Adapter::Sixel.image_hide()?;
Expand Down
18 changes: 10 additions & 8 deletions yazi-adapter/src/ueberzug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use std::{path::{Path, PathBuf}, process::Stdio};

use anyhow::{Result, bail};
use imagesize::ImageSize;
use ratatui::layout::Rect;
use ratatui::layout::{Rect, Size};
use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}};
use tracing::{debug, warn};
use yazi_config::PREVIEW;
use yazi_shared::{RoCell, env_exists};

use crate::{Adapter, Dimension};
use crate::{Adapter, Dimension, Offset};

#[allow(clippy::type_complexity)]
static DEMON: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCell::new();
Expand Down Expand Up @@ -41,7 +41,7 @@ impl Ueberzug {
DEMON.init(Some(tx))
}

pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(super) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
let Some(tx) = &*DEMON else {
bail!("uninitialized ueberzugpp");
};
Expand All @@ -51,11 +51,13 @@ impl Ueberzug {
tokio::task::spawn_blocking(move || imagesize::size(p)).await??;

let area = Dimension::ratio()
.map(|(r1, r2)| Rect {
x: max.x,
y: max.y,
width: max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _),
height: max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _),
.map(|(r1, r2)| {
let width = max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _);
let height = max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _);
let offset = offset.unwrap_or_else(|| {
Offset::from((Size { width, height }, Size { width: max.width, height: max.height }))
});
Rect { x: max.x + offset.x, y: max.y + offset.y, width, height }
})
.unwrap_or(max);

Expand Down
1 change: 1 addition & 0 deletions yazi-config/preset/yazi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ wrap = "no"
tab_size = 2
max_width = 600
max_height = 900
alignment = { horizontal = "center", vertical = "top" }
cache_dir = ""
image_delay = 30
image_filter = "triangle"
Expand Down
6 changes: 5 additions & 1 deletion yazi-config/src/preview/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{borrow::Cow, path::PathBuf, str::FromStr, time::{SystemTime, UNIX_EPOC
use anyhow::Context;
use serde::{Deserialize, Deserializer, Serialize};
use validator::Validate;
use yazi_shared::fs::expand_path;
use yazi_shared::{alignment::Alignment, fs::expand_path};

use super::PreviewWrap;
use crate::Xdg;
Expand All @@ -17,6 +17,7 @@ pub struct Preview {
pub tab_size: u8,
pub max_width: u32,
pub max_height: u32,
pub alignment: Alignment,

pub cache_dir: PathBuf,

Expand Down Expand Up @@ -73,6 +74,8 @@ impl<'de> Deserialize<'de> for Preview {
tab_size: u8,
max_width: u32,
max_height: u32,
#[serde(default)]
alignment: Alignment,

cache_dir: Option<String>,

Expand All @@ -96,6 +99,7 @@ impl<'de> Deserialize<'de> for Preview {
tab_size: preview.tab_size,
max_width: preview.max_width,
max_height: preview.max_height,
alignment: preview.alignment,

cache_dir: preview
.cache_dir
Expand Down
22 changes: 14 additions & 8 deletions yazi-plugin/src/utils/image.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use mlua::{IntoLua, Lua, Table, Value};
use yazi_adapter::{ADAPTOR, Image};
use yazi_adapter::{ADAPTOR, Image, Offset};

use super::Utils;
use crate::{elements::Rect, url::UrlRef};
Expand All @@ -8,13 +8,19 @@ impl Utils {
pub(super) fn image(lua: &Lua, ya: &Table) -> mlua::Result<()> {
ya.raw_set(
"image_show",
lua.create_async_function(|lua, (url, rect): (UrlRef, Rect)| async move {
if let Ok(area) = ADAPTOR.image_show(&url, *rect).await {
Rect::from(area).into_lua(&lua)
} else {
Value::Nil.into_lua(&lua)
}
})?,
lua.create_async_function(
|lua, (url, rect, offset_table): (UrlRef, Rect, Option<Table>)| async move {
let offset = offset_table.map(|lua_offset| Offset {
x: lua_offset.get("x").unwrap_or(0),
y: lua_offset.get("y").unwrap_or(0),
});
if let Ok(area) = ADAPTOR.image_show(&url, *rect, offset).await {
Rect::from(area).into_lua(&lua)
} else {
Value::Nil.into_lua(&lua)
}
},
)?,
)?;

ya.raw_set(
Expand Down
27 changes: 27 additions & 0 deletions yazi-shared/src/alignment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HorizontalAlignment {
Left,
#[default]
Center,
Right,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VerticalAlignment {
#[default]
Top,
Center,
Bottom,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Alignment {
#[serde(default)]
pub horizontal: HorizontalAlignment,
#[serde(default)]
pub vertical: VerticalAlignment,
}
2 changes: 1 addition & 1 deletion yazi-shared/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(clippy::option_map_unit_fn)]

yazi_macro::mod_pub!(errors event fs shell theme translit);
yazi_macro::mod_pub!(alignment errors event fs shell theme translit);

yazi_macro::mod_flat!(chars condition debounce env id layer natsort number os rand ro_cell sync_cell terminal throttle time xdg);

Expand Down

0 comments on commit 2fad7d8

Please sign in to comment.