SWT shell实现漫游窗 博客分类: swt swtshellcoordinatesize
如何在eclipse中视图或某些编辑区域内实现漫游窗?如同Microsoft words样式窗口。更进一步的要求是漫游窗只能在窗口编辑区域内游动。
首先能想到的是使用一个shell,如果一个shell通过代码控制,使其location只能在编辑区内,剩下的旧好办。如此,就要使用非模态shell或dialog了,在SWT中靠SWT.MODELESS表明。
背景知识:
Shell模式:http://blog.csdn.net/g5dsk/article/details/4350307
所谓"模式"窗口, 就是指除非采取有效的关闭手段, 用户的鼠标焦点或者输入光标将一直停留在其上的窗口. 非模态窗口则不会强制此种特性, 用户可以在当前窗口以及其他窗口间进行切换.
Shell组件包含一个称做"模式"的样式, 该样式用于决定是否阻拦该Shell组件依赖的display上的其他输入. 该样式可以是SWT.APPLICATION_MODAL, SWT.MODELESS, SWT.PRIMARY_MODAL或SWT.SYSTEM_MODAL. SWT.PRIMARY_MODAL样式允许Shell组件阻拦对其父组件的输入; SWT.APPLICATION_MODAL样式阻拦Shell组件依赖的display上的所有其他Shell组件的输入; 样式SYSTEM_MODAL样式阻拦当前系统中所有的向Shell组件的输入.
不同的平台对阻拦"模式"的支持有所不同. 在这种情况下, SWT会进行相应的"向上向下兼容". 比如一些操作系统就不支持SYSTEM_MODAL, 此时就会向下兼容, 使用APPLICATION_MODAL.
使用下面的代码创建了一个窗体。
Shell shell = new Shell(getShell(), SWT.MODELESS|SWT.TITLE |SWT.CLOSE);
shell.setSize(100, 100);
shell.open();
这个窗体有边框和title。不符合需要,需要将SWT.TITLE |SWT.CLOSE这两个style去掉,但在实现阶段,先留着,最后再去掉。
下面设置窗体location,先来看看窗体宽度,边框,标题等数据(在windows xp主题下)。
通过使用增加mouseMoveListener监听器方式,看代码:
publicvoid mouseMove(MouseEvent e)
{
System.out.println(shell.getLocation());// shell左定点位置
System.out.println(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
System.out.println(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, false));
System.out.println(e.x + ":" + e.y);// e.x,e.y是shell面板左顶点为坐标0点开始算,不是从title的左顶点为0点计算
System.out.println(shell.computeTrim(e.x, e.y, 100, 100));
System.out.println(shell.getSize());// 宽度变成了123,高度还是设定的100
System.out.println(shell.getClientArea());// 面板宽度是117,高度为68,因此标题高度为32
System.out.println();
}
将shell的标题栏左上角顶点与屏幕的左上角顶点重合。打印结果如下:
Point {0, 0}
Point {70, 96}
Point {70, 96}
1:0
Rectangle {-2, -29, 106, 132}
Point {123, 100}
Rectangle {0, 0, 117, 68}
解释:
Point {0, 0}-->>可知shell.getLocation()在有标题的情况下,得到的是左上顶点的位置
1:0-->>(鼠标从shell的client的左上角划过)可知,鼠标移动中产生的e.x和e.y是相对shell的client左上角的。
将shell的size设定为50,50.再次运行程序。结果如下:
Point {0, 0}
Point {70, 96}
Point {70, 96}
0:0
Rectangle {-3, -29, 106, 132}
Point {123, 50}
Rectangle {0, 0, 117, 18}
解释:
Point {123, 100}
Rectangle {0, 0, 117, 68}
Point {123, 50}
Rectangle {0, 0, 117, 18}
在有标题的情况下,shell.getClientArea()给出client的rect宽度是117,高度是68,再来比较一下设定两个不同shell的size。可知在标题有close叉的情况下,shell的宽度最小为123,其中包括边框宽度3.所以client的宽度就是123-6 = 117.而标题栏的高度是 100 – 68 – 3(边框宽) == 50 – 18 - 3(边框宽) == 29。我们可以通过将shell的size设定为200,200.来验证shell边框的宽度和标题栏的高度。而给出的clientArea的顶点x,y总是clientArea左上角对应于clientArea的位置(也即,总是0,0)。
解释:
Point {70, 96}
Point {70, 96}
从打印结果分析,因为在shell中没有其他的控件,也就是shell内含控件固定,而通过shell.computeSize()优化计算出来的结果,总是一个固定值,而不论shell size的大小。
解释:
shell.computeTrim(e.x, e.y, 100, 100)
0:0
Rectangle {-3, -29, 106, 132}
1:0
Rectangle {-2, -29, 106, 132}
从上面的两个结果来看,要想在指定位置(e.x,e.y)处得到想要的大小(100,100)的clientArea,那么需要设置给shell的大小应该是(106,132)。Shell的位置应该定位在(e.x – 3, e.y – 29)
通过:
System.out.println(shell.toControl(e.x, e.y));
System.out.println(shell.toDisplay(e.x, e.y));
结果:
0:0(e.x,e.y,相对于clientArea)
Point {-3, -29}
Point {3, 29}
shell相对于(e.x,e.y)的位置是(-3,-29).
(e.x,e.y)相对于display的位置(屏幕坐标原点)是(3,29)这里3恰是边框宽度,29是标题栏的宽度。这里不明白的是,clientArea上的(0,0) 对应于display的坐标上,不应该是(3,32)么?32 = 29 + 3(标题栏+border的宽度) 。
下面看shell的实现。有标题的shell,是可以直接拖动的,没有标题通过设置shell的location实现。
让shell实现mouseListener和MouseMoveListener两个监听器,在鼠标左键按下、弹起时分别设置两个值:
boolean LButtonDown = true/false
Point point = new Point(e.x,e.y) / new Point(0,0);
在mouseMove()方法体中,使用下面的代码:
Point oldLoc = shell.getLocation();
System.out.println("shell old loc: " + oldLoc);
System.out.println("old exy: " + point.x + ":" + point.y);
System.out.println("new exy : " + e.x + ":" + e.y);
Point dp = shell.toDisplay(e.x, e.y);
System.out.println("new to display : " + dp);
shell.setLocation(dp.x - 3 - point.x, dp.y - 29 - point.y);
System.out.println("shell new loc: " + shell.getLocation());
得到结果:
shell old loc: Point {2, 1}
old exy: 3:2
new exy : 4:2
new to display : Point {9, 32}
shell new loc: Point {3, 1}
可见在x轴增1的情况下,shell的x轴也随之增1.实现鼠标跟随。其中shell.setLocation(dp.x - 3 - point.x, dp.y - 29 - point.y);是shell在有title的情况下。如果shell没有title(这时shell的bord宽度是1)。则应该是shell.setLocation(dp.x - 1 - point.x, dp.y - 1 - point.y);
进一步,实现shell在一个给定的窗口中漫游。在mouseMove()方法中使用下面代码:
Point dp = shell.toDisplay(e.x, e.y);
int x = dp.x - 1 - point.x;
int y = dp.y - 1 - point.y;
Rectangle shellBounds = shell.getBounds();
Rectangle bounds = viewer.getControl().getBounds();
Point windowXy = viewer.getControl().toDisplay(bounds.x, bounds.y);
x = x < windowXy.x ? windowXy.x : x;
y = y < windowXy.y ? windowXy.y : y;
int m = windowXy.x + bounds.width - shellBounds.width;
int n = windowXy.y + bounds.height - shellBounds.height;
x = x > m ? m : x;
y = y > n ? n : y;
shell.setLocation(x, y);