ebox/docs/user/ebox-user-guide.zh.md

262 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Ebox 用户指南
[English](ebox-user-guide.en.md)
Ebox 是底层 Text/Box 布局与 buffer 渲染包。本指南只使用一套公共 author 语法,
并把框架集成接口单独说明。完整函数清单见[公共 API
参考](ebox-api-reference.zh.md)。
## 1. 加载 Ebox
```elisp
(require 'ebox)
```
加载 Ebox 不会创建 buffer、在当前 buffer 启用 mode也不会构建 Rust。
## 2. 只学习一个 author 模型
Ebox 文档由 Text 和 Box 节点组成。author 语法只有七个入口:
| 入口 | 含义 |
| --- | --- |
| `"text"` | 一个 Text 节点的简写。 |
| `(text ... "text")` | 带显式文本 property 的 Text。 |
| `(box ... CHILD...)` | 普通视觉 Box。 |
| `(row ... CHILD...)` | 使用简单横向子布局的 Box。 |
| `(column ... CHILD...)` | 使用简单纵向子布局的 Box。 |
| `(flex ... CHILD...)` | 使用 Flex 子布局的 Box。 |
| `(grid ... CHILD...)` | 使用 Grid 子布局的 Box。 |
子节点始终直接嵌套。布局 form 只是选择了子布局算法的 Box不是另一种视觉对象。
```elisp
(defvar ebox-guide-input
(ebox-build
'(column :padding (1 2)
:border (1 solid "#8A93A6")
(text :color "#263244" "Research notes")
(row :item-gap 1
(box :id "status" :background-color "#F4F6FB" "Inbox")
(box :background-color "#EEF2FF" "Archive")))))
```
`ebox-build` 返回不透明的 `CanonicalEboxInput`,把 canonical forest 与对应的 source
generation 保持为一个整体。普通 author 代码只需把这个值原样传给渲染和发布函数,
不要取出或重新组装其中的内部 node。
需要空矩形区域时,使用只有几何 property、没有子节点的 `box`,不需要额外节点类型。
## 3. 用布局名称直接表达意图
普通内容用 `box`;直接的一维组合用 `row``column`;需要分配剩余空间或换行时
`flex`;需要二维轨道或显式放置时用 `grid`
### Row 与 column
`row``column` 接受 `:item-gap``:cross-align`。它们的子节点仍然只是 Text
或 Box。
```elisp
(ebox-build
'(row :item-gap 2 :cross-align center
(box :width 12 "Left")
(box :width 12 "Right")))
```
### Flex
Flex 容器 property 属于 `flex`。participation property 直接属于子 `box`,因为它
描述的是父子关系。
```elisp
(ebox-build
'(flex :width (480)
:flex-flow (row wrap)
:gap (1 (12))
(box :flex (1 1 auto) "Primary")
(box :flex-grow 2 "Secondary")))
```
### Grid
Grid 放置从 1 开始。轨道可以是固定值、`auto`、分数、`minmax` 或 `repeat`
placement property 同样直接属于子 `box`
```elisp
(ebox-build
'(grid :width (640)
:grid-template-columns ((200) (fr 1) (fr 1))
:grid-template-rows (1 1)
:gap (1 (12))
(box :grid-column (1 :span 3) "Header")
(box :grid-column 1 :grid-row 2 "Navigation")
(box :grid-column 2 :grid-row 2 "Main")
(box :grid-column 3 :grid-row 2 "Aside")))
```
## 4. 使用几何与绘制 property
普通横向数字表示字符列,`(240)` 这样的单元素 list 表示像素宽度;纵向数字表示
行数。Text 只接受字体、前景/背景和文本装饰 propertypadding、margin、border、
尺寸、`:outer`、overflow、可见性和换行策略只属于 Box。Box 上的字体与颜色可以
作为其 Text 子树的继承来源,但不会让 Text 获得 Box 几何。
```elisp
(ebox-build
'(box :width (420)
:padding (1 2)
:margin (0 1)
:border (1 solid "#8A93A6")
:color "#263244"
:background-color "#FFFFFF"
"A readable panel"))
```
`:outer inline``:outer block` 表达 Box 如何参与父布局。子布局算法仍由 form
名称决定。
## 5. 渲染与发布
`ebox-render` 返回带属性文本,不修改 live buffer`ebox-render-to-buffer` 挂载
retained TP surface 并返回 buffer。它们和 `ebox-display-buffer` 都接收
`ebox-build` 返回的不透明值。
```elisp
(ebox-render ebox-guide-input)
(ebox-render-to-buffer "*Ebox Guide*" ebox-guide-input)
```
重新构建 canonical input 后,用 `ebox-commit` 原子更新:
```elisp
(ebox-commit
"*Ebox Guide*"
(ebox-build
'(column :padding (1 2)
(text "Updated notes")
(box :key body "The new input is caller-owned."))))
```
验证、渲染或发布失败时,旧 buffer 与 runtime 状态保持不变。
`ebox-buffer-update-report` 返回最近一次成功更新报告的防御性副本。
## 6. 查询和更新 mounted surface
`:id`、`:class` 与 `:key` 是 author metadata。Selector 使用 ECSS 语义。直接更新
前,把 `:id` 解析为不透明、surface-scoped 的 handle
```elisp
(let ((handle (ebox-region-resolve "*Ebox Guide*" "status")))
(ebox-region-update handle :color "#166534"))
```
`ebox-selector-query-buffer` 按文档顺序返回 mounted buffer 中的匹配;
`ebox-selector-update-buffer` 对所有可编辑匹配应用一次样式更新。数字 region id
只是诊断用渲染 metadata不是稳定更新 handle。
## 7. Resize 与滚动
有限高度加 `:overflow scroll` 会创建 scroll window。Ebox buffer mode 安装键盘和
滚轮命令:先交给最内层 scroll owner无法继续消费时才回退到普通 Emacs 滚动。
```elisp
(ebox-build
'(box :id log :width (420) :height 8 :overflow scroll
"line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9"))
```
可见 mounted buffer 会跟随其 display window。集成层也可以显式应用 viewport
```elisp
(ebox-rerender-buffer-with-context
(get-buffer "*Ebox Guide*") 800 30)
```
## 8. 独立 `.ebox` 文件
`.ebox` 文件包含一个不加 quote 的结构 form
```elisp
(column :padding '(1 2)
(text :color "#263244" "Title")
(box :width '(240) "Body"))
```
同级 `ebox-playground` 会先求值 property 表达式,再调用 `ebox-build`。因此 `.ebox`
文件中的 list 与 symbol 常量要加 quote如上所示。直接调用 `ebox-build` 时,则像
前面的例子一样传入 inert data。
## 9. Typed 集成 API
已经完成 author normalization 的框架可以绕过 list DSL这是集成 API不是第二套
author 语法。一个 source builder 拥有完整 source generation。每个 TextNode 或
BoxNode 都取得该 builder 返回的不透明 handle并使用由同一份 normalized
declarations 投影出的 node-owned facts。最后封存 builder把 forest 与 source index
作为一个 `CanonicalEboxInput` 传递。
```elisp
(let* ((builder (ebox-source-builder-create))
(root-declarations nil)
(left-declarations nil)
(right-declarations nil)
(root-handle
(ebox-source-builder-bind
builder :declarations root-declarations))
(left-handle
(ebox-source-builder-bind
builder :declarations left-declarations))
(right-handle
(ebox-source-builder-bind
builder :declarations right-declarations))
(left
(ebox-text-create
:value "Left"
:source-handle left-handle
:owned-facts
(ebox-canonical-facts-from-declarations
'text left-declarations)))
(right
(ebox-text-create
:value "Right"
:source-handle right-handle
:owned-facts
(ebox-canonical-facts-from-declarations
'text right-declarations)))
(root
(ebox-box-create
:layout (ebox-row-layout-create
:item-gap 1 :cross-align 'center)
:children (list left right)
:source-handle root-handle
:owned-facts
(ebox-canonical-facts-from-declarations
'row root-declarations))))
(ebox-canonical-input-create
(list root)
(ebox-source-builder-finish builder)))
```
`ebox-source-builder-create`、`ebox-source-builder-bind`、
`ebox-source-builder-finish`、`ebox-canonical-facts-from-declarations`、typed
node/layout constructor 与 `ebox-canonical-input-create` 只属于集成边界。这里的
`:layout`、`:children`、`:source-handle` 与 `:owned-facts` 是 evaluated constructor
字段,不是 author property也没有扩展七入口语法。
## 10. 可选 native reflow 与验证
Rust 模块是可选加速器Ebox 加载时不会构建它:
```elisp
(ebox-native-status)
(ebox-native-build)
```
在仓库根目录运行:
```sh
make docs-contract-tests EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
make dsl-tests EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
make check EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
```