Skip to content
本文 下载 Markdown

窗口与 UI ​

std.graphics.ui 以组件树组织界面。Widget 保存通用状态,Panel、Label、Button、Slider 等组件继承它;布局、绘制和事件行为通过接口约定与方法覆写组合。

一个可检查的组件示例 ​

这个仓库示例建立窗口、面板和自定义组件,检查布局、绘制、点击与移除组件后的状态清理。它是行为测试,不会显示原生窗口。

kelyra
import std.graphics.ui;

class CountingPainter: std.graphics.ui.Painter {
  pub fills: i32;
  pub init() { fills = 0; }
  pub fn fill(bounds: *std.graphics.ui.Rect, color: *std.graphics.ui.Color) -> void {
    fills = fills + 1;
  }
  pub fn text(bounds: *std.graphics.ui.Rect, content: *c.char,
              color: *std.graphics.ui.Color) -> void {}
}

class Probe: std.graphics.ui.Widget {
  pub clicks: i32;
  pub paints: i32;
  pub layouts: i32;
  pub init(x: i32, y: i32, width: i32, height: i32) {
    super(x, y, width, height);
    clicks = 0;
    paints = 0;
    layouts = 0;
    this.focusable = true;
  }
  @override pub fn arrange(area: *std.graphics.ui.Rect) -> void {
    layouts = layouts + 1;
    super.arrange(area);
  }
  @override pub fn paint(painter: *std.graphics.ui.Painter) -> void {
    paints = paints + 1;
    super.paint(painter);
  }
  @override pub fn on_event(event: *std.graphics.ui.Event) -> void {
    if event.kind == std.graphics.ui.EventKind.Click { clicks = clicks + 1; }
    super.on_event(event);
  }
}

@main
pub fn main() -> i32 {
  let window = std.graphics.ui.Window(200, 120);
  let panel = std.graphics.ui.Panel(10, 10, 180, 100);
  let probe = Probe(5, 5, 50, 30);
  if !window.add(&panel) || !panel.add(&probe) { return 1; }
  if probe.frame.x != 15 || probe.frame.y != 15 { return 2; }
  let painter = CountingPainter();
  window.paint(&painter);
  if probe.paints != 1 || probe.layouts < 1 || painter.fills != 1 {
    return 3;
  }
  window.pointer(std.graphics.ui.EventKind.PointerDown, 20, 20);
  window.pointer(std.graphics.ui.EventKind.PointerUp, 20, 20);
  if probe.clicks != 1 { return 4; }
  let component: *std.graphics.ui.Component = &probe;
  let event = std.graphics.ui.Event();
  event.kind = std.graphics.ui.EventKind.Click;
  component.on_event(&event);
  if probe.clicks != 2 { return 5; }
  window.pointer(std.graphics.ui.EventKind.PointerDown, 20, 20);
  let probe_widget: *std.graphics.ui.Widget = &probe;
  if window.capture as usize != probe_widget as usize { return 6; }
  window.remove(&panel);
  if window.capture as usize != 0 || window.focused as usize != 0 ||
      panel.parent as usize != 0 { return 7; }
  return 0;
}

原生窗口 ​

Windows 后端位于 std.graphics.ui.win。窗口消息先由 Win32 窗口过程收集,再由 poll() 送进组件树,包括创建和拉伸时同步到达的消息。Event 提供鼠标左、右、中、扩展键、滚轮、键盘虚拟键与扫描码、重复次数,以及 Unicode 文本输入。关闭事件可以通过 event.handled = true 取消。需要直接发送或检查 Win32 消息时,可以使用 std.win.message 和 std.win.input。

kstd/examples/ui_input_smoke_example.kly 展示这些事件的检验。

Windows 图形窗口示例目前使用 std.graphics.window 打开窗口并绘制三角形。它依赖编译好的 Kelyra Shader 与 DX12 环境。完整流程见Shader 与图形。

Linux 的 Vulkan 图形示例已有代码,但跨平台 UI 后端仍在设计与完善;不能把 std.graphics.ui 的行为测试等同于已完成的跨平台原生组件渲染。

认真写代码,也认真写说明。