# 窗口与 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 与图形](./shader.md)。

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