mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-25 09:46:37 +03:00
feat: support mouse event (#1038)
This commit is contained in:
parent
e4d67121f8
commit
162218c345
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2751,6 +2751,7 @@ version = "0.2.5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
"bitflags 2.5.0",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"globset",
|
"globset",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
@ -53,6 +53,7 @@ impl Chafa {
|
|||||||
height: lines.len() as u16,
|
height: lines.len() as u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Adaptor::Chafa.image_hide()?;
|
||||||
Adaptor::shown_store(area);
|
Adaptor::shown_store(area);
|
||||||
Emulator::move_lock((max.x, max.y), |stderr| {
|
Emulator::move_lock((max.x, max.y), |stderr| {
|
||||||
for (i, line) in lines.into_iter().enumerate() {
|
for (i, line) in lines.into_iter().enumerate() {
|
||||||
|
@ -14,6 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
|
|||||||
# External dependencies
|
# External dependencies
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
arc-swap = "1.7.1"
|
arc-swap = "1.7.1"
|
||||||
|
bitflags = "2.5.0"
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
globset = "0.4.14"
|
globset = "0.4.14"
|
||||||
indexmap = "2.2.6"
|
indexmap = "2.2.6"
|
||||||
|
@ -13,6 +13,7 @@ linemode = "none"
|
|||||||
show_hidden = false
|
show_hidden = false
|
||||||
show_symlink = true
|
show_symlink = true
|
||||||
scrolloff = 5
|
scrolloff = 5
|
||||||
|
mouse_events = [ "click", "scroll" ]
|
||||||
|
|
||||||
[preview]
|
[preview]
|
||||||
tab_size = 2
|
tab_size = 2
|
||||||
@ -86,10 +87,8 @@ fetchers = [
|
|||||||
]
|
]
|
||||||
preloaders = [
|
preloaders = [
|
||||||
# Image
|
# Image
|
||||||
{ mime = "image/svg+xml", run = "magick" },
|
{ mime = "image/{heic,jxl,svg+xml}", run = "magick" },
|
||||||
{ mime = "image/heic", run = "magick" },
|
{ mime = "image/*", run = "image" },
|
||||||
{ mime = "image/jxl", run = "magick" },
|
|
||||||
{ mime = "image/*", run = "image" },
|
|
||||||
# Video
|
# Video
|
||||||
{ mime = "video/*", run = "video" },
|
{ mime = "video/*", run = "video" },
|
||||||
# PDF
|
# PDF
|
||||||
@ -106,10 +105,8 @@ previewers = [
|
|||||||
# JSON
|
# JSON
|
||||||
{ mime = "application/json", run = "json" },
|
{ mime = "application/json", run = "json" },
|
||||||
# Image
|
# Image
|
||||||
{ mime = "image/svg+xml", run = "magick" },
|
{ mime = "image/{heic,jxl,svg+xml}", run = "magick" },
|
||||||
{ mime = "image/heic", run = "magick" },
|
{ mime = "image/*", run = "image" },
|
||||||
{ mime = "image/jxl", run = "magick" },
|
|
||||||
{ mime = "image/*", run = "image" },
|
|
||||||
# Video
|
# Video
|
||||||
{ mime = "video/*", run = "video" },
|
{ mime = "video/*", run = "video" },
|
||||||
# PDF
|
# PDF
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
use super::{ManagerRatio, SortBy};
|
use super::{ManagerRatio, MouseEvents, SortBy};
|
||||||
use crate::{validation::check_validation, MERGED_YAZI};
|
use crate::{validation::check_validation, MERGED_YAZI};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Validate)]
|
#[derive(Debug, Deserialize, Serialize, Validate)]
|
||||||
@ -21,6 +21,7 @@ pub struct Manager {
|
|||||||
pub show_hidden: bool,
|
pub show_hidden: bool,
|
||||||
pub show_symlink: bool,
|
pub show_symlink: bool,
|
||||||
pub scrolloff: u8,
|
pub scrolloff: u8,
|
||||||
|
pub mouse_events: MouseEvents,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Manager {
|
impl Default for Manager {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
mod manager;
|
mod manager;
|
||||||
|
mod mouse;
|
||||||
mod ratio;
|
mod ratio;
|
||||||
mod sorting;
|
mod sorting;
|
||||||
|
|
||||||
pub use manager::*;
|
pub use manager::*;
|
||||||
|
pub use mouse::*;
|
||||||
pub use ratio::*;
|
pub use ratio::*;
|
||||||
pub use sorting::*;
|
pub use sorting::*;
|
||||||
|
63
yazi-config/src/manager/mouse.rs
Normal file
63
yazi-config/src/manager/mouse.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use crossterm::event::MouseEventKind;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
#[serde(try_from = "Vec<String>", into = "Vec<String>")]
|
||||||
|
pub struct MouseEvents: u8 {
|
||||||
|
const CLICK = 0b00001;
|
||||||
|
const SCROLL = 0b00010;
|
||||||
|
const TOUCH = 0b00100;
|
||||||
|
const MOVE = 0b01000;
|
||||||
|
const DRAG = 0b10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MouseEvents {
|
||||||
|
#[inline]
|
||||||
|
pub const fn draggable(self) -> bool { self.contains(Self::DRAG) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Vec<String>> for MouseEvents {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: Vec<String>) -> Result<Self, Self::Error> {
|
||||||
|
value.into_iter().try_fold(Self::empty(), |aac, s| {
|
||||||
|
Ok(match s.as_str() {
|
||||||
|
"click" => aac | Self::CLICK,
|
||||||
|
"scroll" => aac | Self::SCROLL,
|
||||||
|
"touch" => aac | Self::TOUCH,
|
||||||
|
"move" => aac | Self::MOVE,
|
||||||
|
"drag" => aac | Self::DRAG,
|
||||||
|
_ => bail!("Invalid mouse event: {s}"),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MouseEvents> for Vec<String> {
|
||||||
|
fn from(value: MouseEvents) -> Self {
|
||||||
|
let events = [
|
||||||
|
(MouseEvents::CLICK, "click"),
|
||||||
|
(MouseEvents::SCROLL, "scroll"),
|
||||||
|
(MouseEvents::TOUCH, "touch"),
|
||||||
|
(MouseEvents::MOVE, "move"),
|
||||||
|
(MouseEvents::DRAG, "drag"),
|
||||||
|
];
|
||||||
|
events.into_iter().filter(|v| value.contains(v.0)).map(|v| v.1.to_owned()).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crossterm::event::MouseEventKind> for MouseEvents {
|
||||||
|
fn from(value: crossterm::event::MouseEventKind) -> Self {
|
||||||
|
match value {
|
||||||
|
MouseEventKind::Down(_) | MouseEventKind::Up(_) => Self::CLICK,
|
||||||
|
MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => Self::SCROLL,
|
||||||
|
MouseEventKind::ScrollLeft | MouseEventKind::ScrollRight => Self::TOUCH,
|
||||||
|
MouseEventKind::Moved => Self::MOVE,
|
||||||
|
MouseEventKind::Drag(_) => Self::DRAG,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -111,9 +111,10 @@ impl Manager {
|
|||||||
|(p, pp)| matches!(*op, FilesOp::Deleting(ref parent, ref urls) if *parent == pp && urls.contains(p)),
|
|(p, pp)| matches!(*op, FilesOp::Deleting(ref parent, ref urls) if *parent == pp && urls.contains(p)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(f) = tab.history.get_mut(op.url()) {
|
let folder = tab.history.entry(op.url().clone()).or_insert_with(|| Folder::from(op.url()));
|
||||||
let hovered = f.hovered().filter(|_| f.tracing).map(|h| h.url());
|
let hovered = folder.hovered().filter(|_| folder.tracing).map(|h| h.url());
|
||||||
_ = f.update(op.into_owned()) && f.repos(hovered);
|
if folder.update(op.into_owned()) {
|
||||||
|
folder.repos(hovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
if leave {
|
if leave {
|
||||||
|
@ -56,6 +56,7 @@ impl App {
|
|||||||
Event::Seq(cmds, layer) => self.dispatch_seq(cmds, layer),
|
Event::Seq(cmds, layer) => self.dispatch_seq(cmds, layer),
|
||||||
Event::Render => self.dispatch_render(),
|
Event::Render => self.dispatch_render(),
|
||||||
Event::Key(key) => self.dispatch_key(key),
|
Event::Key(key) => self.dispatch_key(key),
|
||||||
|
Event::Mouse(mouse) => self.mouse(mouse),
|
||||||
Event::Resize => self.resize(()),
|
Event::Resize => self.resize(()),
|
||||||
Event::Paste(str) => self.dispatch_paste(str),
|
Event::Paste(str) => self.dispatch_paste(str),
|
||||||
Event::Quit(opt) => self.quit(opt),
|
Event::Quit(opt) => self.quit(opt),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
mod accept_payload;
|
mod accept_payload;
|
||||||
|
mod mouse;
|
||||||
mod notify;
|
mod notify;
|
||||||
mod plugin;
|
mod plugin;
|
||||||
mod quit;
|
mod quit;
|
||||||
|
65
yazi-fm/src/app/commands/mouse.rs
Normal file
65
yazi-fm/src/app/commands/mouse.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use crossterm::event::{MouseEvent, MouseEventKind};
|
||||||
|
use mlua::Table;
|
||||||
|
use ratatui::layout::{Position, Rect};
|
||||||
|
use tracing::error;
|
||||||
|
use yazi_config::{LAYOUT, MANAGER};
|
||||||
|
use yazi_plugin::{bindings::Cast, LUA};
|
||||||
|
|
||||||
|
use crate::{app::App, components, lives::Lives};
|
||||||
|
|
||||||
|
pub struct Opt {
|
||||||
|
event: MouseEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MouseEvent> for Opt {
|
||||||
|
fn from(event: MouseEvent) -> Self { Self { event } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub(crate) fn mouse(&mut self, opt: impl Into<Opt>) {
|
||||||
|
let event = (opt.into() as Opt).event;
|
||||||
|
|
||||||
|
let layout = LAYOUT.load();
|
||||||
|
let position = Position { x: event.column, y: event.row };
|
||||||
|
|
||||||
|
if matches!(event.kind, MouseEventKind::Moved | MouseEventKind::Drag(_)) {
|
||||||
|
self.mouse_do(crate::Root::mouse, event, None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if layout.current.contains(position) {
|
||||||
|
self.mouse_do(components::Current::mouse, event, Some(layout.current));
|
||||||
|
} else if layout.preview.contains(position) {
|
||||||
|
self.mouse_do(components::Preview::mouse, event, Some(layout.preview));
|
||||||
|
} else if layout.parent.contains(position) {
|
||||||
|
self.mouse_do(components::Parent::mouse, event, Some(layout.parent));
|
||||||
|
} else if layout.header.contains(position) {
|
||||||
|
self.mouse_do(components::Header::mouse, event, Some(layout.header));
|
||||||
|
} else if layout.status.contains(position) {
|
||||||
|
self.mouse_do(components::Status::mouse, event, Some(layout.status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_do(
|
||||||
|
&self,
|
||||||
|
f: impl FnOnce(MouseEvent) -> mlua::Result<()>,
|
||||||
|
mut event: MouseEvent,
|
||||||
|
rect: Option<Rect>,
|
||||||
|
) {
|
||||||
|
if matches!(event.kind, MouseEventKind::Down(_) if MANAGER.mouse_events.draggable()) {
|
||||||
|
let evt = yazi_plugin::bindings::MouseEvent::cast(&LUA, event);
|
||||||
|
if let (Ok(evt), Ok(root)) = (evt, LUA.globals().raw_get::<_, Table>("Root")) {
|
||||||
|
root.raw_set("drag_start", evt).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rect) = rect {
|
||||||
|
event.row -= rect.y;
|
||||||
|
event.column -= rect.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = Lives::scope(&self.cx, move |_| f(event)) {
|
||||||
|
error!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
yazi-fm/src/components/current.rs
Normal file
23
yazi-fm/src/components/current.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use crossterm::event::MouseEventKind;
|
||||||
|
use mlua::{Table, TableExt};
|
||||||
|
use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};
|
||||||
|
|
||||||
|
pub(crate) struct Current;
|
||||||
|
|
||||||
|
impl Current {
|
||||||
|
pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
|
||||||
|
let evt = MouseEvent::cast(&LUA, event)?;
|
||||||
|
let comp: Table = LUA.globals().raw_get("Current")?;
|
||||||
|
|
||||||
|
match event.kind {
|
||||||
|
MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?,
|
||||||
|
MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?,
|
||||||
|
MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?,
|
||||||
|
MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
|
use crossterm::event::MouseEventKind;
|
||||||
use mlua::{Table, TableExt};
|
use mlua::{Table, TableExt};
|
||||||
use ratatui::{buffer::Buffer, widgets::Widget};
|
use ratatui::{buffer::Buffer, widgets::Widget};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA};
|
use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA};
|
||||||
|
|
||||||
pub(crate) struct Header;
|
pub(crate) struct Header;
|
||||||
|
|
||||||
@ -18,3 +19,21 @@ impl Widget for Header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
|
||||||
|
let evt = MouseEvent::cast(&LUA, event)?;
|
||||||
|
let comp: Table = LUA.globals().raw_get("Header")?;
|
||||||
|
|
||||||
|
match event.kind {
|
||||||
|
MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?,
|
||||||
|
MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?,
|
||||||
|
MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?,
|
||||||
|
MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
#![allow(clippy::module_inception)]
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
|
mod current;
|
||||||
mod header;
|
mod header;
|
||||||
mod manager;
|
mod manager;
|
||||||
|
mod parent;
|
||||||
mod preview;
|
mod preview;
|
||||||
mod progress;
|
mod progress;
|
||||||
mod status;
|
mod status;
|
||||||
|
|
||||||
|
pub(super) use current::*;
|
||||||
pub(super) use header::*;
|
pub(super) use header::*;
|
||||||
pub(super) use manager::*;
|
pub(super) use manager::*;
|
||||||
|
pub(super) use parent::*;
|
||||||
pub(super) use preview::*;
|
pub(super) use preview::*;
|
||||||
pub(super) use progress::*;
|
pub(super) use progress::*;
|
||||||
pub(super) use status::*;
|
pub(super) use status::*;
|
||||||
|
23
yazi-fm/src/components/parent.rs
Normal file
23
yazi-fm/src/components/parent.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use crossterm::event::MouseEventKind;
|
||||||
|
use mlua::{Table, TableExt};
|
||||||
|
use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};
|
||||||
|
|
||||||
|
pub(crate) struct Parent;
|
||||||
|
|
||||||
|
impl Parent {
|
||||||
|
pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
|
||||||
|
let evt = MouseEvent::cast(&LUA, event)?;
|
||||||
|
let comp: Table = LUA.globals().raw_get("Parent")?;
|
||||||
|
|
||||||
|
match event.kind {
|
||||||
|
MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?,
|
||||||
|
MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?,
|
||||||
|
MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?,
|
||||||
|
MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
|
use crossterm::event::MouseEventKind;
|
||||||
|
use mlua::{Table, TableExt};
|
||||||
use ratatui::{buffer::Buffer, widgets::Widget};
|
use ratatui::{buffer::Buffer, widgets::Widget};
|
||||||
|
use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};
|
||||||
|
|
||||||
use crate::Ctx;
|
use crate::Ctx;
|
||||||
|
|
||||||
@ -9,6 +12,22 @@ pub(crate) struct Preview<'a> {
|
|||||||
impl<'a> Preview<'a> {
|
impl<'a> Preview<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||||
|
|
||||||
|
pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
|
||||||
|
let evt = MouseEvent::cast(&LUA, event)?;
|
||||||
|
let comp: Table = LUA.globals().raw_get("Preview")?;
|
||||||
|
|
||||||
|
match event.kind {
|
||||||
|
MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?,
|
||||||
|
MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?,
|
||||||
|
MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?,
|
||||||
|
MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Preview<'_> {
|
impl Widget for Preview<'_> {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
use crossterm::event::MouseEventKind;
|
||||||
use mlua::{Table, TableExt};
|
use mlua::{Table, TableExt};
|
||||||
use ratatui::widgets::Widget;
|
use ratatui::widgets::Widget;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA};
|
use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA};
|
||||||
|
|
||||||
pub(crate) struct Status;
|
pub(crate) struct Status;
|
||||||
|
|
||||||
@ -18,3 +19,21 @@ impl Widget for Status {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Status {
|
||||||
|
pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
|
||||||
|
let evt = MouseEvent::cast(&LUA, event)?;
|
||||||
|
let comp: Table = LUA.globals().raw_get("Status")?;
|
||||||
|
|
||||||
|
match event.kind {
|
||||||
|
MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?,
|
||||||
|
MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?,
|
||||||
|
MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?,
|
||||||
|
MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?,
|
||||||
|
MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
use crossterm::event::MouseEventKind;
|
||||||
|
use mlua::{Table, TableExt};
|
||||||
use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Rect}, widgets::Widget};
|
use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Rect}, widgets::Widget};
|
||||||
|
use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};
|
||||||
|
|
||||||
use super::{completion, input, select, tasks, which};
|
use super::{completion, input, select, tasks, which};
|
||||||
use crate::{components, help, Ctx};
|
use crate::{components, help, Ctx};
|
||||||
@ -47,3 +50,17 @@ impl<'a> Widget for Root<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Root<'_> {
|
||||||
|
pub(super) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
|
||||||
|
let evt = MouseEvent::cast(&LUA, event)?;
|
||||||
|
let comp: Table = LUA.globals().raw_get("Root")?;
|
||||||
|
|
||||||
|
match event.kind {
|
||||||
|
MouseEventKind::Moved => comp.call_method("move", evt)?,
|
||||||
|
MouseEventKind::Drag(_) => comp.call_method("drag", evt)?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventK
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use tokio::{select, task::JoinHandle};
|
use tokio::{select, task::JoinHandle};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
use yazi_config::MANAGER;
|
||||||
use yazi_shared::event::Event;
|
use yazi_shared::event::Event;
|
||||||
|
|
||||||
pub(super) struct Signals {
|
pub(super) struct Signals {
|
||||||
@ -71,6 +72,11 @@ impl Signals {
|
|||||||
Some(Ok(event)) = reader.next() => {
|
Some(Ok(event)) = reader.next() => {
|
||||||
match event {
|
match event {
|
||||||
CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => Event::Key(key).emit(),
|
CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => Event::Key(key).emit(),
|
||||||
|
CrosstermEvent::Mouse(mouse) => {
|
||||||
|
if MANAGER.mouse_events.contains(mouse.kind.into()) {
|
||||||
|
Event::Mouse(mouse).emit();
|
||||||
|
}
|
||||||
|
},
|
||||||
CrosstermEvent::Paste(str) => Event::Paste(str).emit(),
|
CrosstermEvent::Paste(str) => Event::Paste(str).emit(),
|
||||||
CrosstermEvent::Resize(..) => Event::Resize.emit(),
|
CrosstermEvent::Resize(..) => Event::Resize.emit(),
|
||||||
_ => {},
|
_ => {},
|
||||||
|
@ -42,3 +42,18 @@ function Current:render(area)
|
|||||||
Folder:markers(area, markers),
|
Folder:markers(area, markers),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Current:click(event, up)
|
||||||
|
if up or not event.is_left then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = Folder:by_kind(Folder.CURRENT)
|
||||||
|
if event.y <= #f.window and f.hovered then
|
||||||
|
ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Current:scroll(event, step) ya.manager_emit("arrow", { step }) end
|
||||||
|
|
||||||
|
function Current:touch(event, step) end
|
||||||
|
@ -82,3 +82,8 @@ function Folder:by_kind(kind)
|
|||||||
return cx.active.preview.folder
|
return cx.active.preview.folder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Folder:window(kind)
|
||||||
|
local folder = self:by_kind(kind)
|
||||||
|
return folder and folder.window
|
||||||
|
end
|
||||||
|
@ -66,3 +66,9 @@ function Header:render(area)
|
|||||||
ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT),
|
ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Header:click(event, up) end
|
||||||
|
|
||||||
|
function Header:scroll(event, step) end
|
||||||
|
|
||||||
|
function Header:touch(event, step) end
|
||||||
|
@ -26,3 +26,20 @@ function Parent:render(area)
|
|||||||
Folder:markers(area, markers),
|
Folder:markers(area, markers),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Parent:click(event, up)
|
||||||
|
if up or not event.is_left then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local window = Folder:window(Folder.PARENT) or {}
|
||||||
|
if window[event.y] then
|
||||||
|
ya.manager_emit("reveal", { window[event.y].url })
|
||||||
|
else
|
||||||
|
ya.manager_emit("leave", {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Parent:scroll(event, step) end
|
||||||
|
|
||||||
|
function Parent:touch(event, step) end
|
||||||
|
@ -6,3 +6,20 @@ function Preview:render(area)
|
|||||||
self.area = area
|
self.area = area
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Preview:click(event, up)
|
||||||
|
if up or not event.is_left then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local window = Folder:window(Folder.PREVIEW) or {}
|
||||||
|
if window[event.y] then
|
||||||
|
ya.manager_emit("reveal", { window[event.y].url })
|
||||||
|
else
|
||||||
|
ya.manager_emit("enter", {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Preview:scroll(event, step) ya.manager_emit("seek", { step }) end
|
||||||
|
|
||||||
|
function Preview:touch(event, step) end
|
||||||
|
7
yazi-plugin/preset/components/root.lua
Normal file
7
yazi-plugin/preset/components/root.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Root = {
|
||||||
|
drag_start = ui.Rect.default,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Root:move(event) end
|
||||||
|
|
||||||
|
function Root:drag(event) end
|
@ -121,3 +121,9 @@ function Status:render(area)
|
|||||||
table.unpack(Progress:render(area, right:width())),
|
table.unpack(Progress:render(area, right:width())),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Status:click(event, up) end
|
||||||
|
|
||||||
|
function Status:scroll(event, step) end
|
||||||
|
|
||||||
|
function Status:touch(event, step) end
|
||||||
|
@ -5,6 +5,7 @@ mod cha;
|
|||||||
mod file;
|
mod file;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod mouse;
|
||||||
mod permit;
|
mod permit;
|
||||||
mod position;
|
mod position;
|
||||||
mod range;
|
mod range;
|
||||||
@ -15,6 +16,7 @@ pub use cha::*;
|
|||||||
pub use file::*;
|
pub use file::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
pub use input::*;
|
pub use input::*;
|
||||||
|
pub use mouse::*;
|
||||||
pub use permit::*;
|
pub use permit::*;
|
||||||
pub use position::*;
|
pub use position::*;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
|
35
yazi-plugin/src/bindings/mouse.rs
Normal file
35
yazi-plugin/src/bindings/mouse.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crossterm::event::MouseButton;
|
||||||
|
use mlua::{AnyUserData, Lua, UserDataFields};
|
||||||
|
|
||||||
|
use super::Cast;
|
||||||
|
|
||||||
|
pub struct MouseEvent;
|
||||||
|
|
||||||
|
impl MouseEvent {
|
||||||
|
pub fn register(lua: &Lua) -> mlua::Result<()> {
|
||||||
|
lua.register_userdata_type::<crossterm::event::MouseEvent>(|reg| {
|
||||||
|
reg.add_field_method_get("x", |_, me| Ok(me.column as u32 + 1));
|
||||||
|
reg.add_field_method_get("y", |_, me| Ok(me.row as u32 + 1));
|
||||||
|
reg.add_field_method_get("is_left", |_, me| {
|
||||||
|
use crossterm::event::MouseEventKind as K;
|
||||||
|
Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Left))
|
||||||
|
});
|
||||||
|
reg.add_field_method_get("is_right", |_, me| {
|
||||||
|
use crossterm::event::MouseEventKind as K;
|
||||||
|
Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Right))
|
||||||
|
});
|
||||||
|
reg.add_field_method_get("is_middle", |_, me| {
|
||||||
|
use crossterm::event::MouseEventKind as K;
|
||||||
|
Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Middle))
|
||||||
|
});
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cast<crossterm::event::MouseEvent> for MouseEvent {
|
||||||
|
fn cast(lua: &Lua, data: crossterm::event::MouseEvent) -> mlua::Result<AnyUserData> {
|
||||||
|
lua.create_any_userdata(data)
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> {
|
|||||||
crate::bindings::Cha::register(lua)?;
|
crate::bindings::Cha::register(lua)?;
|
||||||
crate::bindings::File::register(lua)?;
|
crate::bindings::File::register(lua)?;
|
||||||
crate::bindings::Icon::register(lua)?;
|
crate::bindings::Icon::register(lua)?;
|
||||||
|
crate::bindings::MouseEvent::register(lua)?;
|
||||||
crate::elements::pour(lua)?;
|
crate::elements::pour(lua)?;
|
||||||
crate::loader::install(lua)?;
|
crate::loader::install(lua)?;
|
||||||
crate::pubsub::install(lua)?;
|
crate::pubsub::install(lua)?;
|
||||||
@ -38,6 +39,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> {
|
|||||||
lua.load(include_str!("../preset/components/parent.lua")).exec()?;
|
lua.load(include_str!("../preset/components/parent.lua")).exec()?;
|
||||||
lua.load(include_str!("../preset/components/preview.lua")).exec()?;
|
lua.load(include_str!("../preset/components/preview.lua")).exec()?;
|
||||||
lua.load(include_str!("../preset/components/progress.lua")).exec()?;
|
lua.load(include_str!("../preset/components/progress.lua")).exec()?;
|
||||||
|
lua.load(include_str!("../preset/components/root.lua")).exec()?;
|
||||||
lua.load(include_str!("../preset/components/status.lua")).exec()?;
|
lua.load(include_str!("../preset/components/status.lua")).exec()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{collections::VecDeque, ffi::OsString};
|
use std::{collections::VecDeque, ffi::OsString};
|
||||||
|
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::{KeyEvent, MouseEvent};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use super::Cmd;
|
use super::Cmd;
|
||||||
@ -15,6 +15,7 @@ pub enum Event {
|
|||||||
Seq(VecDeque<Cmd>, Layer),
|
Seq(VecDeque<Cmd>, Layer),
|
||||||
Render,
|
Render,
|
||||||
Key(KeyEvent),
|
Key(KeyEvent),
|
||||||
|
Mouse(MouseEvent),
|
||||||
Resize,
|
Resize,
|
||||||
Paste(String),
|
Paste(String),
|
||||||
Quit(EventQuit),
|
Quit(EventQuit),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{io::{self, stderr, BufWriter, Stderr, Write}, mem, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}};
|
use std::{io::{self, stderr, BufWriter, Stderr, Write}, mem, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, WindowSize}};
|
use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, WindowSize}};
|
||||||
use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal};
|
use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal};
|
||||||
|
|
||||||
static CSI_U: AtomicBool = AtomicBool::new(false);
|
static CSI_U: AtomicBool = AtomicBool::new(false);
|
||||||
@ -25,7 +25,7 @@ impl Term {
|
|||||||
BufWriter::new(stderr()),
|
BufWriter::new(stderr()),
|
||||||
EnterAlternateScreen,
|
EnterAlternateScreen,
|
||||||
EnableBracketedPaste,
|
EnableBracketedPaste,
|
||||||
EnableFocusChange,
|
EnableMouseCapture,
|
||||||
SavePosition,
|
SavePosition,
|
||||||
Print("\x1b[?u\x1b[c"),
|
Print("\x1b[?u\x1b[c"),
|
||||||
RestorePosition
|
RestorePosition
|
||||||
@ -56,7 +56,7 @@ impl Term {
|
|||||||
|
|
||||||
execute!(
|
execute!(
|
||||||
stderr(),
|
stderr(),
|
||||||
DisableFocusChange,
|
DisableMouseCapture,
|
||||||
DisableBracketedPaste,
|
DisableBracketedPaste,
|
||||||
LeaveAlternateScreen,
|
LeaveAlternateScreen,
|
||||||
crossterm::cursor::SetCursorStyle::DefaultUserShape
|
crossterm::cursor::SetCursorStyle::DefaultUserShape
|
||||||
@ -74,7 +74,7 @@ impl Term {
|
|||||||
execute!(
|
execute!(
|
||||||
stderr(),
|
stderr(),
|
||||||
SetTitle(""),
|
SetTitle(""),
|
||||||
DisableFocusChange,
|
DisableMouseCapture,
|
||||||
DisableBracketedPaste,
|
DisableBracketedPaste,
|
||||||
LeaveAlternateScreen,
|
LeaveAlternateScreen,
|
||||||
crossterm::cursor::SetCursorStyle::DefaultUserShape,
|
crossterm::cursor::SetCursorStyle::DefaultUserShape,
|
||||||
|
Loading…
Reference in New Issue
Block a user