feat: Lua API for printing escape sequences to the terminal (#4059)

This commit is contained in:
三咲雅 misaki masa 2026-06-19 23:14:13 +08:00 committed by GitHub
parent ab8d634f8e
commit 84a5be0b6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 245 additions and 191 deletions

14
Cargo.lock generated
View file

@ -375,9 +375,9 @@ dependencies = [
[[package]]
name = "bitvec"
version = "1.0.1"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837"
dependencies = [
"funty",
"radium",
@ -519,9 +519,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.11.1"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
[[package]]
name = "castaway"
@ -1393,9 +1393,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "0.14.7"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
dependencies = [
"typenum",
"version_check",
@ -1407,7 +1407,7 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351"
dependencies = [
"generic-array 0.14.7",
"generic-array 0.14.9",
"rustversion",
"typenum",
]

View file

@ -1 +1 @@
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes","Mimelist","renderables","redrawer"],"language":"en","version":"0.2","flagWords":[]}
{"flagWords":[],"version":"0.2","language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes","Mimelist","renderables","redrawer","Padable"]}

View file

@ -25,7 +25,7 @@ impl Actor for Close {
}
drop(guard);
cx.input.main_visible = false;
cx.input.main.visible = false;
act!(cmp:close, cx)?;
succ!(render!());

View file

@ -1,5 +1,7 @@
use std::ops::DerefMut;
use anyhow::Result;
use ratatui_widgets::block::Padding;
use ratatui_core::layout::Margin;
use yazi_config::{THEME, YAZI};
use yazi_macro::{act, render, succ};
use yazi_parser::input::ShowForm;
@ -17,15 +19,16 @@ impl Actor for Show {
fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result<Data> {
act!(input:close, cx)?;
let area = cx.mgr.area(opt.position);
let input = &mut cx.input;
input.main_visible = true;
input.main_title = opt.title.clone();
input.main_position = opt.position;
input.main.visible = true;
input.main.title = opt.title.clone();
input.main.position = opt.position;
opt.styles = (&THEME.input).into();
opt.blinking = YAZI.input.cursor_blink;
opt.position = opt.position.padding(Padding::uniform(1));
input.main = yazi_widgets::input::Input::new(opt)?;
*input.main.deref_mut() = yazi_widgets::input::Input::new(opt)?;
input.main.repos(area.inner(Margin::new(1, 1)));
succ!(render!());
}

View file

@ -1,21 +1,20 @@
use std::{ops::Deref, sync::Arc};
use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields};
use parking_lot::Mutex;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Lives, PtrCell};
pub(super) struct InputAlt(PtrCell<Arc<Mutex<yazi_widgets::input::Input>>>);
pub(super) struct InputAlt(PtrCell<yazi_core::input::InputAlt>);
impl Deref for InputAlt {
type Target = Arc<Mutex<yazi_widgets::input::Input>>;
type Target = yazi_core::input::InputAlt;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl InputAlt {
pub(super) fn make(inner: &Arc<Mutex<yazi_widgets::input::Input>>) -> mlua::Result<AnyUserData> {
pub(super) fn make(inner: &yazi_core::input::InputAlt) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self(inner.into()))
}
}

View file

@ -1,8 +1,9 @@
use std::fmt::Debug;
use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_shim::ratatui::Padable;
use super::{Pos, Rect};
use super::Rect;
use crate::position::Position;
const EXPECTED: &str = "expected a Pos or Rect";
@ -10,7 +11,7 @@ const EXPECTED: &str = "expected a Pos or Rect";
#[derive(Clone, Copy)]
pub enum Area {
Rect(Rect),
Pos(Pos),
Pos(Position),
}
impl Default for Area {
@ -26,6 +27,15 @@ impl Debug for Area {
}
}
impl From<Area> for Position {
fn from(value: Area) -> Self {
match value {
Area::Rect(rect) => Self::from(*rect),
Area::Pos(pos) => pos,
}
}
}
impl Area {
pub fn size(self) -> ratatui_core::layout::Size {
match self {
@ -34,13 +44,10 @@ impl Area {
}
}
pub fn inner(self, padding: ratatui_widgets::block::Padding) -> Self {
pub fn padding(self, padding: ratatui_widgets::block::Padding) -> Self {
match self {
Self::Rect(rect) => Self::Rect(rect.pad(padding.into())),
Self::Pos(mut pos) => {
pos.pad += padding;
Self::Pos(pos)
}
Self::Rect(rect) => Self::Rect(rect.padding(padding).into()),
Self::Pos(pos) => Self::Pos(pos.padding(padding)),
}
}
@ -50,7 +57,7 @@ impl Area {
) -> ratatui_core::layout::Rect {
match self {
Self::Rect(rect) => *rect,
Self::Pos(pos) => *Rect(f(*pos)).pad(pos.pad),
Self::Pos(pos) => f(pos),
}
}
}
@ -73,7 +80,7 @@ impl TryFrom<&AnyUserData> for Area {
fn try_from(value: &AnyUserData) -> Result<Self, Self::Error> {
Ok(if let Ok(rect) = value.borrow::<Rect>() {
Self::Rect(*rect)
} else if let Ok(pos) = value.borrow::<Pos>() {
} else if let Ok(pos) = value.borrow::<Position>() {
Self::Pos(*pos)
} else {
return Err(EXPECTED.into_lua_err());

View file

@ -1 +1 @@
yazi_macro::mod_flat!(align area bar border cell color constraint edge fill gauge layout line list pad pos rect row span spatial table text wrap);
yazi_macro::mod_flat!(align area bar border cell color constraint edge fill gauge layout line list pad rect row span spatial table text wrap);

View file

@ -12,7 +12,11 @@ impl Deref for Pad {
}
impl From<ratatui_widgets::block::Padding> for Pad {
fn from(pad: ratatui_widgets::block::Padding) -> Self { Self(pad) }
fn from(value: ratatui_widgets::block::Padding) -> Self { Self(value) }
}
impl From<Pad> for ratatui_widgets::block::Padding {
fn from(value: Pad) -> Self { value.0 }
}
impl Pad {

View file

@ -1,6 +1,7 @@
use std::ops::Deref;
use mlua::{FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataFields, UserDataMethods, Value};
use yazi_shim::ratatui::Padable;
use super::Pad;
@ -44,16 +45,6 @@ impl Rect {
rect.into_lua(lua)
}
pub(super) fn pad(self, pad: Pad) -> Self {
let mut r = *self;
r.x = r.x.saturating_add(pad.left);
r.y = r.y.saturating_add(pad.top);
r.width = r.width.saturating_sub(pad.left + pad.right);
r.height = r.height.saturating_sub(pad.top + pad.bottom);
Self(r)
}
fn patch(self, t: Table) -> mlua::Result<Self> {
Ok(Self(ratatui_core::layout::Rect {
x: t.raw_get::<Option<_>>("x")?.unwrap_or(self.x),
@ -83,7 +74,7 @@ impl UserData for Rect {
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("pad", |_, me, pad: Pad| Ok(me.pad(pad)));
methods.add_method("pad", |_, me, pad: Pad| Ok(Self(me.padding(pad))));
methods.add_method("contains", |_, me, Self(rect)| Ok(me.contains(rect.into())));
methods.add_meta_method(MetaMethod::Call, |_, me, t: Table| me.patch(t));

View file

@ -1,35 +1,13 @@
use std::{ops::Deref, str::FromStr};
use std::str::FromStr;
use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataFields, UserDataMethods, Value};
use yazi_shim::strum::IntoStr;
use super::Pad;
use crate::position::{Offset, Origin, Position};
use crate::{elements::Pad, position::{Offset, Origin, Position}};
const EXPECTED: &str = "expected a Pos";
#[derive(Clone, Copy, Default)]
pub struct Pos {
inner: Position,
pub(super) pad: Pad,
}
impl Deref for Pos {
type Target = Position;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<Position> for Pos {
fn from(value: Position) -> Self { Self { inner: value, ..Default::default() } }
}
impl From<Pos> for Position {
fn from(value: Pos) -> Self { value.inner }
}
impl TryFrom<&AnyUserData> for Pos {
impl TryFrom<&AnyUserData> for Position {
type Error = mlua::Error;
fn try_from(ud: &AnyUserData) -> Result<Self, Self::Error> {
@ -37,7 +15,7 @@ impl TryFrom<&AnyUserData> for Pos {
}
}
impl TryFrom<Table> for Pos {
impl TryFrom<Table> for Position {
type Error = mlua::Error;
fn try_from(t: Table) -> Result<Self, Self::Error> {
@ -49,11 +27,12 @@ impl TryFrom<Table> for Pos {
width: t.raw_get("w").unwrap_or_default(),
height: t.raw_get("h").unwrap_or_default(),
},
padding: Default::default(),
}))
}
}
impl FromLua for Pos {
impl FromLua for Position {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(tbl) => Self::try_from(tbl),
@ -63,23 +42,18 @@ impl FromLua for Pos {
}
}
impl Pos {
impl Position {
pub fn compose(lua: &Lua) -> mlua::Result<Value> {
let new = lua.create_function(|_, (_, t): (Table, Table)| Self::try_from(t))?;
let position = lua.create_table()?;
position.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?;
let pos = lua.create_table()?;
pos.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?;
position.into_lua(lua)
}
pub fn with_height(mut self, height: u16) -> Self {
self.inner.height = height;
self
pos.into_lua(lua)
}
}
impl UserData for Pos {
impl UserData for Position {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
// TODO: cache
fields.add_field_method_get("1", |_, me| Ok(me.origin.into_str()));
@ -91,7 +65,7 @@ impl UserData for Pos {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("pad", |_, (ud, pad): (AnyUserData, Pad)| {
ud.borrow_mut::<Self>()?.pad = pad;
ud.borrow_mut::<Self>()?.padding = pad.into();
Ok(ud)
});
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(offset origin position);
yazi_macro::mod_flat!(lua offset origin position);

View file

@ -10,6 +10,17 @@ pub struct Offset {
pub height: u16,
}
impl From<ratatui_core::layout::Rect> for Offset {
fn from(value: ratatui_core::layout::Rect) -> Self {
Self {
x: value.x as i16,
y: value.y as i16,
width: value.width,
height: value.height,
}
}
}
impl TryFrom<[i16; 4]> for Offset {
type Error = anyhow::Error;
@ -32,8 +43,3 @@ impl TryFrom<[i16; 4]> for Offset {
})
}
}
impl Offset {
#[inline]
pub fn line() -> Self { Self { x: 0, y: 0, width: u16::MAX, height: 1 } }
}

View file

@ -2,6 +2,7 @@ use std::ops::{Deref, DerefMut};
use ratatui_core::layout::Rect;
use ratatui_widgets::block::Padding;
use yazi_shim::ratatui::Padable;
use super::{Offset, Origin};
@ -9,6 +10,7 @@ use super::{Offset, Origin};
pub struct Position {
pub origin: Origin,
pub offset: Offset,
pub padding: Padding,
}
impl Deref for Position {
@ -21,8 +23,23 @@ impl DerefMut for Position {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.offset }
}
impl From<Rect> for Position {
fn from(value: Rect) -> Self { Self::new(Origin::TopLeft, value.into()) }
}
impl Position {
pub const fn new(origin: Origin, offset: Offset) -> Self { Self { origin, offset } }
pub const fn new(origin: Origin, offset: Offset) -> Self {
Self { origin, offset, padding: Padding::ZERO }
}
pub const fn hovered(offset: Offset) -> Self {
Self { origin: Origin::Hovered, offset, padding: Padding::ZERO }
}
pub fn with_height(mut self, height: u16) -> Self {
self.height = height;
self
}
pub fn rect(&self, (cols, rows): (u16, u16)) -> Rect {
use Origin::*;
@ -52,10 +69,11 @@ impl Position {
width: width.min(cols.saturating_sub(new_x)),
height: height.min(rows.saturating_sub(new_y)),
}
.padding(self.padding)
}
pub fn sticky((cols, rows): (u16, u16), base: Rect, offset: Offset) -> Rect {
let Offset { x, y, width, height } = offset;
pub fn sticky(self, base: Rect, (cols, rows): (u16, u16)) -> Rect {
let Offset { x, y, width, height } = self.offset;
let above =
base.y.saturating_add(base.height).saturating_add(height).saturating_add_signed(y) > rows;
@ -73,31 +91,15 @@ impl Position {
width: width.min(cols.saturating_sub(new_x)),
height: height.min(rows.saturating_sub(new_y)),
}
.padding(self.padding)
}
pub fn padding(mut self, padding: Padding) -> Self {
use Origin::*;
let h_reduction = padding.left + padding.right;
let v_reduction = padding.top + padding.bottom;
self.x = self.x.saturating_add_unsigned(padding.left);
self.y = self.y.saturating_add_unsigned(padding.top);
self.width = self.width.saturating_sub(h_reduction);
self.height = self.height.saturating_sub(v_reduction);
self.x = self.x.saturating_sub_unsigned(match self.origin {
TopCenter | BottomCenter | Center => h_reduction / 2,
TopRight | BottomRight => h_reduction,
_ => 0,
});
self.y = self.y.saturating_sub_unsigned(match self.origin {
BottomLeft | BottomCenter | BottomRight => v_reduction,
Center => v_reduction / 2,
_ => 0,
});
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
let p = padding.into();
self.padding.left = self.padding.left.saturating_add(p.left);
self.padding.right = self.padding.right.saturating_add(p.right);
self.padding.top = self.padding.top.saturating_add(p.top);
self.padding.bottom = self.padding.bottom.saturating_add(p.bottom);
self
}
}

View file

@ -34,8 +34,8 @@ impl Core {
pub fn cursor(&self) -> Option<(Position, SetCursorStyle)> {
if let Some(guard) = self.input.lock() {
let Rect { x, y, .. } = match &guard {
InputGuard::Main(_) => self.mgr.area(self.input.main_position).inner(Margin::new(1, 1)),
InputGuard::Alt(alt) => self.mgr.area(alt.pos),
InputGuard::Main(_) => self.mgr.area(self.input.position()?).inner(Margin::new(1, 1)),
InputGuard::Alt(_) => self.mgr.area(self.input.position()?),
};
return Some((Position { x: x + guard.cursor(), y }, guard.cursor_shape()));
}

View file

@ -1,40 +1,83 @@
use std::sync::Arc;
use std::{ops::{Deref, DerefMut}, sync::Arc};
use parking_lot::Mutex;
use yazi_binding::position::Position;
use yazi_binding::{elements::Spatial, position::Position};
use crate::input::{InputGuard, InputMutGuard};
#[derive(Default)]
pub struct Input {
pub main: yazi_widgets::input::Input,
pub alt: Option<Arc<Mutex<yazi_widgets::input::Input>>>,
pub main_visible: bool,
pub main_title: String,
pub main_position: Position,
pub main: InputMain,
pub alt: Option<InputAlt>,
}
impl Input {
pub fn focus(&self) -> bool { self.main_visible || self.alt.is_some() }
pub fn focus(&self) -> bool { self.main.visible || self.alt.is_some() }
pub fn position(&self) -> Option<Position> {
if self.main.visible {
Some(self.main.position)
} else if let Some(alt) = &self.alt {
Some(alt.position)
} else {
None
}
}
pub fn lock(&self) -> Option<InputGuard<'_>> {
if self.main_visible {
Some(InputGuard::Main(&self.main))
if self.main.visible {
Some(InputGuard::Main(&self.main.inner))
} else if let Some(alt) = &self.alt {
Some(InputGuard::Alt(alt.lock()))
Some(InputGuard::Alt(alt.inner.lock()))
} else {
None
}
}
pub fn lock_mut(&mut self) -> Option<InputMutGuard<'_>> {
if self.main_visible {
Some(InputMutGuard::Main(&mut self.main))
if self.main.visible {
Some(InputMutGuard::Main(&mut self.main.inner))
} else if let Some(alt) = &self.alt {
Some(InputMutGuard::Alt(alt.lock()))
Some(InputMutGuard::Alt(alt.inner.lock()))
} else {
None
}
}
}
// --- InputMain
#[derive(Default)]
pub struct InputMain {
inner: yazi_widgets::input::Input,
pub visible: bool,
pub title: String,
pub position: Position,
}
impl Deref for InputMain {
type Target = yazi_widgets::input::Input;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl DerefMut for InputMain {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
// --- InputAlt
pub struct InputAlt {
inner: Arc<Mutex<yazi_widgets::input::Input>>,
position: Position,
}
impl Deref for InputAlt {
type Target = Arc<Mutex<yazi_widgets::input::Input>>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<&yazi_widgets::input::InputArc> for InputAlt {
fn from(value: &yazi_widgets::input::InputArc) -> Self {
Self { inner: value.deref().clone(), position: value.area().into() }
}
}

View file

@ -93,7 +93,7 @@ impl Tab {
pub fn hovered_rect_based(&self, pos: Position) -> Rect {
let area = TERM.dimension().area();
if let Some(r) = self.hovered_rect() {
Position::sticky(area, r, pos.offset)
pos.sticky(r, area)
} else {
Position::new(Origin::TopCenter, pos.offset).rect(area)
}

View file

@ -39,13 +39,14 @@ impl Widget for Cmp<'_> {
})
.collect();
let input_area = self.core.mgr.area(self.core.input.main_position);
let mut area = Position::sticky(TERM.dimension().area(), input_area, Offset {
let input_area = self.core.mgr.area(self.core.input.main.position);
let mut area = Position::hovered(Offset {
x: 1,
y: 0,
width: input_area.width.saturating_sub(2),
height: items.len() as u16 + 2,
});
})
.sticky(input_area, TERM.dimension().area());
if area.y > input_area.y {
area.y = area.y.saturating_sub(1);

View file

@ -15,13 +15,13 @@ impl Widget for Input<'_> {
fn render(self, _: Rect, buf: &mut Buffer) {
let input = &self.core.input;
let outer = self.core.mgr.area(input.main_position);
let outer = self.core.mgr.area(input.main.position);
yazi_widgets::clear::Clear::default().render(outer, buf);
Block::bordered()
.border_type(BorderType::Rounded)
.border_style(THEME.input.border.get())
.title(Line::styled(&input.main_title, THEME.input.title.get()))
.title(Line::styled(&input.main.title, THEME.input.title.get()))
.render(outer, buf);
input.main.render(outer.inner(Margin::new(1, 1)), buf);

View file

@ -42,7 +42,7 @@ impl Widget for Root<'_> {
pick::Pick::new(self.core).render(area, buf);
}
if self.core.input.main_visible {
if self.core.input.main.visible {
input::Input::new(self.core).render(area, buf);
}

View file

@ -1,5 +1,5 @@
use mlua::{IntoLua, Lua, Value};
use yazi_binding::{Composer, ComposerGet, ComposerSet, elements::{Align, Bar, Border, Color, Constraint, Edge, Fill, Gauge, Layout, Line, List, Pad, Pos, Rect, Row, Span, Table, Text, Wrap}, style::Style};
use yazi_binding::{Composer, ComposerGet, ComposerSet, elements::{Align, Bar, Border, Color, Constraint, Edge, Fill, Gauge, Layout, Line, List, Pad, Rect, Row, Span, Table, Text, Wrap}, position::Position, style::Style};
use yazi_config::THEME;
use yazi_widgets::{clear::Clear, input::InputArc};
@ -21,7 +21,7 @@ pub fn compose() -> Composer<ComposerGet, ComposerSet> {
b"Line" => Line::compose(lua)?,
b"List" => List::compose(lua)?,
b"Pad" => Pad::compose(lua)?,
b"Pos" => Pos::compose(lua)?,
b"Pos" => Position::compose(lua)?,
b"Rect" => Rect::compose(lua)?,
b"Row" => Row::compose(lua)?,
b"Span" => Span::compose(lua)?,

View file

@ -2,7 +2,7 @@ use std::{str::FromStr, time::Duration};
use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, Table, Value};
use tokio_stream::wrappers::UnboundedReceiverStream;
use yazi_binding::{elements::{Line, Pos, Text}, runtime};
use yazi_binding::{elements::{Line, Text}, runtime};
use yazi_config::{Platform, keymap::{Chord, ChordArc}, popup::ConfirmCfg};
use yazi_core::notify::MessageOpt;
use yazi_macro::relay;
@ -79,7 +79,7 @@ impl Utils {
}
let result = ConfirmProxy::show(ConfirmCfg {
position: t.raw_get::<Pos>("pos")?.into(),
position: t.raw_get("pos")?,
title: t.raw_get::<Line>("title")?.into(),
body: t.raw_get::<Option<Text>>("body")?.unwrap_or_default().into(),
list: Default::default(), // TODO

View file

@ -14,7 +14,7 @@ impl Utils {
let mut table = yazi_binding::elements::Table::try_from(&table)?;
let area = table.area();
table.set_area(area.inner(ratatui_widgets::block::Padding::uniform(1)));
table.set_area(area.padding(ratatui_widgets::block::Padding::uniform(1)));
lock.data = vec![
Renderable::Clear(Default::default()).with_area(area),

View file

@ -1 +1 @@
yazi_macro::mod_flat!(line span wrapper);
yazi_macro::mod_flat!(line padding span wrapper);

View file

@ -0,0 +1,20 @@
use ratatui_widgets::block::Padding;
pub trait Padable {
fn padding(self, pad: impl Into<Padding>) -> Self;
}
impl Padable for ratatui_core::layout::Rect {
fn padding(self, padding: impl Into<Padding>) -> Self {
let p = padding.into();
let horizontal = p.left.saturating_add(p.right);
let vertical = p.top.saturating_add(p.bottom);
Self {
x: self.x.saturating_add(p.left),
y: self.y.saturating_add(p.top),
width: self.width.saturating_sub(horizontal),
height: self.height.saturating_sub(vertical),
}
}
}

View file

@ -11,6 +11,11 @@ impl Tty {
let mut w = self.writer();
let result = match kind {
b"Print" => {
write!(w, "{}", t.raw_get::<ByteString>(1)?)
}
// DnD
b"AgreeDrag" => {
let it = t.raw_get::<Table>("mimes")?.sequence_iter::<ByteString>(lua).flatten();
write!(w, "{}", match &*t.raw_get::<BorrowedBytes>("type")? {
@ -48,6 +53,7 @@ impl Tty {
b"move" => write!(w, "{}", FinishDrop::Move),
_ => return Err("invalid FinishDrop type".into_lua_err()),
},
_ => return Err("invalid sequence kind".into_lua_err()),
};

View file

@ -22,7 +22,7 @@ impl Input {
snap.value.replace_range(start..end, &casefied);
snap.op = InputOp::None;
snap.cursor = range.start;
self.snaps.tag(self.pos.width as usize).then(|| self.flush_type());
self.snaps.tag(self.size.width as usize).then(|| self.flush_type());
act!(r#move, self)?;
succ!(render!());

View file

@ -19,7 +19,7 @@ impl Input {
snap.mode = InputMode::Normal;
}
}
self.snaps.tag(self.pos.width as usize);
self.snaps.tag(self.size.width as usize);
succ!();
}
}

View file

@ -15,7 +15,7 @@ impl Input {
render!(self.handle_op(opt.step.add(&snap.value, snap.cursor), false));
let n_cur = self.snap().cursor;
let (limit, snap) = (self.pos.width as usize, self.snap_mut());
let (limit, snap) = (self.size.width as usize, self.snap_mut());
if snap.value.is_empty() {
succ!(snap.offset = 0);
}

View file

@ -29,7 +29,7 @@ impl Input {
(Some(_), Some((len, _))) => snap.value.replace_range(start..start + len, &s),
}
self.snaps.tag(self.pos.width as usize).then(|| self.flush_type());
self.snaps.tag(self.size.width as usize).then(|| self.flush_type());
succ!(render!());
}
}

View file

@ -1,7 +1,7 @@
use std::{borrow::Cow, ops::Range};
use anyhow::Result;
use yazi_binding::position::Position;
use ratatui_core::layout::{Rect, Size};
use yazi_macro::act;
use yazi_shared::id::Ids;
use yazi_shim::path::CROSS_SEPARATOR;
@ -12,7 +12,7 @@ use crate::{CLIPBOARD, input::{InputCallback, InputEvent, InputOpt, InputStyles}
#[derive(Debug, Default)]
pub struct Input {
pub pos: Position,
pub size: Size,
pub snaps: InputSnaps,
pub styles: InputStyles,
pub obscure: bool,
@ -26,10 +26,8 @@ pub struct Input {
impl Input {
pub fn new(opt: InputOpt) -> Result<Self> {
let limit = opt.position.width as usize;
let mut input = Self {
pos: opt.position,
snaps: InputSnaps::new(opt.value, opt.obscure, limit),
snaps: InputSnaps::new(opt.value, opt.obscure),
styles: opt.styles,
obscure: opt.obscure,
blinking: opt.blinking,
@ -48,9 +46,13 @@ impl Input {
Ok(input)
}
pub fn repos(&mut self, pos: Position) {
self.pos = pos;
self.snap_mut().resize(pos.width as usize);
pub fn repos(&mut self, area: Rect) {
let size = area.into();
if self.size != size {
self.size = size;
self.snap_mut().resize(size.width as usize);
}
}
pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
@ -89,7 +91,7 @@ impl Input {
return false;
}
if !matches!(old.op, InputOp::None | InputOp::Select(_)) {
self.snaps.tag(self.pos.width as usize).then(|| self.flush_type());
self.snaps.tag(self.size.width as usize).then(|| self.flush_type());
}
true
}
@ -118,9 +120,9 @@ impl Input {
pub fn display(&self) -> Cow<'_, str> {
if self.obscure {
"".repeat(self.snap().window(self.pos.width as usize).len()).into()
"".repeat(self.snap().window(self.size.width as usize).len()).into()
} else {
self.snap().slice(self.snap().window(self.pos.width as usize)).into()
self.snap().slice(self.snap().window(self.size.width as usize)).into()
}
}
@ -149,7 +151,7 @@ impl Input {
let (start, end) =
if start < snap.cursor { (start, snap.cursor) } else { (snap.cursor + 1, start + 1) };
let win = snap.window(self.pos.width as usize);
let win = snap.window(self.size.width as usize);
let Range { start, end } = start.max(win.start)..end.min(win.end);
let s = snap.width(snap.offset..start);

View file

@ -1,17 +1,24 @@
use std::sync::Arc;
use std::{ops::Deref, sync::Arc};
use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
use parking_lot::Mutex;
use ratatui_core::widgets::Widget;
use yazi_binding::{elements::{Area, Spatial}, impl_area_method, position::{Offset, Origin, Position}};
use yazi_binding::{elements::{Area, Spatial}, impl_area_method};
use crate::input::{Input, InputOpt, InputStyles};
#[derive(Clone, Debug, Default)]
pub struct InputArc {
pub focus: bool,
area: Area,
inner: Arc<Mutex<Input>>,
pub focus: bool,
}
impl Deref for InputArc {
type Target = Arc<Mutex<Input>>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl InputArc {
@ -47,22 +54,9 @@ impl TryFrom<&AnyUserData> for InputArc {
}
impl Spatial for InputArc {
fn area(&self) -> Area { self.inner.lock().pos.into() }
fn area(&self) -> Area { self.area }
fn set_area(&mut self, area: Area) {
self.inner.lock().repos(match area {
Area::Rect(rect) => Position {
origin: Origin::TopLeft,
offset: Offset {
x: rect.x as i16,
y: rect.y as i16,
width: rect.width,
height: rect.height,
},
},
Area::Pos(pos) => pos.into(),
});
}
fn set_area(&mut self, area: Area) { self.area = area; }
}
impl Widget for &InputArc {
@ -70,7 +64,9 @@ impl Widget for &InputArc {
where
Self: Sized,
{
self.inner.lock().render(rect, buf)
let mut guard = self.inner.lock();
guard.repos(rect);
guard.render(rect, buf)
}
}

View file

@ -1,5 +1,5 @@
use mlua::{ExternalError, FromLua, Lua, Table, Value};
use yazi_binding::{elements::Pos, position::Position};
use yazi_binding::position::Position;
use yazi_macro::impl_data_any;
use crate::input::{InputCallback, InputStyles};
@ -48,7 +48,7 @@ impl TryFrom<&Table> for InputOpt {
cursor: None,
obscure: t.raw_get("obscure")?,
blinking: false,
position: t.raw_get::<Pos>("pos")?.with_height(3).into(),
position: t.raw_get::<Position>("pos").unwrap_or_default().with_height(3),
realtime: t.raw_get("realtime")?,
completion: false,
cb: None,

View file

@ -18,7 +18,7 @@ pub struct InputSnap {
}
impl InputSnap {
pub(super) fn new(value: String, obscure: bool, limit: usize) -> Self {
pub(super) fn new(value: String, obscure: bool) -> Self {
let mut snap = Self {
value,
@ -30,7 +30,7 @@ impl InputSnap {
offset: 0,
cursor: usize::MAX,
};
snap.resize(limit);
snap.resize(0);
snap
}

View file

@ -13,15 +13,15 @@ impl Default for InputSnaps {
fn default() -> Self {
Self {
idx: 0,
versions: vec![InputSnap::new(String::new(), false, 0)],
current: InputSnap::new(String::new(), false, 0),
versions: vec![InputSnap::new(String::new(), false)],
current: InputSnap::new(String::new(), false),
}
}
}
impl InputSnaps {
pub fn new(value: String, obscure: bool, limit: usize) -> Self {
let current = InputSnap::new(value, obscure, limit);
pub fn new(value: String, obscure: bool) -> Self {
let current = InputSnap::new(value, obscure);
Self { idx: 0, versions: vec![current.clone()], current }
}