// 源视觉协议的最小组合:measure 决定尺寸,arrange 决定子项位置,Metrics 是被两者读取的业务模型。 // 运行时在只读求解中跟踪这些读取,事件修改模型后重新布局;布局方法不能直接改业务字段。 // 保存宽度与左偏移;两字段只在事件回调中修改。 class Metrics { // width 提供 measure 输出,left 提供 arrange 的首子槽 x;模型状态不是 Mount 的派生几何副本。 width: Number left: Number // 同时改变测量和排列依赖,整帧需收敛后一起发布新尺寸/位置。 // button.on 的绑定事件回调写入两个字段,方法返回后的刷新把受影响布局合并到同一帧。 function expand(self: Metrics, event: Event): Unit { self.width = 260 self.left = 68 } } // 复用原生矩形载荷,仅覆盖尺寸策略;同一 rect 放在不同父容器可得到不同测量。 class MeasuredRect: RectBlock { // 首次测量前结构 Mount 链接已建立,因此可从 parent.block 读取当前出现位置的父策略。 // 此阶段还不能查询最终 bounds,返回值使用 MeasureResult 名义类型。 function measure(self: MeasuredRect, mount: Mount, constraints: Constraints): MeasureResult { // 从当前出现而非共享 rect 查父 Mount,同一 MeasuredRect 换父容器时可读取不同的 metrics。 // 读取真实 Mount 上下文时,当前测量缓存会保守判定易变;不能仅因最终宽度只取模型字段就认为结果可跨帧复用。 parent = mount.parent() // 父 Block 需要符合示例的 metrics 约定;MeasureResult 宽高随后仍由宿主按当前 Constraints 检查和钳制。 return MeasureResult(parent.block.metrics.width, 60) } } // 在原生排列结果上改第一个子槽 x,其他子项仍用 Column 的堆叠规则。 class OffsetColumn: Column { metrics: Metrics // Arrangement 里的 x 是当前排列使用的坐标;读取 metrics.left 成为排列依赖。 function arrange(self: OffsetColumn, mount: Mount, geometry: Bounds): Arrangement { // 保留原生 Column 对全部孩子的槽分配,临时结果可修改,不在 arrange 中写 self.metrics。 positions = Column.arrange(self, mount, geometry) // 这里直接覆盖排列结果坐标为 metrics.left,不是给 geometry.x 再加相对位移;其他槽保持原生结果。 positions.children.at(0).x = self.metrics.left return positions } } // 将原生参数与源字段传给具体子类,button 事件更新模型,Page.render 建立完整展示关系。 metrics = Metrics(200, 44) // 原生矩形构造参数仍提供绘制载荷,实际测量被源覆盖为模型宽度和固定高 60。 rect = MeasuredRect(80, 20, 0, "#526AC7") button = Button("更新布局") // 首个孩子是 rect,metrics 是 Column 子类新增字段,不是额外的视觉子项。 root = OffsetColumn(24, 16, rect, TextBlock("尺寸来自源码 measure,位置来自源码 arrange"), button, metrics) // 保存监听连接使按钮操作同一个模型;布局方法由宿主协议调用,不由事件回调直接手工调用。 connection = button.on("click", metrics.expand) page = Page() // 先建立出现树和临时 Mount 上下文,再运行 measure/arrange,源方法才能在求解阶段查看父关系。 page.render(root)