From c9bca4946f43629e9b5a3f6549dc3851c6e713b3 Mon Sep 17 00:00:00 2001 From: Namilskyy Date: Tue, 1 Apr 2025 22:29:47 +0300 Subject: Implemented the scrolling fucntion --- src/render.rs | 133 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 36 deletions(-) (limited to 'src/render.rs') diff --git a/src/render.rs b/src/render.rs index a0cb234..061dbfe 100644 --- a/src/render.rs +++ b/src/render.rs @@ -3,12 +3,22 @@ use ratatui::{ layout::{Constraint, Direction, Layout}, style::{Color, Style}, text::{Line, Span}, - widgets::{Block, Borders, Paragraph}, + widgets::{ + Block, Borders, Paragraph, ScrollbarState, Scrollbar, ScrollbarOrientation, + }, Terminal, prelude::Alignment, + symbols, }; -pub fn draw(terminal: &mut Terminal, data: &[u8], cursor: usize, filename: &str) -> io::Result<()> { +pub fn draw( + terminal: &mut Terminal, + data: &[u8], + cursor: usize, + filename: &str, + scroll_position: usize, + scrollbar_state: &mut ScrollbarState, +) -> io::Result<()> { terminal.draw(|frame| { let size = frame.size(); let layout = Layout::default() @@ -16,52 +26,103 @@ pub fn draw(terminal: &mut Terminal, data: &[u8] .constraints([Constraint::Min(1), Constraint::Length(1)]) .split(size); - let lines: Vec = data.chunks(16).enumerate().map(|(i, chunk)| { - let addr = Span::raw(format!("{:08x} │ ", i * 16)); - let mut hex: Vec = Vec::new(); - let mut ascii_repr = String::new(); - for (j, &byte) in chunk.iter().enumerate() { - let hex_str = format!("{:02x}", byte); - let index = i * 32 + j * 2; + let total_lines = (data.len() + 15) / 16; + let visible_height = layout[0].height as usize; - for (k, ch) in hex_str.chars().enumerate() { - let cur_idx = index + k; - let style = if cur_idx == cursor { - Style::default().bg(Color::White).fg(Color::Black) + *scrollbar_state = ScrollbarState::new(total_lines) + .viewport_content_length(visible_height) + .position(scroll_position); + + let lines: Vec = data + .chunks(16) + .enumerate() + .skip(scroll_position) + .take(visible_height) + .map(|(i, chunk)| { + let addr = format!("{:08X} │ ", i * 16); + let mut hex_spans = Vec::new(); + let mut ascii = String::new(); + + for (j, &byte) in chunk.iter().enumerate() { + let global_pos = i * 16 + j; + let nibbles = format!("{:02X}", byte); + + for (k, c) in nibbles.chars().enumerate() { + let is_cursor = (global_pos * 2 + k) == cursor; + let style = if is_cursor { + Style::default().bg(Color::White).fg(Color::Black) + } else { + Style::default() + }; + hex_spans.push(Span::styled(c.to_string(), style)); + } + + + if j < 15 { + hex_spans.push(Span::raw(if j % 4 == 3 { " " } else { " " })); + } + + + ascii.push(if byte.is_ascii_graphic() || byte == b' ' { + byte as char } else { - Style::default() - }; - hex.push(Span::styled(ch.to_string(), style)); + '.' + }); } - if j < 15 { - let space = if j % 4 == 3 { " " } else { " " }; - hex.push(Span::raw(space)); + + if chunk.len() < 16 { + let missing = 16 - chunk.len(); + hex_spans.push(Span::raw(" ".repeat(missing * 3))); + ascii.push_str(&".".repeat(missing)); } - ascii_repr.push(if byte.is_ascii_graphic() || byte == b' ' { byte as char } else { '.' }); - } + Line::from(vec![ + Span::raw(addr), + Span::raw(hex_spans.iter().map(|s| s.content.as_ref()).collect::()), + Span::raw(" │ "), + Span::raw(ascii), + ]) + }) + .collect(); + + + frame.render_widget( + Paragraph::new(lines) + .block(Block::default().borders(Borders::ALL)), + layout[0] + ); + - let missing_bytes = 16 - chunk.len(); - let padding = missing_bytes * 3; - let spaces = " ".repeat(padding); + frame.render_stateful_widget( + Scrollbar::default() + .orientation(ScrollbarOrientation::VerticalRight) + .symbols(symbols::scrollbar::VERTICAL), + layout[0], + scrollbar_state, + ); - let ascii = Span::raw(format!("{} │ {}", spaces, ascii_repr)); - Line::from(vec![addr] - .into_iter() - .chain(hex) - .chain(std::iter::once(ascii)) - .collect::>()) - }).collect(); + let progress = if data.is_empty() { + 0 + } else { + (cursor / 2 * 100) / data.len() + }; - frame.render_widget(Paragraph::new(lines).block(Block::default().borders(Borders::ALL)), layout[0]); + let status = format!( + "{} │ Offset: 0x{:08X} │ {}%", + filename, + cursor / 2, + progress + ); - let progress = if data.is_empty() { 100 } else { (cursor / 2 * 100) / data.len() }; - let status_text = format!("{} \t 0x{:08x} \t {:3}%", filename, cursor / 2, progress); - let status_bar = Paragraph::new(Line::from(Span::raw(status_text))).alignment(Alignment::Center); - frame.render_widget(status_bar, layout[1]); + frame.render_widget( + Paragraph::new(status) + .alignment(Alignment::Center) + .block(Block::default().borders(Borders::TOP)), + layout[1] + ); })?; Ok(()) } -- cgit v1.2.3