[tui] Add Explorer widget for left panel.

This commit is contained in:
Shaun Reed 2026-01-17 15:07:05 -05:00
parent fac6ea6bcd
commit b65565adfa
5 changed files with 82 additions and 11 deletions

13
Cargo.lock generated
View File

@ -230,6 +230,8 @@ dependencies = [
"ratatui",
"structopt",
"syntect",
"tui-tree-widget",
"uuid",
]
[[package]]
@ -1908,6 +1910,17 @@ dependencies = [
"time-core",
]
[[package]]
name = "tui-tree-widget"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deca119555009eee2e0cfb9c020f39f632444dc4579918d5fc009d51d75dff92"
dependencies = [
"ratatui-core",
"ratatui-widgets",
"unicode-width 0.2.2",
]
[[package]]
name = "typenum"
version = "1.19.0"

View File

@ -13,6 +13,8 @@ syntect = "5.2.0"
structopt = "0.3.26"
ratatui = "0.30.0"
anyhow = "1.0.100"
tui-tree-widget = "0.24.0"
uuid = { version = "1.19.0", features = ["v4"] }
[build-dependencies]
# The link_qt_object_files feature is required for statically linking Qt 6.

View File

@ -1,4 +1,5 @@
pub mod app;
mod explorer;
use anyhow::{Context, Result};

View File

@ -8,14 +8,18 @@ use ratatui::widgets::{Block, Borders, Padding, Paragraph, Tabs, Wrap};
use ratatui::{DefaultTerminal, symbols};
use std::time::Duration;
use crate::tui::explorer::Explorer;
#[derive(Debug, Clone, Copy)]
pub struct App<'a> {
root_path: &'a std::path::Path,
explorer: Explorer<'a>,
}
impl<'a> App<'a> {
pub(crate) fn new(root_path: &'a std::path::Path) -> Self {
Self { root_path }
Self {
explorer: Explorer::new(root_path),
}
}
pub fn run(mut self, mut terminal: DefaultTerminal) -> anyhow::Result<()> {
@ -84,7 +88,7 @@ impl<'a> App<'a> {
fn draw_terminal(self, area: Rect, buf: &mut Buffer) {
// TODO: Title should be detected shell name
// TODO: Contents should be shell output
Paragraph::new("Terminal placeholder")
Paragraph::new("shaun@pc:~/Code/clide$ ")
.style(Style::default())
.block(
Block::default()
@ -95,13 +99,6 @@ impl<'a> App<'a> {
.wrap(Wrap { trim: false })
.render(area, buf);
}
fn draw_file_explorer(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("File explorer placeholder")
.style(Style::default())
.block(Block::default().borders(Borders::ALL))
.render(area, buf);
}
}
// TODO: Separate complex components into their own widgets.
@ -138,7 +135,7 @@ impl<'a> Widget for App<'a> {
self.draw_status(vertical[0], buf);
self.draw_terminal(vertical[2], buf);
self.draw_file_explorer(horizontal[0], buf);
self.explorer.draw(horizontal[0], buf);
self.draw_tabs(editor_layout[0], buf);
self.draw_editor(editor_layout[1], buf);

58
src/tui/explorer.rs Normal file
View File

@ -0,0 +1,58 @@
use std::fs;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::prelude::Style;
use ratatui::widgets::{Block, Borders, Widget};
use tui_tree_widget::{Tree, TreeItem};
use uuid::Uuid;
#[derive(Clone, Copy, Debug)]
pub struct Explorer<'a> {
root_path: &'a std::path::Path,
}
impl<'a> Explorer<'a> {
pub fn new(path: &'a std::path::Path) -> Self {
Explorer { root_path: path }
}
pub fn draw(self, area: Rect, buf: &mut Buffer) {
let tree_item = Self::build_tree_from_path(self.root_path.to_path_buf());
Tree::new(&tree_item.children())
.expect("Failed to build tree.")
.style(Style::default())
.block(Block::default().borders(Borders::ALL))
.render(area, buf);
}
fn build_tree_from_path(path: std::path::PathBuf) -> TreeItem<'static, String> {
let mut children = vec![];
if let Ok(entries) = fs::read_dir(&path) {
let mut paths = entries
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, std::io::Error>>()
.expect("");
paths.sort();
for path in paths {
if path.is_dir() {
children.push(Self::build_tree_from_path(path));
} else {
children.push(TreeItem::new_leaf(
Uuid::new_v4().to_string(),
path.file_name().unwrap().to_string_lossy().to_string(),
));
}
}
}
TreeItem::new(
Uuid::new_v4().to_string(),
path.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string(),
children,
)
.expect("Failed to build tree from path.")
}
}