前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java SWT入门:自定义背景透明且可鼠标拖动改变尺寸和位置的Composite

java SWT入门:自定义背景透明且可鼠标拖动改变尺寸和位置的Composite

作者头像
10km
发布2019-05-25 22:31:38
2K0
发布2019-05-25 22:31:38
举报
文章被收录于专栏:10km的专栏10km的专栏

版权声明:本文为博主原创文章,转载请注明源地址。 /developer/article/1433715

下面的代码实现了一个透明可移动可改变尺寸的Composite窗体,如下图

鼠标点击窗口获取焦点,在获取焦点时会显示9个锚点用于改变窗口的位置和尺寸。

可以通过鼠标拖动锚点来改变窗口的位置或尺寸,也可以通过上下左右键来移动窗口

ActiveRectangle.java

代码语言:javascript
复制
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;i<anchors.length;++i){if(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

代码语言:javascript
复制
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});

    }
}
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年12月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com