diff --git a/src/main.rs b/src/main.rs index 2d53646..b3be5c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ use structopt::StructOpt; pub mod gui; pub mod tui; - /// Command line interface IDE with full GUI and headless modes. /// If no flags are provided, the GUI editor is launched in a separate process. /// If no path is provided, the current directory is used. diff --git a/src/tui.rs b/src/tui.rs index 98229c0..32a106e 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,4 +1,5 @@ pub mod app; +mod component; mod explorer; use anyhow::{Context, Result}; diff --git a/src/tui/app.rs b/src/tui/app.rs index 7ee8b14..33a4443 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -1,16 +1,12 @@ -use anyhow::Context; +use crate::tui::component::{Action, ClideComponent}; +use crate::tui::explorer::Explorer; use ratatui::buffer::Buffer; -use ratatui::crossterm::event; -use ratatui::crossterm::event::{Event, KeyCode}; use ratatui::layout::{Constraint, Direction, Layout, Rect}; use ratatui::prelude::{Color, Style, Widget}; 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)] +#[derive(Debug, Clone)] pub struct App<'a> { explorer: Explorer<'a>, } @@ -24,37 +20,29 @@ impl<'a> App<'a> { pub fn run(mut self, mut terminal: DefaultTerminal) -> anyhow::Result<()> { loop { - terminal.draw(|f| f.render_widget(self, f.area()))?; - if self.should_quit()? { - break; + terminal.draw(|f| { + f.render_widget(&self, f.area()); + })?; + + // TODO: Handle events based on which component is active. + // match self.explorer.handle_events() { ... } + match self.handle_events() { + Action::Quit => break, + _ => {} } - self.handle_events()?; } Ok(()) } - fn handle_events(&mut self) -> anyhow::Result<()> { - // Handle other keyboard events here, aside from quitting. - Ok(()) - } - - fn should_quit(self) -> anyhow::Result { - if event::poll(Duration::from_millis(250)).context("event poll failed")? { - if let Event::Key(key) = event::read().context("event read failed")? { - return Ok(KeyCode::Char('q') == key.code); - } - } - Ok(false) - } - - fn draw_status(self, area: Rect, buf: &mut Buffer) { + fn draw_status(&self, area: Rect, buf: &mut Buffer) { + // TODO: Status bar should have drop down menus Tabs::new(["File", "Edit", "View", "Help"]) .style(Style::default()) .block(Block::default().borders(Borders::ALL)) .render(area, buf); } - fn draw_tabs(self, area: Rect, buf: &mut Buffer) { + fn draw_tabs(&self, area: Rect, buf: &mut Buffer) { // TODO: Tabs should be opened from file explorer Tabs::new(["file.md", "file.cpp"]) .divider(symbols::DOT) @@ -67,7 +55,7 @@ impl<'a> App<'a> { .render(area, buf); } - fn draw_editor(self, area: Rect, buf: &mut Buffer) { + fn draw_editor(&self, area: Rect, buf: &mut Buffer) { // TODO: Title should be detected programming language name // TODO: Content should be file contents // TODO: Contents should use vim in rendered TTY @@ -85,7 +73,7 @@ impl<'a> App<'a> { .render(area, buf); } - fn draw_terminal(self, area: Rect, buf: &mut Buffer) { + fn draw_terminal(&self, area: Rect, buf: &mut Buffer) { // TODO: Title should be detected shell name // TODO: Contents should be shell output Paragraph::new("shaun@pc:~/Code/clide$ ") @@ -102,7 +90,7 @@ impl<'a> App<'a> { } // TODO: Separate complex components into their own widgets. -impl<'a> Widget for App<'a> { +impl<'a> Widget for &App<'a> { fn render(self, area: Rect, buf: &mut Buffer) where Self: Sized, @@ -128,16 +116,18 @@ impl<'a> Widget for App<'a> { .direction(Direction::Vertical) .constraints([ Constraint::Length(1), // Editor tabs. - Constraint::Fill(1), + Constraint::Fill(1), // Editor contents. ]) .split(horizontal[1]); self.draw_status(vertical[0], buf); self.draw_terminal(vertical[2], buf); - self.explorer.draw(horizontal[0], buf); + self.explorer.render(horizontal[0], buf); self.draw_tabs(editor_layout[0], buf); self.draw_editor(editor_layout[1], buf); } } + +impl<'a> ClideComponent for App<'a> {} diff --git a/src/tui/component.rs b/src/tui/component.rs new file mode 100644 index 0000000..7f13d86 --- /dev/null +++ b/src/tui/component.rs @@ -0,0 +1,40 @@ +use ratatui::crossterm::event; +use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, MouseEvent}; +use std::time::Duration; + +pub enum Action { + Noop, + Quit, +} + +pub trait ClideComponent { + fn handle_events(&mut self) -> Action { + if !event::poll(Duration::from_millis(250)).expect("event poll failed") { + return Action::Noop; + } + + let key_event = event::read().expect("event read failed"); + match key_event { + Event::Key(key_event) => self.handle_key_events(key_event), + Event::Mouse(mouse_event) => self.handle_mouse_events(mouse_event), + _ => Action::Noop, + } + } + + fn handle_key_events(&mut self, key: KeyEvent) -> Action { + match key.code { + KeyCode::Char('q') => Action::Quit, + _ => Action::Noop, + } + } + + fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Action { + Action::Noop + } + + fn update(&mut self, action: Action) -> Action { + Action::Noop + } + + // fn render(&mut self, area: Rect, buf: &mut Buffer); +} diff --git a/src/tui/explorer.rs b/src/tui/explorer.rs index 5be7736..76a37e5 100644 --- a/src/tui/explorer.rs +++ b/src/tui/explorer.rs @@ -1,29 +1,29 @@ -use std::fs; +use crate::tui::component::ClideComponent; +use anyhow::Result; use ratatui::buffer::Buffer; use ratatui::layout::Rect; use ratatui::prelude::Style; use ratatui::widgets::{Block, Borders, Widget}; +use std::fs; use tui_tree_widget::{Tree, TreeItem}; use uuid::Uuid; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub struct Explorer<'a> { root_path: &'a std::path::Path, + tree_items: TreeItem<'a, String>, } impl<'a> Explorer<'a> { pub fn new(path: &'a std::path::Path) -> Self { - Explorer { root_path: path } + let mut explorer = Explorer { + root_path: path, + tree_items: Self::build_tree_from_path(path.into()), + }; + explorer } - 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); - } + pub fn draw(&self, area: Rect, buf: &mut Buffer) {} fn build_tree_from_path(path: std::path::PathBuf) -> TreeItem<'static, String> { let mut children = vec![]; @@ -53,6 +53,18 @@ impl<'a> Explorer<'a> { .to_string(), children, ) - .expect("Failed to build tree from path.") + .expect("Failed to build tree from path.") } } + +impl<'a> Widget for &Explorer<'a> { + fn render(self, area: Rect, buf: &mut Buffer) { + Tree::new(&self.tree_items.children()) + .expect("Failed to build tree.") + .style(Style::default()) + .block(Block::default().borders(Borders::ALL)) + .render(area, buf); + } +} + +impl<'a> ClideComponent for Explorer<'a> {}