Tui-rs:绘制多个小部件时闪烁

Tui-rs: flickering when drawing multiple widgets

晚上好!

我正在尝试编写一个非常简单的终端应用程序,它使用 Rust 和 tui-rs 在屏幕上绘制两个文本框,一个接受输入,另一个显示输出。第一部分完美运行,但是当我尝试同时绘制两个块时出现了问题:出于某种原因,它只显示第二个块(按绘制顺序),如果我移动鼠标,它会在两个块之间闪烁以一种奇怪的方式。我最好的猜测是,这是由于我的绘图实现,当它需要绘制某些东西时,它会以某种方式“清除”屏幕,但如果是这样的话,我找不到任何文档,而且我不知道如何着手解决这个问题。我提供了一些代码,应该足以在较小规模上重现该问题。

#![allow(unused_imports)]
#![allow(unused_variables)]

use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::io
use tui::{
    backend::CrosstermBackend,
    layout::Rect,
    widgets::{Block, Borders},
    Terminal,
};

struct FirstStruct {}

impl FirstStruct {
    pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
        term.draw(|f| {
            let size = f.size();
            let (w, h) = (size.width / 2, size.height);
            let (x, y) = (size.x, size.y);
            let rect = Rect::new(x, y, w, h);
            let block = Block::default()
                .title("One")
                .borders(Borders::ALL);
            
            f.render_widget(block, rect)
        })?;

        Ok(())
    }
}

struct SecondStruct {  }

impl SecondStruct {
    pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
        term.draw(|f| {
            let size = f.size();
            let (w, h) = (size.width / 2, size.height);
            let (x, y) = (size.x + w, size.y);
            let rect = Rect::new(x, y, w, h);
            let block = Block::default()
                .title("Two")
                .borders(Borders::ALL);
            
            f.render_widget(block, rect)
        })?;
        Ok(())
    }
}

fn main() -> io::Result<()>{
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    let first = FirstStruct {};
    let second = SecondStruct {};
    let mut running = true; 

    while running {
        if let Event::Key(key) = event::read()? {
            running = false;
        }
        second.draw(&mut terminal)?;
        first.draw(&mut terminal)?;
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}

有人知道我该如何解决这个问题吗?提前致谢!!

每次调用 Terminal::draw() 时,您必须立即绘制所有 想要显示的内容。不要将 Terminal 传递给您自己的 draw 函数,而是传递您从 Terminal::draw() 获得的 Frame。即替换

second.draw(&mut terminal)?;
first.draw(&mut terminal)?;

terminal.draw(|f| {
    first.draw(f)?;
    second.draw(f)?;
});

并更改 FirstStructSecondStruct 的签名以匹配。

此外,更常见的做法是,不是在各个函数中计算每个小部件的矩形,而是在顶层决定(可能使用 Layout)并传递 Rects直到绘图功能。这样,他们可以在不同的情况下以不同的方式定位。您拥有的东西会起作用,但要改变它并不那么容易。

文档示例中的布局代码,已根据您的情况进行调整:

terminal.draw(|f| {
    let chunks = Layout::default()
        .direction(Direction::Horizontal)
        .constraints(
            [
                Constraint::Percentage(50),
                Constraint::Percentage(50),
            ].as_ref()
        )
        .split(f.size());
    first.draw(f, chunks[0])?;
    second.draw(f, chunks[1])?;
});