//shadow 阴影边框 import gdip.path; import gdip.pathGradientBrush; import gdip.graphics; import win.cur; import util.metaProperty; namespace win.ui; class shadow{ ctor( winform,shadowAlpha,shadowSize,roundRadius,innerRadius,shadowColor,interpolation,focusRatio) { if(winform._shadowWindow) return winform._shadowWindow; winform._shadowWindow = this; if( shadowSize === null ) shadowSize = 10; if( roundRadius === null ) roundRadius = shadowSize * 0.6; if( interpolation === null ) interpolation = 0.14; if( shadowColor === null) shadowColor = 0xFF000000; if( shadowAlpha === null ) shadowAlpha = 65; if( focusRatio === null ) focusRatio = 15; var dpiScaleX,dpiScaleY = ..gdi.getDpiScale(); shadowSize = shadowSize * dpiScaleX; roundRadius = roundRadius * dpiScaleX; if(innerRadius) innerRadius = innerRadius * dpiScaleX; var x,y,cx,cy = winform.getPos(); var frmShadow = ..win._form( cls="AARDIO_SHADOWEDGE";left=x-shadowSize;top=y-shadowSize;right=x+cx+shadowSize*2;bottom=y+cy+shadowSize*2; border = "none";mode="popup";exmode="toolwindow";max=false;min=false;sysmenu=false;title=false;parent=winform.hwnd; exstyle = 0x80000/*_WS_EX_LAYERED*/; ) this._form = frmShadow; this._parent = winform; this.show = function(flag){ frmShadow.show(flag?4/*_SW_SHOWNOACTIVATE*/: 0/*_SW_HIDE*/); } var shadowColor1 = shadowColor&0x01FFFFFF; var shadowColor2 = shadowColor&0x05FFFFFF; this.setRadius = function(r,i){ innerRadius = i * dpiScaleX; roundRadius = r * dpiScaleX; this._onDrawShadow(); } this.onDrawShadow = function(hdc,hMemDc,hMemBitmap,width,height){ var graphics = ..gdip.graphics(hMemDc); graphics.smoothingMode = 2/*_SmoothingModeHighQuality*/; var rcShadow = ::RECT(0,0,width,height) var path = ..gdip.path(); if( roundRadius ) path.addRoundRect(rcShadow,roundRadius); else path.addRectangle( rcShadow.left,rcShadow.top,rcShadow.width(),rcShadow.height() ); var shadowBrush = ..gdip.pathGradientBrush(path); shadowBrush.setInterpolationColors( {shadowColor1;shadowColor2,shadowColor;shadowColor} ,{0;interpolation/2,interpolation;1} ); shadowBrush.setFocusScales((width-focusRatio*shadowSize)/width, (height-focusRatio*shadowSize)/height); if(!_WINXP){ rcShadow.inflate( -shadowSize ,-shadowSize ); rcShadow.offset(0,-1); if( innerRadius ){ var pathClip = ..gdip.path(); pathClip.addRoundRect(rcShadow,innerRadius); graphics.setClipPath(pathClip, 4/*_GdipCombineModeExclude*/); pathClip.delete(); } else{ graphics.setClipRect( rcShadow.left, rcShadow.top,rcShadow.width(),rcShadow.height(), 4/*_GdipCombineModeExclude*/) } } graphics.fillPath(shadowBrush,path); shadowBrush.delete(); path.delete(); graphics.delete(); } var transparency = 1; this.setOpacity = function(v){ if(v!==null){ v = v/255; } else{ v = 1; } if(v == transparency ) return; if(!..win.isWindow(frmShadow.hwnd)) return; transparency = v; this._onDrawShadow(); } this.getOpacity = function(v){ if(transparency !== 1){ return ..math.roundToEven(transparency * 255); } else{ return null; } } this.transparent = function(v){ this.setOpacity(v*255); } this._onDrawShadow = function(){ ..gdi.layeredBuffer( frmShadow.hwnd, function( hdc,hMemDc,hMemBitmap,width,height ){ if( height < 45 ) return; this.onDrawShadow(hdc,hMemDc,hMemBitmap,width,height) ..gdi.updateLayeredWindow( frmShadow.hwnd,hMemDc,::SIZE(width,height) ,..gdi.blendFunction(shadowAlpha/*透明度*/*transparency) ); } ) } frmShadow._adjust = function( cx,cy ) { if(! ::User32.IsWindow(frmShadow.hwnd) ) return; var rc = winform.getRect(); rc.inflate( shadowSize, shadowSize ); rc.offset(0,1); ..win.setRect(frmShadow.hwnd,rc); var rcForm = winform.getRect(); var rcCtrl = rcForm.copy(); rcForm.inflate( shadowSize, shadowSize ); rcForm.offset(0,1); ..win.setRect(frmShadow.hwnd,rcForm); rcCtrl.offset(-rcForm.left,-rcForm.top); rcForm.offset(-rcForm.left,-rcForm.top); var hRgnForm = ::Gdi32.CreateRectRgn( rcForm.ltrb() ); var hRgnCtrl; if( innerRadius ){ hRgnCtrl = ::Gdi32.CreateRoundRectRgn( rcCtrl.left, rcCtrl.top, rcCtrl.right, rcCtrl.bottom, innerRadius, innerRadius ); } else{ hRgnCtrl = ::Gdi32.CreateRectRgn( rcCtrl.ltrb() ); } var hRgnNew = ::Gdi32.CreateRectRgn(0,0,1,1); ::Gdi32.CombineRgn(hRgnNew,hRgnForm,hRgnCtrl,0x4/*_RGN_DIFF*/); ::User32.SetWindowRgn(frmShadow.hwnd,hRgnNew,false); ::Gdi32.DeleteObject(hRgnForm); ::Gdi32.DeleteObject(hRgnCtrl); this._onDrawShadow(); }; frmShadow.disabled = !winform.hasResizeBorder && !..win.getStyle(winform.hwnd,0x10000/*_WS_MAXIMIZEBOX*/); this.setResizeBorder = function(v){ frmShadow.disabled = !v; }; frmShadow.wndproc = { [0x84/*_WM_NCHITTEST*/ ] = function(hwnd,message,wParam,lParam){ if( ..win.isZoomed(winform.hwnd) ) return; var x,y = ..win.getMessagePos(lParam); var htborder = ..win.hitBorder(..win.getRect(frmShadow.hwnd),x,y,12 * dpiScaleX); if(htborder) return htborder; } [0x20/*_WM_SETCURSOR*/] = function(hwnd,message,wParam,lParam){ var ht,message = ..win.getMessagePos(lParam); var c = __cursor[ht]; if( c ) { ..win.cur.setCur(c); return true; } } }; frmShadow.onMouseActivate = function(hwndTop,hitTest,message){ select(hitTest) { case 0xB/*_HTRIGHT*/ winform.hitRight(); case 0x11/*_HTBOTTOMRIGHT*/ winform.hitBottomright(); case 0xF/*_HTBOTTOM*/ winform.hitBottom(); case 0x10/*_HTBOTTOMLEFT*/ winform.hitBottomleft(); case 0xA/*_HTLEFT*/ winform.hitLeft(); case 0xC/*_HTTOP*/ winform.hitTop(); case 0xD/*_HTTOPLEFT*/ winform.hitTopleft(); case 0xE/*_HTTOPRIGHT*/ winform.hitTopright(); } return 0x4/*_MA_NOACTIVATEANDEAT*/ }; winform._onPosChangedWithShadow = function(flags,hwndInsertAfter,x,y,cx,cy){ if(!(flags & 2/*_SWP_NOMOVE*/)){ frmShadow.setPos( x - shadowSize,y - shadowSize + 1 ); } if(flags & 0x40/*_SWP_SHOWWINDOW*/){ frmShadow._adjust(); frmShadow.show(4/*_SW_SHOWNOACTIVATE*/); } elseif(flags & 0x80/*_SWP_HIDEWINDOW*/){ frmShadow.show(false); } } winform._adjustWithShadow = function( cx,cy,wParam ) { if( wParam == 0x1/*_SIZE_MINIMIZED*/ ) return; if( wParam != 0x2/*_SIZE_MAXIMIZED*/ ){ frmShadow._adjust(); if( ..win.isVisible(winform.hwnd) ){ frmShadow.show(0x4/*_SW_SHOWNOACTIVATE*/); } } else { frmShadow._adjust();//防止最大化可能出现的残影 frmShadow.show(false); } }; if( ..win.isVisible( winform.hwnd ) ){ frmShadow._adjust(); frmShadow.show(0x4/*_SW_SHOWNOACTIVATE*/); } }; @_metaProperty; } namespace shadow{ __cursor = { [0xC/*_HTTOP*/] = ..win.cur.load(0x7F85/*_IDC_SIZENS*/); [0xF/*_HTBOTTOM*/] = ..win.cur.load(0x7F85/*_IDC_SIZENS*/); [0xA/*_HTLEFT*/] = ..win.cur.load(0x7F84/*_IDC_SIZEWE*/); [0xB/*_HTRIGHT*/] = ..win.cur.load(0x7F84/*_IDC_SIZEWE*/); [0xD/*_HTTOPLEFT*/] = ..win.cur.load(0x7F82/*_IDC_SIZENWSE*/); [0x11/*_HTBOTTOMRIGHT*/] = ..win.cur.load(0x7F82/*_IDC_SIZENWSE*/); [0xE/*_HTTOPRIGHT*/] = ..win.cur.load(0x7F83/*_IDC_SIZENESW*/); [0x10/*_HTBOTTOMLEFT*/] = ..win.cur.load(0x7F83/*_IDC_SIZENESW*/); } _metaProperty = ..util.metaProperty( opacity = { _get = function(){ return owner.getOpacity(); } _set = function( v ){ owner.setOpacity(v); } }; ) } /**intellisense() win.ui.shadow = 阴影边框 win.ui.shadow( = 创建阴影边框\n创建阴影后,窗体对象的_shadowWindow属性被设置为此对象 win.ui.shadow(.(窗体对象,不透明度,阴影大小,外圆角,内圆角,阻影颜色,暗部插值位置,聚焦系数) = 除参数 @1 以外,其他所有参数可选\n不透明度默认为 65,255 为完全不透明。\n阴影大小默认为 10 像素,\n默认根据阴影大小自动计算外圆角大小。\n内圆角默认为空\n阴影颜色默认为0xFF000000,FF为透明度\n暗部插值位置默认为0.15,这个值是阴影内侧暗部插值点距外边界的百分比\n聚焦系数默认为15,以该系数乘以阴影大小换算为距离外边界的百分比,\n加大聚焦系数会淡化阴影 win.ui.shadow() = !win_ui_shadow. !win_ui_shadow._form = !winform. !win_ui_shadow.show(true) = 暂时显示或隐藏阴影\n阴影会跟随所有者窗体自动显示隐藏,此函数仅用于特殊情况 !win_ui_shadow.onDrawShadow(hdc,hMemDc,hMemBitmap,width,height) = @.onDrawShadow = function(hdc,hMemDc,hMemBitmap,width,height){\n var graphics = ..gdip.graphics(hMemDc);\n graphics.smoothingMode = 4/*_GdipSmoothingModeAntiAlias*/;\n __\n graphics.delete();\n} !win_ui_shadow.setResizeBorder(true) = 允许通过拖动阴影边框改变父窗口大小\n如果窗口当前已经应用win.ui.resizeBorder添加了拖动边框,\n则默认启用此属性,不再需要调用此函数 !win_ui_shadow.opacity = 设置在阴影初始透明度上叠加的不透明度。\n可指定的值范围自 0 到 255,设为 1 禁用叠加不透明度。\n修改普通窗口透明度时会自动修改阴影窗口(如果存在)的叠加透明度。 !win_ui_shadow.setRadius(.(外圆角,内圆角) = 设置圆角大小。\n如果窗口使用了 win.region.round 创建圆角窗口则会自动调用此函数。 end intellisense**/ /**details(关于无边框窗口) 无边框窗口:指的是在窗体设计器中将窗体的边框(border) 属性设为 "none", 这种窗口不再有边框、标题栏。 注意隐藏标题栏、并将边框设为 "resizable"等不是无边框窗口, 这种效果在不同操作系统上显示效果都很差,要么是多出一圈,要么顶上多出一块。 不要这么做。 无边框窗口只要使用下面的代码就可以在窗口周围添加阴影, 并同时使用阴影实现完美的可拖动改变大小效果(即 resizable 边框 )。 import win.ui.shadow; win.ui.shadow(winform); 尤其是嵌入浏览器组件,直接使用 win.ui.resizeBorder 添加可拖动边框可能不起作用。 win.ui.shadow 可以通过阴影实现 win.ui.resizeBorder 的功能。 如果只想添加阴影,不想支持可拖动边框,可以下面这样写: import win.ui.shadow; win.ui.shadow(winform).setResizeBorder(false) 在无边框窗口上,可以自行实现标题栏按钮,调用以下函数模拟标题栏按钮消息 winform.hitMax() //模拟点击最大化按钮 winform.hitMin() //模拟点击最小化按钮 winform.hitClose() //模拟点击关闭按钮 winform.onMouseDown = function(wParam,lParam){ winform.hitCaption() } 标准库提供了以下的类可以直接在无边框窗口上创建虚拟的标题栏以及可拖动边框: win.ui.simpleWindow 实现最普通的标题栏。 win.ui.simpleWindow2 实现的标题栏没有最大化按钮。 win.ui.simpleWindow3 标题栏使用分层窗口实现,并使用 orphanWindow 悬浮在父窗口上面。 end details**/