Files
endorphin-tessera-gui/examples/wrapped-text.endor
T

69 lines
4.9 KiB
Plaintext

// 多窗口共享长段落:同一文字对象在每个 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()