From ca7f22ae34aa23a5b81916d0004e53bd5fe9cced Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 20 Jun 2026 10:26:04 +0300 Subject: [PATCH] tlc: start_line plumbing + buttonbar in editor/viewer Editor gains goto_line(), viewer gains jump_to_line() + open_file(start_line). Both editor and viewer now render the F-key buttonbar at the bottom (1Help 2Save... / 1Help 2Wrap...). --- .../recipes/tui/tlc/source/src/editor/mod.rs | 13 +++++++- .../tui/tlc/source/src/editor/render.rs | 20 +++++++++-- .../recipes/tui/tlc/source/src/viewer/mod.rs | 33 +++++++++++++++++-- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/local/recipes/tui/tlc/source/src/editor/mod.rs b/local/recipes/tui/tlc/source/src/editor/mod.rs index 0c4f710764..f18e6f95fb 100644 --- a/local/recipes/tui/tlc/source/src/editor/mod.rs +++ b/local/recipes/tui/tlc/source/src/editor/mod.rs @@ -299,7 +299,18 @@ bracket_flash: None, } } - /// Borrow the underlying buffer. + /// Jump cursor to a 1-based line number and scroll the + /// viewport so the line is visible. + pub fn goto_line(&mut self, line: u32) { + if line >= 1 { + if let Ok(off) = crate::editor::goto::line_to_offset(&self.buffer, line) { + self.buffer.set_cursor(off); + self.cursor.set_position(self.buffer.cursor(), &self.buffer); + } + } + } + + /// Borrow the underlying buffer. #[must_use] pub fn buffer(&self) -> &Buffer { &self.buffer diff --git a/local/recipes/tui/tlc/source/src/editor/render.rs b/local/recipes/tui/tlc/source/src/editor/render.rs index 580e955e7c..b8f1117df4 100644 --- a/local/recipes/tui/tlc/source/src/editor/render.rs +++ b/local/recipes/tui/tlc/source/src/editor/render.rs @@ -609,8 +609,24 @@ impl Editor { } } - // Status line (last line of the editor area). - if area.height >= 2 { + // Status line + buttonbar. + if area.height >= 3 { + let status_y = area.y + area.height - 2; + let status = Paragraph::new(Line::from(Span::styled( + self.status_string(), + Style::default().fg(linestate_fg).bg(linestate_bg), + ))); + frame.render_widget(status, Rect::new(area.x, status_y, area.width, 1)); + + let bar_y = area.y + area.height - 1; + let bar_area = Rect::new(area.x, bar_y, area.width, 1); + let labels: [(&str, &str); 10] = [ + ("1", "Help"), ("2", "Save"), ("3", "Mark"), ("4", "Repl"), + ("5", "Copy"), ("6", "Move"), ("7", "Search"), ("8", "Delete"), + ("9", "PullDn"), ("10", "Quit"), + ]; + crate::widget::buttonbar::render_buttonbar(frame, bar_area, theme, &labels); + } else if area.height >= 2 { let status_y = area.y + area.height - 1; let status = Paragraph::new(Line::from(Span::styled( self.status_string(), diff --git a/local/recipes/tui/tlc/source/src/viewer/mod.rs b/local/recipes/tui/tlc/source/src/viewer/mod.rs index cbbbedf730..7e8c811813 100644 --- a/local/recipes/tui/tlc/source/src/viewer/mod.rs +++ b/local/recipes/tui/tlc/source/src/viewer/mod.rs @@ -255,6 +255,14 @@ impl Viewer { } } + /// Jump to a 1-based line number. + pub fn jump_to_line(&mut self, line: u64) { + if line >= 1 { + self.top = line.saturating_sub(1); + self.sync_cursor_to_top(); + } + } + fn current_line(&self) -> u64 { self.goto .resolve(self.cursor, goto::GotoKind::Offset) @@ -406,17 +414,26 @@ impl Viewer { let selected_fg = viewer_selected.map(|p| p.fg).unwrap_or(theme.cursor_fg); let selected_bg = viewer_selected.map(|p| p.bg).unwrap_or(theme.cursor_bg); - let constraints = if area.height >= 3 { + let constraints = if area.height >= 4 { [ Constraint::Length(1), Constraint::Min(1), Constraint::Length(1), + Constraint::Length(1), + ] + } else if area.height >= 3 { + [ + Constraint::Length(1), + Constraint::Min(1), + Constraint::Length(1), + Constraint::Length(0), ] } else { [ Constraint::Length(0), Constraint::Min(1), Constraint::Length(0), + Constraint::Length(0), ] }; let chunks = Layout::default() @@ -465,6 +482,15 @@ impl Viewer { ); } + if chunks[3].height > 0 { + let labels: [(&str, &str); 10] = [ + ("1", "Help"), ("2", "Wrap"), ("3", ""), ("4", "Hex"), + ("5", ""), ("6", ""), ("7", "Search"), ("8", "Magic"), + ("9", ""), ("10", "Quit"), + ]; + crate::widget::buttonbar::render_buttonbar(frame, chunks[3], theme, &labels); + } + // Prompt overlay: rendered on the top row so it doesn't // collide with the footer area below. The body's border // block still owns the central chunk. @@ -740,13 +766,16 @@ impl Viewer { } /// Backwards-compat shim: `open_file` was the Phase 0 stub. -pub fn open_file(file: &str) -> Result<()> { +pub fn open_file(file: &str, start_line: Option) -> Result<()> { use crate::terminal::event::translate_key; use crate::terminal::color::DEFAULT_THEME; use termion::event::Event as TermEvent; use termion::input::TermReadEventsAndRaw; let mut v = Viewer::open(file)?; + if let Some(line) = start_line { + v.jump_to_line(line); + } let mut tui = crate::terminal::Tui::new()?; let stdin = std::io::stdin();