feat(tree): move left/right

pull/9/head
wongjiahau 2 years ago
parent c88164f2fa
commit 64059fba47

@ -281,26 +281,36 @@ impl Explorer {
let body_area = area.clip_top(2); let body_area = area.clip_top(2);
let style = editor.theme.get("ui.text"); let style = editor.theme.get("ui.text");
let content = if self.show_help { let content = if self.show_help {
vec![ let instructions = vec![
"? Toggle help", ("?", "Toggle help"),
"a Add file", ("a", "Add file"),
"A Add folder", ("A", "Add folder"),
"r Rename file/folder", ("r", "Rename file/folder"),
"d Delete file", ("d", "Delete file"),
"/ Search", ("/", "Search"),
"f Filter", ("f", "Filter"),
"[ Change root to parent folder", ("[", "Change root to parent folder"),
"] Change root to current folder", ("]", "Change root to current folder"),
"^o Go to previous root", ("^o", "Go to previous root"),
"R Refresh", ("R", "Refresh"),
"+ Increase size", ("+", "Increase size"),
"- Decrease size", ("-", "Decrease size"),
"q Close", ("q", "Close"),
] ]
.into_iter() .into_iter()
.map(|s| s.to_string()) .chain(ui::tree::tree_view_help().into_iter())
.chain(ui::tree::tree_view_help()) .collect::<Vec<_>>();
.collect() let max_left_length = instructions
.iter()
.map(|(key, _)| key.chars().count())
.max()
.unwrap_or(0);
instructions
.into_iter()
.map(|(key, description)| {
format!("{:width$}{}", key, description, width = max_left_length + 1)
})
.collect::<Vec<_>>()
} else { } else {
get_preview(&item.path, body_area.height as usize) get_preview(&item.path, body_area.height as usize)
.unwrap_or_else(|err| vec![err.to_string()]) .unwrap_or_else(|err| vec![err.to_string()])

@ -287,17 +287,22 @@ impl<T> Tree<T> {
pub struct TreeView<T: TreeViewItem> { pub struct TreeView<T: TreeViewItem> {
tree: Tree<T>, tree: Tree<T>,
/// Selected item idex /// Selected item idex
selected: usize, selected: usize,
/// (selected, row) /// (selected, row)
save_view: (usize, usize), save_view: (usize, usize),
/// View row /// For implementing vertical scroll
winline: usize, winline: usize,
previous_area: Rect, previous_area: Rect,
/// For implementing horizontal scoll
column: usize, column: usize,
/// For implementing horizontal scoll
max_len: usize, max_len: usize,
count: usize, count: usize,
tree_symbol_style: String, tree_symbol_style: String,
@ -424,14 +429,14 @@ impl<T: TreeViewItem> TreeView<T> {
self.tree.regenerate_index(); self.tree.regenerate_index();
} }
fn go_to_parent(&mut self) { fn move_to_parent(&mut self) {
if let Some(parent) = self.current_parent() { if let Some(parent) = self.current_parent() {
let index = parent.index; let index = parent.index;
self.set_selected(index) self.set_selected(index)
} }
} }
fn go_to_children(&mut self, filter: &String) -> Result<()> { fn move_to_children(&mut self, filter: &String) -> Result<()> {
let current = self.current_mut(); let current = self.current_mut();
if current.is_opened { if current.is_opened {
self.set_selected(self.selected + 1); self.set_selected(self.selected + 1);
@ -450,38 +455,45 @@ impl<T: TreeViewItem> TreeView<T> {
self.tree.refresh(filter) self.tree.refresh(filter)
} }
fn go_to_first(&mut self) { fn move_to_first(&mut self) {
self.move_up(usize::MAX / 2) self.move_up(usize::MAX / 2)
} }
fn go_to_last(&mut self) { fn move_to_last(&mut self) {
self.move_down(usize::MAX / 2) self.move_down(usize::MAX / 2)
} }
fn set_previous_area(&mut self, area: Rect) { fn set_previous_area(&mut self, area: Rect) {
self.previous_area = area self.previous_area = area
} }
fn move_leftmost(&mut self) {
self.move_left(usize::MAX / 2);
}
fn move_rightmost(&mut self) {
self.move_right(usize::MAX / 2)
}
} }
pub fn tree_view_help() -> Vec<String> { pub fn tree_view_help() -> Vec<(&'static str, &'static str)> {
vec![ vec![
"j/↓ Down", ("j, down", "Down"),
"k/↑ Up", ("k, up", "Up"),
"h/← Go to parent", ("h, left", "Go to parent"),
"l/→ Expand", ("l, right", "Expand"),
"L Scroll right", ("L", "Scroll right"),
"H Scroll left", ("H", "Scroll left"),
"zz Align view center", ("zz", "Align view center"),
"zt Align view top", ("zt", "Align view top"),
"zb Align view bottom", ("zb", "Align view bottom"),
"gg Go to first", ("gg", "Go to first line"),
"ge Go to last", ("ge", "Go to last line"),
"^d Page down", ("gh", "Go to line start"),
"^u Page up", ("gl", "Go to line end"),
("C-d", "Page down"),
("C-u", "Page up"),
] ]
.into_iter()
.map(|s| s.to_string())
.collect()
} }
impl<T: TreeViewItem> TreeView<T> { impl<T: TreeViewItem> TreeView<T> {
@ -577,7 +589,8 @@ impl<T: TreeViewItem> TreeView<T> {
pub fn move_right(&mut self, cols: usize) { pub fn move_right(&mut self, cols: usize) {
let max_scroll = self let max_scroll = self
.max_len .max_len
.saturating_sub(self.previous_area.width as usize); .saturating_sub(self.previous_area.width as usize)
.saturating_add(1);
self.column = max_scroll.min(self.column + cols); self.column = max_scroll.min(self.column + cols);
} }
@ -691,8 +704,6 @@ struct RenderTreeParams<'a, T> {
level: usize, level: usize,
selected: usize, selected: usize,
filter: &'a str, filter: &'a str,
column_start: usize,
max_width: usize,
} }
fn render_tree<T: TreeViewItem>( fn render_tree<T: TreeViewItem>(
@ -702,8 +713,6 @@ fn render_tree<T: TreeViewItem>(
level, level,
selected, selected,
filter, filter,
column_start,
max_width,
}: RenderTreeParams<T>, }: RenderTreeParams<T>,
) -> Vec<RenderedLine> { ) -> Vec<RenderedLine> {
let indent = if level > 0 { let indent = if level > 0 {
@ -720,15 +729,12 @@ fn render_tree<T: TreeViewItem>(
} else { } else {
"".to_string() "".to_string()
}; };
let indent = indent[column_start..].to_string();
let indent_len = indent.len();
let name = tree.item.name(); let name = tree.item.name();
println!("{max_width}");
let head = RenderedLine { let head = RenderedLine {
indent, indent,
selected: selected == tree.index, selected: selected == tree.index,
descendant_selected: selected != tree.index && tree.get(selected).is_some(), descendant_selected: selected != tree.index && tree.get(selected).is_some(),
name: name[..(max_width - indent_len).clamp(0, name.len())].to_string(), name,
}; };
let prefix = format!("{}{}", prefix, if level == 0 { "" } else { " " }); let prefix = format!("{}{}", prefix, if level == 0 { "" } else { " " });
vec![head] vec![head]
@ -740,8 +746,6 @@ fn render_tree<T: TreeViewItem>(
level: level + 1, level: level + 1,
selected, selected,
filter, filter,
column_start,
max_width,
}) })
})) }))
.collect() .collect()
@ -749,7 +753,6 @@ fn render_tree<T: TreeViewItem>(
impl<T: TreeViewItem + Clone> TreeView<T> { impl<T: TreeViewItem + Clone> TreeView<T> {
pub fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context, filter: &String) { pub fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context, filter: &String) {
self.max_len = 0;
let style = cx.editor.theme.get(&self.tree_symbol_style); let style = cx.editor.theme.get(&self.tree_symbol_style);
let ancestor_style = cx.editor.theme.get("ui.text.focus"); let ancestor_style = cx.editor.theme.get("ui.text.focus");
@ -820,14 +823,49 @@ impl<T: TreeViewItem + Clone> TreeView<T> {
level: 0, level: 0,
selected: self.selected, selected: self.selected,
filter, filter,
column_start: self.column,
max_width: self.previous_area.width as usize,
}; };
let lines = render_tree(params); let lines = render_tree(params);
self.max_len = lines
.iter()
.map(|line| {
line.indent
.chars()
.count()
.saturating_add(line.name.chars().count())
})
.max()
.unwrap_or(0);
let max_width = self.previous_area.width as usize;
lines lines
.into_iter() .into_iter()
// Horizontal scroll
.map(|line| {
let skip = self.column;
let indent_len = line.indent.chars().count();
RenderedLine {
indent: if line.indent.is_empty() {
"".to_string()
} else {
line.indent
.chars()
.skip(skip)
.take(max_width)
.collect::<String>()
},
name: line
.name
.chars()
.skip(skip.saturating_sub(indent_len))
.take((max_width.saturating_sub(indent_len)).clamp(0, line.name.len()))
.collect::<String>(),
..line
}
})
// Vertical scroll
.skip(skip) .skip(skip)
.take(area.height as usize) .take(area.height as usize)
.collect() .collect()
@ -862,8 +900,8 @@ impl<T: TreeViewItem + Clone> TreeView<T> {
_ => {} _ => {}
})); }));
} }
key!('h') | key!(Left) => self.go_to_parent(), key!('h') | key!(Left) => self.move_to_parent(),
key!('l') | key!(Right) => match self.go_to_children(filter) { key!('l') | key!(Right) => match self.move_to_children(filter) {
Ok(_) => {} Ok(_) => {}
Err(err) => cx.editor.set_error(err.to_string()), Err(err) => cx.editor.set_error(err.to_string()),
}, },
@ -874,8 +912,10 @@ impl<T: TreeViewItem + Clone> TreeView<T> {
ctrl!('u') => self.move_up_half_page(), ctrl!('u') => self.move_up_half_page(),
key!('g') => { key!('g') => {
self.on_next_key = Some(Box::new(|_, tree, event| match event { self.on_next_key = Some(Box::new(|_, tree, event| match event {
key!('g') => tree.go_to_first(), key!('g') => tree.move_to_first(),
key!('e') => tree.go_to_last(), key!('e') => tree.move_to_last(),
key!('h') => tree.move_leftmost(),
key!('l') => tree.move_rightmost(),
_ => {} _ => {}
})); }));
} }
@ -1141,10 +1181,10 @@ mod test_tree_view {
} }
#[test] #[test]
fn test_go_to_first_last() { fn test_move_to_first_last() {
let mut view = dummy_tree_view(); let mut view = dummy_tree_view();
view.go_to_last(); view.move_to_last();
assert_eq!( assert_eq!(
render(&mut view), render(&mut view),
" "
@ -1157,7 +1197,7 @@ mod test_tree_view {
.trim() .trim()
); );
view.go_to_first(); view.move_to_first();
assert_eq!( assert_eq!(
render(&mut view), render(&mut view),
" "
@ -1255,11 +1295,11 @@ mod test_tree_view {
} }
#[test] #[test]
fn go_to_children_parent() { fn move_to_children_parent() {
let filter = "".to_string(); let filter = "".to_string();
let mut view = dummy_tree_view(); let mut view = dummy_tree_view();
view.move_down(1); view.move_down(1);
view.go_to_children(&filter).unwrap(); view.move_to_children(&filter).unwrap();
assert_eq!( assert_eq!(
render(&mut view), render(&mut view),
" "
@ -1285,7 +1325,7 @@ mod test_tree_view {
.trim() .trim()
); );
view.go_to_parent(); view.move_to_parent();
assert_eq!( assert_eq!(
render(&mut view), render(&mut view),
" "
@ -1298,8 +1338,8 @@ mod test_tree_view {
.trim() .trim()
); );
view.go_to_last(); view.move_to_last();
view.go_to_parent(); view.move_to_parent();
assert_eq!( assert_eq!(
render(&mut view), render(&mut view),
" "
@ -1334,6 +1374,163 @@ mod test_tree_view {
assert_eq!( assert_eq!(
render(&mut view), render(&mut view),
" "
(ho_lives_in_a_pineap)
gary_the_snail
karen
king_neptune
krabby_patty
"
.trim()
);
view.move_right(1);
assert_eq!(
render(&mut view),
"
(o_lives_in_a_pineapp)
gary_the_snail
karen
king_neptune
krabby_patty
"
.trim()
);
view.move_right(1);
assert_eq!(
render(&mut view),
"
(_lives_in_a_pineappl)
ary_the_snail
aren
ing_neptune
rabby_patty
"
.trim()
);
view.move_left(1);
assert_eq!(
render(&mut view),
"
(o_lives_in_a_pineapp)
gary_the_snail
karen
king_neptune
krabby_patty
"
.trim()
);
view.move_leftmost();
assert_eq!(
render(&mut view),
"
(who_lives_in_a_pinea)
gary_the_snail
karen
king_neptune
krabby_patty
"
.trim()
);
view.move_left(1);
assert_eq!(
render(&mut view),
"
(who_lives_in_a_pinea)
gary_the_snail
karen
king_neptune
krabby_patty
"
.trim()
);
view.move_rightmost();
assert_eq!(render(&mut view), "(apple_under_the_sea)\n\n\n\n");
}
#[test]
fn test_move_to_parent_child() {
let mut view = dummy_tree_view();
let filter = "".to_string();
view.move_to_children(&filter).unwrap();
assert_eq!(
render(&mut view),
"
[who_lives_in_a_pineapple_under_the_sea]
(gary_the_snail)
karen
king_neptune
krabby_patty
"
.trim()
);
view.move_to_children(&filter).unwrap();
assert_eq!(
render(&mut view),
"
[who_lives_in_a_pineapple_under_the_sea]
[gary_the_snail]
(e_snail)
gary_th
karen
"
.trim()
);
view.move_down(1);
assert_eq!(
render(&mut view),
"
[who_lives_in_a_pineapple_under_the_sea]
[gary_the_snail]
e_snail
(gary_th)
karen
"
.trim()
);
view.move_to_parent();
assert_eq!(
render(&mut view),
"
[who_lives_in_a_pineapple_under_the_sea]
(gary_the_snail)
e_snail
gary_th
karen
"
.trim()
);
view.move_to_parent();
assert_eq!(
render(&mut view),
"
(who_lives_in_a_pineapple_under_the_sea)
gary_the_snail
e_snail
gary_th
karen
"
.trim()
);
view.move_to_parent();
assert_eq!(
render(&mut view),
"
(who_lives_in_a_pineapple_under_the_sea)
gary_the_snail
e_snail
gary_th
karen
" "
.trim() .trim()
) )

Loading…
Cancel
Save