aardio官方社区

 找回密码
 注册会员

!connect_header_login!

只需一步,快速开始

搜索
查看: 7473|回复: 3

重写了个窗口靠边吸附的效果

[复制链接]

8

主题

45

帖子

278

积分

二级会员

Rank: 3Rank: 3

积分
278
发表于 2017-12-13 15:39:24 | 显示全部楼层 |阅读模式
这个老早以前就有童鞋发过的,最近整理代码时翻了出来,尝试着把它类化,改来改去索性全部重写了。使用了双定时器,动作定时器是按需启动的,主要是为了实现窗口动作(弹出/缩回)的即时翻转。。。代码是在WIN7里完成的,其它系统没有测试。


class dock{
   
ctor(winform, acts=10/*每过程动作次数*/, isTop=false/*置顶*/, isDock=true/*吸附*/, margin=false/*留边*/){
        
assert(winform[["hwnd"]], "参数@1必须是窗体对象");
        acts = ..math.abs(
tonumber(acts) : 1);
        
        
//this = win.form(); this.messageOnly();
        this = winform;
        
this.site, this.msIn = "", 0;
        
        
var scrw, scrh = win.getScreen();
        
var alSign;
        
var wndAlpha = function(v = false){
            
if(margin) return;
            alSign = v ? 1 :
false;
            
this.transparent(alSign);
        }
        
        
var ap = {}; //aTimer动作定时器运行时参数
        var aTimer = win.timer(, 10);
        aTimer.onTimer =
function(hwnd,msg,id,tick){
            
var c = true;
            
if(ap[["site"]] == "top"){
                ap.y += ap.v;
                c = ap.msIn ? !(ap.y<0) : !(ap.y>4-ap.h);
               
if(c) ap.y = !ap.msIn ? 4-ap.h : 0;
            }
            
elseif(ap[["site"]] == "left"){
                ap.x += ap.v;
                c = ap.msIn ? !(ap.x<0) : !(ap.x>4-ap.w);
               
if(c) ap.x = !ap.msIn ? 4-ap.w : 0;
            }
            
elseif(ap[["site"]] == "right"){
                ap.x -= ap.v;
                c = ap.msIn ? !(ap.x>scrw-ap.w) : !(ap.x<scrw-4);
               
if(c) ap.x = ap.msIn ? scrw-ap.w : scrw-4;
            }
            
this.setPos(ap.x, ap.y);
            
if(c){
               
if(#ap[["site"]] && !ap.msIn){
                    wndAlpha(1);
                    
if(!isTop) win.setTopmost(this.hwnd);
                }
               
else if(!isTop) win.setTopmost(this.hwnd, false);
               
owner.disable();
            }
            
else if(alSign) wndAlpha();
        }
        
        
var msPos = ::POINT();
        
var mTimer = win.timer(, 10);
        mTimer.onTimer =
function(hwnd,msg,id,tick){
            
var x, y, w, h = this.getPos();
            
if(w >= scrw) return;
            
            
//**判断窗口停靠位置
            var site = ""
            
if(x <= 0) site = "left";
            
elseif(x+w >= scrw) site = "right";
            
elseif(y <= 0) site = "top";
            
            
if(this.site != site){
               
this.site = site
                ::PostMessage(
this.hwnd, 0x503, 0, 0)
            }
            
//**/
            
            
//**检测鼠标进出窗口 左键状态 判断是否动作
            _getMsPos(msPos)
            
var msIn = _msIn(msPos.x, msPos.y) ? 1 : 0
            
var v = _getAsyncKeyState(1)
            
if(this.msIn != msIn){
               
this.msIn = msIn
                ::PostMessage(
this.hwnd, 0x5A3, 0, 0)
            }
            
else if(!v) return;
            
if(!#site || v) return;
            
//**/
            
            
//**执行动作
            v = (site == "top") ? ..math.ceil(h/acts) : ..math.ceil(w/acts);
            v = msIn ? v : -v;
            ap.site,ap.msIn,ap.x,ap.y,ap.w,ap.h,ap.v = site,msIn,x,y,w,h,v;
            aTimer.enable();
            
//**/
        }
        
        
this.wndMargin = function(v = false){
            margin = !!v
        }
        
        
//**窗口呼出
        this.wndDisplay = function(){
            
var x, y, w, h = owner.getPos();
            
if(x<0) x = 0
            
elseif(x+w>scrw) x = scrw-w
            
if(y<0) y = 0
            
elseif(y+h>scrh) y = scrh-h
            
owner.setPos(x, y)
            
owner.transparent(false)
        }
        
this.wndDisplay()
        
//**/
        
        
//**窗口置顶 避免与aTimer里的win.setTopmost冲突
        this.wndTopmost = function(v){
            isTop = !!v
            win.setTopmost(
owner.hwnd, isTop)
            
return isTop;
        }
        
this.wndTopmost(isTop)
        
//**/
        
        
//**窗口吸附
        this.wndDock = function(v = true){
            isDock = !!v
            
if(isDock) mTimer.enable();
            
else { mTimer.disable(); owner.wndDisplay() }
            
return isDock;
        }
        
this.wndDock(isDock)
        
//**/
        
        
this.wndproc = {
            [0x7E
/*_WM_DISPLAYCHANGE 屏幕分辨率改变*/] = function(hwnd,message,wParam,lParam){
                scrw, scrh = win.getScreen();
               
owner.wndDisplay()
            }
        }
        ::PostMessage(
this.hwnd, 0x5A3, 0, 0);
    };
}
namespace dock{
   
import win.timer;
   
    _getMsPos = ::User32.GetCursorPos;
    _fromPoint = ::User32.WindowFromPoint;
    _getAsyncKeyState = ::User32.GetAsyncKeyState;
    _getWindowThreadProcessId = ::User32.GetWindowThreadProcessId;
    _msIn =
function(x, y){
        
var hwnd = _fromPoint(x, y);
        
return _getWindowThreadProcessId(hwnd, 0) == ..thread.getId();
    }
}


import win.ui;
/*DSG{{*/
var winform = win.form(text="自动吸附窗口测试";right=245;bottom=544)
winform.add(
chkDock={cls=
"checkbox";text="启用吸附";left=42;top=17;right=134;bottom=32;z=4};
chkMargin={cls=
"checkbox";text="是否留边";left=42;top=62;right=134;bottom=77;z=5};
chkTopMost={cls=
"checkbox";text="窗口置顶";left=42;top=40;right=134;bottom=55;z=3};
static={cls=
"static";left=35;top=98;right=139;bottom=125;bgcolor=12639424;center=1;font=LOGFONT(h=-16;weight=700);z=1};
static2={cls=
"static";left=35;top=162;right=139;bottom=189;bgcolor=12639424;center=1;font=LOGFONT(h=-16;weight=700);z=2}
)
/*}}*/

//winform.setPos(1777, -5555)

var wndObj = dock(winform, 30);

//吸附
wndObj.chkDock.checked = wndObj.wndDock()
wndObj.chkDock.oncommand =
function(id,event){
    wndObj.wndDock(wndObj.chkDock.checked)
}

//置顶 应使用内部方法避免冲突
winform.chkTopMost.oncommand = function(id,event){
    winform.wndTopmost(winform.chkTopMost.checked)
   
//win.setTopmost(winform.hwnd, winform.chkTopMost.checked)
}
//留边
winform.chkMargin.oncommand = function(id,event){
    winform.wndMargin(winform.chkMargin.checked)
}
winform.wndproc = {
    [0x201] =
function(hwnd,message,wParam,lParam){
        
owner.hitCaption();
    }
    [0x503] =
function(hwnd,message,wParam,lParam){
        
owner.static.text = owner.site;
    }
    [0x5A3] =
function(hwnd,message,wParam,lParam){
        
if(owner.msIn){
            
owner.static2.text = "鼠标在窗内";
        }
        
else {
            
owner.static2.text = "鼠标在窗外";
        }
    }
}

winform.show();
win.loopMessage();

回复

使用道具 举报

8

主题

45

帖子

278

积分

二级会员

Rank: 3Rank: 3

积分
278
 楼主| 发表于 2017-12-15 14:14:11 | 显示全部楼层
改进了下,用gdi实现留边效果,留边颜色在代码第22行可自行修改成自己喜欢的颜色。。。那个transparent很蛋疼,翻转时会在窗体内留有残影,强制redraw也不行,用hide或show倒是可以清掉残影,但任务栏又闪啊闪的,最后终于找到个叫RedrawWindow的API搞定。。。
class dock{
   
ctor(winform, acts=10/*每过程动作次数*/, isTop=false/*置顶*/, isDock=true/*吸附*/, margin=false/*留边*/){
        
assert(winform[["hwnd"]], "参数@1必须是窗体对象");
        acts = ..math.abs(
tonumber(acts) : 1);
        
        
//this = win.form(); this.messageOnly();
        this = winform;
        
this.site, this.msIn = "", 0;
        
var scrw, scrh = win.getScreen();
        
        
var wndGdi, alSign = _wndGdi();
        
var wndAlpha = function(v = false, w, h){
            alSign = !!v;
            
if(!alSign){
               
this.transparent(false);
               
//this.hide = 1; this.hide = 0;
                //this.redraw();
                _redrawWindow(this.hwnd, , , 0x400/*_RDW_FRAME*/ | 0x1/*_RDW_INVALIDATE*/ | 0x4/*_RDW_ERASE*/)
               
return;
            }
            
this.transparent(true);
            
var col = margin ? 0xaad2d2d2 : 0x01d2d2d2
            wndGdi(
this.hwnd, w, h, function(mdc, w, h){
               
var gph = gdip.graphics(mdc)
               
var gBru = gdip.solidBrush(col)
                gph.fillRectangle(gBru, 0, 0, w, h)
                gBru.delete()
                gph.delete()
                gdi.updateLayeredWindow(
this.hwnd, mdc, ::SIZE(w, h), gdi.blendFunction(255/*透明度*/))
            })
        }
        
        
var ap = {}; //aTimer动作定时器运行时参数
        var aTimer = win.timer(, 10);
        aTimer.onTimer =
function(hwnd,msg,id,tick){
            
var c = true;
            
if(ap[["site"]] == "top"){
                ap.y += ap.v;
                c = ap.msIn ? !(ap.y<0) : !(ap.y>4-ap.h);
               
if(c) ap.y = !ap.msIn ? 4-ap.h : 0;
            }
            
elseif(ap[["site"]] == "left"){
                ap.x += ap.v;
                c = ap.msIn ? !(ap.x<0) : !(ap.x>4-ap.w);
               
if(c) ap.x = !ap.msIn ? 4-ap.w : 0;
            }
            
elseif(ap[["site"]] == "right"){
                ap.x -= ap.v;
                c = ap.msIn ? !(ap.x>scrw-ap.w) : !(ap.x<scrw-4);
               
if(c) ap.x = ap.msIn ? scrw-ap.w : scrw-4;
            }
            
this.setPos(ap.x, ap.y);
            
if(c){
               
if(#ap[["site"]] && !ap.msIn){
                    wndAlpha(1, ap.w, ap.h);
                    
if(!isTop) win.setTopmost(this.hwnd);
                }
               
elseif(!isTop) win.setTopmost(this.hwnd, false);
               
owner.disable();
            }
            
elseif(alSign) wndAlpha(, ap.w, ap.h);
        }
        
        
var msPos = ::POINT();
        
var mTimer = win.timer(, 10);
        mTimer.onTimer =
function(hwnd,msg,id,tick){
            
var x, y, w, h = this.getPos();
            
if(w >= scrw) return;
            
            
//**判断窗口停靠位置
            var site = ""
            
if(x <= 0) site = "left";
            
elseif(x+w >= scrw) site = "right";
            
elseif(y <= 0) site = "top";
            
            
if(this.site != site){
               
this.site = site
                ::PostMessage(
this.hwnd, 0x503, 0, 0)
            }
            
//**/
            
            
//**检测鼠标进出窗口 左键状态 判断是否动作
            _getMsPos(msPos)
            
var msIn = _msIn(msPos.x, msPos.y) ? 1 : 0
            
var v = _getAsyncKeyState(1)
            
if(this.msIn != msIn){
               
this.msIn = msIn
                ::PostMessage(
this.hwnd, 0x5A3, 0, 0)
            }
            
elseif(!v) return;
            
if(!#site || v) return;
            
//**/
            
            
//**执行动作
            v = (site == "top") ? ..math.ceil(h/acts) : ..math.ceil(w/acts);
            v = msIn ? v : -v;
            ap.site,ap.msIn,ap.x,ap.y,ap.w,ap.h,ap.v = site,msIn,x,y,w,h,v;
            aTimer.enable();
            
//**/
        }
        
        
this.wndMargin = function(v = false){
            margin = !!v
        }
        
        
//**窗口呼出
        this.wndDisplay = function(){
            
var x, y, w, h = owner.getPos();
            
if(x<0) x = 0
            
elseif(x+w>scrw) x = scrw-w
            
if(y<0) y = 0
            
elseif(y+h>scrh) y = scrh-h
            
owner.setPos(x, y)
            
owner.transparent(false)
        }
        
this.wndDisplay()
        
//**/
        
        
//**窗口置顶 避免与aTimer里的win.setTopmost冲突
        this.wndTopmost = function(v){
            isTop = !!v
            win.setTopmost(
owner.hwnd, isTop)
            
return isTop;
        }
        
this.wndTopmost(isTop)
        
//**/
        
        
//**窗口吸附
        this.wndDock = function(v = true){
            isDock = !!v
            
if(isDock) mTimer.enable();
            
else { mTimer.disable(); owner.wndDisplay() }
            
return isDock;
        }
        
this.wndDock(isDock)
        
//**/
        
        
this.wndproc = {
            [0x7E
/*_WM_DISPLAYCHANGE 屏幕分辨率改变*/] = function(hwnd,message,wParam,lParam){
                scrw, scrh = win.getScreen();
               
owner.wndDisplay()
            }
        }
        ::PostMessage(
this.hwnd, 0x5A3, 0, 0);
    };
}
namespace dock{
   
import win.timer;
   
import gdip.graphics;
   
import gdip.solidBrush;
   
import gdi;
   
    _getMsPos = ::User32.GetCursorPos;
    _redrawWindow = ::User32.RedrawWindow;
    _fromPoint = ::User32.WindowFromPoint;
    _getAsyncKeyState = ::User32.GetAsyncKeyState;
    _getWindowThreadProcessId = ::User32.GetWindowThreadProcessId;
    _msIn =
function(x, y){
        
var hwnd = _fromPoint(x, y);
        
return _getWindowThreadProcessId(hwnd, 0) == ..thread.getId();
    }
    _wndGdi =
function(){
        
var hdc, mdc, hbmp;
        
var create = function(hwnd, w, h){
            hdc = ::GetWindowDC(hwnd);
            mdc = ::CreateCompatibleDC();
            hbmp = ::CreateCompatibleBitmap(hdc, w, h);
            ::SelectObject(mdc, hbmp);
        }
        
var delete = function(hwnd){
            ::ReleaseDC(hwnd, hdc);
            ::DeleteDC(mdc);
            ::DeleteObject(hbmp);
        }
        
return function(hwnd, w, h, draw, ...){
            create(hwnd, w, h);
            
if(type(draw) == "function") draw(mdc, w, h, ...);
            delete(hwnd);
        }
    }
}


import win.ui;
/*DSG{{*/
var winform = win.form(text="自动吸附窗口测试";right=245;bottom=544)
winform.add(
chkDock={cls=
"checkbox";text="启用吸附";left=42;top=17;right=134;bottom=32;z=4};
chkMargin={cls=
"checkbox";text="是否留边";left=42;top=62;right=134;bottom=77;z=5};
chkTopMost={cls=
"checkbox";text="窗口置顶";left=42;top=40;right=134;bottom=55;z=3};
static={cls=
"static";left=35;top=98;right=139;bottom=125;bgcolor=12639424;center=1;font=LOGFONT(h=-16;weight=700);z=1};
static2={cls=
"static";left=35;top=162;right=139;bottom=189;bgcolor=12639424;center=1;font=LOGFONT(h=-16;weight=700);z=2}
)
/*}}*/

//winform.setPos(1777, -5555)

var wndObj = dock(winform, 30);

//吸附
wndObj.chkDock.checked = wndObj.wndDock()
wndObj.chkDock.oncommand =
function(id,event){
    wndObj.wndDock(wndObj.chkDock.checked)
}

//置顶 应使用内部方法避免冲突
winform.chkTopMost.oncommand = function(id,event){
    winform.wndTopmost(winform.chkTopMost.checked)
   
//win.setTopmost(winform.hwnd, winform.chkTopMost.checked)
}
//留边
winform.chkMargin.oncommand = function(id,event){
    winform.wndMargin(winform.chkMargin.checked)
}
winform.wndproc = {
    [0x201] =
function(hwnd,message,wParam,lParam){
        
owner.hitCaption();
    }
    [0x503] =
function(hwnd,message,wParam,lParam){
        
owner.static.text = owner.site;
    }
    [0x5A3] =
function(hwnd,message,wParam,lParam){
        
if(owner.msIn){
            
owner.static2.text = "鼠标在窗内";
        }
        
else {
            
owner.static2.text = "鼠标在窗外";
        }
    }
}

winform.show();
win.loopMessage();

回复

使用道具 举报

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
发表于 2017-12-13 16:07:18 | 显示全部楼层
感谢分享,代码量不小,写得很漂亮。
我试了一下win10,xp上都可以使用,效果很好,但是XP上必须选择“留边”不然隐藏了就出不来了,win10选不选留边都一个样,可能是因为win10的边框本来就细。

刚才想试一下QQ的吸附,谁知道一拖出现了一个虚框然后就崩溃了,看来要把软件做得完美是真不容易,然后重启再试,发现QQ拖到屏幕边上,他会轻轻地回弹一下,这个会让用户体会到窗口是被吸上去了,就是有一个提示。

因为代码比较长,还没来得及细看,但是检测鼠标是不是移出窗体,也可以使用 win.ui.tracker,例如plus控件的事件都是用 win.ui.tracker实现的,例子:
import win.ui;
/*DSG{{*/
var winform = win.form(text="鼠标移出移入事件监听")
/*}}*/

var track = win.ui.tracker(winform)
track.onMouseLeave =
function(wParam,lParam){
    winform.text =
"鼠标离开了"
}

track.onMouseEnter =
function(wParam,lParam){
    winform.text =
"鼠标进来了"
}

winform.show()
win.loopMessage();

回复

使用道具 举报

8

主题

45

帖子

278

积分

二级会员

Rank: 3Rank: 3

积分
278
 楼主| 发表于 2017-12-13 16:45:53 | 显示全部楼层
Jacen.He 发表于 2017-12-13 16:07
感谢分享,代码量不小,写得很漂亮。
我试了一下win10,xp上都可以使用,效果很好,但是XP上必须选择“留边 ...

看来BUG挺多的,回头得整个虚拟机在逐个系统里改进了,鼠标进出窗体的例子收下了,多谢版主!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

手机版|未经许可严禁引用或转载本站文章|站长邮箱|aardio.com|aardio官方社区 ( 皖ICP备09012014号 )

GMT+8, 2021-6-19 07:21 , Processed in 0.051454 second(s), 24 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表