summaryrefslogtreecommitdiffstats
path: root/default-plugins
diff options
context:
space:
mode:
authorqepasa <pawelpalenica11@gmail.com>2021-10-12 14:37:54 -0700
committerGitHub <noreply@github.com>2021-10-12 22:37:54 +0100
commit0710594588aa768b934f925704dc57486dc0a157 (patch)
tree5b2cd4c9e94ecbda277454b77698ebaa837fab6c /default-plugins
parenta6453f111ea62e6332c919224d3a0fb19e35c249 (diff)
feat(plugin): Add mouse events for plugins (#629)
* feat(plugin): Add mouse events for plugins * Add double click support in strider * Add support for mouse clicks in tab-bar and fix bug in strider with selecting past the list of files and random double click action * continue working on mouse support for tab bar * finish tab change * fix fmt and fix bug in strider double-click * fix clippy * cleanup dbgs and logs * fix clippy * noop change to rerun e2e tests * Rebase and fix mouse click behavior in tab-bar and strider after rebase * fix fmt * remove dbgs and and comment in tab-line/main.rs * cargo fmt * Code review suggestions * rebase fix * fix clippy * fix mouse selection for tabs in tab-bar
Diffstat (limited to 'default-plugins')
-rw-r--r--default-plugins/strider/src/main.rs86
-rw-r--r--default-plugins/strider/src/state.rs40
-rw-r--r--default-plugins/tab-bar/src/main.rs49
3 files changed, 133 insertions, 42 deletions
diff --git a/default-plugins/strider/src/main.rs b/default-plugins/strider/src/main.rs
index f0b5cebfc..3bdceefdd 100644
--- a/default-plugins/strider/src/main.rs
+++ b/default-plugins/strider/src/main.rs
@@ -1,23 +1,27 @@
mod state;
use colored::*;
-use state::{FsEntry, State};
-use std::{cmp::min, fs::read_dir, path::Path};
+use state::{refresh_directory, FsEntry, State};
+use std::{cmp::min, time::Instant};
use zellij_tile::prelude::*;
-const ROOT: &str = "/host";
-
register_plugin!(State);
impl ZellijPlugin for State {
fn load(&mut self) {
refresh_directory(self);
- subscribe(&[EventType::KeyPress]);
+ subscribe(&[EventType::KeyPress, EventType::Mouse]);
}
fn update(&mut self, event: Event) {
- if let Event::KeyPress(key) = event {
- match key {
+ let prev_event = if self.ev_history.len() == 2 {
+ self.ev_history.pop_front()
+ } else {
+ None
+ };
+ self.ev_history.push_back((event.clone(), Instant::now()));
+ match event {
+ Event::KeyPress(key) => match key {
Key::Up | Key::Char('k') => {
*self.selected_mut() = self.selected().saturating_sub(1);
}
@@ -26,13 +30,8 @@ impl ZellijPlugin for State {
*self.selected_mut() = min(self.files.len() - 1, next);
}
Key::Right | Key::Char('\n') | Key::Char('l') if !self.files.is_empty() => {
- match self.files[self.selected()].clone() {
- FsEntry::Dir(p, _) => {
- self.path = p;
- refresh_directory(self);
- }
- FsEntry::File(p, _) => open_file(p.strip_prefix(ROOT).unwrap()),
- }
+ self.traverse_dir_or_open_file();
+ self.ev_history.clear();
}
Key::Left | Key::Char('h') => {
if self.path.components().count() > 2 {
@@ -44,25 +43,59 @@ impl ZellijPlugin for State {
refresh_directory(self);
}
}
-
Key::Char('.') => {
self.toggle_hidden_files();
refresh_directory(self);
}
_ => (),
- };
+ },
+ Event::Mouse(mouse_event) => match mouse_event {
+ Mouse::ScrollDown(_) => {
+ let next = self.selected().saturating_add(1);
+ *self.selected_mut() = min(self.files.len().saturating_sub(1), next);
+ }
+ Mouse::ScrollUp(_) => {
+ *self.selected_mut() = self.selected().saturating_sub(1);
+ }
+ Mouse::MouseRelease(Some((line, _))) => {
+ if line < 0 {
+ return;
+ }
+ let mut should_select = true;
+ if let Some((Event::Mouse(Mouse::MouseRelease(Some((prev_line, _)))), t)) =
+ prev_event
+ {
+ if prev_line == line
+ && Instant::now().saturating_duration_since(t).as_millis() < 400
+ {
+ self.traverse_dir_or_open_file();
+ self.ev_history.clear();
+ should_select = false;
+ }
+ }
+ if should_select && self.scroll() + (line as usize) < self.files.len() {
+ *self.selected_mut() = self.scroll() + (line as usize);
+ }
+ }
+ _ => {}
+ },
+ _ => {
+ dbg!("Unknown event {:?}", event);
+ }
}
}
fn render(&mut self, rows: usize, cols: usize) {
for i in 0..rows {
+ // If the key was pressed, set selected so that we can see the cursor
if self.selected() < self.scroll() {
*self.scroll_mut() = self.selected();
}
if self.selected() - self.scroll() + 2 > rows {
*self.scroll_mut() = self.selected() + 2 - rows;
}
+
let i = self.scroll() + i;
if let Some(entry) = self.files.get(i) {
let mut path = entry.as_line(cols).normal();
@@ -82,24 +115,3 @@ impl ZellijPlugin for State {
}
}
}
-
-fn refresh_directory(state: &mut State) {
- state.files = read_dir(Path::new(ROOT).join(&state.path))
- .unwrap()
- .filter_map(|res| {
- res.and_then(|d| {
- if d.metadata()?.is_dir() {
- let children = read_dir(d.path())?.count();
- Ok(FsEntry::Dir(d.path(), children))
- } else {
- let size = d.metadata()?.len();
- Ok(FsEntry::File(d.path(), size))
- }
- })
- .ok()
- .filter(|d| !d.is_hidden_file() || !state.hide_hidden_files)
- })
- .collect();
-
- state.files.sort_unstable();
-}
diff --git a/default-plugins/strider/src/state.rs b/default-plugins/strider/src/state.rs
index 0ae84ab29..7c19cb695 100644
--- a/default-plugins/strider/src/state.rs
+++ b/default-plugins/strider/src/state.rs
@@ -1,12 +1,20 @@
use pretty_bytes::converter as pb;
-use std::{collections::HashMap, path::PathBuf};
+use std::{
+ collections::{HashMap, VecDeque},
+ fs::read_dir,
+ path::{Path, PathBuf},
+ time::Instant,
+};
+use zellij_tile::prelude::*;
+const ROOT: &str = "/host";
#[derive(Default)]
pub struct State {
pub path: PathBuf,
pub files: Vec<FsEntry>,
pub cursor_hist: HashMap<PathBuf, (usize, usize)>,
pub hide_hidden_files: bool,
+ pub ev_history: VecDeque<(Event, Instant)>, // stores last event, can be expanded in future
}
impl State {
@@ -25,6 +33,15 @@ impl State {
pub fn toggle_hidden_files(&mut self) {
self.hide_hidden_files = !self.hide_hidden_files;
}
+ pub fn traverse_dir_or_open_file(&mut self) {
+ match self.files[self.selected()].clone() {
+ FsEntry::Dir(p, _) => {
+ self.path = p;
+ refresh_directory(self);
+ }
+ FsEntry::File(p, _) => open_file(p.strip_prefix(ROOT).unwrap()),
+ }
+ }
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
@@ -61,3 +78,24 @@ impl FsEntry {
self.name().starts_with('.')
}
}
+
+pub(crate) fn refresh_directory(state: &mut State) {
+ state.files = read_dir(Path::new(ROOT).join(&state.path))
+ .unwrap()
+ .filter_map(|res| {
+ res.and_then(|d| {
+ if d.metadata()?.is_dir() {
+ let children = read_dir(d.path())?.count();
+ Ok(FsEntry::Dir(d.path(), children))
+ } else {
+ let size = d.metadata()?.len();
+ Ok(FsEntry::File(d.path(), size))
+ }
+ })
+ .ok()
+ .filter(|d| !d.is_hidden_file() || !state.hide_hidden_files)
+ })
+ .collect();
+
+ state.files.sort_unstable();
+}
diff --git a/default-plugins/tab-bar/src/main.rs b/default-plugins/tab-bar/src/main.rs
index 850781261..c34693523 100644
--- a/default-plugins/tab-bar/src/main.rs
+++ b/default-plugins/tab-bar/src/main.rs
@@ -1,6 +1,9 @@
mod line;
mod tab;
+use std::cmp::{max, min};
+use std::convert::TryInto;
+
use zellij_tile::prelude::*;
use crate::line::tab_line;
@@ -15,7 +18,10 @@ pub struct LinePart {
#[derive(Default)]
struct State {
tabs: Vec<TabInfo>,
+ active_tab_idx: usize,
mode_info: ModeInfo,
+ mouse_click_pos: usize,
+ should_render: bool,
}
static ARROW_SEPARATOR: &str = "";
@@ -25,13 +31,34 @@ register_plugin!(State);
impl ZellijPlugin for State {
fn load(&mut self) {
set_selectable(false);
- subscribe(&[EventType::TabUpdate, EventType::ModeUpdate]);
+ subscribe(&[
+ EventType::TabUpdate,
+ EventType::ModeUpdate,
+ EventType::Mouse,
+ ]);
}
fn update(&mut self, event: Event) {
match event {
Event::ModeUpdate(mode_info) => self.mode_info = mode_info,
- Event::TabUpdate(tabs) => self.tabs = tabs,
+ Event::TabUpdate(tabs) => {
+ // tabs are indexed starting from 1 so we need to add 1
+ self.active_tab_idx = (&tabs).iter().position(|t| t.active).unwrap() + 1;
+ self.tabs = tabs;
+ }
+ Event::Mouse(me) => match me {
+ Mouse::LeftClick(_, col) => {
+ self.mouse_click_pos = col;
+ self.should_render = true;
+ }
+ Mouse::ScrollUp(_) => {
+ switch_tab_to(min(self.active_tab_idx + 1, self.tabs.len()) as u32);
+ }
+ Mouse::ScrollDown(_) => {
+ switch_tab_to(max(self.active_tab_idx.saturating_sub(1), 1) as u32);
+ }
+ _ => {}
+ },
_ => unimplemented!(), // FIXME: This should be unreachable, but this could be cleaner
}
}
@@ -70,8 +97,21 @@ impl ZellijPlugin for State {
self.mode_info.capabilities,
);
let mut s = String::new();
- for bar_part in tab_line {
- s = format!("{}{}", s, bar_part.part);
+ let mut len_cnt = 0;
+ dbg!(&tab_line);
+ for (idx, bar_part) in tab_line.iter().enumerate() {
+ s = format!("{}{}", s, &bar_part.part);
+
+ if self.should_render
+ && self.mouse_click_pos > len_cnt
+ && self.mouse_click_pos <= len_cnt + bar_part.len
+ && idx > 2
+ {
+ // First three elements of tab_line are "Zellij", session name and empty thing, hence the idx > 2 condition.
+ // Tabs are indexed starting from 1, therefore we need subtract 2 below.
+ switch_tab_to(TryInto::<u32>::try_into(idx).unwrap() - 2);
+ }
+ len_cnt += bar_part.len;
}
match self.mode_info.palette.cyan {
PaletteColor::Rgb((r, g, b)) => {
@@ -81,5 +121,6 @@ impl ZellijPlugin for State {
println!("{}\u{1b}[48;5;{}m\u{1b}[0K", s, color);
}
}
+ self.should_render = false;
}
}