DirectSurface UIDirectSurface UI
开始使用
文档/API 参考

Core API 参考

本文示例使用以下公共入口:

import {  DisposableBag,  EditSession,  FormSession,  RenderPage,  createAppContext,  createAppContextKey,} from 'ds-ui'

Core API 是框架最底层的能力集合,包括 render tree、布局约束、焦点、输入、剪贴板、Popup、上下文、命令、状态绑定和资源释放。业务开发通常优先使用 Widgets API 和 Layout API;只有开发可复用组件、诊断工具或框架扩展时,才需要直接接触 Core API。

导出清单

状态和生命周期:

  • Disposable
  • DisposeFn
  • DisposableBag
  • BindingBag
  • bindSelector
  • ScheduleFn
  • ScheduleMode
  • createStore
  • Store
  • EditSession
  • ErrorProvider
  • ValidationProvider
  • FormSession

编辑和校验类型:

  • EditSessionChange
  • EditSessionFieldEquality
  • EditSessionFieldListener
  • EditSessionListener
  • EditSessionOptions
  • EditSessionTransaction
  • ValidationIssue
  • ValidationIssueFilter
  • ValidationIssueInput
  • ValidationIssueSeverity
  • ValidationTrigger
  • ValidationRuleOutcome
  • ValidationRunResult
  • FieldValidationContext
  • FieldValidationRegistrationOptions
  • FieldValidationRule
  • FormValidationContext
  • FormValidationRegistrationOptions
  • FormValidationRule
  • FormSessionFieldOptions
  • FormEditParticipant
  • FormEditParticipantRegistrationOptions
  • FormSessionListener
  • FormSessionOptions
  • FormSubmitResult
  • FormSubmitStatus
  • FormValidationParticipant
  • ValidationProviderOptions

Render tree 和布局协议:

  • RenderObject
  • PipelineOwner
  • BoxConstraints
  • LayoutContext
  • LayoutPassKind
  • RenderLifecycleAware
  • RenderLoadedContext
  • Offset
  • Rect
  • Size
  • constrainSize
  • tightConstraints
  • looseConstraints

焦点和输入:

  • FocusManager
  • FocusScope
  • FocusScopeType
  • Focusable
  • InputComposer
  • InputSession
  • InputSessionToken

剪贴板:

  • ClipboardController
  • ClipboardTextWriter
  • CopyableSelection
  • isCopyShortcut
  • isCopyableSelection

App Context 和命令:

  • APP_CONTEXT_PROVIDER
  • AppContextKey
  • AppContextRegistry
  • AppContextInitialValues
  • AppContextLookupKey
  • AppContextProvider
  • AppContextSetOptions
  • createAppContext
  • createAppContextKey
  • isAppContextProvider
  • COMMAND_SCOPE_PROVIDER
  • CommandManager
  • CommandScope
  • CommandShortcutController
  • allowAllPermissionService
  • commandManagerContextKey
  • isCommandScopeProvider

Popup、锚点和滚动视口:

  • PopupManager
  • Popup
  • PopupAnchorMode
  • PopupContext
  • PopupContextProvider
  • PopupOpenOptions
  • PopupPaintInvalidator
  • PopupViewport
  • OverlayInvalidator
  • OverlayPaintInvalidator
  • GET_POPUP_ANCHOR_RECT
  • PopupAnchor
  • PopupAnchorPlacement
  • PopupAnchorTarget
  • resolvePopupAnchorRect
  • resolvePopupAnchorTarget
  • ScrollViewport
  • ScrollViewportClient
  • isScrollViewportClient

事件和命中测试:

  • HitTestResult
  • DispatchPhase
  • DispatchEvent
  • HitTestEntry
  • HitTestTarget
  • PointerEvent
  • WheelPointerEvent
  • EventDispatcher
  • InteractiveRenderObject
  • PointerDispatchDebugSnapshot
  • isPrimaryPointerButton

绘制:

  • PaintContext

RenderObject

RenderObject 是所有可布局、可绘制、可命中的对象基类。业务组件通常继承 RenderBox 或已有 widget;只有实现底层渲染协议时才直接继承 RenderObject。

常用职责:

  • 持有 parent、owner、offset、size。
  • 参与 attach / detach / dispose 生命周期。
  • 通过 markNeedsLayout()、markNeedsPaint()、markNeedsTransientPaint() 请求管线刷新。
  • 通过 paintBounds 声明包含阴影等视觉溢出的绘制区域;仅移动节点时用 markNeedsPaintForGeometryChange() 累计旧、新损伤区域。
  • 通过 visible 控制折叠隐藏;不可见时尺寸为 0,不参与 layout、paint、hit test 和焦点。
  • 通过 hitTest()、hitTestPath() 参与事件命中。
  • 通过 debugInfo() 暴露布局检查信息。
  • 通过 getAppContext() / requireAppContext() 读取当前 render tree 的上下文。

自定义组件原则:

  • 在属性 setter 中判断值是否变化,再标记 layout 或 paint。
  • layout 外修改 offset 时先保存旧 paintBounds,再调用 markNeedsPaintForGeometryChange()。
  • 不要在 performPaint() 中修改布局相关状态。
  • 维护好子节点的 parent,替换子节点时处理 detach。
  • dispose() 中释放订阅、定时器、popup、输入会话和外部资源。

PipelineOwner

PipelineOwner 管理 layout、paint、transient paint 和 app context。

常见调用方是 runtime,不是业务页面。组件应通过自身的 markNeedsLayout() / markNeedsPaint() 进入管线,不要直接操作 pipeline 内部队列。

相关概念:

概念 说明
layout 重新计算尺寸和 offset。
paint 重新绘制稳定图层内容。
transient paint 光标闪烁、临时 overlay 等不应触发完整 layout 的轻量绘制。
app context 当前 runtime 根上下文。

约束和几何类型

BoxConstraints:

字段 类型 说明
minWidth number 最小宽度。
maxWidth number 最大宽度。
minHeight number 最小高度。
maxHeight number 最大高度。

几何类型:

类型 字段
Offset { x: number; y: number }
Size { width: number; height: number }
Rect { x: number; y: number; width: number; height: number }

工具函数:

函数 说明
constrainSize(constraints, size) 把自然尺寸限制到约束范围内。
tightConstraints(width, height) 生成固定尺寸约束。
looseConstraints(width, height) 生成最小为 0、最大为指定宽高的约束。

FocusManager

焦点系统负责当前键盘和输入目标。

核心导出:

  • FocusManager
  • FocusScope
  • FocusScopeType
  • Focusable

Focusable 对象通常实现:

  • focusIn()
  • focusOut()
  • onKeyDown(event)
  • 可选 shouldIgnoreKeyDown(event):返回 true 时,本次按键不会进入控件、scope 或未处理按键链路,也不会被焦点系统消费。文本控件用它把组字按键留给原生输入法。

焦点规则:

  • Popup 打开时会建立 popup focus scope。
  • 最后一个交互 popup 关闭时恢复之前焦点。
  • 组件 dispose 时应从焦点系统注销,避免旧对象继续接收键盘事件。

业务页面通常不直接操作 FocusManager;输入控件、窗口、popup 和 runtime 会处理大部分焦点流转。

InputComposer

InputComposer 是隐藏输入承接器,负责键盘输入、中文 composition、粘贴和文本输入会话。

核心导出:

  • InputComposer
  • InputSession
  • InputSessionToken

使用边界:

  • 隐藏输入控件只承接当前输入,不应保存整份大文档。
  • 大文本粘贴应从 paste event 读取文本后直接写入文档模型,并阻止默认落入 textarea。
  • composition update 可以更新预编辑文本;composition end 再提交最终文本。
  • 页面或组件销毁时要结束输入会话。

普通输入组件和文本编辑器已经封装输入会话,业务页面不应直接访问隐藏输入控件。

ClipboardController

剪贴板系统统一处理复制快捷键和可复制选区。

核心导出:

  • ClipboardController
  • ClipboardTextWriter
  • CopyableSelection
  • isCopyShortcut
  • isCopyableSelection

CopyableSelection 组件应提供 getCopyText(),返回当前选区对应文本。TreeView、DataGrid、ObjectInspector、MarkdownViewer、PlainTextEditor 都应复用这套语义。

使用边界:

  • 组件只负责提供文本,不直接调用浏览器剪贴板 API。
  • 剪贴板写入失败要允许业务或运行时降级处理。
  • 调试类组件复制对象时,应复制可读的 key path 和完整值摘要。

App Context

App Context 用于按范围传递依赖对象。它的详细 API 见 App Context API。

最小示例:

const coreNameKey = createAppContextKey<string>('currentName')const coreContext = createAppContext()coreContext.set(coreNameKey, '示例')const coreName = coreContext.require(coreNameKey)coreName.length

关键边界:

  • key 定义在稳定模块,读取端和写入端复用同一个 key。
  • 页面级对象放页面 scope,不放应用根。
  • controller、订阅和定时器使用 setDisposable() 或自定义 disposer。

Commands

命令系统统一按钮、菜单、快捷键、权限和启用状态。详细 API 见 Commands API。

核心对象:

  • CommandManager
  • CommandScope
  • CommandShortcutController
  • RenderCommandScope
  • RenderCommandButton
  • RenderCommandToolbar

关键边界:

  • 命令是业务动作,不是按钮。
  • 页面命令注册在页面 scope,随页面释放。
  • canExecute 是轻量同步求值,不做网络请求和重计算。
  • 状态变化后调用 commandManager.invalidate()。

Popup 底层能力包括:

  • PopupManager
  • Popup
  • PopupAnchorMode
  • PopupContext
  • PopupContextProvider
  • PopupOpenOptions
  • OverlayInvalidator
  • GET_POPUP_ANCHOR_RECT
  • resolvePopupAnchorRect
  • resolvePopupAnchorTarget

详细说明见 Popup 弹出层。

关键边界:

  • 普通业务优先使用 ComboBox、DatePicker、Popover、Modal、Drawer 等上层组件。
  • 自定义 popup 要处理 hit test、外部点击、Escape、滚轮、focus 和 close 释放。
  • 锚点位置应动态读取,不能只在打开时缓存一次。
  • 页面销毁时关闭 owner 关联 popup。

ScrollViewportClient

ScrollViewportClient 用于让外部滚动容器把可视区域同步给子组件。Masonry、虚拟列表、文档查看器等可以据此减少不可见区域计算。

导出:

  • ScrollViewport
  • ScrollViewportClient
  • isScrollViewportClient

如果组件实现了 setScrollViewport(viewport),RenderScrollViewer 会在 layout 和滚动时同步可视信息。

EventDispatcher 和命中测试

事件系统负责从 canvas 事件到 render tree 的路由。

核心导出:

  • EventDispatcher
  • HitTestResult
  • DispatchPhase
  • PointerEvent
  • WheelPointerEvent
  • InteractiveRenderObject
  • isPrimaryPointerButton

业务组件实现复杂交互时应通过 hitTestPath()、onPointerDown()、onPointerMove()、onWheel() 等协议接入,不要直接监听全局 DOM 事件。

DisposableBag

DisposableBag 用于集中释放资源。

适合注册:

  • 事件监听。
  • store 订阅。
  • popup / overlay close 句柄。
  • 定时器释放函数。
  • 业务 controller。

页面、弹窗、控制器和复杂组件都应在 dispose() 中释放自己的 DisposableBag。

class AutoRefreshPage extends RenderPage {  private readonly disposables = new DisposableBag()   constructor() {    super()     this.disposables.setInterval(() => {      reload()    }, 30000)  }   override dispose(): void {    this.disposables.dispose()    super.dispose()  }}

如果资源生命周期跟页面上下文一致,也可以把 DisposableBag 通过 AppContextRegistry.setDisposable() 托管给页面 context。

BindingBag 和 Store

createStore() / Store 提供轻量状态容器,BindingBag / bindSelector() 提供把状态投影绑定到组件属性的能力。

适合场景:

  • demo 和轻量页面状态。
  • UI 局部状态同步。
  • 将 store selector 的变化调度到组件更新。

复杂业务系统可以接入自己的状态管理方案。框架不会强制业务状态必须放入 Store。

Store.batchUpdate() 可以在一个同步批次中多次调用 setState()。批次内读取 state 会得到最新值,最外层批次结束后只通知一次,selector 只比较最终结果。批次回调不能返回 Promise。

EditSession 和 FormSession

EditSession<T> 管理 baseline、draft、dirty、touched、恢复和同步事务。它适合需要取消编辑或判断未保存状态的页面,但不处理校验和提交。

FormSession<T> 在 EditSession 上组合:

  • ErrorProvider:按 source 保存字段和表单问题。
  • ValidationProvider:执行 change、blur、submit 规则,支持异步取消和跨字段规则。
  • 字段注册和首错定位。
  • 校验参与者,用于接入拥有独立错误模型的 Grid。
  • 同步编辑参与者,用于在校验或提交前收尾编辑器内部暂存输入。
  • runSubmit()、重复提交保护和提交取消。
interface Draft {  name: string  amount: number} const form = new FormSession<Draft>({  name: '',  amount: 0,}) form.registerField('name', {  rules: [value => value.trim() ? undefined : '名称不能为空'],})form.setValues({ name: '项目 A', amount: 10 })

这些对象不依赖 RenderObject,也不会扫描组件树。常规表单可用 FormBindingBag 显式连接字段、ValueEditor 和字段展示组件;特殊页面仍可手动注册规则、写值和投影错误。dispose() 会取消未完成的校验和提交并清理订阅。

自定义复合编辑器可以通过 registerEditParticipant(participant, { fields }) 接入同步编辑收尾。声明 fields 后,validateField(field) 只刷新命中该字段的参与者;不声明时按兼容语义参与所有单字段校验。整表 validate() 和 runSubmit() 始终刷新全部参与者。flushPendingEdit(trigger, targetField?) 必须同步完成;resetField()、reset() 和 acceptChanges() 会调用可选的 cancelPendingEdit(field?)。复合编辑器还可以公开 isDirty、isTouched、subscribeState()、resetEdits() 和 acceptEdits(),由 FormSession 聚合状态并传播整表恢复、接受 baseline 生命周期。普通输入组件优先使用 编辑器契约与表单字段绑定,DataGrid 使用 DataGrid 编辑会话,无需自己重复注册参与者。

PaintContext

PaintContext 是绘制上下文封装,组件 paint 阶段通过它访问 canvas 2D context、主题和绘制辅助能力。

业务页面通常不直接使用 PaintContext。只有开发自绘组件、图表、条码、编辑器或文档渲染器时才需要关注。

使用边界

  • 业务页面优先使用 widgets 和 layout API。
  • 自定义通用组件时再深入 core API。
  • 不要依赖未从包入口导出的内部 helper。
  • 不要在业务层直接操作 render tree 私有字段。
  • 不要在 paint 中触发 layout 或业务副作用。
  • 管线调度、频繁重绘、焦点异常优先用运行时诊断定位。

相关文档