mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
parent
ab7acfec5c
commit
564af8827e
19 changed files with 339 additions and 68 deletions
|
|
@ -16,6 +16,25 @@ cache_dir = ""
|
||||||
ueberzug_scale = 1
|
ueberzug_scale = 1
|
||||||
ueberzug_offset = [ 0, 0, 0, 0 ]
|
ueberzug_offset = [ 0, 0, 0, 0 ]
|
||||||
|
|
||||||
|
[input]
|
||||||
|
cd_position = "top"
|
||||||
|
cd_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
|
search_position = "top"
|
||||||
|
search_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
|
find_position = "top"
|
||||||
|
find_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
|
shell_position = "top"
|
||||||
|
shell_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
|
create_position = "top"
|
||||||
|
create_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
|
rename_position = "hovered"
|
||||||
|
rename_offset = [0, 1, 50, 3]
|
||||||
|
|
||||||
[opener]
|
[opener]
|
||||||
edit = [
|
edit = [
|
||||||
{ exec = '$EDITOR "$@"', block = true, for = "unix" },
|
{ exec = '$EDITOR "$@"', block = true, for = "unix" },
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ pub mod manager;
|
||||||
pub mod open;
|
pub mod open;
|
||||||
mod pattern;
|
mod pattern;
|
||||||
pub mod plugins;
|
pub mod plugins;
|
||||||
|
pub mod popup;
|
||||||
mod preset;
|
mod preset;
|
||||||
pub mod preview;
|
pub mod preview;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
|
|
@ -32,6 +33,7 @@ pub static PLUGINS: RoCell<plugins::Plugins> = RoCell::new();
|
||||||
pub static PREVIEW: RoCell<preview::Preview> = RoCell::new();
|
pub static PREVIEW: RoCell<preview::Preview> = RoCell::new();
|
||||||
pub static TASKS: RoCell<tasks::Tasks> = RoCell::new();
|
pub static TASKS: RoCell<tasks::Tasks> = RoCell::new();
|
||||||
pub static THEME: RoCell<theme::Theme> = RoCell::new();
|
pub static THEME: RoCell<theme::Theme> = RoCell::new();
|
||||||
|
pub static INPUTBOX: RoCell<popup::Input> = RoCell::new();
|
||||||
|
|
||||||
pub static BOOT: RoCell<boot::Boot> = RoCell::new();
|
pub static BOOT: RoCell<boot::Boot> = RoCell::new();
|
||||||
|
|
||||||
|
|
@ -48,6 +50,7 @@ pub fn init() {
|
||||||
PREVIEW.with(Default::default);
|
PREVIEW.with(Default::default);
|
||||||
TASKS.with(Default::default);
|
TASKS.with(Default::default);
|
||||||
THEME.with(Default::default);
|
THEME.with(Default::default);
|
||||||
|
INPUTBOX.with(Default::default);
|
||||||
|
|
||||||
BOOT.with(Default::default);
|
BOOT.with(Default::default);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
yazi-config/src/popup/input.rs
Normal file
38
yazi-config/src/popup/input.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::position::{Offset, Position};
|
||||||
|
use crate::MERGED_YAZI;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Input {
|
||||||
|
// cd
|
||||||
|
pub cd_position: Position,
|
||||||
|
pub cd_offset: Offset,
|
||||||
|
// search
|
||||||
|
pub search_position: Position,
|
||||||
|
pub search_offset: Offset,
|
||||||
|
// find
|
||||||
|
pub find_position: Position,
|
||||||
|
pub find_offset: Offset,
|
||||||
|
// shell
|
||||||
|
pub shell_position: Position,
|
||||||
|
pub shell_offset: Offset,
|
||||||
|
// create
|
||||||
|
pub create_position: Position,
|
||||||
|
pub create_offset: Offset,
|
||||||
|
// rename
|
||||||
|
pub rename_position: Position,
|
||||||
|
pub rename_offset: Offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Input {
|
||||||
|
fn default() -> Self {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Outer {
|
||||||
|
input: Input,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().input
|
||||||
|
}
|
||||||
|
}
|
||||||
7
yazi-config/src/popup/mod.rs
Normal file
7
yazi-config/src/popup/mod.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
mod input;
|
||||||
|
mod position;
|
||||||
|
mod select;
|
||||||
|
|
||||||
|
pub use input::*;
|
||||||
|
pub use position::*;
|
||||||
|
pub use select::*;
|
||||||
51
yazi-config/src/popup/position.rs
Normal file
51
yazi-config/src/popup/position.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use anyhow::bail;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub enum Position {
|
||||||
|
#[serde(rename = "top-left")]
|
||||||
|
TopLeft,
|
||||||
|
#[serde(rename = "top-right")]
|
||||||
|
TopRight,
|
||||||
|
#[serde(rename = "top")]
|
||||||
|
Top,
|
||||||
|
#[serde(rename = "center")]
|
||||||
|
Center,
|
||||||
|
#[serde(rename = "bottom")]
|
||||||
|
Bottom,
|
||||||
|
#[serde(rename = "bottom-left")]
|
||||||
|
BottomLeft,
|
||||||
|
#[serde(rename = "bottom-right")]
|
||||||
|
BottomRight,
|
||||||
|
#[serde(rename = "hovered")]
|
||||||
|
Hovered,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
|
#[serde(try_from = "Vec<i16>")]
|
||||||
|
pub struct Offset {
|
||||||
|
pub x: i16,
|
||||||
|
pub y: i16,
|
||||||
|
pub width: u16,
|
||||||
|
pub height: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Vec<i16>> for Offset {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(values: Vec<i16>) -> Result<Self, Self::Error> {
|
||||||
|
if values.len() != 4 {
|
||||||
|
bail!("invalid offset: {:?}", values);
|
||||||
|
}
|
||||||
|
if values[2] < 0 || values[3] < 0 {
|
||||||
|
bail!("invalid offset: {:?}", values);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
x: values[0],
|
||||||
|
y: values[1],
|
||||||
|
width: values[2] as u16,
|
||||||
|
height: values[3] as u16,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
1
yazi-config/src/popup/select.rs
Normal file
1
yazi-config/src/popup/select.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
// TODO:
|
||||||
|
|
@ -2,7 +2,7 @@ use crossterm::terminal::WindowSize;
|
||||||
use ratatui::prelude::Rect;
|
use ratatui::prelude::Rect;
|
||||||
use yazi_shared::Term;
|
use yazi_shared::Term;
|
||||||
|
|
||||||
use crate::{completion::Completion, help::Help, input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position};
|
use crate::{completion::Completion, help::Help, input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position, RectShim};
|
||||||
|
|
||||||
pub struct Ctx {
|
pub struct Ctx {
|
||||||
pub manager: Manager,
|
pub manager: Manager,
|
||||||
|
|
@ -30,29 +30,114 @@ impl Ctx {
|
||||||
pub fn area(&self, pos: &Position) -> Rect {
|
pub fn area(&self, pos: &Position) -> Rect {
|
||||||
let WindowSize { columns, rows, .. } = Term::size();
|
let WindowSize { columns, rows, .. } = Term::size();
|
||||||
|
|
||||||
let (x, y) = match pos {
|
let (x, y) = match *pos {
|
||||||
Position::Top(Rect { mut x, mut y, width, height }) => {
|
Position::TopLeft(RectShim { x_offset, y_offset, width, height }) => {
|
||||||
x = x.min(columns.saturating_sub(*width));
|
let right_max = columns.saturating_sub(width);
|
||||||
y = y.min(rows.saturating_sub(*height));
|
let bottom_max = rows.saturating_sub(height);
|
||||||
((columns / 2).saturating_sub(width / 2) + x, y)
|
|
||||||
|
let base_x = 0_u16;
|
||||||
|
let base_y = 0_u16;
|
||||||
|
|
||||||
|
let x = base_x.saturating_add_signed(x_offset).max(0).min(right_max);
|
||||||
|
let y = base_y.saturating_add_signed(y_offset).max(0).min(bottom_max);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
}
|
}
|
||||||
Position::Sticky(Rect { mut x, y, width, height }, r) => {
|
Position::TopRight(RectShim { x_offset, y_offset, width, height }) => {
|
||||||
x = x.min(columns.saturating_sub(*width));
|
let right_max = columns.saturating_sub(width);
|
||||||
if y + height + r.y + r.height > rows {
|
let bottom_max = rows.saturating_sub(height);
|
||||||
(x + r.x, r.y.saturating_sub(height.saturating_sub(*y)))
|
|
||||||
} else {
|
let base_x = right_max;
|
||||||
(x + r.x, y + r.y + r.height)
|
let base_y = 0_u16;
|
||||||
}
|
|
||||||
|
let x = base_x.saturating_add_signed(x_offset).max(0).min(right_max);
|
||||||
|
let y = base_y.saturating_add_signed(y_offset).max(0).min(bottom_max);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
}
|
}
|
||||||
Position::Hovered(rect) => {
|
Position::Top(RectShim { x_offset, y_offset, width, height }) => {
|
||||||
|
let right_max = columns.saturating_sub(width);
|
||||||
|
let bottom_max = rows.saturating_sub(height);
|
||||||
|
|
||||||
|
let base_x = (columns / 2).saturating_sub(width / 2);
|
||||||
|
let base_y = 0_u16;
|
||||||
|
|
||||||
|
let x = base_x.saturating_add_signed(x_offset).max(0).min(right_max);
|
||||||
|
let y = base_y.saturating_add_signed(y_offset).max(0).min(bottom_max);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
Position::Center(RectShim { x_offset, y_offset, width, height }) => {
|
||||||
|
let right_max = columns.saturating_sub(width);
|
||||||
|
let bottom_max = rows.saturating_sub(height);
|
||||||
|
|
||||||
|
let base_x = (columns / 2).saturating_sub(width / 2);
|
||||||
|
let base_y = (bottom_max / 2).saturating_sub(height / 2);
|
||||||
|
|
||||||
|
let x = base_x.saturating_add_signed(x_offset).max(0).min(right_max);
|
||||||
|
let y = base_y.saturating_add_signed(y_offset).max(0).min(bottom_max);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
Position::Bottom(RectShim { x_offset, y_offset, width, height }) => {
|
||||||
|
let right_max = columns.saturating_sub(width);
|
||||||
|
let bottom_max = rows.saturating_sub(height);
|
||||||
|
|
||||||
|
let base_x = (columns / 2).saturating_sub(width / 2);
|
||||||
|
let base_y = bottom_max;
|
||||||
|
|
||||||
|
let x = base_x.saturating_add_signed(x_offset).max(0).min(right_max);
|
||||||
|
let y = base_y.saturating_add_signed(y_offset).max(0).min(bottom_max);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
Position::BottomLeft(RectShim { x_offset, y_offset, width, height }) => {
|
||||||
|
let right_max = columns.saturating_sub(width);
|
||||||
|
let bottom_max = rows.saturating_sub(height);
|
||||||
|
|
||||||
|
let base_x = 0_u16;
|
||||||
|
let base_y = bottom_max;
|
||||||
|
|
||||||
|
let x = base_x.saturating_add_signed(x_offset).max(0).min(right_max);
|
||||||
|
let y = base_y.saturating_add_signed(y_offset).max(0).min(bottom_max);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
Position::BottomRight(RectShim { x_offset, y_offset, width, height }) => {
|
||||||
|
let right_max = columns.saturating_sub(width);
|
||||||
|
let bottom_max = rows.saturating_sub(height);
|
||||||
|
|
||||||
|
let base_x = right_max;
|
||||||
|
let base_y = bottom_max;
|
||||||
|
|
||||||
|
let x = base_x.saturating_add_signed(x_offset).max(0).min(right_max);
|
||||||
|
let y = base_y.saturating_add_signed(y_offset).max(0).min(bottom_max);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
Position::Hovered(rect_shim) => {
|
||||||
return self.area(&if let Some(r) =
|
return self.area(&if let Some(r) =
|
||||||
self.manager.hovered().and_then(|h| self.manager.current().rect_current(&h.url))
|
self.manager.hovered().and_then(|h| self.manager.current().rect_current(&h.url))
|
||||||
{
|
{
|
||||||
Position::Sticky(*rect, r)
|
Position::Sticky(rect_shim, r)
|
||||||
} else {
|
} else {
|
||||||
Position::Top(*rect)
|
Position::Top(rect_shim)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Position::Sticky(RectShim { x_offset, y_offset, width, height }, r) => {
|
||||||
|
// TODO:
|
||||||
|
unimplemented!("Position::Sticky is not implemented");
|
||||||
|
// let mut x = columns.saturating_sub(width);
|
||||||
|
// x = x.saturating_add_signed(x_offset);
|
||||||
|
// let mut y = r.y;
|
||||||
|
// y = y.saturating_add_signed(y_offset);
|
||||||
|
|
||||||
|
// if y + height + r.height > rows {
|
||||||
|
// (x + r.x, r.y.saturating_sub(height.saturating_sub(y)))
|
||||||
|
// } else {
|
||||||
|
// (x + r.x, y + r.height)
|
||||||
|
// }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (w, h) = pos.dimension();
|
let (w, h) = pos.dimension();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ratatui::prelude::Rect;
|
use yazi_config::popup::{Offset as CfgOffset, Position as CfgPosition};
|
||||||
|
|
||||||
use crate::Position;
|
use crate::{Position, RectShim};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InputOpt {
|
pub struct InputOpt {
|
||||||
|
|
@ -12,23 +12,48 @@ pub struct InputOpt {
|
||||||
pub highlight: bool,
|
pub highlight: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputOpt {
|
macro_rules! gen_method {
|
||||||
pub fn top(title: impl AsRef<str>) -> Self {
|
($func_name:ident, $position:ident) => {
|
||||||
Self {
|
pub fn $func_name(title: impl AsRef<str>, rect: RectShim) -> InputOpt {
|
||||||
title: title.as_ref().to_owned(),
|
InputOpt {
|
||||||
position: Position::Top(/* TODO: hardcode */ Rect { x: 0, y: 2, width: 50, height: 3 }),
|
title: title.as_ref().to_owned(),
|
||||||
..Default::default()
|
position: Position::$position(rect),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hovered(title: impl AsRef<str>) -> Self {
|
impl InputOpt {
|
||||||
Self {
|
gen_method!(top_left, TopLeft);
|
||||||
title: title.as_ref().to_owned(),
|
|
||||||
position: Position::Hovered(
|
gen_method!(top_right, TopRight);
|
||||||
// TODO: hardcode
|
|
||||||
Rect { x: 0, y: 1, width: 50, height: 3 },
|
gen_method!(top, Top);
|
||||||
),
|
|
||||||
..Default::default()
|
gen_method!(center, Center);
|
||||||
|
|
||||||
|
gen_method!(bottom, Bottom);
|
||||||
|
|
||||||
|
gen_method!(bottom_left, BottomLeft);
|
||||||
|
|
||||||
|
gen_method!(bottom_right, BottomRight);
|
||||||
|
|
||||||
|
gen_method!(hovered, Hovered);
|
||||||
|
|
||||||
|
pub fn from_cfg(title: impl AsRef<str>, pos: &CfgPosition, rect: &CfgOffset) -> Self {
|
||||||
|
let rect =
|
||||||
|
RectShim { x_offset: rect.x, y_offset: rect.y, width: rect.width, height: rect.height };
|
||||||
|
|
||||||
|
match pos {
|
||||||
|
CfgPosition::TopLeft => Self::top_left(title, rect),
|
||||||
|
CfgPosition::TopRight => Self::top_right(title, rect),
|
||||||
|
CfgPosition::Top => Self::top(title, rect),
|
||||||
|
CfgPosition::Center => Self::center(title, rect),
|
||||||
|
CfgPosition::Bottom => Self::bottom(title, rect),
|
||||||
|
CfgPosition::BottomLeft => Self::bottom_left(title, rect),
|
||||||
|
CfgPosition::BottomRight => Self::bottom_right(title, rect),
|
||||||
|
CfgPosition::Hovered => Self::hovered(title, rect),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,17 @@ impl Manager {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let cwd = self.cwd().to_owned();
|
let cwd = self.cwd().to_owned();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut result = emit!(Input(InputOpt::top("Create:")));
|
let mut result = emit!(Input(InputOpt::top("Create:", Default::default())));
|
||||||
let Some(Ok(name)) = result.recv().await else {
|
let Some(Ok(name)) = result.recv().await else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = cwd.join(&name);
|
let path = cwd.join(&name);
|
||||||
if !opt.force && fs::symlink_metadata(&path).await.is_ok() {
|
if !opt.force && fs::symlink_metadata(&path).await.is_ok() {
|
||||||
match emit!(Input(InputOpt::top("Overwrite an existing file? (y/N)"))).recv().await {
|
match emit!(Input(InputOpt::top("Overwrite an existing file? (y/N)", Default::default())))
|
||||||
|
.recv()
|
||||||
|
.await
|
||||||
|
{
|
||||||
Some(Ok(c)) if c == "y" || c == "Y" => (),
|
Some(Ok(c)) if c == "y" || c == "Y" => (),
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,10 @@ impl Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut result =
|
let mut result = emit!(Input(InputOpt::top(
|
||||||
emit!(Input(InputOpt::top(format!("{tasks} tasks running, sure to quit? (y/N)"))));
|
format!("{tasks} tasks running, sure to quit? (y/N)",),
|
||||||
|
Default::default()
|
||||||
|
)));
|
||||||
|
|
||||||
if let Some(Ok(choice)) = result.recv().await {
|
if let Some(Ok(choice)) = result.recv().await {
|
||||||
if choice == "y" || choice == "Y" {
|
if choice == "y" || choice == "Y" {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{collections::BTreeSet, ffi::OsStr, io::{stdout, BufWriter, Write}, pat
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
||||||
use yazi_config::{keymap::Exec, OPEN, PREVIEW};
|
use yazi_config::{keymap::Exec, INPUTBOX, OPEN, PREVIEW};
|
||||||
use yazi_shared::{max_common_root, Defer, Term, Url};
|
use yazi_shared::{max_common_root, Defer, Term, Url};
|
||||||
|
|
||||||
use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::InputOpt, manager::Manager, Event, BLOCKER};
|
use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::InputOpt, manager::Manager, Event, BLOCKER};
|
||||||
|
|
@ -43,7 +43,8 @@ impl Manager {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut result = emit!(Input(
|
let mut result = emit!(Input(
|
||||||
InputOpt::hovered("Rename:").with_value(hovered.file_name().unwrap().to_string_lossy())
|
InputOpt::from_cfg("Rename:", &INPUTBOX.rename_position, &INPUTBOX.rename_offset)
|
||||||
|
.with_value(hovered.file_name().unwrap().to_string_lossy())
|
||||||
));
|
));
|
||||||
|
|
||||||
let Some(Ok(name)) = result.recv().await else {
|
let Some(Ok(name)) = result.recv().await else {
|
||||||
|
|
@ -56,7 +57,11 @@ impl Manager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = emit!(Input(InputOpt::hovered("Overwrite an existing file? (y/N)")));
|
let mut result = emit!(Input(InputOpt::from_cfg(
|
||||||
|
"Overwrite an existing file? (y/N)",
|
||||||
|
&INPUTBOX.rename_position,
|
||||||
|
&INPUTBOX.rename_offset
|
||||||
|
)));
|
||||||
if let Some(Ok(choice)) = result.recv().await {
|
if let Some(Ok(choice)) = result.recv().await {
|
||||||
if choice == "y" || choice == "Y" {
|
if choice == "y" || choice == "Y" {
|
||||||
Self::rename_and_hover(hovered, Url::from(new)).await.ok();
|
Self::rename_and_hover(hovered, Url::from(new)).await.ok();
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,47 @@
|
||||||
use ratatui::prelude::Rect;
|
use ratatui::prelude::Rect;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct RectShim {
|
||||||
|
pub x_offset: i16,
|
||||||
|
pub y_offset: i16,
|
||||||
|
pub width: u16,
|
||||||
|
pub height: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RectShim {
|
||||||
|
fn default() -> Self { Self { x_offset: 0, y_offset: 2, width: 50, height: 3 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum Position {
|
pub enum Position {
|
||||||
Top(Rect),
|
TopLeft(RectShim),
|
||||||
Sticky(Rect, Rect),
|
TopRight(RectShim),
|
||||||
Hovered(Rect),
|
Top(RectShim),
|
||||||
|
Center(RectShim),
|
||||||
|
Bottom(RectShim),
|
||||||
|
BottomLeft(RectShim),
|
||||||
|
BottomRight(RectShim),
|
||||||
|
Hovered(RectShim),
|
||||||
|
Sticky(RectShim, Rect),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Position {
|
impl Default for Position {
|
||||||
fn default() -> Self { Self::Top(Rect::default()) }
|
fn default() -> Self { Self::Top(RectShim::default()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rect(&self) -> Rect {
|
pub fn rect(&self) -> &RectShim {
|
||||||
match self {
|
match self {
|
||||||
Position::Top(rect) => *rect,
|
Position::TopLeft(rect) => rect,
|
||||||
Position::Sticky(rect, _) => *rect,
|
Position::TopRight(rect) => rect,
|
||||||
Position::Hovered(rect) => *rect,
|
Position::Top(rect) => rect,
|
||||||
|
Position::Center(rect) => rect,
|
||||||
|
Position::Bottom(rect) => rect,
|
||||||
|
Position::BottomLeft(rect) => rect,
|
||||||
|
Position::BottomRight(rect) => rect,
|
||||||
|
Position::Hovered(rect) => rect,
|
||||||
|
Position::Sticky(rect, _) => rect,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ratatui::prelude::Rect;
|
use ratatui::prelude::Rect;
|
||||||
|
|
||||||
use crate::Position;
|
use crate::{Position, RectShim};
|
||||||
|
|
||||||
pub struct SelectOpt {
|
pub struct SelectOpt {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|
@ -14,7 +14,10 @@ impl SelectOpt {
|
||||||
Self {
|
Self {
|
||||||
title: title.to_owned(),
|
title: title.to_owned(),
|
||||||
items,
|
items,
|
||||||
position: Position::Top(/* TODO: hardcode */ Rect { x: 0, y: 2, width: 50, height }),
|
position: Position::Top(
|
||||||
|
// TODO:
|
||||||
|
RectShim { x_offset: 0, y_offset: 2, width: 50, height },
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,8 +27,8 @@ impl SelectOpt {
|
||||||
title: title.to_owned(),
|
title: title.to_owned(),
|
||||||
items,
|
items,
|
||||||
position: Position::Hovered(
|
position: Position::Hovered(
|
||||||
// TODO: hardcode
|
// TODO:
|
||||||
Rect { x: 0, y: 1, width: 50, height },
|
RectShim { x_offset: 0, y_offset: 1, width: 50, height },
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{mem, time::Duration};
|
||||||
|
|
||||||
use tokio::{fs, pin};
|
use tokio::{fs, pin};
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::keymap::{Exec, KeymapLayer};
|
use yazi_config::{keymap::{Exec, KeymapLayer}, INPUTBOX};
|
||||||
use yazi_shared::{expand_path, Debounce, InputError, Url};
|
use yazi_shared::{expand_path, Debounce, InputError, Url};
|
||||||
|
|
||||||
use crate::{emit, input::InputOpt, tab::Tab};
|
use crate::{emit, input::InputOpt, tab::Tab};
|
||||||
|
|
@ -66,7 +66,7 @@ impl Tab {
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let rx = emit!(Input(
|
let rx = emit!(Input(
|
||||||
InputOpt::top("Change directory:")
|
InputOpt::from_cfg("Change directory:", &INPUTBOX.cd_position, &INPUTBOX.cd_offset)
|
||||||
.with_value(opt.target.to_string_lossy())
|
.with_value(opt.target.to_string_lossy())
|
||||||
.with_completion()
|
.with_completion()
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::time::Duration;
|
||||||
|
|
||||||
use tokio::pin;
|
use tokio::pin;
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::keymap::{Exec, KeymapLayer};
|
use yazi_config::{keymap::{Exec, KeymapLayer}, INPUTBOX};
|
||||||
use yazi_shared::{Debounce, InputError};
|
use yazi_shared::{Debounce, InputError};
|
||||||
|
|
||||||
use crate::{emit, input::InputOpt, tab::{Finder, FinderCase, Tab}};
|
use crate::{emit, input::InputOpt, tab::{Finder, FinderCase, Tab}};
|
||||||
|
|
@ -39,8 +39,9 @@ impl Tab {
|
||||||
pub fn find<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn find<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
let title = if opt.prev { "Find previous:" } else { "Find next:" };
|
||||||
let rx = emit!(Input(
|
let rx = emit!(Input(
|
||||||
InputOpt::top(if opt.prev { "Find previous:" } else { "Find next:" }).with_realtime()
|
InputOpt::from_cfg(title, &INPUTBOX.find_position, &INPUTBOX.find_offset).with_realtime()
|
||||||
));
|
));
|
||||||
|
|
||||||
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
|
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{mem, time::Duration};
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use tokio::pin;
|
use tokio::pin;
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::keymap::{Exec, KeymapLayer};
|
use yazi_config::{keymap::{Exec, KeymapLayer}, INPUTBOX};
|
||||||
|
|
||||||
use crate::{emit, external, files::FilesOp, input::InputOpt, tab::Tab};
|
use crate::{emit, external, files::FilesOp, input::InputOpt, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ impl Tab {
|
||||||
let hidden = self.conf.show_hidden;
|
let hidden = self.conf.show_hidden;
|
||||||
|
|
||||||
self.search = Some(tokio::spawn(async move {
|
self.search = Some(tokio::spawn(async move {
|
||||||
let Some(Ok(subject)) = emit!(Input(InputOpt::top("Search:"))).recv().await else {
|
let Some(Ok(subject)) = emit!(Input(InputOpt::from_cfg("Search:", &INPUTBOX.cd_position, &INPUTBOX.cd_offset))).recv().await else {
|
||||||
bail!("")
|
bail!("")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_config::{keymap::Exec, open::Opener};
|
use yazi_config::{keymap::Exec, open::Opener, INPUTBOX};
|
||||||
|
|
||||||
use crate::{emit, input::InputOpt, tab::Tab};
|
use crate::{emit, input::InputOpt, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -29,8 +29,9 @@ impl Tab {
|
||||||
let mut opt = opt.into() as Opt;
|
let mut opt = opt.into() as Opt;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if !opt.confirm || opt.cmd.is_empty() {
|
if !opt.confirm || opt.cmd.is_empty() {
|
||||||
|
let title = if opt.block { "Shell (block):" } else { "Shell:" };
|
||||||
let mut result = emit!(Input(
|
let mut result = emit!(Input(
|
||||||
InputOpt::top(if opt.block { "Shell (block):" } else { "Shell:" })
|
InputOpt::from_cfg(title, &INPUTBOX.shell_position, &INPUTBOX.shell_offset)
|
||||||
.with_value(opt.cmd)
|
.with_value(opt.cmd)
|
||||||
.with_highlight()
|
.with_highlight()
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -111,11 +111,12 @@ impl Tasks {
|
||||||
let scheduler = self.scheduler.clone();
|
let scheduler = self.scheduler.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let s = if targets.len() > 1 { "s" } else { "" };
|
let s = if targets.len() > 1 { "s" } else { "" };
|
||||||
let mut result = emit!(Input(InputOpt::hovered(if permanently {
|
let prompt = if permanently {
|
||||||
format!("Delete {} selected file{s} permanently? (y/N)", targets.len())
|
format!("Delete {} selected file{s} permanently? (y/N)", targets.len())
|
||||||
} else {
|
} else {
|
||||||
format!("Move {} selected file{s} to trash? (y/N)", targets.len())
|
format!("Move {} selected file{s} to trash? (y/N)", targets.len())
|
||||||
})));
|
};
|
||||||
|
let mut result = emit!(Input(InputOpt::hovered(prompt, Default::default())));
|
||||||
|
|
||||||
if let Some(Ok(choice)) = result.recv().await {
|
if let Some(Ok(choice)) = result.recv().await {
|
||||||
if choice != "y" && choice != "Y" {
|
if choice != "y" && choice != "Y" {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::{Block, BorderType, Borders, Clear, List, ListItem, Widget}};
|
use ratatui::{buffer::Buffer, layout::Rect, widgets::{Block, BorderType, Borders, Clear, List, ListItem, Widget}};
|
||||||
use yazi_config::THEME;
|
use yazi_config::THEME;
|
||||||
use yazi_core::{Ctx, Position};
|
use yazi_core::{Ctx, Position, RectShim};
|
||||||
|
|
||||||
pub(crate) struct Completion<'a> {
|
pub(crate) struct Completion<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
@ -40,11 +40,12 @@ impl<'a> Widget for Completion<'a> {
|
||||||
|
|
||||||
let input_area = self.cx.area(&self.cx.input.position);
|
let input_area = self.cx.area(&self.cx.input.position);
|
||||||
let mut area = self.cx.area(&Position::Sticky(
|
let mut area = self.cx.area(&Position::Sticky(
|
||||||
Rect {
|
// TODO:
|
||||||
x: 1,
|
RectShim {
|
||||||
y: 0,
|
x_offset: 1,
|
||||||
width: input_area.width.saturating_sub(2),
|
y_offset: 0,
|
||||||
height: items.len() as u16 + 2,
|
width: input_area.width.saturating_sub(2),
|
||||||
|
height: items.len() as u16 + 2,
|
||||||
},
|
},
|
||||||
input_area,
|
input_area,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue