// 多窗口共享长段落:同一文字对象在每个 Mount 按当前宽度独立换行,后继位置跟随实际高度。 // 源子类显式委托原生 measure/paint,保留系统字体塑形、组合字符和 emoji 的行为。 // 没有独立宽度副本;约束与换行结果属于出现位置,文本内容属于共享对象。 class Paragraph: TextBlock { // 将当前 Mount 与 Constraints 交给原生文字测量,宽度变化触发重测高度。 // 宿主测量驱动调用源覆盖,源方法再显式调用原生基类,使用当前出现的可用宽度与字体系统计算高度。 function measure(self: Paragraph, mount: Mount, constraints: Constraints): MeasureResult { return TextBlock.measure(self, mount, constraints) } // 使用本次 geometry 画文字,和原生测量采用相同换行区域。 // 源绘制驱动在布局完成后传本轮 Bounds,原生文字命令携带对应换行边界,不重新猜另一套宽度。 function paint(self: Paragraph, mount: Mount, geometry: Bounds): DisplayList { return TextBlock.paint(self, mount, geometry) } } // 保存主窗口、共享文字和原全文,四个按钮分别改窗口尺寸或改文字内容。 class Actions { // 只保存主窗口资源;narrow/wide 不改下面 other 的尺寸,两个窗口共享的是 paragraph 而非 viewport。 window: Window paragraph: TextBlock full: String // 请求主窗口变窄;真正的 viewport 由原生尺寸回传后影响换行。 function narrow(self: Actions, event: Event): Unit { self.window.resize(800, 650) } // 请求主窗口变宽,文字高度随之减小,不改另一窗口尺寸。 function wide(self: Actions, event: Event): Unit { self.window.resize(1200, 650) } // 用短文本替换同一对象内容,两个窗口的文字都重新测量。 // 短文本提示只是示例字符串;真实 paragraph 仍同时出现在两个窗口,修改内容不会把两处 Mount 合成一个。 function short(self: Actions, event: Event): Unit { self.paragraph.setText("短文本:同一个对象、同一个 Mount。") } // 恢复保存的 full 字符串,不创建新的 Paragraph。 function restore(self: Actions, event: Event): Unit { self.paragraph.setText(self.full) } } full = "GUI 和代码操控同一套 Aster 内存对象。这个段落同时出现在两个窗口:对象内容相同,每个 Mount 根据自己的可用宽度换行。窗口变窄,段落高度增加,后面的对象向下移动;窗口变宽,段落高度减小,后面的对象向上移动。对象身份和已有 Mount 身份保持稳定。English words wrap at word boundaries; a_long_unbroken_identifier_also_wraps_when_it_cannot_fit_on_one_line. 中文、组合字符 é 与 emoji 👩‍💻 使用同一套字体整形。再次修改文字会重新计算高度,并更新两个窗口,不需要复制业务对象。" // 只创建一次共享业务对象,full 字符串另由 Actions 保存供 restore 恢复,不重新 new 一个段落实例。 paragraph = Paragraph(full) // 后继文字没有硬编码 y,由 Column 安排读取前一个段落本轮实际高度来定位。 tail = TextBlock("这一行的位置由前面段落的实际高度决定。") actions = Actions(ui.window(), paragraph, full) narrow = Button("变窄") wide = Button("变宽") short = Button("短文本") restore = Button("恢复段落") // 四个连接各绑定同一 Actions 实例的不同事件方法;方法声明的 event 由点击派发提供。 narrowConnection = narrow.on("click", actions.narrow) wideConnection = wide.on("click", actions.wide) shortConnection = short.on("click", actions.short) restoreConnection = restore.on("click", actions.restore) // 主页面先展示四按钮、共享段落和 tail,页面与窗口身份和 paragraph 业务身份分别保存。 page = Page() page.title = "约束下的文字换行" page.render(Column(24, 16, Row(0, 12, narrow, wide, short, restore), paragraph, tail)) // 新窗口有独立可用宽度,但 second.render 仍用同一 paragraph,所以两个 Mount 可有不同高度。 // 创建同 VM 的另一个窗口资源,后面的 resize/title/move 是提交后由 GUI 执行的窗口命令。 other = Window() other.resize(720, 500) other.setTitle("同一个段落 · 另一组宽度") other.moveTo(1700, 80) // 第二页面使用不同的 Column 宽度上下文,再引用同一个 paragraph,产生独立换行测量和 Mount。 second = Page() second.title = "共享 Paragraph" second.render(Column(24, 16, paragraph)) // 将此页归到副窗口,不把主窗口 Page 或它的 Mount 直接复制过去。 second.open(other) // 恢复主页面活动状态并设置原生窗口尺寸/标题/桌面位置,focus 命令在成功输入后交宿主执行。 page.activate() ui.window().resize(800, 650) ui.window().setTitle("文字换行与相邻布局") ui.window().moveTo(50, 80) ui.window().focus()