From e0b84e3c6dc698706217a600102a7371c4f95d5e Mon Sep 17 00:00:00 2001 From: Kiril Ivanov Date: Sat, 31 May 2025 16:48:15 +0300 Subject: [PATCH 1/5] Add empty checkbox, separate from align-only --- src/bin/edit/draw_menubar.rs | 7 +++++- src/tui.rs | 42 ++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/bin/edit/draw_menubar.rs b/src/bin/edit/draw_menubar.rs index 07b7c713d056..6a3c157637e9 100644 --- a/src/bin/edit/draw_menubar.rs +++ b/src/bin/edit/draw_menubar.rs @@ -113,7 +113,12 @@ fn draw_menu_view(ctx: &mut Context, state: &mut State) { if ctx.menubar_menu_button(loc(LocId::FileGoto), 'G', kbmod::CTRL | vk::G) { state.wants_goto = true; } - if ctx.menubar_menu_checkbox(loc(LocId::ViewWordWrap), 'W', kbmod::ALT | vk::Z, word_wrap) { + if ctx.menubar_menu_checkbox( + loc(LocId::ViewWordWrap), + 'W', + kbmod::ALT | vk::Z, + word_wrap.into(), + ) { tb.set_word_wrap(!word_wrap); ctx.needs_rerender(); } diff --git a/src/tui.rs b/src/tui.rs index 87d2b4a28c63..85a368391b89 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -259,11 +259,32 @@ pub enum Overflow { TruncateTail, } +/// If specified, a checkmark 🗹 / ☐ to be prepended to the button text +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub enum Checkmark { + /// No checkmark (or space for it) + #[default] + Absent, + /// Add whitespace where a checkmark would be + /// (to keep aligned with checkmark'd buttons) + AlignOnly, + /// Add a 🗹 prefix + Checked, + /// Add a ☐ prefix + Unchecked, +} + +impl From for Checkmark { + fn from(checked: bool) -> Self { + if checked { Checkmark::Checked } else { Checkmark::Unchecked } + } +} + /// Controls the style with which a button label renders #[derive(Clone, Copy)] pub struct ButtonStyle { accelerator: Option, - checked: Option, + checkmark: Checkmark, bracketed: bool, } @@ -275,8 +296,8 @@ impl ButtonStyle { Self { accelerator: Some(char), ..self } } /// Draw a checkbox prefix: `[🗹 Example Button]` - pub fn checked(self, checked: bool) -> Self { - Self { checked: Some(checked), ..self } + pub fn checkmark(self, checkmark: Checkmark) -> Self { + Self { checkmark, ..self } } /// Draw with or without brackets: `[Example Button]` or `Example Button` pub fn bracketed(self, bracketed: bool) -> Self { @@ -288,7 +309,7 @@ impl Default for ButtonStyle { fn default() -> Self { Self { accelerator: None, - checked: None, + checkmark: Checkmark::Absent, bracketed: true, // Default style for most buttons. Brackets may be disabled e.g. for buttons in menus } } @@ -3142,7 +3163,7 @@ impl<'a> Context<'a, '_> { accelerator: char, shortcut: InputKey, ) -> bool { - self.menubar_menu_checkbox(text, accelerator, shortcut, false) + self.menubar_menu_checkbox(text, accelerator, shortcut, Checkmark::AlignOnly) } /// Appends a checkbox to the current menu. @@ -3152,7 +3173,7 @@ impl<'a> Context<'a, '_> { text: &str, accelerator: char, shortcut: InputKey, - checked: bool, + checkmark: Checkmark, ) -> bool { self.table_next_row(); self.attr_focusable(); @@ -3173,7 +3194,7 @@ impl<'a> Context<'a, '_> { self.button_label( "menu_checkbox", text, - ButtonStyle::default().bracketed(false).checked(checked).accelerator(accelerator), + ButtonStyle::default().bracketed(false).checkmark(checkmark).accelerator(accelerator), ); self.menubar_shortcut(shortcut); @@ -3227,8 +3248,11 @@ impl<'a> Context<'a, '_> { if style.bracketed { self.styled_label_add_text("["); } - if let Some(checked) = style.checked { - self.styled_label_add_text(if checked { "🗹 " } else { " " }); + match style.checkmark { + Checkmark::Absent => {} + Checkmark::AlignOnly => self.styled_label_add_text(" "), + Checkmark::Checked => self.styled_label_add_text("🗹 "), + Checkmark::Unchecked => self.styled_label_add_text("☐ "), } // Label text match style.accelerator { From 8cff670ee439defd30ebfed2dc6ec174785a6397 Mon Sep 17 00:00:00 2001 From: Kiril Ivanov Date: Sat, 31 May 2025 18:08:10 +0300 Subject: [PATCH 2/5] Refactor checkbox to use button --- src/tui.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/tui.rs b/src/tui.rs index 85a368391b89..9346b2a6fe26 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -2025,17 +2025,9 @@ impl<'a> Context<'a, '_> { /// Creates a checkbox with the given text. /// Returns true if the checkbox was activated. pub fn checkbox(&mut self, classname: &'static str, text: &str, checked: &mut bool) -> bool { - self.styled_label_begin(classname); - self.attr_focusable(); - if self.is_focused() { - self.attr_reverse(); - } - self.styled_label_add_text(if *checked { "[🗹 " } else { "[☐ " }); - self.styled_label_add_text(text); - self.styled_label_add_text("]"); - self.styled_label_end(); + let activated = + self.button(classname, text, ButtonStyle::default().checkmark((*checked).into())); - let activated = self.button_activated(); if activated { *checked = !*checked; } From ae996d1bb8619db36460e4b54f460ccfcff876a4 Mon Sep 17 00:00:00 2001 From: Kiril Ivanov Date: Sat, 31 May 2025 18:23:43 +0300 Subject: [PATCH 3/5] Change overtype indicator to a checkbox --- src/bin/edit/draw_statusbar.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bin/edit/draw_statusbar.rs b/src/bin/edit/draw_statusbar.rs index 474d7dd383b0..566f16783626 100644 --- a/src/bin/edit/draw_statusbar.rs +++ b/src/bin/edit/draw_statusbar.rs @@ -161,8 +161,9 @@ pub fn draw_statusbar(ctx: &mut Context, state: &mut State) { &arena_format!(ctx.arena(), "{}/{}", tb.logical_line_count(), tb.visual_line_count(),), ); - if tb.is_overtype() && ctx.button("overtype", "OVR", ButtonStyle::default()) { - tb.set_overtype(false); + let mut is_overtype = tb.is_overtype(); + if ctx.checkbox("overtype", "OVR", &mut is_overtype) { + tb.set_overtype(is_overtype); ctx.needs_rerender(); } From 48fd7d599482e7d799040d1f6f3bb57129a97baa Mon Sep 17 00:00:00 2001 From: Kiril Ivanov Date: Mon, 2 Jun 2025 18:17:42 +0300 Subject: [PATCH 4/5] Revert "Change overtype indicator to a checkbox" This reverts commit ae996d1 (post-rebase) / da527ec (pre-rabse). --- src/bin/edit/draw_statusbar.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bin/edit/draw_statusbar.rs b/src/bin/edit/draw_statusbar.rs index 566f16783626..474d7dd383b0 100644 --- a/src/bin/edit/draw_statusbar.rs +++ b/src/bin/edit/draw_statusbar.rs @@ -161,9 +161,8 @@ pub fn draw_statusbar(ctx: &mut Context, state: &mut State) { &arena_format!(ctx.arena(), "{}/{}", tb.logical_line_count(), tb.visual_line_count(),), ); - let mut is_overtype = tb.is_overtype(); - if ctx.checkbox("overtype", "OVR", &mut is_overtype) { - tb.set_overtype(is_overtype); + if tb.is_overtype() && ctx.button("overtype", "OVR", ButtonStyle::default()) { + tb.set_overtype(false); ctx.needs_rerender(); } From 5ca82563ede93d9962c1a754d36b8c446d22eff0 Mon Sep 17 00:00:00 2001 From: Kiril Ivanov Date: Mon, 2 Jun 2025 19:01:29 +0300 Subject: [PATCH 5/5] Hide checkmark internals from tui API --- src/bin/edit/draw_menubar.rs | 7 +------ src/tui.rs | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/bin/edit/draw_menubar.rs b/src/bin/edit/draw_menubar.rs index 6a3c157637e9..07b7c713d056 100644 --- a/src/bin/edit/draw_menubar.rs +++ b/src/bin/edit/draw_menubar.rs @@ -113,12 +113,7 @@ fn draw_menu_view(ctx: &mut Context, state: &mut State) { if ctx.menubar_menu_button(loc(LocId::FileGoto), 'G', kbmod::CTRL | vk::G) { state.wants_goto = true; } - if ctx.menubar_menu_checkbox( - loc(LocId::ViewWordWrap), - 'W', - kbmod::ALT | vk::Z, - word_wrap.into(), - ) { + if ctx.menubar_menu_checkbox(loc(LocId::ViewWordWrap), 'W', kbmod::ALT | vk::Z, word_wrap) { tb.set_word_wrap(!word_wrap); ctx.needs_rerender(); } diff --git a/src/tui.rs b/src/tui.rs index 9346b2a6fe26..f89f16775735 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -261,7 +261,7 @@ pub enum Overflow { /// If specified, a checkmark 🗹 / ☐ to be prepended to the button text #[derive(Default, Clone, Copy, PartialEq, Eq)] -pub enum Checkmark { +enum Checkmark { /// No checkmark (or space for it) #[default] Absent, @@ -295,14 +295,16 @@ impl ButtonStyle { pub fn accelerator(self, char: char) -> Self { Self { accelerator: Some(char), ..self } } - /// Draw a checkbox prefix: `[🗹 Example Button]` - pub fn checkmark(self, checkmark: Checkmark) -> Self { - Self { checkmark, ..self } - } /// Draw with or without brackets: `[Example Button]` or `Example Button` pub fn bracketed(self, bracketed: bool) -> Self { Self { bracketed, ..self } } + /// Draw a checkbox prefix: `[🗹 Example Button]` + /// + /// Note: use `checkbox` or `menubar_menu_checkbox` + fn checkmark(self, checkmark: Checkmark) -> Self { + Self { checkmark, ..self } + } } impl Default for ButtonStyle { @@ -3155,12 +3157,23 @@ impl<'a> Context<'a, '_> { accelerator: char, shortcut: InputKey, ) -> bool { - self.menubar_menu_checkbox(text, accelerator, shortcut, Checkmark::AlignOnly) + self.menubar_menu_item(text, accelerator, shortcut, Checkmark::AlignOnly) } /// Appends a checkbox to the current menu. /// Returns true if the checkbox was activated. pub fn menubar_menu_checkbox( + &mut self, + text: &str, + accelerator: char, + shortcut: InputKey, + checked: bool, + ) -> bool { + self.menubar_menu_item(text, accelerator, shortcut, checked.into()) + } + + /// Appends a button or checkbox to the current menu, + fn menubar_menu_item( &mut self, text: &str, accelerator: char,