mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-24 09:12:43 +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);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Select(mut opt, tx) => {
|
Event::Select(opt, tx) => {
|
||||||
opt.position = self.cx.position(opt.position);
|
|
||||||
self.cx.select.show(opt, tx);
|
self.cx.select.show(opt, tx);
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
Event::Input(mut opt, tx) => {
|
Event::Input(opt, tx) => {
|
||||||
opt.position = self.cx.position(opt.position);
|
|
||||||
self.cx.input.show(opt, tx);
|
self.cx.input.show(opt, tx);
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use core::{input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position};
|
use core::{input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position};
|
||||||
|
|
||||||
use config::keymap::KeymapLayer;
|
use config::keymap::KeymapLayer;
|
||||||
|
use libc::winsize;
|
||||||
|
use ratatui::prelude::Rect;
|
||||||
use shared::tty_size;
|
use shared::tty_size;
|
||||||
|
|
||||||
pub struct Ctx {
|
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]
|
#[inline]
|
||||||
pub(super) fn cursor(&self) -> Option<(u16, u16)> {
|
pub(super) fn cursor(&self) -> Option<(u16, u16)> {
|
||||||
if self.input.visible {
|
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
|
None
|
||||||
}
|
}
|
||||||
@ -49,17 +82,4 @@ impl Ctx {
|
|||||||
pub(super) fn image_layer(&self) -> bool {
|
pub(super) fn image_layer(&self) -> bool {
|
||||||
!matches!(self.layer(), KeymapLayer::Which | KeymapLayer::Tasks)
|
!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 core::input::InputMode;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
use ansi_to_tui::IntoText;
|
use ansi_to_tui::IntoText;
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, text::{Line, Text}, widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget}};
|
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> {
|
impl<'a> Widget for Input<'a> {
|
||||||
fn render(self, _: Rect, buf: &mut Buffer) {
|
fn render(self, _: Rect, buf: &mut Buffer) {
|
||||||
let input = &self.cx.input;
|
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() {
|
let value = if let Ok(v) = input.value_pretty() {
|
||||||
v.into_text().unwrap()
|
v.into_text().unwrap()
|
||||||
@ -41,8 +42,11 @@ impl<'a> Widget for Input<'a> {
|
|||||||
.style(Style::new().fg(Color::White))
|
.style(Style::new().fg(Color::White))
|
||||||
.render(area, buf);
|
.render(area, buf);
|
||||||
|
|
||||||
if let Some(selected) = input.selected() {
|
if let Some(Range { start, end }) = input.selected() {
|
||||||
buf.set_style(selected, Style::new().bg(Color::Rgb(72, 77, 102)))
|
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() {
|
let _ = match input.mode() {
|
||||||
|
@ -13,7 +13,7 @@ impl<'a> Select<'a> {
|
|||||||
impl<'a> Widget for Select<'a> {
|
impl<'a> Widget for Select<'a> {
|
||||||
fn render(self, _: Rect, buf: &mut Buffer) {
|
fn render(self, _: Rect, buf: &mut Buffer) {
|
||||||
let select = &self.cx.select;
|
let select = &self.cx.select;
|
||||||
let area = select.area();
|
let area = self.cx.area(&select.position);
|
||||||
|
|
||||||
let items = select
|
let items = select
|
||||||
.window()
|
.window()
|
||||||
|
@ -31,7 +31,7 @@ impl Preset {
|
|||||||
|
|
||||||
fn merge_str(user: &str, base: &str) -> String {
|
fn merge_str(user: &str, base: &str) -> String {
|
||||||
let path = BaseDirectories::new().unwrap().get_config_file(user);
|
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();
|
let base = base.parse::<Table>().unwrap();
|
||||||
Self::merge(&mut user, &base, 2);
|
Self::merge(&mut user, &base, 2);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use ratatui::layout::Rect;
|
|
||||||
use shared::CharKind;
|
use shared::CharKind;
|
||||||
use tokio::sync::oneshot::Sender;
|
use tokio::sync::oneshot::Sender;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
@ -15,7 +14,7 @@ pub struct Input {
|
|||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
|
|
||||||
title: String,
|
title: String,
|
||||||
position: (u16, u16),
|
pub position: Position,
|
||||||
callback: Option<Sender<Result<String>>>,
|
callback: Option<Sender<Result<String>>>,
|
||||||
|
|
||||||
// Shell
|
// Shell
|
||||||
@ -29,10 +28,7 @@ impl Input {
|
|||||||
self.visible = true;
|
self.visible = true;
|
||||||
|
|
||||||
self.title = opt.title;
|
self.title = opt.title;
|
||||||
self.position = match opt.position {
|
self.position = opt.position;
|
||||||
Position::Coords(x, y) => (x, y),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
self.callback = Some(tx);
|
self.callback = Some(tx);
|
||||||
|
|
||||||
// Shell
|
// Shell
|
||||||
@ -317,21 +313,12 @@ impl Input {
|
|||||||
pub fn mode(&self) -> InputMode { self.snap().mode }
|
pub fn mode(&self) -> InputMode { self.snap().mode }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn area(&self) -> Rect {
|
pub fn cursor(&self) -> u16 {
|
||||||
// TODO: hardcode
|
|
||||||
Rect { x: self.position.0, y: self.position.1 + 2, width: 50, height: 3 }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn cursor(&self) -> (u16, u16) {
|
|
||||||
let snap = self.snap();
|
let snap = self.snap();
|
||||||
let width = snap.slice(snap.offset..snap.cursor).width() as u16;
|
snap.slice(snap.offset..snap.cursor).width() as u16
|
||||||
|
|
||||||
let area = self.area();
|
|
||||||
(area.x + width + 1, area.y + 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected(&self) -> Option<Rect> {
|
pub fn selected(&self) -> Option<Range<u16>> {
|
||||||
let snap = self.snap();
|
let snap = self.snap();
|
||||||
let start = snap.op.start()?;
|
let start = snap.op.start()?;
|
||||||
|
|
||||||
@ -341,12 +328,8 @@ impl Input {
|
|||||||
let win = snap.window();
|
let win = snap.window();
|
||||||
let Range { start, end } = start.max(win.start)..end.min(win.end);
|
let Range { start, end } = start.max(win.start)..end.min(win.end);
|
||||||
|
|
||||||
Some(Rect {
|
let s = snap.slice(snap.offset..start).width() as u16;
|
||||||
x: self.position.0 + 1 + snap.slice(snap.offset..start).width() as u16,
|
Some(s..s + snap.slice(start..end).width() as u16)
|
||||||
y: self.position.1 + 3,
|
|
||||||
width: snap.slice(start..end).width() as u16,
|
|
||||||
height: 1,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use ratatui::prelude::Rect;
|
||||||
|
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
|
|
||||||
pub struct InputOpt {
|
pub struct InputOpt {
|
||||||
@ -12,7 +14,7 @@ impl InputOpt {
|
|||||||
Self {
|
Self {
|
||||||
title: title.as_ref().to_owned(),
|
title: title.as_ref().to_owned(),
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
position: Position::Top,
|
position: Position::Top(/* TODO: hardcode */ Rect { x: 0, y: 2, width: 50, height: 3 }),
|
||||||
highlight: false,
|
highlight: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,7 +23,10 @@ impl InputOpt {
|
|||||||
Self {
|
Self {
|
||||||
title: title.as_ref().to_owned(),
|
title: title.as_ref().to_owned(),
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
position: Position::Hovered,
|
position: Position::Hovered(
|
||||||
|
// TODO: hardcode
|
||||||
|
Rect { x: 0, y: 1, width: 50, height: 3 },
|
||||||
|
),
|
||||||
highlight: false,
|
highlight: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use shared::MIME_DIR;
|
|||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
use super::{PreviewData, Tab, Tabs, Watcher};
|
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 {
|
pub struct Manager {
|
||||||
tabs: Tabs,
|
tabs: Tabs,
|
||||||
@ -161,11 +161,10 @@ impl Manager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = emit!(Select(SelectOpt {
|
let result = emit!(Select(SelectOpt::hovered(
|
||||||
title: "Open with:".to_string(),
|
"Open with:",
|
||||||
items: openers.iter().map(|o| o.display_name.clone()).collect(),
|
openers.iter().map(|o| o.display_name.clone()).collect()
|
||||||
position: Position::Hovered,
|
)));
|
||||||
}));
|
|
||||||
if let Ok(choice) = result.await {
|
if let Ok(choice) = result.await {
|
||||||
emit!(Open(files, Some(openers[choice].clone())));
|
emit!(Open(files, Some(openers[choice].clone())));
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
|
use ratatui::prelude::Rect;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub enum Position {
|
pub enum Position {
|
||||||
Top,
|
#[default]
|
||||||
Hovered,
|
None,
|
||||||
Coords(u16, u16),
|
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;
|
mod select;
|
||||||
|
|
||||||
|
pub use option::*;
|
||||||
pub use select::*;
|
pub use select::*;
|
||||||
|
|
||||||
pub const SELECT_PADDING: u16 = 2;
|
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 anyhow::{anyhow, Result};
|
||||||
use ratatui::prelude::Rect;
|
|
||||||
use shared::tty_size;
|
use shared::tty_size;
|
||||||
use tokio::sync::oneshot::Sender;
|
use tokio::sync::oneshot::Sender;
|
||||||
|
|
||||||
use super::SELECT_PADDING;
|
use super::{SelectOpt, SELECT_PADDING};
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Select {
|
pub struct Select {
|
||||||
title: String,
|
title: String,
|
||||||
items: Vec<String>,
|
items: Vec<String>,
|
||||||
position: (u16, u16),
|
pub position: Position,
|
||||||
|
|
||||||
offset: usize,
|
offset: usize,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
@ -19,22 +18,14 @@ pub struct Select {
|
|||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SelectOpt {
|
|
||||||
pub title: String,
|
|
||||||
pub items: Vec<String>,
|
|
||||||
pub position: Position,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Select {
|
impl Select {
|
||||||
pub fn show(&mut self, opt: SelectOpt, tx: Sender<Result<usize>>) {
|
pub fn show(&mut self, opt: SelectOpt, tx: Sender<Result<usize>>) {
|
||||||
self.close(false);
|
self.close(false);
|
||||||
|
|
||||||
self.title = opt.title;
|
self.title = opt.title;
|
||||||
self.items = opt.items;
|
self.items = opt.items;
|
||||||
self.position = match opt.position {
|
self.position = opt.position;
|
||||||
Position::Coords(x, y) => (x, y),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
self.callback = Some(tx);
|
self.callback = Some(tx);
|
||||||
self.visible = true;
|
self.visible = true;
|
||||||
}
|
}
|
||||||
@ -96,14 +87,4 @@ impl Select {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rel_cursor(&self) -> usize { self.cursor - self.offset }
|
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