tlc: phase 16+ — add ButtonKind::Narrow + unicode-safe width tests

After cross-referencing MC's button.c (lib/widget/button.c) and the
ratatui button pattern survey, two gaps remained in Phase 16:

1. MC defines NARROW_BUTTON ([X], no inner padding) — adds for
   future compact toolbar/dialog use. Width formula: label + 2.

2. While width was already chars().count() (Unicode-safe CJK), no
   test asserted this. Added explicit assertion: '日本語' Normal = 7,
   Default = 9 cells.

Tests: 1111 passed (was 1109, +2).
Binaries rebuild clean.

Ref: MC lib/widget/button.c button_flags_t {NORMAL,DEFPUSH,NARROW,HIDDEN}.
This commit is contained in:
vasilito
2026-06-20 16:09:33 +03:00
parent c4d4bdc586
commit 00bc6990eb
@@ -35,6 +35,9 @@ pub enum ButtonKind {
/// MC `DEFPUSH_BUTTON` — `[< X >]`. Enter triggers this when
/// focused.
Default,
/// MC `NARROW_BUTTON` — `[X]` (no inner padding). Used for
/// compact dialogs and toolbar items.
Narrow,
}
/// Per-button render spec.
@@ -85,6 +88,7 @@ pub fn render_button(
let (left, right) = match spec.kind {
ButtonKind::Normal => ("[ ", " ]"),
ButtonKind::Default => ("[< ", " >]"),
ButtonKind::Narrow => ("[", "]"),
};
let mut spans = vec![Span::styled(
left.to_string(),
@@ -133,14 +137,16 @@ pub fn render_button_row(
}
}
/// MC button width formula: `label_chars + 4` (Normal) or
/// `label_chars + 6` (Default).
/// MC button width formula: `label_chars + 4` (Normal), `+ 6`
/// (Default), or `+ 2` (Narrow). All variants use unicode-safe
/// `chars().count()` so CJK labels compute correctly.
#[must_use]
pub fn button_width(spec: ButtonSpec<'_>) -> u16 {
let label_chars = spec.label.chars().count() as u16;
let padding = match spec.kind {
ButtonKind::Normal => 4,
ButtonKind::Default => 6,
ButtonKind::Narrow => 2,
};
label_chars + padding
}
@@ -321,4 +327,37 @@ mod tests {
assert!(s.starts_with("[ "), "want `[ ...`, got {s:?}");
assert!(s.trim_end().ends_with(" ]"), "want `... ]`, got {s:?}");
}
#[test]
fn narrow_button_fused_brackets_no_padding() {
// MC NARROW_BUTTON: "[X]" — label + 2, no inner space.
let s = render_test(
ButtonSpec {
label: "OK",
hotkey: Some('O'),
kind: ButtonKind::Narrow,
},
false,
);
assert!(s.starts_with("[OK"), "want `[OK...`, got {s:?}");
assert!(s.trim_end().ends_with("K]"), "want `...K]`, got {s:?}");
}
#[test]
fn button_width_unicode_safe() {
// 3 CJK characters + Normal = 3 + 4 = 7 cells (not bytes).
let spec = ButtonSpec {
label: "日本語",
hotkey: Some('本'),
kind: ButtonKind::Normal,
};
assert_eq!(button_width(spec), 7);
// Same with Default variant.
let spec_d = ButtonSpec {
label: "日本語",
hotkey: Some('本'),
kind: ButtonKind::Default,
};
assert_eq!(button_width(spec_d), 9);
}
}