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...).
This commit is contained in:
2026-06-20 10:26:04 +03:00
parent a351544731
commit ca7f22ae34
3 changed files with 61 additions and 5 deletions
+12 -1
View File
@@ -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
@@ -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(),
+31 -2
View File
@@ -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<u64>) -> 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();