mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-19 23:01:36 +03:00
fix: crashes caused by pop-up components (#52)
This commit is contained in:
parent
4480db86b7
commit
938c4ec865
@ -155,13 +155,11 @@ impl App {
|
||||
emit!(Render);
|
||||
}
|
||||
|
||||
Event::Select(mut opt, tx) => {
|
||||
opt.position = self.cx.position(opt.position);
|
||||
Event::Select(opt, tx) => {
|
||||
self.cx.select.show(opt, tx);
|
||||
emit!(Render);
|
||||
}
|
||||
Event::Input(mut opt, tx) => {
|
||||
opt.position = self.cx.position(opt.position);
|
||||
Event::Input(opt, tx) => {
|
||||
self.cx.input.show(opt, tx);
|
||||
emit!(Render);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
use core::{input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position};
|
||||
|
||||
use config::keymap::KeymapLayer;
|
||||
use libc::winsize;
|
||||
use ratatui::prelude::Rect;
|
||||
use shared::tty_size;
|
||||
|
||||
pub struct Ctx {
|
||||
@ -22,10 +24,41 @@ impl Ctx {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn area(&self, pos: &Position) -> Rect {
|
||||
let winsize { ws_row, ws_col, .. } = tty_size();
|
||||
|
||||
let (x, y) = match pos {
|
||||
Position::None => return Rect::default(),
|
||||
Position::Top(Rect { mut x, mut y, width, height }) => {
|
||||
x = x.min(ws_col.saturating_sub(*width));
|
||||
y = y.min(ws_row.saturating_sub(*height));
|
||||
((tty_size().ws_col / 2).saturating_sub(width / 2) + x, y)
|
||||
}
|
||||
Position::Hovered(rect @ Rect { mut x, y, width, height }) => {
|
||||
let Some(r) =
|
||||
self.manager.hovered().and_then(|h| self.manager.current().rect_current(&h.path))
|
||||
else {
|
||||
return self.area(&Position::Top(*rect));
|
||||
};
|
||||
|
||||
x = x.min(ws_col.saturating_sub(*width));
|
||||
if y + height + r.y + r.height > ws_row {
|
||||
(x + r.x, r.y.saturating_sub(height.saturating_sub(1)))
|
||||
} else {
|
||||
(x + r.x, y + r.y + r.height)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (w, h) = pos.dimension().unwrap();
|
||||
Rect { x, y, width: w.min(ws_col.saturating_sub(x)), height: h.min(ws_row.saturating_sub(y)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn cursor(&self) -> Option<(u16, u16)> {
|
||||
if self.input.visible {
|
||||
return Some(self.input.cursor());
|
||||
let Rect { x, y, .. } = self.area(&self.input.position);
|
||||
return Some((x + 1 + self.input.cursor(), y + 1));
|
||||
}
|
||||
None
|
||||
}
|
||||
@ -49,17 +82,4 @@ impl Ctx {
|
||||
pub(super) fn image_layer(&self) -> bool {
|
||||
!matches!(self.layer(), KeymapLayer::Which | KeymapLayer::Tasks)
|
||||
}
|
||||
|
||||
pub(super) fn position(&self, pos: Position) -> Position {
|
||||
match pos {
|
||||
Position::Top => Position::Coords((tty_size().ws_col / 2).saturating_sub(25), 2),
|
||||
Position::Hovered => self
|
||||
.manager
|
||||
.hovered()
|
||||
.and_then(|h| self.manager.current().rect_current(&h.path))
|
||||
.map(|r| Position::Coords(r.x, r.y))
|
||||
.unwrap_or_else(|| self.position(Position::Top)),
|
||||
p @ Position::Coords(..) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use core::input::InputMode;
|
||||
use std::ops::Range;
|
||||
|
||||
use ansi_to_tui::IntoText;
|
||||
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, text::{Line, Text}, widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget}};
|
||||
@ -17,7 +18,7 @@ impl<'a> Input<'a> {
|
||||
impl<'a> Widget for Input<'a> {
|
||||
fn render(self, _: Rect, buf: &mut Buffer) {
|
||||
let input = &self.cx.input;
|
||||
let area = input.area();
|
||||
let area = self.cx.area(&input.position);
|
||||
|
||||
let value = if let Ok(v) = input.value_pretty() {
|
||||
v.into_text().unwrap()
|
||||
@ -41,8 +42,11 @@ impl<'a> Widget for Input<'a> {
|
||||
.style(Style::new().fg(Color::White))
|
||||
.render(area, buf);
|
||||
|
||||
if let Some(selected) = input.selected() {
|
||||
buf.set_style(selected, Style::new().bg(Color::Rgb(72, 77, 102)))
|
||||
if let Some(Range { start, end }) = input.selected() {
|
||||
buf.set_style(
|
||||
Rect { x: area.x + 1 + start, y: area.y + 1, width: end - start, height: 1 },
|
||||
Style::new().bg(Color::Rgb(72, 77, 102)),
|
||||
)
|
||||
}
|
||||
|
||||
let _ = match input.mode() {
|
||||
|
@ -13,7 +13,7 @@ impl<'a> Select<'a> {
|
||||
impl<'a> Widget for Select<'a> {
|
||||
fn render(self, _: Rect, buf: &mut Buffer) {
|
||||
let select = &self.cx.select;
|
||||
let area = select.area();
|
||||
let area = self.cx.area(&select.position);
|
||||
|
||||
let items = select
|
||||
.window()
|
||||
|
@ -31,7 +31,7 @@ impl Preset {
|
||||
|
||||
fn merge_str(user: &str, base: &str) -> String {
|
||||
let path = BaseDirectories::new().unwrap().get_config_file(user);
|
||||
let mut user = fs::read_to_string(path).unwrap_or("".to_string()).parse::<Table>().unwrap();
|
||||
let mut user = fs::read_to_string(path).unwrap_or_default().parse::<Table>().unwrap();
|
||||
|
||||
let base = base.parse::<Table>().unwrap();
|
||||
Self::merge(&mut user, &base, 2);
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use ratatui::layout::Rect;
|
||||
use shared::CharKind;
|
||||
use tokio::sync::oneshot::Sender;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
@ -14,9 +13,9 @@ pub struct Input {
|
||||
snaps: InputSnaps,
|
||||
pub visible: bool,
|
||||
|
||||
title: String,
|
||||
position: (u16, u16),
|
||||
callback: Option<Sender<Result<String>>>,
|
||||
title: String,
|
||||
pub position: Position,
|
||||
callback: Option<Sender<Result<String>>>,
|
||||
|
||||
// Shell
|
||||
pub(super) highlight: bool,
|
||||
@ -29,10 +28,7 @@ impl Input {
|
||||
self.visible = true;
|
||||
|
||||
self.title = opt.title;
|
||||
self.position = match opt.position {
|
||||
Position::Coords(x, y) => (x, y),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.position = opt.position;
|
||||
self.callback = Some(tx);
|
||||
|
||||
// Shell
|
||||
@ -317,21 +313,12 @@ impl Input {
|
||||
pub fn mode(&self) -> InputMode { self.snap().mode }
|
||||
|
||||
#[inline]
|
||||
pub fn area(&self) -> Rect {
|
||||
// TODO: hardcode
|
||||
Rect { x: self.position.0, y: self.position.1 + 2, width: 50, height: 3 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cursor(&self) -> (u16, u16) {
|
||||
pub fn cursor(&self) -> u16 {
|
||||
let snap = self.snap();
|
||||
let width = snap.slice(snap.offset..snap.cursor).width() as u16;
|
||||
|
||||
let area = self.area();
|
||||
(area.x + width + 1, area.y + 1)
|
||||
snap.slice(snap.offset..snap.cursor).width() as u16
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<Rect> {
|
||||
pub fn selected(&self) -> Option<Range<u16>> {
|
||||
let snap = self.snap();
|
||||
let start = snap.op.start()?;
|
||||
|
||||
@ -341,12 +328,8 @@ impl Input {
|
||||
let win = snap.window();
|
||||
let Range { start, end } = start.max(win.start)..end.min(win.end);
|
||||
|
||||
Some(Rect {
|
||||
x: self.position.0 + 1 + snap.slice(snap.offset..start).width() as u16,
|
||||
y: self.position.1 + 3,
|
||||
width: snap.slice(start..end).width() as u16,
|
||||
height: 1,
|
||||
})
|
||||
let s = snap.slice(snap.offset..start).width() as u16;
|
||||
Some(s..s + snap.slice(start..end).width() as u16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1,3 +1,5 @@
|
||||
use ratatui::prelude::Rect;
|
||||
|
||||
use crate::Position;
|
||||
|
||||
pub struct InputOpt {
|
||||
@ -12,7 +14,7 @@ impl InputOpt {
|
||||
Self {
|
||||
title: title.as_ref().to_owned(),
|
||||
value: String::new(),
|
||||
position: Position::Top,
|
||||
position: Position::Top(/* TODO: hardcode */ Rect { x: 0, y: 2, width: 50, height: 3 }),
|
||||
highlight: false,
|
||||
}
|
||||
}
|
||||
@ -21,7 +23,10 @@ impl InputOpt {
|
||||
Self {
|
||||
title: title.as_ref().to_owned(),
|
||||
value: String::new(),
|
||||
position: Position::Hovered,
|
||||
position: Position::Hovered(
|
||||
// TODO: hardcode
|
||||
Rect { x: 0, y: 1, width: 50, height: 3 },
|
||||
),
|
||||
highlight: false,
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use shared::MIME_DIR;
|
||||
use tokio::fs;
|
||||
|
||||
use super::{PreviewData, Tab, Tabs, Watcher};
|
||||
use crate::{emit, external, files::{File, FilesOp}, input::InputOpt, manager::Folder, select::SelectOpt, tasks::Tasks, Position};
|
||||
use crate::{emit, external, files::{File, FilesOp}, input::InputOpt, manager::Folder, select::SelectOpt, tasks::Tasks};
|
||||
|
||||
pub struct Manager {
|
||||
tabs: Tabs,
|
||||
@ -161,11 +161,10 @@ impl Manager {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = emit!(Select(SelectOpt {
|
||||
title: "Open with:".to_string(),
|
||||
items: openers.iter().map(|o| o.display_name.clone()).collect(),
|
||||
position: Position::Hovered,
|
||||
}));
|
||||
let result = emit!(Select(SelectOpt::hovered(
|
||||
"Open with:",
|
||||
openers.iter().map(|o| o.display_name.clone()).collect()
|
||||
)));
|
||||
if let Ok(choice) = result.await {
|
||||
emit!(Open(files, Some(openers[choice].clone())));
|
||||
}
|
||||
|
@ -1,5 +1,23 @@
|
||||
use ratatui::prelude::Rect;
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum Position {
|
||||
Top,
|
||||
Hovered,
|
||||
Coords(u16, u16),
|
||||
#[default]
|
||||
None,
|
||||
Top(Rect),
|
||||
Hovered(Rect),
|
||||
}
|
||||
|
||||
impl Position {
|
||||
#[inline]
|
||||
pub fn rect(&self) -> Option<Rect> {
|
||||
match self {
|
||||
Position::None => None,
|
||||
Position::Top(rect) => Some(*rect),
|
||||
Position::Hovered(rect) => Some(*rect),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dimension(&self) -> Option<(u16, u16)> { self.rect().map(|r| (r.width, r.height)) }
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
mod option;
|
||||
mod select;
|
||||
|
||||
pub use option::*;
|
||||
pub use select::*;
|
||||
|
||||
pub const SELECT_PADDING: u16 = 2;
|
||||
|
30
core/src/select/option.rs
Normal file
30
core/src/select/option.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use ratatui::prelude::Rect;
|
||||
|
||||
use crate::Position;
|
||||
|
||||
pub struct SelectOpt {
|
||||
pub title: String,
|
||||
pub items: Vec<String>,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
impl SelectOpt {
|
||||
pub fn top(title: &str, items: Vec<String>) -> Self {
|
||||
Self {
|
||||
title: title.to_owned(),
|
||||
items,
|
||||
position: Position::Top(/* TODO: hardcode */ Rect { x: 0, y: 2, width: 50, height: 3 }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hovered(title: &str, items: Vec<String>) -> Self {
|
||||
Self {
|
||||
title: title.to_owned(),
|
||||
items,
|
||||
position: Position::Hovered(
|
||||
// TODO: hardcode
|
||||
Rect { x: 0, y: 1, width: 50, height: 3 },
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use ratatui::prelude::Rect;
|
||||
use shared::tty_size;
|
||||
use tokio::sync::oneshot::Sender;
|
||||
|
||||
use super::SELECT_PADDING;
|
||||
use super::{SelectOpt, SELECT_PADDING};
|
||||
use crate::Position;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Select {
|
||||
title: String,
|
||||
items: Vec<String>,
|
||||
position: (u16, u16),
|
||||
title: String,
|
||||
items: Vec<String>,
|
||||
pub position: Position,
|
||||
|
||||
offset: usize,
|
||||
cursor: usize,
|
||||
@ -19,22 +18,14 @@ pub struct Select {
|
||||
pub visible: bool,
|
||||
}
|
||||
|
||||
pub struct SelectOpt {
|
||||
pub title: String,
|
||||
pub items: Vec<String>,
|
||||
pub position: Position,
|
||||
}
|
||||
|
||||
impl Select {
|
||||
pub fn show(&mut self, opt: SelectOpt, tx: Sender<Result<usize>>) {
|
||||
self.close(false);
|
||||
|
||||
self.title = opt.title;
|
||||
self.items = opt.items;
|
||||
self.position = match opt.position {
|
||||
Position::Coords(x, y) => (x, y),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.position = opt.position;
|
||||
|
||||
self.callback = Some(tx);
|
||||
self.visible = true;
|
||||
}
|
||||
@ -96,14 +87,4 @@ impl Select {
|
||||
|
||||
#[inline]
|
||||
pub fn rel_cursor(&self) -> usize { self.cursor - self.offset }
|
||||
|
||||
#[inline]
|
||||
pub fn area(&self) -> Rect {
|
||||
Rect {
|
||||
x: self.position.0,
|
||||
y: self.position.1 + 2,
|
||||
width: 50,
|
||||
height: self.limit() as u16 + SELECT_PADDING,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user