From e050cb568cb7640e428199493d66ce1f78d67c35 Mon Sep 17 00:00:00 2001 From: Madeleine Date: Thu, 10 Nov 2022 06:44:18 +0000 Subject: [PATCH] ui components working --- src/terminal/screen.rs | 2 +- src/terminal/tui.rs | 131 -------------------------------- src/tui/components/statusbar.rs | 10 +-- src/tui/components/welcome.rs | 79 ++++++++++++------- src/tui/ui.rs | 37 ++++++--- 5 files changed, 84 insertions(+), 175 deletions(-) delete mode 100644 src/terminal/tui.rs diff --git a/src/terminal/screen.rs b/src/terminal/screen.rs index 4e38f02..c880a83 100644 --- a/src/terminal/screen.rs +++ b/src/terminal/screen.rs @@ -146,7 +146,7 @@ impl Screen { execute!(stdout(), Print(text)).unwrap(); } - pub fn write_at(&mut self, text: String, position: Coords) { + pub fn write_at(&mut self, text: &String, position: Coords) { // Writes a line at a set of coordinates self.cursor.move_to(position); Screen::write(&text); diff --git a/src/terminal/tui.rs b/src/terminal/tui.rs deleted file mode 100644 index a939730..0000000 --- a/src/terminal/tui.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::core::editor::Editor; -use crate::terminal::screen::{Coords, Screen}; -use crossterm::style::Stylize; -use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers}; - -// Utils -fn with_spaces(text: &str) -> String { - format!(" {} ", text) -} -fn calc_x(screen_width: usize, item_length: usize) -> usize { - (screen_width / 2) - (item_length / 2) -} -fn longest_element_in_array(elements: Vec<&str>) -> usize { - elements.iter().max_by_key(|x: &&&str| x.len()).unwrap().len() -} - -pub fn draw_status(screen: &mut Screen, editor: &Editor) -> Result<(), ()> { - // Calculate where to draw the status bar - let status_height = screen.size.height - 2; - - // Get the editor logo from the config - let editor_logo = &with_spaces(editor.config.logo) as &str; - // Get the current mode into a string - let mode_string = &with_spaces(editor.mode.as_str()) as &str; - // Get the current open file name - let file_name = &with_spaces(editor.buffer.name) as &str; - - // Calculate the total length of all the status bar components - let total_length = editor_logo.len() + mode_string.len() + file_name.len() + 1; - - // If the screen isn't wide enough, panic as we can't draw the status bar - if screen.size.width < total_length { - Err(()) - - } else { - // Write the editor logo - screen.write_at( - editor_logo.yellow().bold().reverse().to_string(), - Coords::from(0, status_height), - ); - - // Calculate where to write the current mode - let x = editor_logo.len() - 1; - // Write the current mode - screen.write_at( - mode_string.green().bold().reverse().to_string(), - Coords::from(x, status_height), - ); - // Calculate where to write the file name - let x = x + mode_string.len(); - // Write the current file name - screen.write_at( - file_name.magenta().bold().reverse().to_string(), - Coords::from(x, status_height), - ); - - // Draw the rest of the status bar - let x = x + file_name.len(); - screen.write_at( - " ".repeat(screen.size.width - x).reverse().to_string(), - Coords::from(x, status_height), - ); - - Ok(()) - } -} - -pub fn draw_welcome(screen: &mut Screen, editor: &Editor) { - // The welcome message - let title = format!("{} {}", editor.config.logo, editor.config.friendly_name); - let message: [&str; 5] = [ - "Hackable text editor for nerds", - "", - "Type :help to open the README.md document", - "Type :o to open a file and edit", - "Type :q! or to quit lambda", - ]; - - // If the screen is big enough, we can draw - if screen.size.width > longest_element_in_array(message.to_vec()) && screen.size.height > message.len() + 4 { - // The starting y position in the centre of the screen - let mut y = (screen.size.height / 2) - (message.len() / 2) - 2; - - // Calculate where to place the title - let x = calc_x(screen.size.width, title.len()); - - // Write the title to the screen - screen.write_at(title.yellow().to_string(), Coords::from(x, y)); - - for line in message { - // Each line has different width so requires a different x position to center it - let x = calc_x(screen.size.width, line.len()) ; - - // For each line we move downwards so increment y - y += 1; - - // Write the line to the screen at position (x, y) - screen.write_at(line.to_string(), Coords::from(x, y)); - } - } -} - -pub fn start(screen: &mut Screen, editor: Editor) { - loop { - // Refresh the screen - screen.refresh().unwrap(); - - // Draw the welcome message - draw_welcome(screen, &editor); - - // Draw the status bar - draw_status(screen, &editor).unwrap(); - - // Check for any key presses - match read().unwrap() { - Event::Key(KeyEvent { - code: KeyCode::Char('q'), - modifiers: KeyModifiers::CONTROL, - .. - }) => break, - Event::Key(KeyEvent { - code: KeyCode::Char('c'), - modifiers: KeyModifiers::CONTROL, - .. - }) => break, - _ => (), - } - } - - screen.exit(); -} diff --git a/src/tui/components/statusbar.rs b/src/tui/components/statusbar.rs index e40db63..8c74359 100644 --- a/src/tui/components/statusbar.rs +++ b/src/tui/components/statusbar.rs @@ -1,5 +1,5 @@ -use crate::tui::ui::Component; use crate::tui::utils::with_spaces; +use crate::tui::ui::Component; use crate::core::editor::Editor; use crate::terminal::screen::{Coords, Screen}; use crossterm::style::Stylize; @@ -42,7 +42,7 @@ impl<'a> Component for StatusBar<'a> { } else { // Write the editor logo screen.write_at( - editor_logo.yellow().bold().reverse().to_string(), + &editor_logo.yellow().bold().reverse().to_string(), Coords::from(0, status_height), ); @@ -50,21 +50,21 @@ impl<'a> Component for StatusBar<'a> { let x = editor_logo.len() - 1; // Write the current mode screen.write_at( - mode_string.green().bold().reverse().to_string(), + &mode_string.green().bold().reverse().to_string(), Coords::from(x, status_height), ); // Calculate where to write the file name let x = x + mode_string.len(); // Write the current file name screen.write_at( - file_name.magenta().bold().reverse().to_string(), + &file_name.magenta().bold().reverse().to_string(), Coords::from(x, status_height), ); // Draw the rest of the status bar let x = x + file_name.len(); screen.write_at( - " ".repeat(screen.size.width - x).reverse().to_string(), + &" ".repeat(screen.size.width - x).reverse().to_string(), Coords::from(x, status_height), ); diff --git a/src/tui/components/welcome.rs b/src/tui/components/welcome.rs index 8c90a85..fbe4e08 100644 --- a/src/tui/components/welcome.rs +++ b/src/tui/components/welcome.rs @@ -1,40 +1,61 @@ use crate::tui::utils; +use crate::tui::ui::Component; use crate::core::editor::Editor; use crate::terminal::screen::{Coords, Screen}; use crossterm::style::Stylize; -pub fn draw_welcome(screen: &mut Screen, editor: &Editor) { - // The welcome message - let title = format!("{} {}", editor.config.logo, editor.config.friendly_name); - let message: [&str; 5] = [ - "Hackable text editor for nerds", - "", - "Type :help to open the README.md document", - "Type :o to open a file and edit", - "Type :q! or to quit lambda", - ]; +pub struct WelcomeMessage<'a> { + title: String, + message: [&'a str; 5], +} - // If the screen is big enough, we can draw - if screen.size.width > utils::longest_element_in_vec(message.to_vec()) && screen.size.height > message.len() + 4 { - // The starting y position in the centre of the screen - let mut y = (screen.size.height / 2) - (message.len() / 2) - 2; +impl<'a> WelcomeMessage<'a> { + pub fn new(editor: &'a Editor<'a>) -> Self { + let title = format!("{} {}", editor.config.logo, editor.config.friendly_name); - // Calculate where to place the title - let x = utils::calc_centered_x(screen.size.width, title.len()); - - // Write the title to the screen - screen.write_at(title.yellow().to_string(), Coords::from(x, y)); - - for line in message { - // Each line has different width so requires a different x position to center it - let x = utils::calc_centered_x(screen.size.width, line.len()) ; - - // For each line we move downwards so increment y - y += 1; - - // Write the line to the screen at position (x, y) - screen.write_at(line.to_string(), Coords::from(x, y)); + // The welcome message + let message: [&str; 5] = [ + "Hackable text editor for nerds", + "", + "Type :help to open the README.md document", + "Type :o to open a file and edit", + "Type :q! or to quit lambda", + ]; + Self { + title, + message, } } } +impl<'a> Component for WelcomeMessage<'a> { + fn draw(&self, screen: &mut Screen, _editor: &Editor) -> Result<(), ()> { + // If the screen is big enough, we can draw + if screen.size.width > utils::longest_element_in_vec(self.message.to_vec()) && screen.size.height > self.message.len() + 4 { + // The starting y position in the centre of the screen + let mut y = (screen.size.height / 2) - (self.message.len() / 2) - 2; + + // Calculate where to place the title + let x = utils::calc_centered_x(screen.size.width, self.title.len()); + + // Write the title to the screen + screen.write_at(&self.title.clone().yellow().to_string(), Coords::from(x, y)); + + for line in &self.message { + // Each line has different width so requires a different x position to center it + let x = utils::calc_centered_x(screen.size.width, line.len()) ; + + // For each line we move downwards so increment y + y += 1; + + // Write the line to the screen at position (x, y) + screen.write_at(&line.to_string(), Coords::from(x, y)); + }; + + Ok(()) + + } else { + Err(()) + } + } +} diff --git a/src/tui/ui.rs b/src/tui/ui.rs index 1e74af9..15ad0ef 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -7,25 +7,44 @@ pub trait Component { fn draw(&self, screen: &mut Screen, editor: &Editor) -> Result<(), ()>; } -struct Components { - bottom: Vec>, +struct Components<'a> { + bottom: Vec>, + centre: Vec>, } -pub struct Ui { - components: Components, +pub struct Ui<'a> { + components: Components<'a>, } -impl Ui { - pub fn new<'a>(editor: &'a Editor<'a>) -> Self { +impl<'a> Ui<'a> { + pub fn new(editor: &'a Editor<'a>) -> Self { let status_bar = components::statusbar::StatusBar::new(editor); + let welcome_msg = components::welcome::WelcomeMessage::new(editor); Self { - components: Components { bottom: vec![Box::new(status_bar)]}, + components: Components { + bottom: vec![Box::new(status_bar)], + centre: vec![Box::new(welcome_msg)], + }, } } - pub fn draw(&self) { + pub fn draw(&self, screen: &mut Screen, editor: &Editor) { + // Dereference the box + let _ = &*self.components.bottom; + // Draw all components on the bottom + for component in &self.components.bottom { + component.draw(screen, &editor).unwrap(); + }; + + // Dereference the box + let _ = &*self.components.centre; + + // Draw all components in the center + for component in &self.components.centre { + component.draw(screen, &editor).unwrap(); + } } pub fn run(screen: &mut Screen, editor: Editor) { @@ -37,7 +56,7 @@ impl Ui { let components = Ui::new(&editor); // Draw all UI elements - components.draw(); + components.draw(screen, &editor); // Check for any key presses match read().unwrap() {