diff --git a/src/action.rs b/src/action.rs index 5b8be38..48b9185 100644 --- a/src/action.rs +++ b/src/action.rs @@ -285,7 +285,7 @@ impl Buffer { self.clamp_screen_to_primary_cursor(window_size); } - fn scroll_down(&mut self, window_size: WindowSize) { + pub fn scroll_down(&mut self, window_size: WindowSize) { if self.contents.len() <= 5 * BYTES_PER_LINE { return; } self.scroll_position = min( @@ -297,7 +297,7 @@ impl Buffer { self.combine_cursors_if_overlapping(); } - fn scroll_up(&mut self, window_size: WindowSize) { + pub fn scroll_up(&mut self, window_size: WindowSize) { self.scroll_position = self.scroll_position.saturating_sub(BYTES_PER_LINE); self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); self.combine_cursors_if_overlapping(); diff --git a/src/app/mod.rs b/src/app/mod.rs index 6887c22..438ec32 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,7 +1,7 @@ use std::{env, process::exit}; -use crossterm::{event::{self, Event, KeyCode, KeyEvent, KeyModifiers}, terminal::window_size}; -use ratatui::{style::Stylize, text::Span}; -use crate::{BYTES_PER_LINE, action::AppAction, buffer::Buffer, config::Config}; +use crossterm::{ExecutableCommand, event::{self, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}, terminal::window_size}; +use ratatui::{DefaultTerminal, style::Stylize, text::Span}; +use crate::{BYTES_PER_LINE, action::AppAction, buffer::Buffer, config::Config, cursor::Cursor}; mod widget; @@ -35,41 +35,43 @@ impl App { exit(1); } + let window_size = WindowSize { + rows: window_size().unwrap().rows as usize, + covered_rows: if buffers.len() > 1 { + 2 // status line and tab bar + } else { + 1 // status line + }, + }; + Self { config: Config::default(), buffers, current_buffer_index: 0, - window_size: WindowSize { - rows: window_size().unwrap().rows as usize, - // 1 because of the status line - covered_rows: 1, - }, + window_size, should_quit: false, } } - #[allow(clippy::too_many_lines)] - pub fn handle_events(&mut self) { - #[allow(clippy::collapsible_match)] + pub fn handle_events(&mut self, terminal: &mut DefaultTerminal) { match event::read().unwrap() { Event::Resize(_, height) => { self.window_size.rows = height as usize; } - Event::Key(key_event) => self.handle_key(key_event), - // Event::Mouse(mouse_event) => { - // mouse_event.kind - // }, + Event::Key(key_event) => self.handle_key(key_event, terminal), + Event::Mouse(mouse_event) => self.handle_mouse(mouse_event), _ => {} } } - fn handle_key(&mut self, key_event: KeyEvent) { + fn handle_key(&mut self, key_event: KeyEvent, terminal: &mut DefaultTerminal) { if key_event.modifiers == KeyModifiers::CONTROL && key_event.code == KeyCode::Char('c') { + terminal.backend_mut().execute(DisableMouseCapture).unwrap(); crossterm::terminal::disable_raw_mode().unwrap(); ratatui::restore(); exit(130); @@ -92,6 +94,62 @@ impl App { } } + fn handle_mouse(&mut self, mouse_event: MouseEvent) { + let tab_bar_rows = usize::from(self.buffers.len() > 1); + let current_buffer = &mut self.buffers[self.current_buffer_index]; + + match mouse_event.kind { + MouseEventKind::Down(_) => { + let byte_column = match mouse_event.column { + 10..=11 => Some(0), + 13..=14 => Some(1), + 16..=17 => Some(2), + 19..=20 => Some(3), + + 23..=24 => Some(4), + 26..=27 => Some(5), + 29..=30 => Some(6), + 32..=33 => Some(7), + + 36..=37 => Some(8), + 39..=40 => Some(9), + 42..=43 => Some(10), + 45..=46 => Some(11), + + 49..=50 => Some(12), + 52..=53 => Some(13), + 55..=56 => Some(14), + 58..=59 => Some(15), + + _ => None, + }; + + + if let Some(byte_column) = byte_column && + mouse_event.row as usize - tab_bar_rows < self.window_size.hex_rows() + { + current_buffer.primary_cursor = Cursor::at( + current_buffer.scroll_position + + (mouse_event.row as usize - tab_bar_rows) * BYTES_PER_LINE + + byte_column + ); + current_buffer.cursors.clear(); + } + }, + MouseEventKind::ScrollDown => { + for _ in 0..3 { + current_buffer.scroll_down(self.window_size); + } + }, + MouseEventKind::ScrollUp => { + for _ in 0..3 { + current_buffer.scroll_up(self.window_size); + } + }, + _ => (), + } + } + fn quit_if_saved(&mut self) { if self.buffers.iter().all(Buffer::all_changes_saved) { self.quit(); diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 9311555..0dfce9e 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -88,7 +88,7 @@ impl Buffer { primary_cursor: Cursor::default(), cursors: Vec::new(), - marks: HashSet::from([0, 4, 12, 15, 16, 17]), + marks: HashSet::new(), mode: Mode::Normal, partial_action: None, diff --git a/src/main.rs b/src/main.rs index 308360f..9bd1e09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ #![feature(hash_set_entry)] use app::App; +use crossterm::{QueueableCommand, event::{DisableMouseCapture, EnableMouseCapture}}; mod app; mod buffer; @@ -22,9 +23,9 @@ const BYTES_PER_CHUNK: usize = 4; const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK; // TODO: -// - m mark offset // - search // - s/A-k/A-K +// - C-a/C-x // - modifications // - insert/append // - mode @@ -56,15 +57,17 @@ fn main() { let mut app = App::new(); let mut terminal = ratatui::init(); crossterm::terminal::enable_raw_mode().unwrap(); + terminal.backend_mut().queue(EnableMouseCapture).unwrap(); while !app.should_quit { terminal.draw(|frame| { frame.render_widget(&app, frame.area()); }).unwrap(); - app.handle_events(); + app.handle_events(&mut terminal); } + terminal.backend_mut().queue(DisableMouseCapture).unwrap(); crossterm::terminal::disable_raw_mode().unwrap(); ratatui::restore();