下面的代码实现了一个透明可移动可改变尺寸的Composite窗体,如下图
鼠标点击窗口获取焦点,在获取焦点时会显示9个锚点用于改变窗口的位置和尺寸。
可以通过鼠标拖动锚点来改变窗口的位置或尺寸,也可以通过上下左右键来移动窗口
ActiveRectangle.java
package net.gdface.ui;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.wb.swt.SWTResourceManager;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
/** * 自定义透明窗口, * 窗口位置和尺寸可以通过鼠标和上下左右键修改 * @author gudong */
public class ActiveRectangle extends Composite {
/** * 锚点对象 * @author guyadong */
private class Anchor {
/** * 描述锚点位置尺寸的矩形对象 */
final Rectangle rect=new Rectangle(0, 0, anchorSize, anchorSize);
/** * 锚点的光标对象 */
final Cursor cusor;
/** * 锚点位置计算的掩码 */
final Rectangle mask;
Anchor(Cursor cusor, Rectangle mask) {
this.cusor = cusor;
this.mask=mask;
}
}
/** * 焦点矩形边框颜色 */
private static Color focusRectColor=SWTResourceManager.getColor(SWT.COLOR_GREEN);
/** * 非焦点矩形边框颜色 */
private static Color nofocusRectColor=SWTResourceManager.getColor(SWT.COLOR_RED);
/** * 矩形边框线宽 */
private static int lineWidth=1;
/** * 锚点矩形尺寸 */
private static int anchorSize=8;
/** * 上下左右键移动窗口的步长 */
private static int arrowStep=1;
/** * 是否焦点 */
boolean focus=true;
/** * 当前鼠标位置所在的锚点索引 {@link #anchors} */
private int anchorIndex=-1;
// 光标定义
private final Cursor CURSOR_SIZENESW=new Cursor(Display.getDefault(),SWT.CURSOR_SIZENESW);
private final Cursor CURSOR_SIZENS=new Cursor(Display.getDefault(),SWT.CURSOR_SIZENS);
private final Cursor CURSOR_SIZENWSE=new Cursor(Display.getDefault(),SWT.CURSOR_SIZENWSE);
private final Cursor CURSOR_SIZEWE=new Cursor(Display.getDefault(),SWT.CURSOR_SIZEWE);
private final Cursor CURSOR_SIZEALL=new Cursor(Display.getDefault(),SWT.CURSOR_SIZEALL);
//lt+-------+-------+rt
// | t |
// | |
// | c |
//l + + +r
// | |
// | |
// | b |
//lb+-------+-------+rb
enum AnchorType{
LT,T,RT,L,C,R,LB,B,RB
}
/** * 9个锚点位置({@link Anchor})对象数组(按从左到右从上到下顺序排序) */
private final Anchor[] anchors=new Anchor[]{
new Anchor(CURSOR_SIZENWSE,new Rectangle(1,1,-1,-1)),
new Anchor(CURSOR_SIZENS,new Rectangle(0,1,0,-1)),
new Anchor(CURSOR_SIZENESW,new Rectangle(0,1,1,-1)),
new Anchor(CURSOR_SIZEWE,new Rectangle(1,0,-1,0)),
new Anchor(CURSOR_SIZEALL,new Rectangle(1,1,0,0)),
new Anchor(CURSOR_SIZEWE,new Rectangle(0,0,1,0)),
new Anchor(CURSOR_SIZENESW,new Rectangle(1,0,-1,1)),
new Anchor(CURSOR_SIZENS,new Rectangle(0,0,0,1)),
new Anchor(CURSOR_SIZENWSE,new Rectangle(0,0,1,1))};;
/** * 矩形修改标记,为true时,处于鼠标拖动修改窗口位置和尺寸的状态, * 鼠标位于9个锚点区域({@link Anchor})之一,且鼠标键按下(mouseDown) */
private boolean onMouseModfied;
/** * 矩形修改状态下({@link #onMouseModfied}为true),记录上次鼠标位置 */
private Point lastPos;
/** * 真实的窗口位置和尺寸 */
protected Rectangle originalBounds;
private boolean originalBoundsNeedupdate=false;
/** * 父窗口x轴缩放比例 */
private float zoomY;
/** * 父窗口y轴缩放比例 */
private float zoomX;
/** * Create the composite. * @param parent * @param isFocus 是否为焦点对象 * @param originalBounds 对象原始位置尺寸 */
public ActiveRectangle(Composite parent, boolean isFocus, Rectangle originalBounds) {
// 透明背景样式
super(parent, SWT.TRANSPARENT);
this.originalBounds = originalBounds;
this.focus=isFocus;
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
// 绘制透明矩形窗口
e.gc.setLineStyle(SWT.LINE_SOLID);
e.gc.setLineWidth(lineWidth);
e.gc.setForeground(focus?focusRectColor:nofocusRectColor);
Point size = getSize();
e.gc.drawRectangle(new Rectangle(0,0,--size.x,--size.y));
if(focus){
Color bc = e.gc.getBackground();
e.gc.setBackground(focusRectColor);
e.gc.fillRectangle(anchors[AnchorType.LT.ordinal()].rect.x, anchors[AnchorType.LT.ordinal()].rect.y, anchorSize, anchorSize);
e.gc.fillRectangle(anchors[AnchorType.T.ordinal()].rect.x, anchors[AnchorType.T.ordinal()].rect.y, anchorSize, anchorSize);
e.gc.fillRectangle(anchors[AnchorType.RT.ordinal()].rect.x, anchors[AnchorType.RT.ordinal()].rect.y, anchorSize, anchorSize);
e.gc.fillRectangle(anchors[AnchorType.L.ordinal()].rect.x, anchors[AnchorType.L.ordinal()].rect.y, anchorSize, anchorSize);
// 画中心十字
e.gc.drawLine(anchors[AnchorType.C.ordinal()].rect.x+(anchorSize>>1), anchors[AnchorType.C.ordinal()].rect.y, anchors[AnchorType.C.ordinal()].rect.x+(anchorSize>>1), anchors[AnchorType.C.ordinal()].rect.y+anchorSize);
e.gc.drawLine(anchors[AnchorType.C.ordinal()].rect.x, anchors[AnchorType.C.ordinal()].rect.y+(anchorSize>>1), anchors[AnchorType.C.ordinal()].rect.x+anchorSize, anchors[AnchorType.C.ordinal()].rect.y+(anchorSize>>1));
e.gc.fillRectangle(anchors[AnchorType.R.ordinal()].rect.x, anchors[AnchorType.R.ordinal()].rect.y, anchorSize, anchorSize);
e.gc.fillRectangle(anchors[AnchorType.LB.ordinal()].rect.x, anchors[AnchorType.LB.ordinal()].rect.y, anchorSize, anchorSize);
e.gc.fillRectangle(anchors[AnchorType.B.ordinal()].rect.x, anchors[AnchorType.B.ordinal()].rect.y, anchorSize, anchorSize);
e.gc.fillRectangle(anchors[AnchorType.RB.ordinal()].rect.x, anchors[AnchorType.RB.ordinal()].rect.y, anchorSize, anchorSize);
// 恢复背景色
e.gc.setBackground(bc);
}
}
});
// 当获取/失去焦点时重绘窗口
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
focus=true;
// 将对象设置到顶层,否则无法响应所有的mouseMove事件
moveAbove(null);
((Control)(e.widget)).redraw();
}
@Override
public void focusLost(FocusEvent e) {
focus=false;
((Control)(e.widget)).redraw();
}
});
// 实现上下左右键移动窗口
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch(e.keyCode){
case SWT.ARROW_LEFT:
modify(-arrowStep,0,anchors[AnchorType.C.ordinal()].mask);
break;
case SWT.ARROW_RIGHT:
modify(arrowStep,0,anchors[AnchorType.C.ordinal()].mask);
break;
case SWT.ARROW_UP:
modify(0,-arrowStep,anchors[AnchorType.C.ordinal()].mask);
break;
case SWT.ARROW_DOWN:
modify(0,arrowStep,anchors[AnchorType.C.ordinal()].mask);
break;
default:
}
}
});
// 加入mouseMove事件处理,实现鼠标拖动锚点改变窗口位置和尺寸
addMouseMoveListener(new MouseMoveListener() {
private final Cursor defCursor=getCursor();
public void mouseMove(MouseEvent e) {
if(focus){
if(onMouseModfied){
// 计算鼠标移动的距离
Point pos=((Control)(e.widget)).toDisplay(e.x, e.y);
modify(pos.x-lastPos.x,pos.y-lastPos.y,anchors[anchorIndex].mask);
// 记录当前鼠标位置,以供下次mouseMove消息时计算鼠标移动距离
lastPos=pos;
}else if((anchorIndex=anchored(e.x, e.y))>=0&&anchors[anchorIndex].cusor!=getCursor()){
// 当鼠标位置某个锚点时,更新鼠标形状
setCursor(anchors[anchorIndex].cusor);
}else if(defCursor!=getCursor())
// 鼠标不在锚点时,恢复默认鼠标形状
setCursor(defCursor);
}else
anchorIndex=-1;
}
});
// 配合MouseMoveListener实现鼠标改变窗口位置和尺寸
addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
if(anchorIndex>=0){
// 记录当前鼠标位置,以供mouseMove消息时计算鼠标移动距离
lastPos=((Control)(e.widget)).toDisplay(e.x, e.y);
onMouseModfied=true;
}else if(!isFocusControl()){
// 当前对象非焦点对象时,设置当前对象为焦点对象
setFocus();
}
}
@Override
public void mouseUp(MouseEvent e) {
onMouseModfied=false;
}
});
}
@Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
/** * 改变窗口位置和尺寸 * @param x x轴移动距离 * @param y y轴移动距离 * @param mask 锚点位置计算的掩码 */
private void modify(int x,int y,Rectangle mask){
// 计算出新的窗口位置和尺寸
Rectangle bound = getBounds();
bound.x+=x*mask.x;
bound.y+=y*mask.y;
bound.width+=x*mask.width;
bound.height+=y*mask.height;
// 设置新的窗口位置
this.originalBoundsNeedupdate=true;
setBounds(bound);
this.originalBoundsNeedupdate=false;
}
@Override
public void dispose() {
// 释放光标资源
CURSOR_SIZENESW.dispose();
CURSOR_SIZENS.dispose();
CURSOR_SIZENWSE.dispose();
CURSOR_SIZEWE.dispose();
CURSOR_SIZEALL.dispose();
super.dispose();
}
/** * 根据窗口尺寸计算各个{@link Anchor}的位置 */
private void setAnchors(){
Point size = getSize();
setAnchorPos(anchors[AnchorType.LT.ordinal()].rect,0, 0);
setAnchorPos(anchors[AnchorType.T.ordinal()].rect,(size.x-anchorSize)>>1, 0);
setAnchorPos(anchors[AnchorType.RT.ordinal()].rect,size.x-anchorSize, 0);
setAnchorPos(anchors[AnchorType.L.ordinal()].rect,0, (size.y-anchorSize)>>1);
setAnchorPos(anchors[AnchorType.C.ordinal()].rect,(size.x-anchorSize)>>1, (size.y-anchorSize)>>1);
setAnchorPos(anchors[AnchorType.R.ordinal()].rect,size.x-anchorSize, (size.y-anchorSize)>>1);
setAnchorPos(anchors[AnchorType.LB.ordinal()].rect,0, size.y-anchorSize);
setAnchorPos(anchors[AnchorType.B.ordinal()].rect,(size.x-anchorSize)>>1, size.y-anchorSize);
setAnchorPos(anchors[AnchorType.RB.ordinal()].rect,size.x-anchorSize, size.y-anchorSize);
}
private void setAnchorPos(Rectangle anchorRect,int x,int y){
anchorRect.x=x;
anchorRect.y=y;
}
/** * 计算指定的坐标(x,y)是否位于某个{@link Anchor}区域 * @param x * @param y * @return 返回 {@link #anchors}的索引值,不在任何一个{@link Anchor}则返回-1 */
private int anchored(int x,int y){
for(int i=0;iif(anchors[i].rect.contains(x, y))return i;}
return -1;
}
private void setOriginalBounds(Rectangle rect){
// 如果originalBounds没有初始化,则用第一次调用setBounds的参数来初始化
if(null==originalBounds)
originalBounds=rect;
else if(originalBoundsNeedupdate)// 非zoom状态下更新将当前窗口的尺寸换算后更新originalBounds
originalBounds=unZoom(zoomX,zoomY);
}
@Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
// 重写setBounds方法,修改窗口位置和尺寸时同步重新计算所有锚点位置
setAnchors();
setOriginalBounds(new Rectangle(x, y, width, height));
}
@Override
public void setBounds(Rectangle rect) {
super.setBounds(rect);
// 重写setBounds方法,修改窗口位置和尺寸时同步重新计算所有锚点位置
setAnchors();
setOriginalBounds(rect);
}
/** * 根据zoom缩放比例缩放矩形显示 * @param zoomX x轴缩放比例 * @param zoomY y轴缩放比例 */
protected void zoom(float zoomX, float zoomY) {
if(null!=originalBounds){
this.zoomX=zoomX;
this.zoomY=zoomY;
setBounds(new Rectangle((int)(originalBounds.x*zoomX),(int)(originalBounds.y*zoomY),(int)(originalBounds.width*zoomX),(int)(originalBounds.height*zoomY)));
}
}
/** * x,y轴等比例缩放 * @param rect * @param zoom x,y轴缩放比例 * @see #zoom(float, float) */
void zoom(Rectangle rect, float zoom) {
zoom(zoom,zoom);
}
/** * 根据zoom缩放比例返回缩放前的矩形对象({@link Rectangle}) * @param zoomX x轴缩放比例 * @param zoomY y轴缩放比例 * @return */
protected Rectangle unZoom(float zoomX, float zoomY) {
Rectangle bounds = getBounds();
return new Rectangle((int)(bounds.x/zoomX),(int)(bounds.y/zoomY),(int)(bounds.width/zoomX),(int)(bounds.height/zoomY));
}
/** * x,y轴等比例缩放 * @param zoom x,y轴缩放比例 * @return * @see #unZoom(float, float) */
protected Rectangle unZoom(float zoom) {
return unZoom(zoom,zoom);
}
}
测试代码
TestActiveRectangle.java
package testwb;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import net.gdface.ui.ActiveRectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Control;
public class TestActiveRectangle extends Dialog {
protected Object result;
protected Shell shell;
/** * Create the dialog. * @param parent * @param style */
public TestActiveRectangle(Shell parent, int style) {
super(parent, style);
setText("SWT Dialog");
}
/** * Open the dialog. * @return the result */
public Object open() {
createContents();
shell.open();
shell.layout();
Display display = getParent().getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
return result;
}
/** * Create contents of the dialog. */
private void createContents() {
shell = new Shell(getParent(), getStyle());
shell.setSize(463, 366);
shell.setText(getText());
Group group = new Group(shell, SWT.NONE);
group.setBounds(10, 10, 423, 302);
Composite composite2 = new ActiveRectangle(group, false, null);
composite2.setBounds(81, 102, 51, 90);
Composite composite1 = new ActiveRectangle(group, true, null);
composite1.setBounds(24, 141, 147, 90);
group.setTabList(new Control[]{composite2, composite1});
shell.setTabList(new Control[]{group});
}
}