[tui] Add ComponentOf trait.
I think it will help with fetching a component by type from the Components vector attached to App?
This commit is contained in:
parent
7149ad0118
commit
4d81cd51a6
113
src/tui/app.rs
113
src/tui/app.rs
@ -24,6 +24,59 @@ pub enum AppComponents<'a> {
|
|||||||
AppComponent(Box<dyn Component>),
|
AppComponent(Box<dyn Component>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Usage: get_component_mut::<Editor>() OR get_component::<Editor>()
|
||||||
|
///
|
||||||
|
/// Implementing this trait for each AppComponent allows for easy lookup in the vector.
|
||||||
|
trait ComponentOf<T> {
|
||||||
|
fn as_ref(&self) -> Option<&T>;
|
||||||
|
fn as_mut(&mut self) -> Option<&mut T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ComponentOf<Logger> for AppComponents<'a> {
|
||||||
|
fn as_ref(&self) -> Option<&Logger> {
|
||||||
|
if let AppComponents::AppLogger(ref e) = *self {
|
||||||
|
return Some(e);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn as_mut(&mut self) -> Option<&mut Logger> {
|
||||||
|
if let AppComponents::AppLogger(ref mut e) = *self {
|
||||||
|
return Some(e);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ComponentOf<Editor> for AppComponents<'a> {
|
||||||
|
fn as_ref(&self) -> Option<&Editor> {
|
||||||
|
if let AppComponents::AppEditor(ref e) = *self {
|
||||||
|
return Some(e);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn as_mut(&mut self) -> Option<&mut Editor> {
|
||||||
|
if let AppComponents::AppEditor(ref mut e) = *self {
|
||||||
|
return Some(e);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ComponentOf<Explorer<'a>> for AppComponents<'a> {
|
||||||
|
fn as_ref(&self) -> Option<&Explorer<'a>> {
|
||||||
|
if let AppComponents::AppExplorer(ref e) = *self {
|
||||||
|
return Some(e);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn as_mut(&mut self) -> Option<&mut Explorer<'a>> {
|
||||||
|
if let AppComponents::AppExplorer(ref mut e) = *self {
|
||||||
|
return Some(e);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct App<'a> {
|
pub struct App<'a> {
|
||||||
components: Vec<AppComponents<'a>>,
|
components: Vec<AppComponents<'a>>,
|
||||||
}
|
}
|
||||||
@ -37,7 +90,7 @@ impl<'a> App<'a> {
|
|||||||
AppComponents::AppLogger(Logger::new()),
|
AppComponents::AppLogger(Logger::new()),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
app.get_editor_mut()
|
app.get_component_mut::<Editor>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_contents(&root_path.join("src/tui/app.rs"))
|
.set_contents(&root_path.join("src/tui/app.rs"))
|
||||||
.context(format!(
|
.context(format!(
|
||||||
@ -47,44 +100,18 @@ impl<'a> App<'a> {
|
|||||||
Ok(app)
|
Ok(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_explorer(&self) -> Result<&Explorer<'a>> {
|
fn get_component<T>(&self) -> Option<&T>
|
||||||
for component in &self.components {
|
where
|
||||||
if let AppComponents::AppExplorer(explorer) = component {
|
AppComponents<'a>: ComponentOf<T>,
|
||||||
return Ok(explorer);
|
{
|
||||||
}
|
self.components.iter().find_map(|c| c.as_ref())
|
||||||
}
|
|
||||||
bail!("Failed to find project explorer widget.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_explorer_mut(&mut self) -> Result<&mut Explorer<'a>> {
|
fn get_component_mut<T>(&mut self) -> Option<&mut T>
|
||||||
for component in &mut self.components {
|
where
|
||||||
if let AppComponents::AppExplorer(explorer) = component {
|
AppComponents<'a>: ComponentOf<T>,
|
||||||
return Ok(explorer);
|
{
|
||||||
}
|
self.components.iter_mut().find_map(|c| c.as_mut())
|
||||||
}
|
|
||||||
bail!("Failed to find project explorer widget.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_editor(&self) -> Option<&Editor> {
|
|
||||||
for component in &self.components {
|
|
||||||
if let AppComponents::AppEditor(editor) = component {
|
|
||||||
return Some(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is no editor currently opened.
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_editor_mut(&mut self) -> Option<&mut Editor> {
|
|
||||||
for component in &mut self.components {
|
|
||||||
if let AppComponents::AppEditor(editor) = component {
|
|
||||||
return Some(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is no editor currently opened.
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
||||||
@ -121,7 +148,7 @@ impl<'a> App<'a> {
|
|||||||
fn draw_tabs(&self, area: Rect, buf: &mut Buffer) {
|
fn draw_tabs(&self, area: Rect, buf: &mut Buffer) {
|
||||||
// Determine the tab title from the current file (or use a fallback).
|
// Determine the tab title from the current file (or use a fallback).
|
||||||
let mut title: Option<&str> = None;
|
let mut title: Option<&str> = None;
|
||||||
if let Some(editor) = self.get_editor() {
|
if let Some(editor) = self.get_component::<Editor>() {
|
||||||
title = editor
|
title = editor
|
||||||
.file_path
|
.file_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -144,12 +171,12 @@ impl<'a> App<'a> {
|
|||||||
/// If the selected item is not a file, this does nothing.
|
/// If the selected item is not a file, this does nothing.
|
||||||
fn refresh_editor_contents(&mut self) -> Result<()> {
|
fn refresh_editor_contents(&mut self) -> Result<()> {
|
||||||
// Use the currently selected TreeItem or get an absolute path to this source file.
|
// Use the currently selected TreeItem or get an absolute path to this source file.
|
||||||
let selected_pathbuf = match self.get_explorer()?.selected() {
|
let selected_pathbuf = match self.get_component::<Explorer>().unwrap().selected() {
|
||||||
Ok(path) => PathBuf::from(path),
|
Ok(path) => PathBuf::from(path),
|
||||||
Err(_) => PathBuf::from(std::path::absolute(file!())?.to_string_lossy().to_string()),
|
Err(_) => PathBuf::from(std::path::absolute(file!())?.to_string_lossy().to_string()),
|
||||||
};
|
};
|
||||||
let editor = self
|
let editor = self
|
||||||
.get_editor_mut()
|
.get_component_mut::<Editor>()
|
||||||
.context("Failed to get active editor while refreshing contents.")?;
|
.context("Failed to get active editor while refreshing contents.")?;
|
||||||
if let Some(current_file_path) = editor.file_path.clone() {
|
if let Some(current_file_path) = editor.file_path.clone() {
|
||||||
if selected_pathbuf == current_file_path || !selected_pathbuf.is_file() {
|
if selected_pathbuf == current_file_path || !selected_pathbuf.is_file() {
|
||||||
@ -194,15 +221,15 @@ impl<'a> Widget for &mut App<'a> {
|
|||||||
|
|
||||||
self.draw_status(vertical[0], buf);
|
self.draw_status(vertical[0], buf);
|
||||||
self.draw_tabs(editor_layout[0], buf);
|
self.draw_tabs(editor_layout[0], buf);
|
||||||
|
let id = self.id().to_string();
|
||||||
for component in &mut self.components {
|
for component in &mut self.components {
|
||||||
match component {
|
match component {
|
||||||
AppComponents::AppEditor(editor) => editor.render(editor_layout[1], buf),
|
AppComponents::AppEditor(editor) => editor.render(editor_layout[1], buf),
|
||||||
AppComponents::AppExplorer(explorer) => {
|
AppComponents::AppExplorer(explorer) => {
|
||||||
// TODO: What to do about errors during rendering?
|
|
||||||
// Once there is a debug console, maybe log it and discard? Panic isn't great.
|
|
||||||
explorer
|
explorer
|
||||||
.render(horizontal[0], buf)
|
.render(horizontal[0], buf)
|
||||||
.expect("Failed to render Explorer");
|
.context("Failed to render Explorer")
|
||||||
|
.unwrap_or_else(|e| error!(target:id.as_str(), "{}", e));
|
||||||
}
|
}
|
||||||
AppComponents::AppLogger(logger) => logger.render(vertical[2], buf),
|
AppComponents::AppLogger(logger) => logger.render(vertical[2], buf),
|
||||||
AppComponents::AppComponent(_) => {}
|
AppComponents::AppComponent(_) => {}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user