class Paragraph: TextBlock { function measure(self: Paragraph, mount: Mount, constraints: Constraints): MeasureResult { return TextBlock.measure(self, mount, constraints) } function paint(self: Paragraph, mount: Mount, geometry: Bounds): DisplayList { return TextBlock.paint(self, mount, geometry) } } class Actions { window: Window paragraph: TextBlock full: String function narrow(self: Actions, event: Event): Unit { self.window.resize(800, 650) } function wide(self: Actions, event: Event): Unit { self.window.resize(1200, 650) } function short(self: Actions, event: Event): Unit { self.paragraph.setText("短文本:同一个对象、同一个 Mount。") } 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 👩‍💻 使用同一套字体整形。再次修改文字会重新计算高度,并更新两个窗口,不需要复制业务对象。" paragraph = Paragraph(full) tail = TextBlock("这一行的位置由前面段落的实际高度决定。") actions = Actions(ui.window(), paragraph, full) narrow = Button("变窄") wide = Button("变宽") short = Button("短文本") restore = Button("恢复段落") narrowConnection = narrow.on("click", actions.narrow) wideConnection = wide.on("click", actions.wide) shortConnection = short.on("click", actions.short) restoreConnection = restore.on("click", actions.restore) page = Page() page.title = "约束下的文字换行" page.render(Column(24, 16, Row(0, 12, narrow, wide, short, restore), paragraph, tail)) other = Window() other.resize(720, 500) other.setTitle("同一个段落 · 另一组宽度") other.moveTo(1700, 80) second = Page() second.title = "共享 Paragraph" second.render(Column(24, 16, paragraph)) second.open(other) page.activate() ui.window().resize(800, 650) ui.window().setTitle("文字换行与相邻布局") ui.window().moveTo(50, 80) ui.window().focus()