docs: clarify example inheritance and type contract calls
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 = "构造器契约"
|
||||
|
||||
@@ -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 和源码写入同一个受检查的字段")))
|
||||
|
||||
@@ -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 = "函数类型检查"
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")))
|
||||
|
||||
Reference in New Issue
Block a user