Compare commits
1 Commits
b2eb9aef6e
...
711f92b7dd
| Author | SHA1 | Date | |
|---|---|---|---|
| 711f92b7dd |
151
src/tui/editor_tab.rs
Normal file
151
src/tui/editor_tab.rs
Normal file
@ -0,0 +1,151 @@
|
||||
use crate::tui::component::{Action, Component};
|
||||
use crate::tui::editor::Editor;
|
||||
use anyhow::{Context, Result};
|
||||
use log::trace;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::prelude::{Color, Style};
|
||||
use ratatui::widgets::{Block, Borders, Padding, Tabs, Widget};
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Render the tabs with keys as titles
|
||||
// Tab keys can be file names.
|
||||
// Render the editor using the key as a reference for lookup
|
||||
pub struct EditorTab {
|
||||
pub(crate) editors: HashMap<String, Editor>,
|
||||
tab_order: Vec<String>,
|
||||
current_editor: usize,
|
||||
}
|
||||
|
||||
impl EditorTab {
|
||||
fn id() -> &'static str {
|
||||
"EditorTab"
|
||||
}
|
||||
|
||||
pub fn new(path: &std::path::PathBuf) -> Self {
|
||||
trace!(target:Self::id(), "Building EditorTab with path '{path:?}'");
|
||||
let tab_order = vec![path.to_string_lossy().to_string()];
|
||||
Self {
|
||||
editors: HashMap::from([(tab_order.first().unwrap().to_owned(), Editor::new())]),
|
||||
tab_order,
|
||||
current_editor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_editor(&mut self) {
|
||||
self.current_editor = (self.current_editor + 1) % self.tab_order.len();
|
||||
}
|
||||
|
||||
pub fn prev_editor(&mut self) {
|
||||
self.current_editor = self
|
||||
.current_editor
|
||||
.checked_sub(1)
|
||||
.unwrap_or(self.tab_order.len() - 1);
|
||||
}
|
||||
|
||||
pub fn current_editor(&self) -> Option<&Editor> {
|
||||
self.editors.get(&self.tab_order[self.current_editor])
|
||||
}
|
||||
|
||||
pub fn current_editor_mut(&mut self) -> Option<&mut Editor> {
|
||||
self.editors.get_mut(&self.tab_order[self.current_editor])
|
||||
}
|
||||
|
||||
pub fn open_tab(&mut self, path: &std::path::PathBuf) -> Result<()> {
|
||||
if self
|
||||
.editors
|
||||
.contains_key(&path.to_string_lossy().to_string())
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let path_str = path.to_string_lossy().to_string();
|
||||
self.tab_order.push(path_str.clone());
|
||||
let mut editor = Editor::new();
|
||||
editor.set_contents(path).context("Failed to open tab")?;
|
||||
self.editors.insert(path_str, editor);
|
||||
self.current_editor = self.tab_order.len() - 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render(&mut self, tabs_area: Rect, editor_area: Rect, buf: &mut Buffer) {
|
||||
// TODO: Only file name is displayed in tab title, so files with the same name in different
|
||||
// directories will appear confusing.
|
||||
let tab_titles = self.tab_order.iter().map(|t| {
|
||||
std::path::PathBuf::from(t)
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy().to_string())
|
||||
.unwrap_or("Unknown".to_string())
|
||||
});
|
||||
Tabs::new(tab_titles)
|
||||
.select(self.current_editor)
|
||||
.divider("|")
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::NONE)
|
||||
.padding(Padding::new(0, 0, 0, 0)),
|
||||
)
|
||||
.highlight_style(Style::default().fg(Color::LightRed))
|
||||
.render(tabs_area, buf);
|
||||
Widget::render(self, editor_area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &mut EditorTab {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if let Some(editor) = self.current_editor_mut() {
|
||||
editor.render(area, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for EditorTab {
|
||||
fn handle_event(&mut self, event: Event) -> Result<Action> {
|
||||
if let Some(key) = event.as_key_event() {
|
||||
let action = self.handle_key_events(key)?;
|
||||
match action {
|
||||
Action::Quit | Action::Handled => return Ok(action),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
self.current_editor_mut()
|
||||
.context("Failed to get current editor")?
|
||||
.handle_event(event)
|
||||
}
|
||||
|
||||
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Action> {
|
||||
match key {
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('h'),
|
||||
modifiers: KeyModifiers::ALT,
|
||||
..
|
||||
}
|
||||
| KeyEvent {
|
||||
code: KeyCode::Left,
|
||||
modifiers: KeyModifiers::ALT,
|
||||
..
|
||||
} => {
|
||||
self.prev_editor();
|
||||
Ok(Action::Handled)
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('l'),
|
||||
modifiers: KeyModifiers::ALT,
|
||||
..
|
||||
}
|
||||
| KeyEvent {
|
||||
code: KeyCode::Right,
|
||||
modifiers: KeyModifiers::ALT,
|
||||
..
|
||||
} => {
|
||||
self.next_editor();
|
||||
Ok(Action::Handled)
|
||||
}
|
||||
_ => Ok(Action::Noop),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user