如何监听Shell标题栏点击?SWT双ON_TOP窗口层级变更事件检测
SWT ON_TOP Shell窗口层级变化捕获问题
问题场景
现有两个带SWT.ON_TOP样式的Shell窗口:
- 状态1:A位于B上方,B处于激活状态(通过点击B的内容区域进入此状态)
- 状态2:点击B的标题栏后,B被置于A上方,但此操作无法触发
Activate、Deactivate或MouseDown事件,界面却已发生层级变化
需要捕获用户点击B标题栏切换到状态2的操作,测试代码如下:
public class ShellTest { public static void main(String[] args) { Shell shellA = new Shell( SWT.SHELL_TRIM | SWT.ON_TOP ) ; shellA.setSize( 300, 300 ) ; shellA.setText( "A" ) ; Shell shellB = new Shell( SWT.SHELL_TRIM | SWT.ON_TOP ) ; shellB.setSize( 300, 300 ) ; shellB.setText( "B" ) ; shellB.addListener( SWT.Activate, e -> { if ( shellA != null && !shellA.isDisposed() ) { shellA.moveAbove( null ) ; } } ) ; Display display = shellB.getDisplay() ; display.addFilter( SWT.Activate, e -> System.out.println( e ) ) ; display.addFilter( SWT.Deactivate, e -> System.out.println( e ) ) ; shellA.open() ; shellB.open() ; while ( !shellA.isDisposed() && !shellB.isDisposed() ) { if ( !display.readAndDispatch() ) { display.sleep() ; } } display.dispose() ; } }
原因分析
SWT.ON_TOP窗口的层级调整由操作系统窗口管理器直接处理,未触发SWT的常规激活/鼠标事件,因此无法通过常规事件监听捕获。
解决方案
1. 监听SWT.Move事件
部分系统中,点击标题栏调整层级时会触发窗口的SWT.Move事件,可通过此事件触发层级检查:
// 给两个Shell添加Move事件监听 shellA.addListener(SWT.Move, e -> checkZOrder(shellA, shellB)); shellB.addListener(SWT.Move, e -> checkZOrder(shellA, shellB)); private static void checkZOrder(Shell a, Shell b) { if (a.isDisposed() || b.isDisposed()) return; // 判断B是否在A上方 boolean bAboveA = b.isAbove(a); if (bAboveA) { System.out.println("检测到B被置于A上方"); } }
2. 全局监听SWT.ShellActivated事件
通过Display的全局过滤器监听SWT.ShellActivated,并延迟判断层级(确保系统完成层级调整):
Display display = shellB.getDisplay(); display.addFilter(SWT.ShellActivated, e -> { Shell target = (Shell) e.widget; if (target == shellB) { // 延迟100ms执行,避免系统层级调整未完成 display.timerExec(100, () -> { if (!shellA.isDisposed() && !shellB.isDisposed()) { if (shellB.isAbove(shellA)) { System.out.println("捕获到B标题栏点击操作,层级切换"); } } }); } });
3. 定时轮询层级(兜底方案)
如果上述事件均无法触发,可通过定时任务轮询窗口层级状态:
boolean lastState = false; // 记录上次层级状态 Display display = shellB.getDisplay(); display.timerExec(200, new Runnable() { @Override public void run() { if (shellA.isDisposed() || shellB.isDisposed()) return; boolean currentState = shellB.isAbove(shellA); if (currentState != lastState) { lastState = currentState; if (currentState) { System.out.println("检测到B切换到A上方"); } } // 继续定时轮询 display.timerExec(200, this); } });
关键说明
isAbove(Shell)方法是SWT提供的直接判断窗口层级关系的API,返回目标窗口是否在当前窗口的视觉上方- 延迟执行和定时轮询的时间可根据系统响应速度调整,建议在100-300ms区间
内容的提问来源于stack exchange,提问作者Bourbon_7
相关产品推荐
相关产品推荐

