Don't measure widths while presenting

This commit is contained in:
Joscha 2023-02-17 18:59:25 +01:00
parent fae12a4b9f
commit ba6ee45110
5 changed files with 56 additions and 49 deletions

View file

@ -12,13 +12,12 @@ fn draw(f: &mut Frame) {
} }
fn render_frame(term: &mut Terminal) { fn render_frame(term: &mut Terminal) {
// Must be called before rendering, otherwise the terminal has out-of-date let mut dirty = true;
// size information and will present garbage. while dirty {
term.autoresize().unwrap(); dirty = term.measure_widths().unwrap();
draw(term.frame());
while term.present().unwrap() {
term.autoresize().unwrap(); term.autoresize().unwrap();
draw(term.frame()); draw(term.frame());
term.present().unwrap();
} }
} }

View file

@ -22,7 +22,11 @@ fn widget() -> impl Widget<io::Error> {
} }
fn render_frame(term: &mut Terminal) { 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() { fn main() {

View file

@ -49,13 +49,12 @@ fn draw(f: &mut Frame) {
} }
fn render_frame(term: &mut Terminal) { fn render_frame(term: &mut Terminal) {
// Must be called before rendering, otherwise the terminal has out-of-date let mut dirty = true;
// size information and will present garbage. while dirty {
term.autoresize().unwrap(); dirty = term.measure_widths().unwrap();
draw(term.frame());
while term.present().unwrap() {
term.autoresize().unwrap(); term.autoresize().unwrap();
draw(term.frame()); draw(term.frame());
term.present().unwrap();
} }
} }

View file

@ -38,13 +38,12 @@ fn draw(f: &mut Frame) {
} }
fn render_frame(term: &mut Terminal) { fn render_frame(term: &mut Terminal) {
// Must be called before rendering, otherwise the terminal has out-of-date let mut dirty = true;
// size information and will present garbage. while dirty {
term.autoresize().unwrap(); dirty = term.measure_widths().unwrap();
draw(term.frame());
while term.present().unwrap() {
term.autoresize().unwrap(); term.autoresize().unwrap();
draw(term.frame()); draw(term.frame());
term.present().unwrap();
} }
} }

View file

@ -121,11 +121,12 @@ impl Terminal {
/// Handling of wide characters is inconsistent from terminal emulator to /// Handling of wide characters is inconsistent from terminal emulator to
/// terminal emulator, and may even depend on the font the user is using. /// terminal emulator, and may even depend on the font the user is using.
/// ///
/// When enabled, any newly encountered graphemes are measured whenever a /// When enabled, any newly encountered graphemes are measured whenever
/// new frame is presented. This is done by clearing the screen, printing /// [`Self::measure_widths`] is called. This is done by clearing the screen,
/// the grapheme and measuring the resulting cursor position. Because of /// printing the grapheme and measuring the resulting cursor position.
/// this, the screen will flicker occasionally. However, grapheme widths /// Because of this, the screen will flicker occasionally. However, grapheme
/// will always be accurate independent of the terminal configuration. /// widths will always be accurate independent of the terminal
/// configuration.
/// ///
/// When disabled, the width of graphemes is estimated using the Unicode /// When disabled, the width of graphemes is estimated using the Unicode
/// Standard Annex #11. This usually works fine, but may break on some emoji /// Standard Annex #11. This usually works fine, but may break on some emoji
@ -134,6 +135,25 @@ impl Terminal {
self.frame.widthdb.active 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<bool> {
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 /// Resize the frame and other internal buffers if the terminal size has
/// changed. /// changed.
/// ///
@ -164,26 +184,12 @@ impl Terminal {
/// Display the current frame on the screen and prepare the next frame. /// Display the current frame on the screen and prepare the next frame.
/// ///
/// Before drawing and presenting a frame, [`Self::autoresize`] should be /// Before drawing and presenting a frame, [`Self::measure_widths`] and
/// called. [`Self::present`] does **not** call it automatically. /// [`Self::autoresize`] should be called.
///
/// 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.
/// ///
/// After calling this function, the frame returned by [`Self::frame`] will /// After calling this function, the frame returned by [`Self::frame`] will
/// be empty again and have no cursor position. /// be empty again and have no cursor position.
pub fn present(&mut self) -> io::Result<bool> { 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;
}
if self.full_redraw { if self.full_redraw {
io::stdout().queue(Clear(ClearType::All))?; io::stdout().queue(Clear(ClearType::All))?;
self.prev_frame_buffer.reset(); // Because the screen is now empty 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); mem::swap(&mut self.prev_frame_buffer, &mut self.frame.buffer);
self.frame.reset(); self.frame.reset();
Ok(measure) Ok(())
} }
/// Display a [`Widget`] on the screen. /// Display a [`Widget`] on the screen.
/// ///
/// Internally calls [`Self::autoresize`] and [`Self::present`], and passes /// Before creating and presenting a widget, [`Self::masure_widths`] should
/// on the value returned by [`Self::present`]. /// be called. There is no need to call [`Self::autoresize`].
pub fn present_widget<E, W>(&mut self, widget: W) -> Result<bool, E> pub fn present_widget<E, W>(&mut self, widget: W) -> Result<(), E>
where where
E: From<io::Error>, E: From<io::Error>,
W: Widget<E>, W: Widget<E>,
{ {
self.autoresize()?; self.autoresize()?;
widget.draw(self.frame())?; widget.draw(self.frame())?;
let dirty = self.present()?; self.present()?;
Ok(dirty) Ok(())
} }
/// Display an [`AsyncWidget`] on the screen. /// Display an [`AsyncWidget`] on the screen.
/// ///
/// Internally calls [`Self::autoresize`] and [`Self::present`], and passes /// Before creating and presenting a widget, [`Self::masure_widths`] should
/// on the value returned by [`Self::present`]. /// be called. There is no need to call [`Self::autoresize`].
pub async fn present_async_widget<E, W>(&mut self, widget: W) -> Result<bool, E> pub async fn present_async_widget<E, W>(&mut self, widget: W) -> Result<(), E>
where where
E: From<io::Error>, E: From<io::Error>,
W: AsyncWidget<E>, W: AsyncWidget<E>,
{ {
self.autoresize()?; self.autoresize()?;
widget.draw(self.frame()).await?; widget.draw(self.frame()).await?;
let dirty = self.present()?; self.present()?;
Ok(dirty) Ok(())
} }
fn draw_differences(&mut self) -> io::Result<()> { fn draw_differences(&mut self) -> io::Result<()> {