feat: custom manager layout (#76)

This commit is contained in:
三咲雅 · Misaki Masa 2023-08-20 10:39:05 +08:00 committed by GitHub
parent 8c0ca4c649
commit 4f52d829bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 114 additions and 83 deletions

View File

@ -1,5 +1,4 @@
use core::manager::{ALL_RATIO, CURRENT_RATIO, PARENT_RATIO, PREVIEW_RATIO};
use config::MANAGER;
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}};
use super::{Folder, Preview};
@ -15,15 +14,16 @@ impl<'a> Layout<'a> {
impl<'a> Widget for Layout<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
let layout = &MANAGER.layout;
let manager = &self.cx.manager;
let chunks = layout::Layout::new()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Ratio(PARENT_RATIO, ALL_RATIO),
Constraint::Ratio(CURRENT_RATIO, ALL_RATIO),
Constraint::Ratio(PREVIEW_RATIO, ALL_RATIO),
Constraint::Ratio(layout.parent, layout.all),
Constraint::Ratio(layout.current, layout.all),
Constraint::Ratio(layout.preview, layout.all),
]
.as_ref(),
)

View File

@ -1,7 +1,7 @@
use core::manager::{PreviewData, PREVIEW_BORDER};
use core::manager::PreviewData;
use ansi_to_tui::IntoText;
use ratatui::{buffer::Buffer, layout::Rect, widgets::{Clear, Paragraph, Widget}};
use ratatui::{buffer::Buffer, layout::Rect, widgets::{Paragraph, Widget}};
use super::Folder;
use crate::Ctx;
@ -16,16 +16,6 @@ impl<'a> Preview<'a> {
impl<'a> Widget for Preview<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
Clear.render(
Rect {
x: area.x,
y: area.y,
width: area.width + PREVIEW_BORDER / 2,
height: area.height,
},
buf,
);
let manager = &self.cx.manager;
let Some(hovered) = manager.hovered().map(|h| &h.path) else {
return;

View File

@ -2,6 +2,10 @@
## manager
- layout: Manager layout by ratio, 3-element array
- `[1, 4, 3]`: 1/8 width for parent, 4/8 width for current, 3/8 width for preview
- sort_by: File sorting method
- `"alphabetical"`: Sort alphabetically

View File

@ -169,11 +169,11 @@ keymap = [
{ on = [ "x" ], exec = [ "delete --cut", "move 1 --in-operating" ] },
# Yank/Paste
{ on = [ "y" ], exec = [ "yank" ] },
{ on = [ "p" ], exec = [ "paste" ] },
{ on = [ "P" ], exec = [ "paste --before" ] },
{ on = [ "y" ], exec = "yank" },
{ on = [ "p" ], exec = "paste" },
{ on = [ "P" ], exec = "paste --before" },
# Undo/Redo
{ on = [ "u" ], exec = [ "undo" ] },
{ on = [ "<C-r>" ], exec = [ "redo" ] },
{ on = [ "u" ], exec = "undo" },
{ on = [ "<C-r>" ], exec = "redo" },
]

View File

@ -1,4 +1,5 @@
[manager]
layout = [ 1, 4, 3 ]
sort_by = "modified"
sort_reverse = true
sort_dir_first = true

View File

@ -0,0 +1,69 @@
use anyhow::bail;
use crossterm::terminal::WindowSize;
use ratatui::prelude::Rect;
use serde::Deserialize;
use shared::Term;
use super::{FOLDER_MARGIN, PREVIEW_BORDER, PREVIEW_MARGIN};
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
#[serde(try_from = "Vec<u32>")]
pub struct ManagerLayout {
pub parent: u32,
pub current: u32,
pub preview: u32,
pub all: u32,
}
impl TryFrom<Vec<u32>> for ManagerLayout {
type Error = anyhow::Error;
fn try_from(ratio: Vec<u32>) -> Result<Self, Self::Error> {
if ratio.len() != 3 {
bail!("invalid layout ratio: {:?}", ratio);
}
if ratio.iter().all(|&r| r == 0) {
bail!("at least one layout ratio must be non-zero: {:?}", ratio);
}
Ok(Self {
parent: ratio[0],
current: ratio[1],
preview: ratio[2],
all: ratio[0] + ratio[1] + ratio[2],
})
}
}
impl ManagerLayout {
pub fn preview_rect(&self) -> Rect {
let WindowSize { columns, rows, .. } = Term::size();
let x = (columns as u32 * (self.parent + self.current) / self.all) as u16;
let width = (columns as u32 * self.preview / self.all) as u16;
Rect {
x: x.saturating_add(PREVIEW_BORDER / 2),
y: PREVIEW_MARGIN / 2,
width: width.saturating_sub(PREVIEW_BORDER),
height: rows.saturating_sub(PREVIEW_MARGIN),
}
}
#[inline]
pub fn preview_height(&self) -> usize { self.preview_rect().height as usize }
pub fn folder_rect(&self) -> Rect {
let WindowSize { columns, rows, .. } = Term::size();
Rect {
x: (columns as u32 * self.parent / self.all) as u16,
y: FOLDER_MARGIN / 2,
width: (columns as u32 * self.current / self.all) as u16,
height: rows.saturating_sub(FOLDER_MARGIN),
}
}
#[inline]
pub fn folder_height(&self) -> usize { self.folder_rect().height as usize }
}

View File

@ -1,10 +1,12 @@
use serde::Deserialize;
use super::SortBy;
use super::{ManagerLayout, SortBy};
use crate::MERGED_YAZI;
#[derive(Debug, Deserialize)]
pub struct Manager {
pub layout: ManagerLayout,
// Sorting
pub sort_by: SortBy,
pub sort_reverse: bool,

View File

@ -1,5 +1,12 @@
mod layout;
mod manager;
mod sorting;
pub use layout::*;
pub use manager::*;
pub use sorting::*;
const FOLDER_MARGIN: u16 = 2;
const PREVIEW_BORDER: u16 = 2;
const PREVIEW_MARGIN: u16 = 2;

View File

@ -1,11 +1,9 @@
use std::path::{Path, PathBuf};
use crossterm::terminal::WindowSize;
use config::MANAGER;
use indexmap::map::Slice;
use ratatui::layout::Rect;
use shared::Term;
use super::{ALL_RATIO, CURRENT_RATIO, DIR_PADDING, PARENT_RATIO};
use crate::{emit, files::{File, Files, FilesOp}};
#[derive(Default)]
@ -27,9 +25,6 @@ impl Folder {
Self { cwd: cwd.to_path_buf(), in_search: true, ..Default::default() }
}
#[inline]
pub fn limit() -> usize { Term::size().rows.saturating_sub(DIR_PADDING) as usize }
pub fn update(&mut self, op: FilesOp) -> bool {
let b = match op {
FilesOp::Read(_, items) => self.files.update_read(items),
@ -55,7 +50,7 @@ impl Folder {
}
pub fn set_page(&mut self, force: bool) -> bool {
let limit = Self::limit();
let limit = MANAGER.layout.folder_height();
let new = if limit == 0 { 0 } else { self.cursor / limit };
if !force && self.page == new {
return false;
@ -77,7 +72,7 @@ impl Folder {
self.hovered = self.files.duplicate(self.cursor);
self.set_page(false);
let limit = Self::limit();
let limit = MANAGER.layout.folder_height();
if self.cursor >= (self.offset + limit).min(len).saturating_sub(5) {
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
}
@ -109,7 +104,7 @@ impl Folder {
#[inline]
pub fn window(&self) -> &Slice<PathBuf, File> {
let end = (self.offset + Self::limit()).min(self.files.len());
let end = (self.offset + MANAGER.layout.folder_height()).min(self.files.len());
self.files.get_range(self.offset..end).unwrap()
}
@ -172,7 +167,7 @@ impl Folder {
pub fn paginate(&self) -> &Slice<PathBuf, File> {
let len = self.files.len();
let limit = Self::limit();
let limit = MANAGER.layout.folder_height();
let start = (self.page * limit).min(len.saturating_sub(1));
let end = (start + limit).min(len);
@ -183,14 +178,11 @@ impl Folder {
pub fn has_selected(&self) -> bool { self.files.iter().any(|(_, f)| f.is_selected) }
pub fn rect_current(&self, path: &Path) -> Option<Rect> {
let pos = self.position(path)? - self.offset;
let WindowSize { columns, .. } = Term::size();
let y = self.position(path)? - self.offset;
Some(Rect {
x: (columns as u32 * PARENT_RATIO / ALL_RATIO) as u16,
y: pos as u16,
width: (columns as u32 * CURRENT_RATIO / ALL_RATIO) as u16,
height: 1,
})
let mut rect = MANAGER.layout.folder_rect();
rect.y = rect.y.saturating_sub(1) + y as u16;
rect.height = 1;
Some(rect)
}
}

View File

@ -13,13 +13,3 @@ pub use preview::*;
pub use tab::*;
pub use tabs::*;
pub use watcher::*;
pub const PARENT_RATIO: u32 = 1;
pub const CURRENT_RATIO: u32 = 4;
pub const PREVIEW_RATIO: u32 = 3;
pub const ALL_RATIO: u32 = PARENT_RATIO + CURRENT_RATIO + PREVIEW_RATIO;
pub const DIR_PADDING: u16 = 2;
pub const PREVIEW_BORDER: u16 = 2;
pub const PREVIEW_MARGIN: u16 = 2;

View File

@ -2,14 +2,11 @@ use std::{io::BufRead, mem, path::{Path, PathBuf}, sync::{atomic::{AtomicUsize,
use adaptor::Adaptor;
use anyhow::{anyhow, bail, Result};
use config::{BOOT, PREVIEW};
use crossterm::terminal::WindowSize;
use ratatui::prelude::Rect;
use shared::{MimeKind, Term};
use config::{BOOT, MANAGER, PREVIEW};
use shared::MimeKind;
use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped};
use tokio::{fs, task::JoinHandle};
use super::{ALL_RATIO, CURRENT_RATIO, PARENT_RATIO, PREVIEW_BORDER, PREVIEW_MARGIN, PREVIEW_RATIO};
use crate::{emit, external, files::{Files, FilesOp}, highlighter};
#[derive(Default)]
@ -31,20 +28,6 @@ pub enum PreviewData {
}
impl Preview {
fn rect() -> Rect {
let WindowSize { columns, rows, .. } = Term::size();
let x = (columns as u32 * (PARENT_RATIO + CURRENT_RATIO) / ALL_RATIO) as u16;
let width = (columns as u32 * PREVIEW_RATIO / ALL_RATIO) as u16;
Rect {
x: x.saturating_add(PREVIEW_BORDER / 2),
y: PREVIEW_MARGIN / 2,
width: width.saturating_sub(PREVIEW_BORDER),
height: rows.saturating_sub(PREVIEW_MARGIN),
}
}
pub fn go(&mut self, path: &Path, mime: &str, show_image: bool) {
let kind = MimeKind::new(mime);
if !show_image && matches!(kind, MimeKind::Image | MimeKind::Video) {
@ -78,7 +61,7 @@ impl Preview {
pub fn reset(&mut self) -> bool {
self.handle.take().map(|h| h.abort());
self.incr.fetch_add(1, Ordering::Relaxed);
Adaptor::image_hide(Self::rect()).ok();
Adaptor::image_hide(MANAGER.layout.preview_rect()).ok();
self.lock = None;
!matches!(
@ -90,7 +73,7 @@ impl Preview {
pub fn reset_image(&mut self) -> bool {
self.handle.take().map(|h| h.abort());
self.incr.fetch_add(1, Ordering::Relaxed);
Adaptor::image_hide(Self::rect()).ok();
Adaptor::image_hide(MANAGER.layout.preview_rect()).ok();
if matches!(self.data, PreviewData::Image) {
self.lock = None;
@ -109,7 +92,7 @@ impl Preview {
}
pub async fn image(path: &Path) -> Result<PreviewData> {
Adaptor::image_show(path, Self::rect()).await?;
Adaptor::image_show(path, MANAGER.layout.preview_rect()).await?;
Ok(PreviewData::Image)
}
@ -132,14 +115,7 @@ impl Preview {
}
pub async fn json(path: &Path) -> Result<String> {
Ok(
external::jq(path, Self::rect().height as usize)
.await?
.lines()
.take(Self::rect().height as usize)
.collect::<Vec<_>>()
.join("\n"),
)
external::jq(path, MANAGER.layout.preview_height()).await
}
pub async fn archive(path: &Path) -> Result<String> {
@ -147,7 +123,7 @@ impl Preview {
external::lsar(path)
.await?
.into_iter()
.take(Self::rect().height as usize)
.take(MANAGER.layout.preview_height())
.map(|f| f.name)
.collect::<Vec<_>>()
.join("\n"),
@ -165,7 +141,7 @@ impl Preview {
let mut line = String::new();
let mut buf = String::new();
let mut i = Self::rect().height as usize;
let mut i = MANAGER.layout.preview_height();
while i > 0 && h.reader.read_line(&mut line)? > 0 {
if tick != incr.load(Ordering::Relaxed) {
bail!("Preview cancelled");