在 Rust 的 Cursive 库中更新 TextView 的正确方法

问题描述

我目前正在为个人项目中的服务器应用程序编写 tui,我选择了草书库来完成它。我的应用程序的一个组件遇到了问题,该组件应该用作服务器的控制台窗口。在设置将文本输出到窗口的功能时,我遇到了无法更新 TextView 的问题。

根据 this issue 中的最后一条评论,这样做的方法是使用 TextContent 并更新它,我在下面设置了它。虽然初始内容显示在 UI 中,但不会对内容进行更新。我已经尝试通过给它一个名字来直接在 TextView 上设置内容,但没有运气。我已经能够在 TextView 本身上获取 get_content 并看到内容已更新,只是没有反映在 UI 中。

有什么办法可以做到这一点,还是我需要为输出制作自定义视图?

这是有问题的代码

use std::ops::Add;
use std::borrow::{BorrowMut,Borrow};

use cursive::{View,Printer,Vec2,Rect,With};
use cursive::event::{Event,EventResult,AnyCb,Key};
use cursive::view::{Selector,ViewNotFound,ViewWrapper};
use cursive::direction::Direction;
use cursive::views::{EditView,LinearLayout,OnEventView,DummyView,TextView,TextContent};
use cursive::traits::{Nameable,Scrollable,Resizable};
use cursive::view::scroll::Scroller;

pub const CONSOLE_INPUT_NAME: &str = "console_input";
pub const CONSOLE_OUTPUT_LENGTH: u16 = 1000;

pub(crate) struct ConsoleView {
    inner_view: LinearLayout,console_output: TextContent,console_output_history: LinkedList<String>
}

impl Default for ConsoleView {
    fn default() -> Self {
        let console_output = TextContent::new("This content should get replaced.");
        let inner_view = LinearLayout::vertical()
            .child(TextView::new_with_content(console_output.clone())
                .full_screen()
                .scrollable()
                .wrap_with(OnEventView::new)
                .on_pre_event_inner(Key::PageUp,|v,_| {
                    let scroller = v.get_scroller_mut();
                    if scroller.can_scroll_up() {
                        scroller.scroll_up(
                            scroller.last_outer_size().y.saturating_sub(1),);
                    }
                    Some(EventResult::Consumed(None))
                })
                .on_pre_event_inner(Key::PageDown,_| {
                    let scroller = v.get_scroller_mut();
                    if scroller.can_scroll_down() {
                        scroller.scroll_down(
                            scroller.last_outer_size().y.saturating_sub(1),);
                    }
                    Some(EventResult::Consumed(None))
                }))
            .child(DummyView)
            .child(EditView::new().with_name(CONSOLE_INPUT_NAME));

        return Self {
            inner_view,console_output,console_output_history: LinkedList::new()
        }
    }
}

impl ViewWrapper for ConsoleView {
    type V = LinearLayout;

    fn with_view<F,R>(&self,f: F) -> Option<R> where F: FnOnce(&Self::V) -> R {
        return Some(f(self.inner_view.borrow()));
    }

    fn with_view_mut<F,R>(&mut self,f: F) -> Option<R> where F: FnOnce(&mut Self::V) -> R {
        return Some(f(self.inner_view.borrow_mut()));
    }

    fn into_inner(self) -> Result<Self::V,Self> where Self: Sized,Self::V: Sized {
        return Ok(self.inner_view);
    }

    fn wrap_draw(&self,printer: &Printer) {
        self.inner_view.draw(printer);
    }

    fn wrap_required_size(&mut self,req: Vec2) -> Vec2 {
        return req;
    }

    fn wrap_on_event(&mut self,ch: Event) -> EventResult {
        return self.inner_view.on_event(ch);
    }

    fn wrap_layout(&mut self,size: Vec2) {
        self.inner_view.layout(size);
    }

    fn wrap_take_focus(&mut self,source: Direction) -> bool {
        return self.inner_view.take_focus(source);
    }

    fn wrap_call_on_any<'a>(&mut self,selector: &Selector<'_>,callback: AnyCb<'a>) {
        self.inner_view.call_on_any(selector,callback);
    }

    fn wrap_focus_view(&mut self,selector: &Selector<'_>) -> Result<(),ViewNotFound> {
        return self.inner_view.focus_view(selector);
    }

    fn wrap_needs_relayout(&self) -> bool {
        return self.inner_view.needs_relayout();
    }

    fn wrap_important_area(&self,size: Vec2) -> Rect {
        return self.inner_view.important_area(size);
    }
}

impl ConsoleView {
    pub fn print(&mut self,line: &str) {
        log::info!("Printing to console: {}",line);
        self.console_output_history.push_back(line.to_string());

        if self.console_output_history.len() > CONSOLE_OUTPUT_LENGTH as usize {
            self.console_output_history.pop_front();
        }

        let out = self.console_output_history.iter().fold("".to_string(),|acc,s| {
            return acc.add(s.as_str()).add("\n");
        });

        self.console_output.set_content(out);
    }
}

这是用来演示问题的 main()。基本上,一旦设置了 UI 并启动了事件循环,我就无法更新 UI 中显示的信息。鉴于在我启动事件循环之前 UI 不会显示,这是一个问题。

    let mut siv = cursive::default();

    siv.add_layer(ConsoleView::default().with_name("view_name"));

    siv.add_global_callback(Key::Esc,|s| s.quit());

    siv.run();

    siv.call_on_name("view_name",|view: &mut ConsoleView| {
        view.print("I should see this text in the app.");
    });
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...