mirror of
https://github.com/sayanarijit/xplr.git
synced 2024-09-11 19:07:47 +03:00
Adds Vim-Like Scrolling to XPLR (#704)
- Added through a setting `vimlike_scrolling` which is turned off by default - A hard-coded _(for now)_ cushion of `5` lines that allows for previewing the next lines while scrolling - A separate struct `ScrollState` with getters and setters for the `current_focus` field to disallow setting the field without updating the `last_focus` field
This commit is contained in:
commit
c1bb251fef
@ -121,7 +121,7 @@ fn draw_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function("draw on terminal", |b| {
|
c.bench_function("draw on terminal", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
terminal.draw(|f| ui::draw(f, &app, &lua)).unwrap();
|
terminal.draw(|f| ui::draw(f, &mut app, &lua)).unwrap();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -42,6 +42,12 @@ Set it to `true` if you want to hide all remaps in the help menu.
|
|||||||
|
|
||||||
Type: boolean
|
Type: boolean
|
||||||
|
|
||||||
|
#### xplr.config.general.vimlike_scrolling
|
||||||
|
|
||||||
|
Set it to `true` if you want vim-like scrolling.
|
||||||
|
|
||||||
|
Type: boolean
|
||||||
|
|
||||||
#### xplr.config.general.enforce_bounded_index_navigation
|
#### xplr.config.general.enforce_bounded_index_navigation
|
||||||
|
|
||||||
Set it to `true` if you want the cursor to stay in the same position when
|
Set it to `true` if you want the cursor to stay in the same position when
|
||||||
|
46
src/app.rs
46
src/app.rs
@ -752,7 +752,10 @@ impl App {
|
|||||||
self.explorer_config.clone(),
|
self.explorer_config.clone(),
|
||||||
self.pwd.clone().into(),
|
self.pwd.clone().into(),
|
||||||
focus.as_ref().map(PathBuf::from),
|
focus.as_ref().map(PathBuf::from),
|
||||||
self.directory_buffer.as_ref().map(|d| d.focus).unwrap_or(0),
|
self.directory_buffer
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| d.scroll_state.get_focus())
|
||||||
|
.unwrap_or(0),
|
||||||
) {
|
) {
|
||||||
Ok(dir) => self.set_directory(dir),
|
Ok(dir) => self.set_directory(dir),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -791,7 +794,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dir.focus = 0;
|
dir.scroll_state.set_focus(0);
|
||||||
|
|
||||||
if save_history {
|
if save_history {
|
||||||
if let Some(n) = self.focused_node() {
|
if let Some(n) = self.focused_node() {
|
||||||
@ -809,7 +812,7 @@ impl App {
|
|||||||
history = history.push(n.absolute_path.clone());
|
history = history.push(n.absolute_path.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
dir.focus = dir.total.saturating_sub(1);
|
dir.scroll_state.set_focus(dir.total.saturating_sub(1));
|
||||||
|
|
||||||
if let Some(n) = dir.focused_node() {
|
if let Some(n) = dir.focused_node() {
|
||||||
self.history = history.push(n.absolute_path.clone());
|
self.history = history.push(n.absolute_path.clone());
|
||||||
@ -822,14 +825,13 @@ impl App {
|
|||||||
let bounded = self.config.general.enforce_bounded_index_navigation;
|
let bounded = self.config.general.enforce_bounded_index_navigation;
|
||||||
|
|
||||||
if let Some(dir) = self.directory_buffer_mut() {
|
if let Some(dir) = self.directory_buffer_mut() {
|
||||||
dir.focus = if dir.focus == 0 {
|
if dir.scroll_state.get_focus() == 0 {
|
||||||
if bounded {
|
if !bounded {
|
||||||
dir.focus
|
dir.scroll_state.set_focus(dir.total.saturating_sub(1));
|
||||||
} else {
|
|
||||||
dir.total.saturating_sub(1)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dir.focus.saturating_sub(1)
|
dir.scroll_state
|
||||||
|
.set_focus(dir.scroll_state.get_focus().saturating_sub(1));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
Ok(self)
|
Ok(self)
|
||||||
@ -882,7 +884,8 @@ impl App {
|
|||||||
history = history.push(n.absolute_path.clone());
|
history = history.push(n.absolute_path.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
dir.focus = dir.focus.saturating_sub(index);
|
dir.scroll_state
|
||||||
|
.set_focus(dir.scroll_state.get_focus().saturating_sub(index));
|
||||||
if let Some(n) = self.focused_node() {
|
if let Some(n) = self.focused_node() {
|
||||||
self.history = history.push(n.absolute_path.clone());
|
self.history = history.push(n.absolute_path.clone());
|
||||||
}
|
}
|
||||||
@ -907,14 +910,12 @@ impl App {
|
|||||||
let bounded = self.config.general.enforce_bounded_index_navigation;
|
let bounded = self.config.general.enforce_bounded_index_navigation;
|
||||||
|
|
||||||
if let Some(dir) = self.directory_buffer_mut() {
|
if let Some(dir) = self.directory_buffer_mut() {
|
||||||
dir.focus = if (dir.focus + 1) == dir.total {
|
if (dir.scroll_state.get_focus() + 1) == dir.total {
|
||||||
if bounded {
|
if !bounded {
|
||||||
dir.focus
|
dir.scroll_state.set_focus(0);
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dir.focus + 1
|
dir.scroll_state.set_focus(dir.scroll_state.get_focus() + 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(self)
|
Ok(self)
|
||||||
@ -967,10 +968,12 @@ impl App {
|
|||||||
history = history.push(n.absolute_path.clone());
|
history = history.push(n.absolute_path.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
dir.focus = dir
|
dir.scroll_state.set_focus(
|
||||||
.focus
|
dir.scroll_state
|
||||||
|
.get_focus()
|
||||||
.saturating_add(index)
|
.saturating_add(index)
|
||||||
.min(dir.total.saturating_sub(1));
|
.min(dir.total.saturating_sub(1)),
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(n) = self.focused_node() {
|
if let Some(n) = self.focused_node() {
|
||||||
self.history = history.push(n.absolute_path.clone());
|
self.history = history.push(n.absolute_path.clone());
|
||||||
@ -1238,7 +1241,8 @@ impl App {
|
|||||||
fn focus_by_index(mut self, index: usize) -> Result<Self> {
|
fn focus_by_index(mut self, index: usize) -> Result<Self> {
|
||||||
let history = self.history.clone();
|
let history = self.history.clone();
|
||||||
if let Some(dir) = self.directory_buffer_mut() {
|
if let Some(dir) = self.directory_buffer_mut() {
|
||||||
dir.focus = index.min(dir.total.saturating_sub(1));
|
dir.scroll_state
|
||||||
|
.set_focus(index.min(dir.total.saturating_sub(1)));
|
||||||
if let Some(n) = self.focused_node() {
|
if let Some(n) = self.focused_node() {
|
||||||
self.history = history.push(n.absolute_path.clone());
|
self.history = history.push(n.absolute_path.clone());
|
||||||
}
|
}
|
||||||
@ -1275,7 +1279,7 @@ impl App {
|
|||||||
history = history.push(n.absolute_path.clone());
|
history = history.push(n.absolute_path.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dir_buf.focus = focus;
|
dir_buf.scroll_state.set_focus(focus);
|
||||||
if save_history {
|
if save_history {
|
||||||
if let Some(n) = dir_buf.focused_node() {
|
if let Some(n) = dir_buf.focused_node() {
|
||||||
self.history = history.push(n.absolute_path.clone());
|
self.history = history.push(n.absolute_path.clone());
|
||||||
|
@ -353,6 +353,9 @@ pub struct GeneralConfig {
|
|||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub global_key_bindings: KeyBindings,
|
pub global_key_bindings: KeyBindings,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub vimlike_scrolling: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
@ -2,31 +2,123 @@ use crate::node::Node;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct ScrollState {
|
||||||
|
current_focus: usize,
|
||||||
|
pub last_focus: Option<usize>,
|
||||||
|
pub skipped_rows: usize,
|
||||||
|
/* The number of visible next lines when scrolling towards either ends of the view port */
|
||||||
|
pub initial_preview_cushion: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScrollState {
|
||||||
|
pub fn set_focus(&mut self, current_focus: usize) {
|
||||||
|
self.last_focus = Some(self.current_focus);
|
||||||
|
self.current_focus = current_focus;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_focus(&self) -> usize {
|
||||||
|
self.current_focus
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calc_skipped_rows(
|
||||||
|
&self,
|
||||||
|
height: usize,
|
||||||
|
total: usize,
|
||||||
|
vimlike_scrolling: bool,
|
||||||
|
) -> usize {
|
||||||
|
let preview_cushion = if height >= self.initial_preview_cushion * 3 {
|
||||||
|
self.initial_preview_cushion
|
||||||
|
} else if height >= 9 {
|
||||||
|
3
|
||||||
|
} else if height >= 3 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_focus = self.current_focus;
|
||||||
|
let last_focus = self.last_focus;
|
||||||
|
let first_visible_row = self.skipped_rows;
|
||||||
|
|
||||||
|
// Calculate the cushion rows at the start and end of the view port
|
||||||
|
let start_cushion_row = first_visible_row + preview_cushion;
|
||||||
|
let end_cushion_row = (first_visible_row + height)
|
||||||
|
.saturating_sub(preview_cushion + 1)
|
||||||
|
.min(total.saturating_sub(preview_cushion + 1));
|
||||||
|
|
||||||
|
if !vimlike_scrolling {
|
||||||
|
height * (self.current_focus / height.max(1))
|
||||||
|
} else if last_focus.is_none() {
|
||||||
|
// Just entered the directory
|
||||||
|
0
|
||||||
|
} else if current_focus == 0 {
|
||||||
|
// When focus goes to first node
|
||||||
|
0
|
||||||
|
} else if current_focus == total.saturating_sub(1) {
|
||||||
|
// When focus goes to last node
|
||||||
|
total.saturating_sub(height)
|
||||||
|
} else if (start_cushion_row..=end_cushion_row).contains(¤t_focus) {
|
||||||
|
// If within cushioned area; do nothing
|
||||||
|
first_visible_row
|
||||||
|
} else if current_focus > last_focus.unwrap() {
|
||||||
|
// When scrolling down the cushioned area
|
||||||
|
if current_focus > total.saturating_sub(preview_cushion + 1) {
|
||||||
|
// When focusing the last nodes; always view the full last page
|
||||||
|
total.saturating_sub(height)
|
||||||
|
} else {
|
||||||
|
// When scrolling down the cushioned area without reaching the last nodes
|
||||||
|
current_focus.saturating_sub(height.saturating_sub(preview_cushion + 1))
|
||||||
|
}
|
||||||
|
} else if current_focus < last_focus.unwrap() {
|
||||||
|
// When scrolling up the cushioned area
|
||||||
|
if current_focus < preview_cushion {
|
||||||
|
// When focusing the first nodes; always view the full first page
|
||||||
|
0
|
||||||
|
} else if current_focus > end_cushion_row {
|
||||||
|
// When scrolling up from the last rows; do nothing
|
||||||
|
first_visible_row
|
||||||
|
} else {
|
||||||
|
// When scrolling up the cushioned area without reaching the first nodes
|
||||||
|
current_focus.saturating_sub(preview_cushion)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If nothing matches; do nothing
|
||||||
|
first_visible_row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct DirectoryBuffer {
|
pub struct DirectoryBuffer {
|
||||||
pub parent: String,
|
pub parent: String,
|
||||||
pub nodes: Vec<Node>,
|
pub nodes: Vec<Node>,
|
||||||
pub total: usize,
|
pub total: usize,
|
||||||
pub focus: usize,
|
pub scroll_state: ScrollState,
|
||||||
|
|
||||||
#[serde(skip, default = "now")]
|
#[serde(skip, default = "now")]
|
||||||
pub explored_at: OffsetDateTime,
|
pub explored_at: OffsetDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectoryBuffer {
|
impl DirectoryBuffer {
|
||||||
pub fn new(parent: String, nodes: Vec<Node>, focus: usize) -> Self {
|
pub fn new(parent: String, nodes: Vec<Node>, current_focus: usize) -> Self {
|
||||||
let total = nodes.len();
|
let total = nodes.len();
|
||||||
Self {
|
Self {
|
||||||
parent,
|
parent,
|
||||||
nodes,
|
nodes,
|
||||||
total,
|
total,
|
||||||
focus,
|
scroll_state: ScrollState {
|
||||||
|
current_focus,
|
||||||
|
last_focus: None,
|
||||||
|
skipped_rows: 0,
|
||||||
|
initial_preview_cushion: 5,
|
||||||
|
},
|
||||||
explored_at: now(),
|
explored_at: now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focused_node(&self) -> Option<&Node> {
|
pub fn focused_node(&self) -> Option<&Node> {
|
||||||
self.nodes.get(self.focus)
|
self.nodes.get(self.scroll_state.current_focus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,3 +127,112 @@ fn now() -> OffsetDateTime {
|
|||||||
.ok()
|
.ok()
|
||||||
.unwrap_or_else(OffsetDateTime::now_utc)
|
.unwrap_or_else(OffsetDateTime::now_utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calc_skipped_rows_non_vimlike_scrolling() {
|
||||||
|
let state = ScrollState {
|
||||||
|
current_focus: 10,
|
||||||
|
last_focus: Some(8),
|
||||||
|
skipped_rows: 0,
|
||||||
|
initial_preview_cushion: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = 5;
|
||||||
|
let total = 20;
|
||||||
|
let vimlike_scrolling = false;
|
||||||
|
|
||||||
|
let result = state.calc_skipped_rows(height, total, vimlike_scrolling);
|
||||||
|
assert_eq!(result, height * (state.current_focus / height.max(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calc_skipped_rows_entered_directory() {
|
||||||
|
let state = ScrollState {
|
||||||
|
current_focus: 10,
|
||||||
|
last_focus: None,
|
||||||
|
skipped_rows: 0,
|
||||||
|
initial_preview_cushion: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = 5;
|
||||||
|
let total = 20;
|
||||||
|
let vimlike_scrolling = true;
|
||||||
|
|
||||||
|
let result = state.calc_skipped_rows(height, total, vimlike_scrolling);
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calc_skipped_rows_top_of_directory() {
|
||||||
|
let state = ScrollState {
|
||||||
|
current_focus: 0,
|
||||||
|
last_focus: Some(8),
|
||||||
|
skipped_rows: 5,
|
||||||
|
initial_preview_cushion: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = 5;
|
||||||
|
let total = 20;
|
||||||
|
let vimlike_scrolling = true;
|
||||||
|
|
||||||
|
let result = state.calc_skipped_rows(height, total, vimlike_scrolling);
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calc_skipped_rows_bottom_of_directory() {
|
||||||
|
let state = ScrollState {
|
||||||
|
current_focus: 19,
|
||||||
|
last_focus: Some(18),
|
||||||
|
skipped_rows: 15,
|
||||||
|
initial_preview_cushion: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = 5;
|
||||||
|
let total = 20;
|
||||||
|
let vimlike_scrolling = true;
|
||||||
|
|
||||||
|
let result = state.calc_skipped_rows(height, total, vimlike_scrolling);
|
||||||
|
assert_eq!(result, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calc_skipped_rows_scrolling_down() {
|
||||||
|
let state = ScrollState {
|
||||||
|
current_focus: 12,
|
||||||
|
last_focus: Some(10),
|
||||||
|
skipped_rows: 10,
|
||||||
|
initial_preview_cushion: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = 5;
|
||||||
|
let total = 20;
|
||||||
|
let vimlike_scrolling = true;
|
||||||
|
|
||||||
|
let result = state.calc_skipped_rows(height, total, vimlike_scrolling);
|
||||||
|
assert_eq!(result, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calc_skipped_rows_scrolling_up() {
|
||||||
|
let state = ScrollState {
|
||||||
|
current_focus: 8,
|
||||||
|
last_focus: Some(10),
|
||||||
|
skipped_rows: 10,
|
||||||
|
initial_preview_cushion: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = 5;
|
||||||
|
let total = 20;
|
||||||
|
let vimlike_scrolling = true;
|
||||||
|
|
||||||
|
let result = state.calc_skipped_rows(height, total, vimlike_scrolling);
|
||||||
|
assert_eq!(result, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more tests for other scenarios...
|
||||||
|
}
|
||||||
|
@ -91,6 +91,11 @@ xplr.config.general.enable_recover_mode = false
|
|||||||
-- Type: boolean
|
-- Type: boolean
|
||||||
xplr.config.general.hide_remaps_in_help_menu = false
|
xplr.config.general.hide_remaps_in_help_menu = false
|
||||||
|
|
||||||
|
-- Set it to `true` if you want vim-like scrolling.
|
||||||
|
--
|
||||||
|
-- Type: boolean
|
||||||
|
xplr.config.general.vimlike_scrolling = false
|
||||||
|
|
||||||
-- Set it to `true` if you want the cursor to stay in the same position when
|
-- Set it to `true` if you want the cursor to stay in the same position when
|
||||||
-- the focus is on the first path and you navigate to the previous path
|
-- the focus is on the first path and you navigate to the previous path
|
||||||
-- (by pressing `up`/`k`), or when the focus is on the last path and you
|
-- (by pressing `up`/`k`), or when the focus is on the last path and you
|
||||||
|
@ -89,7 +89,7 @@ fn call(
|
|||||||
let focus_index = app
|
let focus_index = app
|
||||||
.directory_buffer
|
.directory_buffer
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|d| d.focus)
|
.map(|d| d.scroll_state.get_focus())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
@ -279,7 +279,10 @@ impl Runner {
|
|||||||
app.explorer_config.clone(),
|
app.explorer_config.clone(),
|
||||||
app.pwd.clone().into(),
|
app.pwd.clone().into(),
|
||||||
self.focused_path,
|
self.focused_path,
|
||||||
app.directory_buffer.as_ref().map(|d| d.focus).unwrap_or(0),
|
app.directory_buffer
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| d.scroll_state.get_focus())
|
||||||
|
.unwrap_or(0),
|
||||||
tx_msg_in.clone(),
|
tx_msg_in.clone(),
|
||||||
);
|
);
|
||||||
tx_pwd_watcher.send(app.pwd.clone())?;
|
tx_pwd_watcher.send(app.pwd.clone())?;
|
||||||
@ -430,7 +433,7 @@ impl Runner {
|
|||||||
.map(|n| n.relative_path.clone().into()),
|
.map(|n| n.relative_path.clone().into()),
|
||||||
app.directory_buffer
|
app.directory_buffer
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|d| d.focus)
|
.map(|d| d.scroll_state.get_focus())
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
tx_msg_in.clone(),
|
tx_msg_in.clone(),
|
||||||
);
|
);
|
||||||
@ -445,7 +448,7 @@ impl Runner {
|
|||||||
.map(|n| n.relative_path.clone().into()),
|
.map(|n| n.relative_path.clone().into()),
|
||||||
app.directory_buffer
|
app.directory_buffer
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|d| d.focus)
|
.map(|d| d.scroll_state.get_focus())
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
tx_msg_in.clone(),
|
tx_msg_in.clone(),
|
||||||
);
|
);
|
||||||
@ -493,7 +496,7 @@ impl Runner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
terminal.draw(|f| ui::draw(f, &app, &lua))?;
|
terminal.draw(|f| ui::draw(f, &mut app, &lua))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnableMouse => {
|
EnableMouse => {
|
||||||
|
31
src/ui.rs
31
src/ui.rs
@ -722,7 +722,7 @@ fn draw_table(
|
|||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
screen_size: TuiRect,
|
screen_size: TuiRect,
|
||||||
layout_size: TuiRect,
|
layout_size: TuiRect,
|
||||||
app: &app::App,
|
app: &mut app::App,
|
||||||
lua: &Lua,
|
lua: &Lua,
|
||||||
) {
|
) {
|
||||||
let panel_config = &app.config.general.panel_ui;
|
let panel_config = &app.config.general.panel_ui;
|
||||||
@ -735,15 +735,20 @@ fn draw_table(
|
|||||||
|
|
||||||
let rows = app
|
let rows = app
|
||||||
.directory_buffer
|
.directory_buffer
|
||||||
.as_ref()
|
.as_mut()
|
||||||
.map(|dir| {
|
.map(|dir| {
|
||||||
|
dir.scroll_state.skipped_rows = dir.scroll_state.calc_skipped_rows(
|
||||||
|
height,
|
||||||
|
dir.total,
|
||||||
|
app.config.general.vimlike_scrolling,
|
||||||
|
);
|
||||||
dir.nodes
|
dir.nodes
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.skip(height * (dir.focus / height.max(1)))
|
.skip(dir.scroll_state.skipped_rows)
|
||||||
.take(height)
|
.take(height)
|
||||||
.map(|(index, node)| {
|
.map(|(index, node)| {
|
||||||
let is_focused = dir.focus == index;
|
let is_focused = dir.scroll_state.get_focus() == index;
|
||||||
|
|
||||||
let is_selected = app
|
let is_selected = app
|
||||||
.selection
|
.selection
|
||||||
@ -772,9 +777,13 @@ fn draw_table(
|
|||||||
let node_type = app_config.node_types.get(node);
|
let node_type = app_config.node_types.get(node);
|
||||||
|
|
||||||
let (relative_index, is_before_focus, is_after_focus) =
|
let (relative_index, is_before_focus, is_after_focus) =
|
||||||
match dir.focus.cmp(&index) {
|
match dir.scroll_state.get_focus().cmp(&index) {
|
||||||
Ordering::Greater => (dir.focus - index, true, false),
|
Ordering::Greater => {
|
||||||
Ordering::Less => (index - dir.focus, false, true),
|
(dir.scroll_state.get_focus() - index, true, false)
|
||||||
|
}
|
||||||
|
Ordering::Less => {
|
||||||
|
(index - dir.scroll_state.get_focus(), false, true)
|
||||||
|
}
|
||||||
Ordering::Equal => (0, false, false),
|
Ordering::Equal => (0, false, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1284,7 +1293,7 @@ pub fn draw_dynamic(
|
|||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
screen_size: TuiRect,
|
screen_size: TuiRect,
|
||||||
layout_size: TuiRect,
|
layout_size: TuiRect,
|
||||||
app: &app::App,
|
app: &mut app::App,
|
||||||
func: &str,
|
func: &str,
|
||||||
lua: &Lua,
|
lua: &Lua,
|
||||||
) {
|
) {
|
||||||
@ -1308,7 +1317,7 @@ pub fn draw_static(
|
|||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
screen_size: TuiRect,
|
screen_size: TuiRect,
|
||||||
layout_size: TuiRect,
|
layout_size: TuiRect,
|
||||||
app: &app::App,
|
app: &mut app::App,
|
||||||
panel: CustomPanel,
|
panel: CustomPanel,
|
||||||
_lua: &Lua,
|
_lua: &Lua,
|
||||||
) {
|
) {
|
||||||
@ -1402,7 +1411,7 @@ pub fn draw_layout(
|
|||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
screen_size: TuiRect,
|
screen_size: TuiRect,
|
||||||
layout_size: TuiRect,
|
layout_size: TuiRect,
|
||||||
app: &app::App,
|
app: &mut app::App,
|
||||||
lua: &Lua,
|
lua: &Lua,
|
||||||
) {
|
) {
|
||||||
match layout {
|
match layout {
|
||||||
@ -1493,7 +1502,7 @@ pub fn draw_layout(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(f: &mut Frame, app: &app::App, lua: &Lua) {
|
pub fn draw(f: &mut Frame, app: &mut app::App, lua: &Lua) {
|
||||||
let screen_size = f.size();
|
let screen_size = f.size();
|
||||||
let layout = app.mode.layout.as_ref().unwrap_or(&app.layout).to_owned();
|
let layout = app.mode.layout.as_ref().unwrap_or(&app.layout).to_owned();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user