diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 391075e..59603dc 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -12,13 +12,12 @@ fn draw(f: &mut Frame) { } fn render_frame(term: &mut Terminal) { - // Must be called before rendering, otherwise the terminal has out-of-date - // size information and will present garbage. - term.autoresize().unwrap(); - draw(term.frame()); - while term.present().unwrap() { + let mut dirty = true; + while dirty { + dirty = term.measure_widths().unwrap(); term.autoresize().unwrap(); draw(term.frame()); + term.present().unwrap(); } } diff --git a/examples/hello_world_widgets.rs b/examples/hello_world_widgets.rs index 27c81c1..9949dc6 100644 --- a/examples/hello_world_widgets.rs +++ b/examples/hello_world_widgets.rs @@ -22,7 +22,11 @@ fn widget() -> impl Widget { } fn render_frame(term: &mut Terminal) { - while term.present_widget(widget()).unwrap() {} + let mut dirty = true; + while dirty { + dirty = term.measure_widths().unwrap(); + term.present_widget(widget()).unwrap(); + } } fn main() { diff --git a/examples/overlapping_graphemes.rs b/examples/overlapping_graphemes.rs index 0a8f90b..0108e3f 100644 --- a/examples/overlapping_graphemes.rs +++ b/examples/overlapping_graphemes.rs @@ -49,13 +49,12 @@ fn draw(f: &mut Frame) { } fn render_frame(term: &mut Terminal) { - // Must be called before rendering, otherwise the terminal has out-of-date - // size information and will present garbage. - term.autoresize().unwrap(); - draw(term.frame()); - while term.present().unwrap() { + let mut dirty = true; + while dirty { + dirty = term.measure_widths().unwrap(); term.autoresize().unwrap(); draw(term.frame()); + term.present().unwrap(); } } diff --git a/examples/text_wrapping.rs b/examples/text_wrapping.rs index 21f0ffe..c5791d9 100644 --- a/examples/text_wrapping.rs +++ b/examples/text_wrapping.rs @@ -38,13 +38,12 @@ fn draw(f: &mut Frame) { } fn render_frame(term: &mut Terminal) { - // Must be called before rendering, otherwise the terminal has out-of-date - // size information and will present garbage. - term.autoresize().unwrap(); - draw(term.frame()); - while term.present().unwrap() { + let mut dirty = true; + while dirty { + dirty = term.measure_widths().unwrap(); term.autoresize().unwrap(); draw(term.frame()); + term.present().unwrap(); } } diff --git a/src/terminal.rs b/src/terminal.rs index 86f96f0..0adf87d 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -121,11 +121,12 @@ impl Terminal { /// Handling of wide characters is inconsistent from terminal emulator to /// terminal emulator, and may even depend on the font the user is using. /// - /// When enabled, any newly encountered graphemes are measured whenever a - /// new frame is presented. This is done by clearing the screen, printing - /// the grapheme and measuring the resulting cursor position. Because of - /// this, the screen will flicker occasionally. However, grapheme widths - /// will always be accurate independent of the terminal configuration. + /// When enabled, any newly encountered graphemes are measured whenever + /// [`Self::measure_widths`] is called. This is done by clearing the screen, + /// printing the grapheme and measuring the resulting cursor position. + /// Because of this, the screen will flicker occasionally. However, grapheme + /// widths will always be accurate independent of the terminal + /// configuration. /// /// When disabled, the width of graphemes is estimated using the Unicode /// Standard Annex #11. This usually works fine, but may break on some emoji @@ -134,6 +135,25 @@ impl Terminal { self.frame.widthdb.active } + /// Measure widths of newly encountered graphemes. + /// + /// If width measurements are disabled, this function does nothing. For more + /// info, see [`Self::measuring`]. + /// + /// Returns `true` if graphemes were measured and the screen must be + /// redrawn. Keep in mind that after redrawing the screen, new graphemes may + /// have become visible that have not yet been measured. You should keep + /// re-measuring and re-drawing until this function returns `false`. + pub fn measure_widths(&mut self) -> io::Result { + if self.frame.widthdb.measuring_required() { + self.full_redraw = true; + self.frame.widthdb.measure_widths(&mut self.out)?; + Ok(true) + } else { + Ok(false) + } + } + /// Resize the frame and other internal buffers if the terminal size has /// changed. /// @@ -164,26 +184,12 @@ impl Terminal { /// Display the current frame on the screen and prepare the next frame. /// - /// Before drawing and presenting a frame, [`Self::autoresize`] should be - /// called. [`Self::present`] does **not** call it automatically. - /// - /// If width measurements are turned on, any new graphemes encountered since - /// the last [`Self::present`] call will be measured. This can lead to the - /// screen flickering or being mostly blank until measurements complete. - /// - /// Returns `true` if any new graphemes were measured. Since their widths - /// may have changed because of the measurements, the application using this - /// [`Terminal`] should re-draw and re-present the current frame. + /// Before drawing and presenting a frame, [`Self::measure_widths`] and + /// [`Self::autoresize`] should be called. /// /// After calling this function, the frame returned by [`Self::frame`] will /// be empty again and have no cursor position. - pub fn present(&mut self) -> io::Result { - let measure = self.frame.widthdb.measuring_required(); - if measure { - self.frame.widthdb.measure_widths(&mut self.out)?; - self.full_redraw = true; - } - + pub fn present(&mut self) -> io::Result<()> { if self.full_redraw { io::stdout().queue(Clear(ClearType::All))?; self.prev_frame_buffer.reset(); // Because the screen is now empty @@ -197,37 +203,37 @@ impl Terminal { mem::swap(&mut self.prev_frame_buffer, &mut self.frame.buffer); self.frame.reset(); - Ok(measure) + Ok(()) } /// Display a [`Widget`] on the screen. /// - /// Internally calls [`Self::autoresize`] and [`Self::present`], and passes - /// on the value returned by [`Self::present`]. - pub fn present_widget(&mut self, widget: W) -> Result + /// Before creating and presenting a widget, [`Self::masure_widths`] should + /// be called. There is no need to call [`Self::autoresize`]. + pub fn present_widget(&mut self, widget: W) -> Result<(), E> where E: From, W: Widget, { self.autoresize()?; widget.draw(self.frame())?; - let dirty = self.present()?; - Ok(dirty) + self.present()?; + Ok(()) } /// Display an [`AsyncWidget`] on the screen. /// - /// Internally calls [`Self::autoresize`] and [`Self::present`], and passes - /// on the value returned by [`Self::present`]. - pub async fn present_async_widget(&mut self, widget: W) -> Result + /// Before creating and presenting a widget, [`Self::masure_widths`] should + /// be called. There is no need to call [`Self::autoresize`]. + pub async fn present_async_widget(&mut self, widget: W) -> Result<(), E> where E: From, W: AsyncWidget, { self.autoresize()?; widget.draw(self.frame()).await?; - let dirty = self.present()?; - Ok(dirty) + self.present()?; + Ok(()) } fn draw_differences(&mut self) -> io::Result<()> {