#tui #elm #ui #terminal #ui-framework #send-message

douglas

基于 Elm 架构构建的一个小型 TUI 框架

2 个版本

0.1.1 2022 年 5 月 13 日
0.1.0 2022 年 5 月 13 日

#1089GUI

MIT 许可协议

58KB
415

douglas

Douglas 是一个基于 Elm 架构构建的微型终端 UI 框架。它深受 Elm 架构 影响,并受到了 bubbleteaiced 的启发。

使用方法

使用 Douglas 构建 UI 非常简单!

use douglas::{Config, Program};

struct App;

impl Program for App {
    type Message = ();

    fn view(&self) -> String {
        "Hello, world!\n".into()
    }
}

fn main() {
    App.run(&mut Config::default()).unwrap();
}

当然,这并不令人兴奋。Douglas 应用程序只需要三个要素

  1. init 初始化你的状态模型,并执行任何设置
  2. update 处理消息和用户输入
  3. view 渲染你的 UI(你已见过这个!)
use douglas::{Command, Config, Mailbox, Program};

// declare your state model
struct App {
    counter: usize,
}

// setup a constructor
impl App {
    fn new() -> Self {
        Self {
            counter: 0,
        }
    }
}

// declare message type
enum Message {
    Increment,
    Decrement,
}

impl Program for App {
    type Message = Message;

    fn init(&mut self, _: Mailbox<Self::Message>) -> Command<Self::Message> {
        // send an Increment message right away
        Command::send(Message::Increment)
    }

    fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
        // you can pattern match on incoming messages
        match message {
            Message::Increment => self.counter += 1,
            Message::Decrement => self.counter -= 1,
        }

        Command::none()
    }

    fn view(&self) -> String {
        format!("count: {}\n", self.counter)
    }
}

fn main() {
    let app = App::new();
    let config = Config::default();
    app.run(&mut config).unwrap();
}

最后,如果你需要执行任何清理工作,你可以实现 exit

use douglas::Program;

impl Program for App {
    // (...snip...)

    fn exit(self) {
        println!("Goodbye, cruel world!");
    }
}

处理事件

除了 update,应用程序还可以实现 on_event 来响应用户在终端上的按键等事件,并发送消息

use douglas::{Command, Program};
use crossterm::event::{Event, KeyCode, KeyEvent};

impl Program for App {
    // (...snip...)

    fn on_event(event: Event) -> Command<Self::Message> {
        match event {
            Event::Key(KeyEvent { code: KeyCode::Up, .. }) =>
                Command::send(Message::Increment),
            _ => Command::none()
        }
    }
}

外部交互

有时,响应发生在程序生命周期之外的事件很有用,比如当数据在网络中可用时响应,或者每隔一段时间发送消息。你可以使用程序的信箱

use douglas::{Command, Mailbox, Program, Timer};
use crossterm::event::{Event, KeyCode, KeyEvent};
use std::time::Duration;

struct App {
    timer: Timer,
}

#[derive(Clone)]
enum Message {
    Tick,
}

impl App {
    fn new() -> Self {
        Self {
            timer: Timer::new(Duration::from_millis(1_000), Message::Tick),
        }
    }
}

impl Program for App {
    // (...snip...)

    fn init(&mut self, mailbox: Mailbox<Self::Message>) -> Command<Self::Message> {
        self.timer.start(mailbox);

        Command::none()
    }

    // make sure to clean up!
    fn exit(mut self) {
        self.timer.stop();
    }
}

示例

你可以查看 examples 目录,以查看一些实际项目的操作。你还可以直接运行一个示例

cargo run --package hello_world

贡献

此项目正在开发中。所有贡献都欢迎!

此项目遵循 贡献者公约。请互相友好相处 :)

许可协议

此项目根据 MIT 许可协议 发布。版权所有 2022 Aaron Ross,保留所有权利。

依赖项

~2.5MB
~41K SLoC