docs: clarify example inheritance and type contract calls

This commit is contained in:
2026-11-12 19:40:58 +08:00
parent 07812bc47a
commit 9cdbf00961
7 changed files with 37 additions and 0 deletions
+6
View File
@@ -2,17 +2,20 @@
// 两个 textOf 共享同一模型字段,修改一次业务值会更新两个展示位置。
// 定义业务状态和公共事件入口,Number/String 字段在构造与赋值时按声明契约检查。
class Counter {
// value 是两处 textOf 的读取目标,lastEvent 是第三处绑定的读取目标;它们都属于同一模型。
value: Number
lastEvent: String
// 保存实际事件种类,再通过 self 调用可覆盖的方法;接收者保持具体子类身份。
function activate(self: Counter, event: Event): Unit {
// Button.onClick 派发传入本次 Event;先记录种类,再调用下面的动态业务方法。
self.lastEvent = event.kind
self.change()
}
// 基类默认行为把值置为 10,子类可复用入口并替换这部分策略。
function change(self: Counter): Unit {
// 仅直接使用 Counter 或未覆盖 change 的子类才采用此默认值;本例 DemoCounter 会替换它。
self.value = 10
}
}
@@ -21,15 +24,18 @@ class Counter {
class DemoCounter: Counter {
// 覆盖把值设为 12,演示从继承方法内部发起的调用也会选中实际接收者的方法。
function change(self: DemoCounter): Unit {
// 本例从 Counter.activate 的 self.change() 进入这里,self 仍是 model,两个数值绑定一起失效。
self.value = 12
}
}
// 创建唯一模型,之后所有绑定和按钮回调都指向它,无需把 GUI 状态复制到另一份存储。
model = DemoCounter(9, "等待点击")
// 两次调用各创建一个展示对象,但都读取 model.value;不是把初值 9 各自复制成互不关联的计数器。
first = ui.textOf(model, "value", 2)
second = ui.textOf(model, "value", 2)
eventName = ui.textOf(model, "lastEvent")
// 构造按钮只建立对象,下一行 onClick 注册后才拥有修改模型的点击行为。
button = Button("调用继承方法,再分派到覆盖方法")
// 注册绑定方法,运行时将 click 的 Event 传给 activate;最后 Page.render 才把对象树显示出来。
button.onClick(model.activate)
+6
View File
@@ -10,17 +10,23 @@ class TaggedEvent: Event { tag: String }
class TaggedList: ObjectList { tag: String }
// 将需要更新的矩形作为强业务引用保存,按钮回调只修改它的颜色。
class Actions {
// 字段保存 TaggedRect 的对象引用,recolor 的写入会被当前页面中该对象的绘制读取观察到。
rect: TaggedRect
// 写原对象的 color,挂载显示由依赖跟踪刷新,不需要重新构造整个页面。
// 由下面 Button.onClick 注册为零参绑定方法;不接收 Event,也不修改 tag。
function recolor(self: Actions): Unit { self.rect.color = "#237A45" }
}
// 这里刻意省略原生可选项,用末尾字符串初始化子类字段;后面的列表/Column 做对应可变参数演示。
rect = TaggedRect(180, 60, "省略 radius/color 后的字段")
// kind 是原生 Event 参数,第二个字符串才是 TaggedEvent 的源字段 tag。
event = TaggedEvent("click", "事件字段")
// 前两个托管对象归入原生 ObjectList 成员,末尾字符串留给子类字段,不作为第三个列表成员。
items = TaggedList(rect, event, "列表成员之后的字段")
actions = Actions(rect)
button = Button("调用原对象方法")
// 这里只注册回调,不立即执行 recolor,因此首次显示仍使用矩形原生构造的默认颜色。
button.onClick(actions.recolor)
// Column 原生参数接收 padding/gap 和视觉孩子;末尾 tag 字符串不参与布局,textOf 则读取各对象的 tag。
root = TaggedColumn(24, 16, TextBlock("原生选项与子类字段分别检查", 2), rect, ui.textOf(rect, "tag"), ui.textOf(event, "tag"), ui.textOf(items, "tag"), button, "可变子项之后的字段")
page = Page()
page.title = "构造器契约"
+5
View File
@@ -2,21 +2,26 @@
// 绑定显示读取该字段,GUI 回调与 Console 赋值受同一契约约束。
// 增加 value:Number;构造时先传原生 text,再传源字段值。
class CounterText: TextBlock {
// value 是源字段,不是 TextBlock 的原生 text;页面把两者作为不同内容分别展示。
value: Number
// 收到 click 后把已有数值改为 10,依赖文字随之更新,不修改原生 text 内容。
function change(self: CounterText, event: Event): Unit {
// button.on("click", ...) 派发时提供 event;本例不读取事件载荷,只向受 Number 契约保护的字段写值。
self.value = 10
}
}
// 声明 target 必须是 Block,可接收 CounterText,因为它是 TextBlock 的子类。
class Model {
// 这里用父类型约束对象身份;不复制 counter,也不将它退化为只有 Block 字段的新对象。
target: Block
}
// 构造唯一受检对象,Model、事件回调和 textOf 均引用它。
counter = CounterText("共享的原生子类对象", 9)
model = Model(counter)
// model 仅演示构造时接受子类型;下面的回调和绑定直接引用 counter,不经过 model.target。
button = Button("把 Number 字段改成 10")
connection = button.on("click", counter.change)
// 保留 connection 供后续管理订阅;回调的接收者已经绑定为 counter,页面只负责把它与数值绑定挂载出来。
page = Page()
page.title = "字段类型契约"
page.render(Column(24, 16, counter, ui.textOf(counter, "value"), button, TextBlock("GUI 和源码写入同一个受检查的字段")))
+5
View File
@@ -3,11 +3,13 @@
// Number 输入原样返回,用于说明参数类型在进入函数体前检查。
function identity(value: Number): Number { return value }
// 接受 Function 值并零参数调用,结果仍需符合本函数的 Number 返回契约。
// 顶层 initial 的求值会调用它,callback 是已绑定 actions 的 read,调用时无需再传 self。
function callNumber(callback: Function): Number { return callback() }
// 接受声明 Visual 能力的对象,返回 Block;调用时两侧契约都要成立。
function visual(value: Visual): Block { return value }
// 保留共享 state,read 作为普通绑定函数使用,click 作为接收 Event 的事件回调。
class Actions {
// Object 允许保存原生状态对象;实际 read/click 仍要求该对象提供 value/increment 成员。
state: Object
// 读取模型值而不修改业务状态,保存 actions.read 时接收者已绑定。
function read(self: Actions): Number { return self.state.value }
@@ -15,12 +17,15 @@ class Actions {
function click(self: Actions, event: Event): Unit { self.state.increment() }
}
state = ui.state(0)
// Actions 与 textOf 持有同一状态,点击 increment 后依赖显示读取新的 value,而非重新计算 initial。
actions = Actions(state)
// 保存绑定方法对象,之后无参调用仍使用 actions 作为 self;不是重新按字符串查找对象。
savedFunction = actions.read
initial = identity(callNumber(savedFunction))
// initial 只在顶层求值一次,未注册为响应式绑定,也未加入页面,因此后续点击不更新这个变量。
button = Button("调用带类型检查的对象方法")
connection = button.on("click",actions.click)
// Column 具备 Visual 能力且满足返回 Block 契约,visual 只验证并返回原对象,不额外包一层容器。
root = visual(Column(24,16,TextBlock("源码、原生与绑定方法统一为 Function"),ui.textOf(state,"value"),button))
page = Page()
page.title = "函数类型检查"
+5
View File
@@ -6,6 +6,7 @@ class Sized: MeasureResult {}
class BaseRect: RectBlock {
// 原生方法按给定出现位置与约束测量,不把几何存进共享对象。
function measure(self: BaseRect, mount: Mount, constraints: Constraints): MeasureResult {
// 由 WideRect.measure 显式调用此实现,再委托 RectBlock 原生测量;传递的仍是当前出现与当前约束。
return RectBlock.measure(self, mount, constraints)
}
}
@@ -13,7 +14,9 @@ class BaseRect: RectBlock {
class WideRect: BaseRect {
// 调用基类后复制测量数值到新的 Sized;新结果仍满足 MeasureResult 协议。
function measure(self: WideRect, mount: Object, constraints: Object): Sized {
// 宿主布局驱动仍传入 Mount/Constraints;Object 注解只放宽覆盖签名,不改变实际输入对象。
measured = BaseRect.measure(self, mount, constraints)
// 拷贝的是本轮测量结果的两个数值,不复制业务矩形或 Mount;返回新结果供宿主校验。
return Sized(measured.width, measured.height)
}
}
@@ -23,6 +26,7 @@ class Actions {
count: Object
// 先改 width 再递增计数,新的布局刷新会使用上述动态测量覆盖。
function widen(self: Actions): Unit {
// Button.onClick 调用零参绑定方法;width 写入令依赖该原生尺寸的测量失效,count 驱动另一文字绑定。
self.rect.width = 260
self.count.increment()
}
@@ -31,6 +35,7 @@ class Actions {
rect = WideRect(180, 60, 8, "#ED6A5A")
count = ui.state(0)
actions = Actions(rect, count)
// 所有点击都操作同一个 rect/count;重复设 width=260 不继续增加宽度,但 increment 每次都会增加计数。
button = Button("扩大矩形")
button.onClick(actions.widen)
root = Column(24, 16, TextBlock("参数可以更宽,返回类型可以更具体", 2), rect, button, ui.textOf(count, "value"))
+4
View File
@@ -2,14 +2,18 @@
// 同一事件注册机制按实际签名调用;label 绑定再把事件 kind 转成可显示的字符串。
// 保留最后一次收到的 Event 对象,初值 kind=waiting;不是只存一份格式化事件文本。
received = ui.state(Event("waiting"))
// 单独计数的状态与 received 无关,第二个按钮不会改变 eventKind 读取的事件对象。
count = ui.state(0)
// 只读计算提取当前事件种类,作为 label.text 绑定的依赖。
// 由 label.bind 的绑定求值调用,无参数;读取链经过状态 value 再到 Event.kind,返回可显示字符串。
function eventKind(): String { return received.value.kind }
label = TextBlock("waiting")
subscription = label.bind("text", eventKind)
// subscription 管理字段绑定,下面两个 Connection 管理输入监听,两种注册的用途不同。
withEvent = Button("传递 Event 给原生 set")
// received.set 是一参数原生方法,分发器传入实际 click Event。
firstConnection = withEvent.on("click", received.set)
// 保存的是绑定 count 的原生方法,事件分发按它的零参签名调用,无需人为包装一个丢弃 Event 的函数。
withoutEvent = Button("调用零参数原生 increment")
// count.increment 的签名为零参数,分发器调用时不传多余 Event。
secondConnection = withoutEvent.on("click", count.increment)
+6
View File
@@ -2,10 +2,12 @@
// label 在树中出现多次但仍是同一对象,原生方法和源字段写入共同更新各出现位置。
// 扩展 TextBlock 的业务字段;其文本/颜色仍由原生父类存储与绘制。
class StatusText: TextBlock {
// 构造后保存颜色策略;CustomStatus 继续继承此字段,源覆盖中通过同一 self 读取。
activeColor: String
// 用继承的 setText/setColor 改原对象;activeColor 是构造时追加的源字段。
function activate(self: StatusText, event: Event): Unit {
// 这是基类事件策略,本例实际 label 是 CustomStatus,因此 ActionButton 转发会选中下方覆盖。
self.setText("继承的原生文本已更新")
self.setColor(self.activeColor)
}
@@ -17,6 +19,7 @@ class CustomStatus: StatusText {
// 显式调用基类 setText 以示范原生未绑定方法的接收者传递,再用继承方法改颜色。
function activate(self: CustomStatus, event: Event): Unit {
// 接收的是按钮转发的同一 Event;显式基类方法调用只传目标 self 和新文本,不把 Event 当作文本参数。
TextBlock.setText(self, self.message)
self.setColor(self.activeColor)
}
@@ -28,6 +31,7 @@ class ActionButton: Button {
// 不复制目标或创建新展示对象,直接调用目标业务方法,选择实际类型的覆盖实现。
function activate(self: ActionButton, event: Event): Unit {
// button.onClick(button.activate) 派发进入这里,target 实际为 CustomStatus,动态查找其 activate 覆盖。
self.target.activate(event)
}
}
@@ -41,5 +45,7 @@ class CustomPage: Page {
label = CustomStatus("点击按钮更新同一个自定义文本对象", 2, "#242422", "#237A45", "源码子类与原生载荷共同工作")
button = ActionButton("调用源码子类方法", label)
button.onClick(button.activate)
// caption 是 CustomPage 的源构造字段;下面 render 只把它的当前字符串用于标题 TextBlock,没有注册 caption 绑定。
page = CustomPage("Endor 原生类型的源码继承")
// label 在根孩子中引用两次而不复制,setText/setColor 一次写入会刷新两处出现;末尾 textOf 单独展示 message 字段。
page.render(Column(24, 16, TextBlock(page.caption, 2), label, label, button, ui.textOf(label, "message")))