// 自定义形状命中:对象原始矩形宽 160,但 paint 只画左侧直径 80 的圆,hitTest 也只接受圆内。 // 圆外部分可落到父容器监听器;按下与释放须经相同合法目标复核。 // 扩展矩形的绘制、命中和事件方法,用同一个 hits 状态观察交互结果。 class CircleTarget: RectBlock { // hits 由下面顶层 state 提供,源命中只读它所需的几何,计数写入留给 onEvent。 hits: Object // point 已转换到该出现位置局部坐标;使用圆心(40,40)、半径40,而不是整个 160x80 外框。 function hitTest(self: CircleTarget, mount: Mount, point: Point): Bool { // source_hit 在候选出现的命中阶段调用,point 已转换为局部坐标,不应再减 geometry.x/y。 return point.inCircle(40, 40, 40) } // 把原生命令宽度缩为 80、圆角半径设为 40,得到与 hitTest 对应的圆形区域。 function paint(self: CircleTarget, mount: Mount, geometry: Bounds): DisplayList { // source_paint 调用此覆盖;修改新生成的第一条矩形命令,不把共享对象的原生 width 从 160 改成 80。 drawing = RectBlock.paint(self, mount, geometry) drawing.commands.at(0).width = 80 drawing.commands.at(0).radius = 40 return drawing } // 圆内点击递增自身计数并停止向祖先传播;父容器不会为同一次点击再递增。 function onEvent(self: CircleTarget, event: Event): Unit { // 此示例的点击分发选中圆后调用对象方法;stopPropagation 只阻止后续祖先组,不撤销已经执行的捕获处理。 // 此覆盖没有按 event.kind 过滤;若从源码派发其他事件,同样会递增 hits 并停止其后续祖先传播。 self.hits.increment() event.stopPropagation() } } hits = ui.state(0) // 父监听器单独计数;它和圆形目标显示在同页,便于比较穿过透明区的输入。 background = ui.state(0) circle = CircleTarget(160, 80, 0, "#237A45", hits) // 布局仍给 circle 原生的 160×80 盒,只有绘制和形状命中缩成左侧圆;透明右半部并非不存在的布局区域。 root = Column(24, 16, circle, TextBlock("点击圆内计入圆;透明右半区域交给父容器"), TextBlock("圆内点击"), ui.textOf(hits, "value"), TextBlock("父容器点击"), ui.textOf(background, "value")) // 订阅在父 Column 上,圆外有效区域可回退到父目标;源命中只读检查不执行这些业务回调。 connection = root.on("click", background.increment) page = Page() page.render(root)