Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support mouse event #1038

Merged
merged 1 commit into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions yazi-adaptor/src/chafa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ impl Chafa {
height: lines.len() as u16,
};

Adaptor::Chafa.image_hide()?;
Adaptor::shown_store(area);
Emulator::move_lock((max.x, max.y), |stderr| {
for (i, line) in lines.into_iter().enumerate() {
Expand Down
1 change: 1 addition & 0 deletions yazi-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
# External dependencies
anyhow = "1.0.86"
arc-swap = "1.7.1"
bitflags = "2.5.0"
crossterm = "0.27.0"
globset = "0.4.14"
indexmap = "2.2.6"
Expand Down
13 changes: 5 additions & 8 deletions yazi-config/preset/yazi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ linemode = "none"
show_hidden = false
show_symlink = true
scrolloff = 5
mouse_events = [ "click", "scroll" ]

[preview]
tab_size = 2
Expand Down Expand Up @@ -86,10 +87,8 @@ fetchers = [
]
preloaders = [
# Image
{ mime = "image/svg+xml", run = "magick" },
{ mime = "image/heic", run = "magick" },
{ mime = "image/jxl", run = "magick" },
{ mime = "image/*", run = "image" },
{ mime = "image/{heic,jxl,svg+xml}", run = "magick" },
{ mime = "image/*", run = "image" },
# Video
{ mime = "video/*", run = "video" },
# PDF
Expand All @@ -106,10 +105,8 @@ previewers = [
# JSON
{ mime = "application/json", run = "json" },
# Image
{ mime = "image/svg+xml", run = "magick" },
{ mime = "image/heic", run = "magick" },
{ mime = "image/jxl", run = "magick" },
{ mime = "image/*", run = "image" },
{ mime = "image/{heic,jxl,svg+xml}", run = "magick" },
{ mime = "image/*", run = "image" },
# Video
{ mime = "video/*", run = "video" },
# PDF
Expand Down
3 changes: 2 additions & 1 deletion yazi-config/src/manager/manager.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use validator::Validate;

use super::{ManagerRatio, SortBy};
use super::{ManagerRatio, MouseEvents, SortBy};
use crate::{validation::check_validation, MERGED_YAZI};

#[derive(Debug, Deserialize, Serialize, Validate)]
Expand All @@ -21,6 +21,7 @@ pub struct Manager {
pub show_hidden: bool,
pub show_symlink: bool,
pub scrolloff: u8,
pub mouse_events: MouseEvents,
}

impl Default for Manager {
Expand Down
2 changes: 2 additions & 0 deletions yazi-config/src/manager/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod manager;
mod mouse;
mod ratio;
mod sorting;

pub use manager::*;
pub use mouse::*;
pub use ratio::*;
pub use sorting::*;
63 changes: 63 additions & 0 deletions yazi-config/src/manager/mouse.rs
Original file line number Diff line number Diff line change
@@ -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,
}
}
}
7 changes: 4 additions & 3 deletions yazi-core/src/manager/commands/update_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,10 @@ impl Manager {
|(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 hovered = f.hovered().filter(|_| f.tracing).map(|h| h.url());
_ = f.update(op.into_owned()) && f.repos(hovered);
let folder = tab.history.entry(op.url().clone()).or_insert_with(|| Folder::from(op.url()));
let hovered = folder.hovered().filter(|_| folder.tracing).map(|h| h.url());
if folder.update(op.into_owned()) {
folder.repos(hovered);
}

if leave {
Expand Down
1 change: 1 addition & 0 deletions yazi-fm/src/app/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl App {
Event::Seq(cmds, layer) => self.dispatch_seq(cmds, layer),
Event::Render => self.dispatch_render(),
Event::Key(key) => self.dispatch_key(key),
Event::Mouse(mouse) => self.mouse(mouse),
Event::Resize => self.resize(()),
Event::Paste(str) => self.dispatch_paste(str),
Event::Quit(opt) => self.quit(opt),
Expand Down
1 change: 1 addition & 0 deletions yazi-fm/src/app/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod accept_payload;
mod mouse;
mod notify;
mod plugin;
mod quit;
Expand Down
65 changes: 65 additions & 0 deletions yazi-fm/src/app/commands/mouse.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 23 additions & 0 deletions yazi-fm/src/components/current.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
}
21 changes: 20 additions & 1 deletion yazi-fm/src/components/header.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crossterm::event::MouseEventKind;
use mlua::{Table, TableExt};
use ratatui::{buffer::Buffer, widgets::Widget};
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;

Expand All @@ -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(())
}
}
4 changes: 4 additions & 0 deletions yazi-fm/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#![allow(clippy::module_inception)]

mod current;
mod header;
mod manager;
mod parent;
mod preview;
mod progress;
mod status;

pub(super) use current::*;
pub(super) use header::*;
pub(super) use manager::*;
pub(super) use parent::*;
pub(super) use preview::*;
pub(super) use progress::*;
pub(super) use status::*;
23 changes: 23 additions & 0 deletions yazi-fm/src/components/parent.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
}
19 changes: 19 additions & 0 deletions yazi-fm/src/components/preview.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crossterm::event::MouseEventKind;
use mlua::{Table, TableExt};
use ratatui::{buffer::Buffer, widgets::Widget};
use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};

use crate::Ctx;

Expand All @@ -9,6 +12,22 @@ pub(crate) struct Preview<'a> {
impl<'a> Preview<'a> {
#[inline]
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<'_> {
Expand Down
Loading