[tui] Add basic component trait.
This commit is contained in:
parent
b65565adfa
commit
733a43ccde
@ -6,7 +6,6 @@ use structopt::StructOpt;
|
|||||||
|
|
||||||
pub mod gui;
|
pub mod gui;
|
||||||
pub mod tui;
|
pub mod tui;
|
||||||
|
|
||||||
/// Command line interface IDE with full GUI and headless modes.
|
/// 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 flags are provided, the GUI editor is launched in a separate process.
|
||||||
/// If no path is provided, the current directory is used.
|
/// If no path is provided, the current directory is used.
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
pub mod app;
|
pub mod app;
|
||||||
|
mod component;
|
||||||
mod explorer;
|
mod explorer;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
use anyhow::Context;
|
use crate::tui::component::{Action, ClideComponent};
|
||||||
|
use crate::tui::explorer::Explorer;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::crossterm::event;
|
|
||||||
use ratatui::crossterm::event::{Event, KeyCode};
|
|
||||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
||||||
use ratatui::prelude::{Color, Style, Widget};
|
use ratatui::prelude::{Color, Style, Widget};
|
||||||
use ratatui::widgets::{Block, Borders, Padding, Paragraph, Tabs, Wrap};
|
use ratatui::widgets::{Block, Borders, Padding, Paragraph, Tabs, Wrap};
|
||||||
use ratatui::{DefaultTerminal, symbols};
|
use ratatui::{DefaultTerminal, symbols};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crate::tui::explorer::Explorer;
|
#[derive(Debug, Clone)]
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct App<'a> {
|
pub struct App<'a> {
|
||||||
explorer: Explorer<'a>,
|
explorer: Explorer<'a>,
|
||||||
}
|
}
|
||||||
@ -24,37 +20,29 @@ impl<'a> App<'a> {
|
|||||||
|
|
||||||
pub fn run(mut self, mut terminal: DefaultTerminal) -> anyhow::Result<()> {
|
pub fn run(mut self, mut terminal: DefaultTerminal) -> anyhow::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| f.render_widget(self, f.area()))?;
|
terminal.draw(|f| {
|
||||||
if self.should_quit()? {
|
f.render_widget(&self, f.area());
|
||||||
break;
|
})?;
|
||||||
|
|
||||||
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self) -> anyhow::Result<()> {
|
fn draw_status(&self, area: Rect, buf: &mut Buffer) {
|
||||||
// Handle other keyboard events here, aside from quitting.
|
// TODO: Status bar should have drop down menus
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_quit(self) -> anyhow::Result<bool> {
|
|
||||||
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) {
|
|
||||||
Tabs::new(["File", "Edit", "View", "Help"])
|
Tabs::new(["File", "Edit", "View", "Help"])
|
||||||
.style(Style::default())
|
.style(Style::default())
|
||||||
.block(Block::default().borders(Borders::ALL))
|
.block(Block::default().borders(Borders::ALL))
|
||||||
.render(area, buf);
|
.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
|
// TODO: Tabs should be opened from file explorer
|
||||||
Tabs::new(["file.md", "file.cpp"])
|
Tabs::new(["file.md", "file.cpp"])
|
||||||
.divider(symbols::DOT)
|
.divider(symbols::DOT)
|
||||||
@ -67,7 +55,7 @@ impl<'a> App<'a> {
|
|||||||
.render(area, buf);
|
.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: Title should be detected programming language name
|
||||||
// TODO: Content should be file contents
|
// TODO: Content should be file contents
|
||||||
// TODO: Contents should use vim in rendered TTY
|
// TODO: Contents should use vim in rendered TTY
|
||||||
@ -85,7 +73,7 @@ impl<'a> App<'a> {
|
|||||||
.render(area, buf);
|
.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: Title should be detected shell name
|
||||||
// TODO: Contents should be shell output
|
// TODO: Contents should be shell output
|
||||||
Paragraph::new("shaun@pc:~/Code/clide$ ")
|
Paragraph::new("shaun@pc:~/Code/clide$ ")
|
||||||
@ -102,7 +90,7 @@ impl<'a> App<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Separate complex components into their own widgets.
|
// 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)
|
fn render(self, area: Rect, buf: &mut Buffer)
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
@ -128,16 +116,18 @@ impl<'a> Widget for App<'a> {
|
|||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Length(1), // Editor tabs.
|
Constraint::Length(1), // Editor tabs.
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1), // Editor contents.
|
||||||
])
|
])
|
||||||
.split(horizontal[1]);
|
.split(horizontal[1]);
|
||||||
|
|
||||||
self.draw_status(vertical[0], buf);
|
self.draw_status(vertical[0], buf);
|
||||||
self.draw_terminal(vertical[2], 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_tabs(editor_layout[0], buf);
|
||||||
self.draw_editor(editor_layout[1], buf);
|
self.draw_editor(editor_layout[1], buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ClideComponent for App<'a> {}
|
||||||
|
|||||||
40
src/tui/component.rs
Normal file
40
src/tui/component.rs
Normal file
@ -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);
|
||||||
|
}
|
||||||
@ -1,29 +1,29 @@
|
|||||||
use std::fs;
|
use crate::tui::component::ClideComponent;
|
||||||
|
use anyhow::Result;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use ratatui::prelude::Style;
|
use ratatui::prelude::Style;
|
||||||
use ratatui::widgets::{Block, Borders, Widget};
|
use ratatui::widgets::{Block, Borders, Widget};
|
||||||
|
use std::fs;
|
||||||
use tui_tree_widget::{Tree, TreeItem};
|
use tui_tree_widget::{Tree, TreeItem};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Explorer<'a> {
|
pub struct Explorer<'a> {
|
||||||
root_path: &'a std::path::Path,
|
root_path: &'a std::path::Path,
|
||||||
|
tree_items: TreeItem<'a, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Explorer<'a> {
|
impl<'a> Explorer<'a> {
|
||||||
pub fn new(path: &'a std::path::Path) -> Self {
|
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) {
|
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> {
|
fn build_tree_from_path(path: std::path::PathBuf) -> TreeItem<'static, String> {
|
||||||
let mut children = vec![];
|
let mut children = vec![];
|
||||||
@ -56,3 +56,15 @@ impl<'a> Explorer<'a> {
|
|||||||
.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> {}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user