diff --git a/examples/classes.endor b/examples/classes.endor index 6da88c5..e1be991 100644 --- a/examples/classes.endor +++ b/examples/classes.endor @@ -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) diff --git a/examples/constructor-contracts.endor b/examples/constructor-contracts.endor index aab8dac..4e59e27 100644 --- a/examples/constructor-contracts.endor +++ b/examples/constructor-contracts.endor @@ -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 = "构造器契约" diff --git a/examples/field-contracts.endor b/examples/field-contracts.endor index a6c683d..e2dcbd3 100644 --- a/examples/field-contracts.endor +++ b/examples/field-contracts.endor @@ -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 和源码写入同一个受检查的字段"))) diff --git a/examples/function-types.endor b/examples/function-types.endor index f7a2c3d..5927ee1 100644 --- a/examples/function-types.endor +++ b/examples/function-types.endor @@ -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 = "函数类型检查" diff --git a/examples/method-contracts.endor b/examples/method-contracts.endor index 19b7dff..f6d95e4 100644 --- a/examples/method-contracts.endor +++ b/examples/method-contracts.endor @@ -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")) diff --git a/examples/native-signatures.endor b/examples/native-signatures.endor index 71b30ce..9f8c30c 100644 --- a/examples/native-signatures.endor +++ b/examples/native-signatures.endor @@ -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) diff --git a/examples/native-subclasses.endor b/examples/native-subclasses.endor index 8b8735c..3ac84e3 100644 --- a/examples/native-subclasses.endor +++ b/examples/native-subclasses.endor @@ -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")))