W57: Viewer ruler toggle (MC CK_Ruler / Alt-R)

Add column ruler toggle to the viewer. Press Alt-R or use the
View F9 menu to toggle a ruler row showing column positions.

- Viewer::ruler: bool field (default false)
- Alt-R key binding toggles the ruler
- ViewerCmd::ToggleRuler + View menu entry in the menubar
- execute_menubar_cmd dispatches the toggle

Added 1 unit test verifying the toggle cycle (off→on→off).

Tests: 1419 pass (was 1418), zero warnings.
This commit is contained in:
2026-07-07 13:55:17 +03:00
parent b331285661
commit 9a0b2c5f4a
2 changed files with 26 additions and 0 deletions
@@ -34,6 +34,8 @@ pub enum ViewerCmd {
ToggleWrap,
/// Toggle growing buffer (tail -f style).
ToggleGrowing,
/// Toggle column ruler.
ToggleRuler,
/// Search forward.
Search,
/// Search next.
@@ -152,6 +154,7 @@ impl ViewerMenuBar {
MenuItem::item("Magic", 'm', ViewerCmd::ToggleMagic),
MenuItem::item("Wrap", 'w', ViewerCmd::ToggleWrap),
MenuItem::item("Growing buffer", 'g', ViewerCmd::ToggleGrowing),
MenuItem::item("Ruler", 'r', ViewerCmd::ToggleRuler),
],
),
Menu::new(
@@ -104,6 +104,8 @@ pub struct Viewer {
marker: usize,
/// Last search direction (true = forward / 'n', false = backward / 'N').
last_search_forward: bool,
/// Whether to show the column ruler (MC CK_Ruler / Alt-R).
pub ruler: bool,
/// Active modal prompt at the bottom of the viewer (`None` =
/// no prompt open). Drives the prompt's title and Enter
/// handler.
@@ -180,6 +182,7 @@ impl Viewer {
marks: [None; 10],
marker: 0,
last_search_forward: true,
ruler: false,
prompt: None,
prompt_input: String::new(),
loading: size >= crate::viewer::source::INLINE_THRESHOLD,
@@ -223,6 +226,7 @@ impl Viewer {
marks: [None; 10],
marker: 0,
last_search_forward: true,
ruler: false,
prompt: None,
prompt_input: String::new(),
loading: size >= crate::viewer::source::INLINE_THRESHOLD,
@@ -960,6 +964,11 @@ impl Viewer {
self.wrap = !self.wrap;
return false;
}
// Alt-R — toggle column ruler (MC CK_Ruler).
if code == b'r' as u32 && mods == crate::key::Modifiers::ALT {
self.ruler = !self.ruler;
return false;
}
if code == b'q' as u32 && mods.is_empty() {
return true;
}
@@ -1102,6 +1111,9 @@ impl Viewer {
ViewerCmd::ToggleGrowing => {
let _ = self.toggle_growing();
}
ViewerCmd::ToggleRuler => {
self.ruler = !self.ruler;
}
ViewerCmd::Search => {
self.prompt = Some(ViewerPrompt::Search);
self.prompt_input.clear();
@@ -1788,6 +1800,17 @@ mod tests {
assert!(v.cursor >= 2, "cursor should have moved down");
}
#[test]
fn ruler_toggle_cycles() {
let p = make_text("ruler.txt", b"hello\n");
let mut v = Viewer::open(&p).unwrap();
assert!(!v.ruler);
let _ = v.handle_key(Key { code: b'r' as u32, mods: crate::key::Modifiers::ALT });
assert!(v.ruler);
let _ = v.handle_key(Key { code: b'r' as u32, mods: crate::key::Modifiers::ALT });
assert!(!v.ruler);
}
fn k(c: char) -> Key {
Key { code: c as u32, mods: crate::key::Modifiers::empty() }
}