SWT shell实现遨游窗

SWT shell实现漫游窗

如何在eclipse中视图或某些编辑区域内实现漫游窗?如同Microsoft words样式窗口。更进一步的要求是漫游窗只能在窗口编辑区域内游动。

 

首先能想到的是使用一个shell,如果一个shell通过代码控制,使其location只能在编辑区内,剩下的旧好办。如此,就要使用非模态shelldialog了,在SWT中靠SWT.MODELESS表明。

背景知识:

Shell模式:http://blog.csdn.net/g5dsk/article/details/4350307

所谓"模式"窗口, 就是指除非采取有效的关闭手段, 用户的鼠标焦点或者输入光标将一直停留在其上的窗口. 非模态窗口则不会强制此种特性, 用户可以在当前窗口以及其他窗口间进行切换.

Shell组件包含一个称做"模式"的样式, 该样式用于决定是否阻拦该Shell组件依赖的display上的其他输入. 该样式可以是SWT.APPLICATION_MODAL, SWT.MODELESS, SWT.PRIMARY_MODALSWT.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.yshell面板左顶点为坐标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-->>(鼠标从shellclient的左上角划过)可知,鼠标移动中产生的e.xe.y是相对shellclient左上角的。

 

shellsize设定为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()给出clientrect宽度是117,高度是68,再来比较一下设定两个不同shellsize。可知在标题有close叉的情况下,shell的宽度最小为123,其中包括边框宽度3.所以client的宽度就是123-6 = 117.而标题栏的高度是 100 – 68 – 3(边框宽) == 50 – 18 - 3(边框宽) == 29。我们可以通过shellsize设定为200,200.来验证shell边框的宽度和标题栏的高度。而给出的clientArea的顶点xy总是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)处得到想要的大小(100100)的clientArea,那么需要设置给shell的大小应该是(106132)。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:0e.x,e.y,相对于clientArea

Point {-3, -29}

Point {3, 29}

shell相对于(e.xe.y)的位置是(-3-29.

(e.x,e.y)相对于display的位置(屏幕坐标原点)是(329)这里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);