#immediate-mode #user-input #ui #character #grid #region #system

tuig-ui

针对字符网格的可预测即时模式 UI 系统

1 个不稳定版本

0.0.5 2023年11月10日

#371GUI


tuig 中使用

BSD-3-Clause 协议

250KB
3.5K SLoC

tuig-ui

tuig-ui 是一个针对字符网格的可预测即时模式 UI 系统。

使用方法

请查看 示例 以获取起点。您需要运行一个 'main loop'/'UI thread'。

  • 获取下一个用户输入。
  • 使用该输入创建一个 Region 和一个 Screen
  • 将某种类型的 Attachment 附加到渲染。通常顶级组件不需要直接实现 Attachment,因为可以直接使用 Region
  • Screen 绘制到实际屏幕。

执行第一步和最后一步的 预期 方法是使用 tuig-iosys,但没有理由您不能从您方便的任何来源创建 Action/Screen

常见问题解答 (FAQ)

“可预测”是什么意思?

文本模式或字符网格 UI 很奇怪,因为您通常在非常小的“分辨率”下工作,即网格的大小——我写这个窗口大约有 130 列和 60 行——但每个“像素”可以包含相当多的信息。因此,您需要一个您可以确保一致控制的布局的 UI。不要与空白、单个像素、边距或奇怪的内容换行作斗争;东西就放在你放的地方。

缺点是,为了保持可预测性,它必须很简单。然而,考虑到通常在文本模式中看到的 UI 类型,它仍然适用于大多数用例。

即时模式是什么意思?

即时模式 是指一种图形编程中的API风格,其中您调用的API(假装)直接在屏幕上绘制。在UI的情况下,这意味着UI元素的定义(以及相关的输入处理和渲染)在每个帧的过程中同时进行。

例如,即时模式UI可能看起来像这样

struct ChatWindow {
  friends: Vec<People>,
  current: usize,
  histories: Vec<History>,
  partial: String,
}
impl<'s> Attachment<'s> for &ChatWindow {
  type Output = ();
  fn attach(self, root: Region<'s>) {
    let (people, right) = root.split(cols!(15 *));
    if let Some(sel) = people.attach(FriendsList::new(&self.friends, &self.current)) {
      self.current = sel;
    }
    let (history, entry) = right.split(rows!(* 1));
    history.attach(ChatHistory::new(&self.histories[self.current]));
    if entry.attach(TextInput::new(&mut self.partial)).is_submitted() {
      self.histories[self.current].add(mem::take(self.partial));
    }
  }
}

注意

  • 您没有存储任何UI元素。您对象中保持的状态仅仅是您关心的渲染状态。
  • 您的 attach 函数定义了您的UI布局和您对输入的响应。注意例如,当 FriendsList 被连接时,它会返回用户刚选中的朋友(如果有的话),而 TextInput 返回一个可以描述许多事情复杂的状态对象。

一方面,这种方法往往会导致代码更简单。您不需要担心回调或元素树或任何其他东西;您只需直接编写您想要的UI外观即可。另一方面,您的UI在函数中定义,该函数也是您所有事件处理代码的地方,因此它必须在每次输入时运行,并在没有输入时定期运行以显示外部状态更改。与懒加载输入并仅执行所需的精确回调相比,这可能会消耗更多的CPU。

尽管如此,除非您的UI非常复杂,否则这并不是很多。考虑到 tuig-ui 首要的设计目标是易于使用,性能虽然重要但次之,这种权衡是非常值得的。

依赖项

~0.7–1.2MB
~27K SLoC