diff --git a/CHANGELOG.md b/CHANGELOG.md index bef60bc..7728464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,35 +13,6 @@ Procedure when bumping the version number: ## Unreleased -## v0.3.4 - 2025-03-8 - -### Added -- `Frame::set_bell` to print a bell character when the frame is displayed -- `widgets::bell` - -## v0.3.3 - 2025-02-28 - -### Fixed -- Rendering glitches in unicode-based with estimation - -## v0.3.2 - 2025-02-23 - -### Added -- Unicode-based grapheme width estimation method - -## v0.3.1 - 2025-02-21 - -### Fixed -- Rendering glitches, mainly related to emoji - -## v0.3.0 - 2024-11-06 - -### Added -- `Terminal::mark_dirty` - -### Changed -- **(breaking)** Updated dependencies - ## v0.2.3 - 2024-04-25 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 22967fc..618da80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "toss" -version = "0.3.4" +version = "0.2.3" edition = "2021" [dependencies] -async-trait = "0.1.83" -crossterm = "0.28.1" +async-trait = "0.1.80" +crossterm = "0.27.0" unicode-linebreak = "0.1.5" -unicode-segmentation = "1.12.0" -unicode-width = "0.2.0" +unicode-segmentation = "1.11.0" +unicode-width = "0.1.11" diff --git a/src/frame.rs b/src/frame.rs index e42ba6b..03fbb04 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -8,7 +8,6 @@ pub struct Frame { pub(crate) widthdb: WidthDb, pub(crate) buffer: Buffer, pub(crate) title: Option, - pub(crate) bell: bool, } impl Frame { @@ -49,10 +48,6 @@ impl Frame { self.title = title; } - pub fn set_bell(&mut self, bell: bool) { - self.bell = bell; - } - pub fn widthdb(&mut self) -> &mut WidthDb { &mut self.widthdb } diff --git a/src/terminal.rs b/src/terminal.rs index 07fe686..33c37fa 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -8,7 +8,7 @@ use crossterm::event::{ DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, }; -use crossterm::style::{Print, PrintStyledContent, StyledContent}; +use crossterm::style::{PrintStyledContent, StyledContent}; use crossterm::terminal::{ BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, @@ -16,7 +16,7 @@ use crossterm::terminal::{ use crossterm::{ExecutableCommand, QueueableCommand}; use crate::buffer::Buffer; -use crate::{AsyncWidget, Frame, Size, Widget, WidthDb, WidthEstimationMethod}; +use crate::{AsyncWidget, Frame, Size, Widget, WidthDb}; /// Wrapper that manages terminal output. /// @@ -112,25 +112,11 @@ impl Terminal { self.frame.widthdb.tab_width } - /// Set the grapheme width estimation method. - /// - /// For more details, see [`WidthEstimationMethod`]. - pub fn set_width_estimation_method(&mut self, method: WidthEstimationMethod) { - self.frame.widthdb.estimate = method; - } - - /// The grapheme width estimation method. - /// - /// For more details, see [`WidthEstimationMethod`]. - pub fn width_estimation_method(&mut self) -> WidthEstimationMethod { - self.frame.widthdb.estimate - } - /// Enable or disable grapheme width measurements. /// /// For more details, see [`Self::measuring`]. pub fn set_measuring(&mut self, active: bool) { - self.frame.widthdb.measure = active; + self.frame.widthdb.active = active; } /// Whether grapheme widths should be measured or estimated. @@ -149,7 +135,7 @@ impl Terminal { /// Standard Annex #11. This usually works fine, but may break on some emoji /// or other less commonly used character sequences. pub fn measuring(&self) -> bool { - self.frame.widthdb.measure + self.frame.widthdb.active } /// Whether any unmeasured graphemes were seen since the last call to @@ -207,12 +193,6 @@ impl Terminal { &mut self.frame.widthdb } - /// Mark the terminal as dirty, forcing a full redraw whenever any variant - /// of [`Self::present`] is called. - pub fn mark_dirty(&mut self) { - self.full_redraw = true; - } - /// Display the current frame on the screen and prepare the next frame. /// /// Before drawing and presenting a frame, [`Self::measure_widths`] and @@ -274,7 +254,6 @@ impl Terminal { self.draw_differences()?; self.update_cursor()?; self.update_title()?; - self.ring_bell()?; Ok(()) } @@ -316,12 +295,4 @@ impl Terminal { } Ok(()) } - - fn ring_bell(&mut self) -> io::Result<()> { - if self.frame.bell { - self.out.queue(Print('\x07'))?; - } - self.frame.bell = false; - Ok(()) - } } diff --git a/src/widgets.rs b/src/widgets.rs index cbbff7c..28b44d8 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -1,5 +1,4 @@ pub mod background; -pub mod bell; pub mod border; pub mod boxed; pub mod cursor; @@ -17,7 +16,6 @@ pub mod text; pub mod title; pub use background::*; -pub use bell::*; pub use border::*; pub use boxed::*; pub use cursor::*; diff --git a/src/widgets/bell.rs b/src/widgets/bell.rs deleted file mode 100644 index b37fb67..0000000 --- a/src/widgets/bell.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::{Frame, Size, Widget, WidthDb}; - -/////////// -// State // -/////////// - -#[derive(Debug, Default, Clone)] -pub struct BellState { - // Whether the bell should be rung the next time the widget is displayed. - pub ring: bool, -} - -impl BellState { - pub fn new() -> Self { - Self::default() - } - - pub fn widget(&mut self) -> Bell<'_> { - Bell { state: self } - } -} - -//////////// -// Widget // -//////////// - -#[derive(Debug)] -pub struct Bell<'a> { - state: &'a mut BellState, -} - -impl Bell<'_> { - pub fn state(&mut self) -> &mut BellState { - self.state - } -} - -impl Widget for Bell<'_> { - fn size( - &self, - _widthdb: &mut WidthDb, - _max_width: Option, - _max_height: Option, - ) -> Result { - Ok(Size::ZERO) - } - - fn draw(self, frame: &mut Frame) -> Result<(), E> { - if self.state.ring { - frame.set_bell(true); - self.state.ring = false - } - Ok(()) - } -} diff --git a/src/widthdb.rs b/src/widthdb.rs index fe5a26e..200765f 100644 --- a/src/widthdb.rs +++ b/src/widthdb.rs @@ -6,31 +6,14 @@ use crossterm::style::Print; use crossterm::terminal::{Clear, ClearType}; use crossterm::QueueableCommand; use unicode_segmentation::UnicodeSegmentation; -use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; +use unicode_width::UnicodeWidthStr; use crate::wrap; -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub enum WidthEstimationMethod { - /// Estimate the width of a grapheme using legacy methods. - /// - /// Different terminal emulators all use different approaches to determine - /// grapheme widths, so this method will never be able to give a fully - /// correct solution. For that, the only possible approach is measuring the - /// actual grapheme width. - #[default] - Legacy, - - /// Estimate the width of a grapheme using the unicode standard in a - /// best-effort manner. - Unicode, -} - /// Measures and stores the with (in terminal coordinates) of graphemes. #[derive(Debug)] pub struct WidthDb { - pub(crate) estimate: WidthEstimationMethod, - pub(crate) measure: bool, + pub(crate) active: bool, pub(crate) tab_width: u8, known: HashMap, requested: HashSet, @@ -39,8 +22,7 @@ pub struct WidthDb { impl Default for WidthDb { fn default() -> Self { Self { - estimate: WidthEstimationMethod::default(), - measure: false, + active: false, tab_width: 8, known: Default::default(), requested: Default::default(), @@ -65,37 +47,14 @@ impl WidthDb { if grapheme == "\t" { return self.tab_width_at_column(col); } - - if self.measure { - if let Some(width) = self.known.get(grapheme) { - return *width; - } - self.requested.insert(grapheme.to_string()); + if !self.active { + return grapheme.width() as u8; } - - match self.estimate { - // A character-wise width calculation is a simple and obvious - // approach to compute character widths. The idea is that dumb - // terminal emulators tend to do something roughly like this, and - // smart terminal emulators try to emulate dumb ones for - // compatibility. In practice, this approach seems to be fairly - // robust. - WidthEstimationMethod::Legacy => grapheme - .chars() - .filter(|c| !c.is_ascii_control()) - .flat_map(|c| c.width()) - .sum::() - .try_into() - .unwrap_or(u8::MAX), - - // The unicode width crate considers control chars to have a width - // of 1 even though they usually have a width of 0 when displayed. - WidthEstimationMethod::Unicode => grapheme - .split(|c: char| c.is_ascii_control()) - .map(|s| s.width()) - .sum::() - .try_into() - .unwrap_or(u8::MAX), + if let Some(width) = self.known.get(grapheme) { + *width + } else { + self.requested.insert(grapheme.to_string()); + grapheme.width() as u8 } } @@ -128,7 +87,7 @@ impl WidthDb { /// Whether any new graphemes have been seen since the last time /// [`Self::measure_widths`] was called. pub(crate) fn measuring_required(&self) -> bool { - self.measure && !self.requested.is_empty() + self.active && !self.requested.is_empty() } /// Measure the width of all new graphemes that have been seen since the @@ -138,11 +97,11 @@ impl WidthDb { /// the terminal. After it finishes, the terminal's contents should be /// assumed to be garbage and a full redraw should be performed. pub(crate) fn measure_widths(&mut self, out: &mut impl Write) -> io::Result<()> { - if !self.measure { + if !self.active { return Ok(()); } for grapheme in self.requested.drain() { - if grapheme.chars().any(|c| c.is_ascii_control()) { + if grapheme.chars().any(|c|c.is_ascii_control()){ // ASCII control characters like the escape character or the // bell character tend to be interpreted specially by terminals. // This may break width measurements. To avoid this, we just