From 00bc6990eb719d5b6ade0ee4213b75d44c16501a Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 20 Jun 2026 16:09:33 +0300 Subject: [PATCH] =?UTF-8?q?tlc:=20phase=2016+=20=E2=80=94=20add=20ButtonKi?= =?UTF-8?q?nd::Narrow=20+=20unicode-safe=20width=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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}. --- .../tui/tlc/source/src/widget/button.rs | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/local/recipes/tui/tlc/source/src/widget/button.rs b/local/recipes/tui/tlc/source/src/widget/button.rs index 4261a2ffa4..46426c1d60 100644 --- a/local/recipes/tui/tlc/source/src/widget/button.rs +++ b/local/recipes/tui/tlc/source/src/widget/button.rs @@ -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); + } } \ No newline at end of file