diff --git a/README.md b/README.md index 453eef2..eaf039c 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,45 @@ pacman -S heh apk add heh ``` +## Using as a Ratatui widget + +`heh` can be used a library and embedded into other TUI applications which use [Ratatui](https://ratatui.rs) and [crossterm](https://github.com/crossterm-rs/crossterm). + +Add `heh` to your dependencies in `Cargo.toml`: + +```toml +[dependencies] +ratatui = "0.24" +crossterm = "0.27" +heh = "0.4" +``` + +Create the application: + +```rust +use heh::app::Application as Heh; +use heh::decoder::Encoding; + +let file = std::fs::OpenOptions::new().read(true).write(true).open(path).unwrap(); +let heh = Heh::new(file, Encoding::Ascii, 0).unwrap(); +``` + +Then you can render a frame as follows: + +```rust +terminal.draw(|frame| { + heh.render_frame(frame, frame.size()); +}); +``` + +To handle key events: + +```rust +heh.handle_input(&crossterm::event::Event::Key(/* */)).unwrap(); +``` + +See the [binsider](https://github.com/orhun/binsider) project for an example use case. + # Contributing See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/src/app.rs b/src/app.rs index eaf9ccb..d500e11 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,19 +2,24 @@ //! //! The application holds the main components of the other modules, like the [`ScreenHandler`], //! [`LabelHandler`], and input handling, as well as the state data that each of them need. +//! +//! [`ScreenHandler`]: crate::screen::Handler +//! [`LabelHandler`]: crate::label::Handler use std::{error::Error, fs::File, process}; use arboard::Clipboard; use crossterm::event::{self, Event, KeyEventKind}; +use ratatui::layout::Rect; +use ratatui::Frame; use crate::buffer::AsyncBuffer; use crate::decoder::Encoding; use crate::windows::search::Search; use crate::{ input, - label::LabelHandler, - screen::ScreenHandler, + label::Handler as LabelHandler, + screen::Handler as ScreenHandler, windows::{ editor::Editor, jump_to_byte::JumpToByte, unsaved_changes::UnsavedChanges, KeyHandler, Window, @@ -53,9 +58,9 @@ pub(crate) enum Action { } /// State Information needed by the [`ScreenHandler`] and [`KeyHandler`]. -pub(crate) struct AppData { +pub struct Data { /// The file under editing. - pub(crate) file: File, + pub file: File, /// The file content. pub(crate) contents: AsyncBuffer, @@ -103,7 +108,7 @@ pub(crate) struct AppData { pub(crate) search_offsets: Vec, } -impl AppData { +impl Data { /// Reindexes contents to find locations of the user's search term. pub(crate) fn reindex_search(&mut self) { self.search_offsets = self @@ -117,32 +122,30 @@ impl AppData { /// Application provides the user interaction interface and renders the terminal screen in response /// to user actions. -pub(crate) struct Application { +pub struct Application { /// The application's state and data. - pub(crate) data: AppData, + pub data: Data, /// Renders and displays objects to the terminal. pub(crate) display: ScreenHandler, /// The labels at the bottom of the UI that provide information /// based on the current offset. - pub(crate) labels: LabelHandler, + pub labels: LabelHandler, /// The window that handles keyboard input. This is usually in the form of the Hex/ASCII editor /// or popups. - pub(crate) key_handler: Box, + pub key_handler: Box, } impl Application { /// Creates a new application, focusing the Hex editor and starting with an offset of 0 by /// default. This is called once at the beginning of the program. /// + /// # Errors + /// /// This errors out if the file specified is empty. - pub(crate) fn new( - file: File, - encoding: Encoding, - offset: usize, - ) -> Result> { + pub fn new(file: File, encoding: Encoding, offset: usize) -> Result> { let contents = AsyncBuffer::new(&file)?; if contents.is_empty() { eprintln!("heh does not support editing empty files"); @@ -164,7 +167,7 @@ impl Application { let display = ScreenHandler::new()?; let app = Self { - data: AppData { + data: Data { file, contents, encoding, @@ -193,11 +196,16 @@ impl Application { /// A loop that repeatedly renders the terminal and modifies state based on input. Is stopped /// when input handling receives CNTRLq, the command to stop. - pub(crate) fn run(&mut self) -> Result<(), Box> { + /// + /// # Errors + /// + /// This errors when the UI fails to render. + pub fn run(&mut self) -> Result<(), Box> { ScreenHandler::setup()?; loop { self.render_display()?; - if !self.handle_input()? { + let event = event::read()?; + if !self.handle_input(&event)? { break; } } @@ -208,25 +216,54 @@ impl Application { /// Renders the display. This is a wrapper around [`ScreenHandler`'s /// render](ScreenHandler::render) method. fn render_display(&mut self) -> Result<(), Box> { - self.display.render(&mut self.data, &self.labels, self.key_handler.as_ref())?; - Ok(()) + self.display.render(&mut self.data, &self.labels, self.key_handler.as_ref()) + } + + /// Renders a single frame for the given area. + pub fn render_frame(&mut self, frame: &mut Frame, area: Rect) { + self.data.contents.compute_new_window(self.data.offset); + // We check if we need to recompute the terminal size in the case that the saved off + // variable differs from the current frame, which can occur when a terminal is resized + // between an event handling and a rendering. + if area != self.display.terminal_size { + self.display.terminal_size = area; + self.display.comp_layouts = + ScreenHandler::calculate_dimensions(area, self.key_handler.as_ref()); + // We change the start_address here to ensure that 0 is ALWAYS the first start + // address. We round to preventing constant resizing always moving to 0. + self.data.start_address = (self.data.start_address + + (self.display.comp_layouts.bytes_per_line / 2)) + / self.display.comp_layouts.bytes_per_line + * self.display.comp_layouts.bytes_per_line; + } + ScreenHandler::render_frame( + frame, + self.display.terminal_size, + &mut self.data, + &self.labels, + self.key_handler.as_ref(), + &self.display.comp_layouts, + ); } /// Handles all forms of user input. This calls out to code in [input], which uses /// [Application's `key_handler` method](Application::key_handler) to determine what to do for /// key input. - fn handle_input(&mut self) -> Result> { - let event = event::read()?; + /// + /// # Errors + /// + /// This errors when handling the key event fails. + pub fn handle_input(&mut self, event: &Event) -> Result> { match event { Event::Key(key) => { if key.kind == KeyEventKind::Press { self.labels.notification.clear(); - return input::handle_key_input(self, key); + return input::handle_key_input(self, *key); } } Event::Mouse(mouse) => { self.labels.notification.clear(); - input::handle_mouse_input(self, mouse); + input::handle_mouse_input(self, *mouse); } Event::Resize(_, _) | Event::FocusGained | Event::FocusLost | Event::Paste(_) => {} } diff --git a/src/decoder.rs b/src/decoder.rs index 51b193b..198737f 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -1,3 +1,5 @@ +//! Decoder utilities. + use std::str::from_utf8; use crate::character::{Category, RichChar, Type, CHARACTER_FILL, CHARACTER_UNKNOWN}; @@ -80,7 +82,7 @@ impl<'a> Iterator for LossyUTF8Decoder<'a> { } #[derive(Copy, Clone, Debug)] -pub(crate) enum Encoding { +pub enum Encoding { Ascii, Utf8, } diff --git a/src/label.rs b/src/label.rs index cc2e1ee..028cdce 100644 --- a/src/label.rs +++ b/src/label.rs @@ -42,7 +42,7 @@ impl fmt::Display for Endianness { } #[derive(Default)] -pub(crate) struct LabelHandler { +pub struct Handler { signed_eight: String, signed_sixteen: String, signed_thirtytwo: String, @@ -59,11 +59,11 @@ pub(crate) struct LabelHandler { stream_length: usize, stream_length_string: String, pub(crate) offset: String, - pub(crate) notification: String, + pub notification: String, pub(crate) endianness: Endianness, } -impl Index<&str> for LabelHandler { +impl Index<&str> for Handler { type Output = String; fn index(&self, index: &str) -> &Self::Output { @@ -89,7 +89,7 @@ impl Index<&str> for LabelHandler { } } -impl LabelHandler { +impl Handler { pub(crate) fn new(bytes: &[u8], offset: usize) -> Self { let mut labels = Self { ..Default::default() }; labels.update_stream_length(8); @@ -243,7 +243,7 @@ mod tests { fn test_binary_label() { // Given a label handler with the content 'hello' and offset of 0 let content = "hello".as_bytes(); - let mut label_handler = LabelHandler::new(content, 0); + let mut label_handler = Handler::new(content, 0); // The binary label should contain the binary veresion of the first character assert!(label_handler.binary.eq("01101000")); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e14d1db --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +pub mod app; +mod buffer; +mod character; +mod chunk; +pub mod decoder; +pub mod input; +pub mod label; +pub mod screen; +pub mod windows; diff --git a/src/main.rs b/src/main.rs index 4b09420..0790311 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,19 +10,8 @@ use std::{error::Error, fs::OpenOptions, io, process}; use clap::{Parser, ValueEnum}; use crossterm::tty::IsTty; -use app::Application; - -use crate::decoder::Encoding; - -mod app; -mod buffer; -mod character; -mod chunk; -mod decoder; -mod input; -mod label; -mod screen; -mod windows; +use heh::app::Application; +use heh::decoder::Encoding; const ABOUT: &str = " A HEx Helper to edit bytes by the nibble. diff --git a/src/screen.rs b/src/screen.rs index ab0f641..8837f8b 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -18,26 +18,26 @@ use ratatui::{ style::{Color, Style}, text::{Line, Span, Text}, widgets::{Block, Borders, Clear, Paragraph}, - Terminal, + Frame, Terminal, }; use crate::chunk::OverlappingChunks; use crate::{ - app::{AppData, Nibble}, + app::{Data, Nibble}, decoder::ByteAlignedDecoder, - label::{LabelHandler, LABEL_TITLES}, + label::{Handler as LabelHandler, LABEL_TITLES}, windows::{editor::Editor, KeyHandler, Window}, }; const COLOR_NULL: Color = Color::DarkGray; -pub(crate) struct ScreenHandler { - terminal: Terminal>, - pub(crate) terminal_size: Rect, - pub(crate) comp_layouts: ComponentLayouts, +pub struct Handler { + pub terminal: Terminal>, + pub terminal_size: Rect, + pub comp_layouts: ComponentLayouts, } -pub(crate) struct ComponentLayouts { +pub struct ComponentLayouts { line_numbers: Rect, pub(crate) hex: Rect, pub(crate) ascii: Rect, @@ -47,8 +47,13 @@ pub(crate) struct ComponentLayouts { pub(crate) lines_per_screen: usize, } -impl ScreenHandler { - pub(crate) fn new() -> Result> { +impl Handler { + /// Creates a new screen handler. + /// + /// # Errors + /// + /// This errors when constructing the terminal or retrieving the terminal size fails. + pub fn new() -> Result> { let terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?; let terminal_size = terminal.size()?; Ok(Self { @@ -94,7 +99,7 @@ impl ScreenHandler { /// Calculates the dimensions of the components that will be continually displayed. /// /// This includes the editors, labels, and address table. - fn calculate_dimensions(frame: Rect, window: &dyn KeyHandler) -> ComponentLayouts { + pub fn calculate_dimensions(frame: Rect, window: &dyn KeyHandler) -> ComponentLayouts { // Establish Constraints let sections = Layout::default() .direction(Direction::Vertical) @@ -169,7 +174,7 @@ impl ScreenHandler { /// Generates all the visuals of the file contents to be displayed to user by calling /// [`generate_hex`] and [`generate_decoded`]. fn generate_text( - app_info: &mut AppData, + app_info: &mut Data, bytes_per_line: usize, lines_per_screen: usize, ) -> (Text, Text, Text) { @@ -200,17 +205,17 @@ impl ScreenHandler { /// [`calculate_dimensions`](Self::calculate_dimensions). pub(crate) fn render( &mut self, - app_info: &mut AppData, + app_info: &mut Data, labels: &LabelHandler, window: &dyn KeyHandler, ) -> Result<(), Box> { app_info.contents.compute_new_window(app_info.offset); - self.terminal.draw(|f| { + self.terminal.draw(|frame| { // We check if we need to recompute the terminal size in the case that the saved off // variable differs from the current frame, which can occur when a terminal is resized // between an event handling and a rendering. - let size = f.size(); + let size = frame.size(); if size != self.terminal_size { self.terminal_size = size; self.comp_layouts = Self::calculate_dimensions(self.terminal_size, window); @@ -223,80 +228,101 @@ impl ScreenHandler { * self.comp_layouts.bytes_per_line; } - // Check if terminal is large enough - if self.terminal_size.width < 50 || self.terminal_size.height < 15 { - let dimension_notification = Paragraph::new("Terminal dimensions must be larger!") - .block(Block::default()) - .alignment(Alignment::Center); - let vertical_center = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Percentage(40), - Constraint::Percentage(20), - Constraint::Percentage(40), - ]) - .split(self.terminal_size); - f.render_widget(dimension_notification, vertical_center[1]); - return; - } - - let (address_text, hex_text, ascii_text) = Self::generate_text( + Self::render_frame( + frame, + self.terminal_size, app_info, - self.comp_layouts.bytes_per_line, - self.comp_layouts.lines_per_screen, + labels, + window, + &self.comp_layouts, ); + })?; + Ok(()) + } - // Render Line Numbers - f.render_widget( - Paragraph::new(address_text) - .block(Block::default().borders(Borders::ALL).title("Address")), - self.comp_layouts.line_numbers, - ); + /// Display the addresses, editors, labels, and popups based off of the specifications of + /// [`ComponentLayouts`], defined by + /// [`calculate_dimensions`](Self::calculate_dimensions). + pub(crate) fn render_frame( + frame: &mut Frame, + area: Rect, + app_info: &mut Data, + labels: &LabelHandler, + window: &dyn KeyHandler, + comp_layouts: &ComponentLayouts, + ) { + // Check if terminal is large enough + if area.width < 50 || area.height < 15 { + let dimension_notification = Paragraph::new("Terminal dimensions must be larger!") + .block(Block::default()) + .alignment(Alignment::Center); + let vertical_center = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage(40), + Constraint::Percentage(20), + Constraint::Percentage(40), + ]) + .split(area); + frame.render_widget(dimension_notification, vertical_center[1]); + return; + } - // Render Hex - f.render_widget( - Paragraph::new(hex_text).block( - Block::default().borders(Borders::ALL).title("Hex").style( - if window.is_focusing(Window::Hex) { - Style::default().fg(Color::Yellow) - } else { - Style::default() - }, - ), + let (address_text, hex_text, ascii_text) = Self::generate_text( + app_info, + comp_layouts.bytes_per_line, + comp_layouts.lines_per_screen, + ); + + // Render Line Numbers + frame.render_widget( + Paragraph::new(address_text) + .block(Block::default().borders(Borders::ALL).title("Address")), + comp_layouts.line_numbers, + ); + + // Render Hex + frame.render_widget( + Paragraph::new(hex_text).block( + Block::default().borders(Borders::ALL).title("Hex").style( + if window.is_focusing(Window::Hex) { + Style::default().fg(Color::Yellow) + } else { + Style::default() + }, ), - self.comp_layouts.hex, - ); - - // Render ASCII - f.render_widget( - Paragraph::new(ascii_text).block( - Block::default().borders(Borders::ALL).title("ASCII").style( - if window.is_focusing(Window::Ascii) { - Style::default().fg(Color::Yellow) - } else { - Style::default() - }, - ), + ), + comp_layouts.hex, + ); + + // Render ASCII + frame.render_widget( + Paragraph::new(ascii_text).block( + Block::default().borders(Borders::ALL).title("ASCII").style( + if window.is_focusing(Window::Ascii) { + Style::default().fg(Color::Yellow) + } else { + Style::default() + }, ), - self.comp_layouts.ascii, + ), + comp_layouts.ascii, + ); + + // Render Info + for (i, label) in comp_layouts.labels.iter().enumerate() { + frame.render_widget( + Paragraph::new(labels[LABEL_TITLES[i]].clone()) + .block(Block::default().borders(Borders::ALL).title(LABEL_TITLES[i])), + *label, ); + } - // Render Info - for (i, label) in self.comp_layouts.labels.iter().enumerate() { - f.render_widget( - Paragraph::new(labels[LABEL_TITLES[i]].clone()) - .block(Block::default().borders(Borders::ALL).title(LABEL_TITLES[i])), - *label, - ); - } - - // Render Popup - if !window.is_focusing(Window::Hex) && !window.is_focusing(Window::Ascii) { - f.render_widget(Clear, self.comp_layouts.popup); - f.render_widget(window.widget(), self.comp_layouts.popup); - } - })?; - Ok(()) + // Render Popup + if !window.is_focusing(Window::Hex) && !window.is_focusing(Window::Ascii) { + frame.render_widget(Clear, comp_layouts.popup); + frame.render_widget(window.widget(), comp_layouts.popup); + } } } @@ -306,7 +332,7 @@ impl ScreenHandler { /// NOTE: In UTF-8, a character takes up to 4 bytes and thus the encoding can break at the ends of a /// chunk. Increasing the chunk size by 3 bytes at both ends before decoding and cropping them of /// afterwards solves the issue for the visible parts. -fn generate_hex(app_info: &AppData, bytes_per_line: usize, lines_per_screen: usize) -> Vec { +fn generate_hex(app_info: &Data, bytes_per_line: usize, lines_per_screen: usize) -> Vec { let initial_offset = app_info.start_address.min(3); OverlappingChunks::new( &app_info.contents[(app_info.start_address - initial_offset)..], @@ -395,11 +421,7 @@ fn generate_hex(app_info: &AppData, bytes_per_line: usize, lines_per_screen: usi /// NOTE: In UTF-8, a character takes up to 4 bytes and thus the encoding can break at the ends of a /// chunk. Increasing the chunk size by 3 bytes at both ends before decoding and cropping them of /// afterwards solves the issue for the visible parts. -fn generate_decoded( - app_info: &AppData, - bytes_per_line: usize, - lines_per_screen: usize, -) -> Vec { +fn generate_decoded(app_info: &Data, bytes_per_line: usize, lines_per_screen: usize) -> Vec { let initial_offset = app_info.start_address.min(3); OverlappingChunks::new( &app_info.contents[(app_info.start_address - initial_offset)..], @@ -474,8 +496,7 @@ mod tests { // Given a terminal size of 100 x 100, when dimensions are calculated let key_handler: Box = Box::from(Editor::Ascii); - let layout = - ScreenHandler::calculate_dimensions(Rect::new(0, 0, width, height), &*key_handler); + let layout = Handler::calculate_dimensions(Rect::new(0, 0, width, height), &*key_handler); // The "editors" section, which consists of the line number column, Hex input box, and // ASCII input box should have a size of height - 12 (there are 4 labels per column and diff --git a/src/windows/editor.rs b/src/windows/editor.rs index dda4a61..6cb1c6f 100644 --- a/src/windows/editor.rs +++ b/src/windows/editor.rs @@ -1,9 +1,9 @@ use std::cmp; use crate::{ - app::{Action, AppData, Nibble}, - label::LabelHandler, - screen::ScreenHandler, + app::{Action, Data, Nibble}, + label::Handler as LabelHandler, + screen::Handler as ScreenHandler, }; use super::{ @@ -26,7 +26,7 @@ impl KeyHandler for Editor { Self::Hex => window_type == Window::Hex, } } - fn left(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn left(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { app.last_drag = None; app.drag_nibble = None; match self { @@ -45,7 +45,7 @@ impl KeyHandler for Editor { } } } - fn right(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn right(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { app.last_drag = None; app.drag_nibble = None; match self { @@ -64,7 +64,7 @@ impl KeyHandler for Editor { } } } - fn up(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn up(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { app.last_drag = None; app.drag_nibble = None; if let Some(new_offset) = app.offset.checked_sub(display.comp_layouts.bytes_per_line) { @@ -73,7 +73,7 @@ impl KeyHandler for Editor { adjust_offset(app, display, labels); } } - fn down(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn down(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { app.last_drag = None; app.drag_nibble = None; if let Some(new_offset) = app.offset.checked_add(display.comp_layouts.bytes_per_line) { @@ -84,7 +84,7 @@ impl KeyHandler for Editor { } } } - fn home(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn home(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { app.last_drag = None; app.drag_nibble = None; let bytes_per_line = display.comp_layouts.bytes_per_line; @@ -96,7 +96,7 @@ impl KeyHandler for Editor { app.nibble = Nibble::Beginning; } } - fn end(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn end(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { app.last_drag = None; app.drag_nibble = None; let bytes_per_line = display.comp_layouts.bytes_per_line; @@ -111,12 +111,7 @@ impl KeyHandler for Editor { app.nibble = Nibble::End; } } - fn page_up( - &mut self, - app: &mut AppData, - display: &mut ScreenHandler, - labels: &mut LabelHandler, - ) { + fn page_up(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { app.last_drag = None; app.drag_nibble = None; app.offset = app.offset.saturating_sub( @@ -127,7 +122,7 @@ impl KeyHandler for Editor { } fn page_down( &mut self, - app: &mut AppData, + app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler, ) { @@ -144,7 +139,7 @@ impl KeyHandler for Editor { } fn backspace( &mut self, - app: &mut AppData, + app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler, ) { @@ -159,12 +154,7 @@ impl KeyHandler for Editor { app.dirty = true; } } - fn delete( - &mut self, - app: &mut AppData, - display: &mut ScreenHandler, - labels: &mut LabelHandler, - ) { + fn delete(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { if app.contents.len() > 1 { app.actions.push(Action::Delete(app.offset, app.contents.remove(app.offset))); labels.update_all(&app.contents[app.offset..]); @@ -174,7 +164,7 @@ impl KeyHandler for Editor { } fn char( &mut self, - app: &mut AppData, + app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler, c: char, @@ -236,12 +226,7 @@ impl KeyHandler for Editor { } } - fn enter( - &mut self, - data: &mut AppData, - display: &mut ScreenHandler, - labels: &mut LabelHandler, - ) { + fn enter(&mut self, data: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { perform_search(data, display, labels, &SearchDirection::Forward); } } diff --git a/src/windows/jump_to_byte.rs b/src/windows/jump_to_byte.rs index 30a3f2d..7824aad 100644 --- a/src/windows/jump_to_byte.rs +++ b/src/windows/jump_to_byte.rs @@ -4,7 +4,7 @@ use ratatui::{ widgets::{Block, Borders, Paragraph}, }; -use crate::{app::AppData, label::LabelHandler, screen::ScreenHandler}; +use crate::{app::Data, label::Handler as LabelHandler, screen::Handler as ScreenHandler}; use super::{adjust_offset, KeyHandler, PopupOutput, Window}; @@ -22,16 +22,16 @@ impl KeyHandler for JumpToByte { fn is_focusing(&self, window_type: Window) -> bool { window_type == Window::JumpToByte } - fn char(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler, c: char) { + fn char(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler, c: char) { self.input.push(c); } fn get_user_input(&self) -> PopupOutput { PopupOutput::Str(&self.input) } - fn backspace(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) { + fn backspace(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) { self.input.pop(); } - fn enter(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn enter(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { let new_offset = if self.input.starts_with("0x") { usize::from_str_radix(&self.input[2..], 16) } else { diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 3d25198..3548679 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -8,13 +8,13 @@ pub(crate) mod unsaved_changes; use ratatui::widgets::Paragraph; -use crate::{app::AppData, label::LabelHandler, screen::ScreenHandler}; +use crate::{app::Data, label::Handler as LabelHandler, screen::Handler as ScreenHandler}; /// An enumeration of all the potential components that can be clicked. Used to identify which /// component has been most recently clicked, and is also used to detmine which window is /// focused in the `Application`'s input field. #[derive(PartialEq, Eq, Copy, Clone)] -pub(crate) enum Window { +pub enum Window { Ascii, Hex, JumpToByte, @@ -26,7 +26,7 @@ pub(crate) enum Window { /// Represents the possible output of a variety of different popups. #[derive(PartialEq, Eq)] -pub(crate) enum PopupOutput<'a> { +pub enum PopupOutput<'a> { Str(&'a str), Boolean(bool), NoOutput, @@ -37,23 +37,23 @@ pub(crate) enum PopupOutput<'a> { /// Depending on what is currently focused, user input can be handled in different ways. For /// example, pressing enter should not modify the opened file in any form, but doing so while the /// "Jump To Byte" popup is focused should attempt to move the cursor to the inputted byte. -pub(crate) trait KeyHandler { +pub trait KeyHandler { /// Checks if the current [`KeyHandler`] is a certain [`Window`]. fn is_focusing(&self, window_type: Window) -> bool; // Methods that handle their respective keypresses. - fn left(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn right(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn up(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn down(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn home(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn end(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn page_up(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn page_down(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn backspace(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn delete(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn enter(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) {} - fn char(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler, _: char) {} + fn left(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn right(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn up(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn down(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn home(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn end(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn page_up(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn page_down(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn backspace(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn delete(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn enter(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) {} + fn char(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler, _: char) {} /// Returns user input. Is currently used to get information from popups. fn get_user_input(&self) -> PopupOutput { @@ -81,7 +81,7 @@ pub(crate) trait KeyHandler { /// If the cursor's location is past the end of the viewports, the viewports will move so that /// the cursor is included in the final row. pub(crate) fn adjust_offset( - app: &mut AppData, + app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler, ) { diff --git a/src/windows/search.rs b/src/windows/search.rs index c44c1ce..8d88216 100644 --- a/src/windows/search.rs +++ b/src/windows/search.rs @@ -4,7 +4,7 @@ use ratatui::{ widgets::{Block, Borders, Paragraph}, }; -use crate::{app::AppData, label::LabelHandler, screen::ScreenHandler}; +use crate::{app::Data, label::Handler as LabelHandler, screen::Handler as ScreenHandler}; use super::{adjust_offset, KeyHandler, PopupOutput, Window}; @@ -33,16 +33,16 @@ impl KeyHandler for Search { fn is_focusing(&self, window_type: super::Window) -> bool { window_type == Window::Search } - fn char(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler, c: char) { + fn char(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler, c: char) { self.input.push(c); } fn get_user_input(&self) -> PopupOutput { PopupOutput::Str(&self.input) } - fn backspace(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) { + fn backspace(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) { self.input.pop(); } - fn enter(&mut self, app: &mut AppData, display: &mut ScreenHandler, labels: &mut LabelHandler) { + fn enter(&mut self, app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler) { let byte_sequence_to_search = match parse_input(&self.input) { Ok(s) => s, Err(e) => { @@ -107,7 +107,7 @@ pub(crate) enum SearchDirection { } pub(crate) fn perform_search( - app: &mut AppData, + app: &mut Data, display: &mut ScreenHandler, labels: &mut LabelHandler, search_direction: &SearchDirection, diff --git a/src/windows/unsaved_changes.rs b/src/windows/unsaved_changes.rs index f431223..5bfbb2c 100644 --- a/src/windows/unsaved_changes.rs +++ b/src/windows/unsaved_changes.rs @@ -5,7 +5,7 @@ use ratatui::{ widgets::{Block, Borders, Paragraph}, }; -use crate::{app::AppData, label::LabelHandler, screen::ScreenHandler}; +use crate::{app::Data, label::Handler as LabelHandler, screen::Handler as ScreenHandler}; use super::{KeyHandler, PopupOutput, Window}; @@ -17,12 +17,12 @@ impl KeyHandler for UnsavedChanges { fn is_focusing(&self, window_type: Window) -> bool { window_type == Window::UnsavedChanges } - fn left(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) { + fn left(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) { if !self.should_quit { self.should_quit = true; } } - fn right(&mut self, _: &mut AppData, _: &mut ScreenHandler, _: &mut LabelHandler) { + fn right(&mut self, _: &mut Data, _: &mut ScreenHandler, _: &mut LabelHandler) { if self.should_quit { self.should_quit = false; }