底部有v2版BTT
这是用 GDI+ 完全重写的 ToolTip 。
BeautifulToolTip ,以下简称 BTT 。 作者:空
为什么要重写一个 ToolTip 呢,因为下面4个问题真的很烦人。
- 文本难以对齐
- 闪烁
- 坐标乱飘
- 难以在显示前预知大小
BTT 被设计为可以完全取代 ToolTip ,所以它完全兼容 ToolTip 的语法及设置。
- 显示一个内容
btt("This is BeautifulToolTip")
- 清空一个内容
btt()
- 指定 x,y 及 WhichToolTip
btt("This is BeautifulToolTip`n`nWhichToolTip = 2",500,500,2) Sleep, 5000 btt(,,,2)
- CoordMode 命令也对 BTT 生效
CoordMode, ToolTip, Screen btt("This is BeautifulToolTip") Sleep, 5000 btt()
除了上面基本用法, BTT 还拥有多个内置样式,使用依然简单。
- 使用内置样式“Style1”
btt("This is BeautifulToolTip",,,, "Style1")
更多内置样式的效果
自定义样式也非常简单,只需更改模板参数!
#SingleInstance Force SetBatchLines, -1 Text= ( 使用模板可以轻松创建自己的风格。 欢迎分享,带张截图!!! Use template to easily create your own style. Please share your custom style and include a screenshot. It will help a lot of people. ) ; 照着模板改参数就可以创建自己的风格了。建好后可以添加到 btt() 函数里,就可以变内置风格了。 Template := {Border:20 ; If omitted, 1 will be used. Range 0-20. , Rounded:30 ; If omitted, 3 will be used. Range 0-30. , Margin:30 ; If omitted, 5 will be used. Range 0-30. , TabStops:[50, 80, 100] ; If omitted, [50] will be used. This value must be an array. , BorderColor:0xffaabbcc ; ARGB , BorderColorLinearGradientStart:0xff16a085 ; ARGB , BorderColorLinearGradientEnd:0xfff4d03f ; ARGB , BorderColorLinearGradientAngle:45 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , BorderColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , TextColor:0xff112233 ; ARGB , TextColorLinearGradientStart:0xff00416a ; ARGB , TextColorLinearGradientEnd:0xffe4e5e6 ; ARGB , TextColorLinearGradientAngle:90 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , TextColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , BackgroundColor:0xff778899 ; ARGB , BackgroundColorLinearGradientStart:0xff8DA5D3 ; ARGB , BackgroundColorLinearGradientEnd:0xffF4CFC9 ; ARGB , BackgroundColorLinearGradientAngle:135 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , BackgroundColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , Font:"Font Name" ; If omitted, ToolTip's Font will be used. Can specify the font file. , FontSize:20 ; If omitted, 12 will be used. , FontRender:5 ; If omitted, 5 will be used. Range 0-5. , FontStyle:"Regular Bold Italic BoldItalic Underline Strikeout"} ; Same as Style7 OwnStyle1 := {Border:20 , Rounded:30 , Margin:30 , BorderColor:0xffaabbcc , TextColor:0xff112233 , BackgroundColorLinearGradientStart:0xffF4CFC9 , BackgroundColorLinearGradientEnd:0xff8DA5D3 , BackgroundColorLinearGradientAngle:0 , BackgroundColorLinearGradientMode:8 , FontStyle:"BoldItalic Underline"} btt(Text,,,,OwnStyle1) Sleep, 10000 ExitApp
通过更改 BTT 的 Options 参数,可以很容易的实现一些特效。
- 淡入
for k, v in [15,35,55,75,95,115,135,155,175,195,215,235,255] { btt(Text,,,,"Style7",{Transparent:v}) Sleep, 30 }
- 淡出
for k, v in [240,220,200,180,160,140,120,100,80,60,40,20,0] { btt(Text,,,,"Style7",{Transparent:v}) Sleep, 30 }
- 改变“渐变角度”参数实现动画效果
- 窗口跟随
btt(Text,800-1,600-1,,"Style1",{TargetHWND:target})
- 计算最终显示大小
ret := btt("test",,,,,{JustCalculateSize:1}) MsgBox, % "宽:" ret.w " 高:" ret.h
除了以上这些 BTT 还实现了
- 多显示器支持
- 多DPI支持
但这里就不再演示了,自己看打包的示例或者源码中的说明。
最后,在即没有原版那些烦人问题,又拥有诸多新特性的同时。
BTT 的性能 —— 比原版快 2-1000 倍!!!!!!
- 原版 CPU 占用率
- BTT CPU 占用率
- 耗时对比
强烈建议你把 BTT库 及 引用库 放 AHK 公用 Lib 目录里。
也就是说,把 BTT.ahk 、 Gdip_All.ahk 、 NonNull.ahk 放下面目录里(没有就手动新建)
C:\我的电脑\Documents\AutoHotkey\Lib
这样你在每次使用的时候,就不需要再 #Include BTT.ahk 了。
好了,快抛弃 ToolTip 拥抱 BTT 吧!!!秀出你自定义的style!!!
下载地址:
https://github.com/telppa/BeautifulToolTip/releases
更新日志:
- 2021.10.03
改变 Include 方式,降低库冲突的可能性。
- 2021.09.29
支持设置 TabStops 。
除 GDIP 库外所有函数内置到 Class 中,降低库冲突的可能性。
- 2021.04.30
修复 Win7 下不能运行的 bug 。(2021.04.20 引起)
- 2021.04.20
支持根据显示器 DPI 缩放比例自动缩放,与 ToolTip 特性保持一致。
支持直接使用未安装的本地字体。
Options 增加 JustCalculateSize 参数,可在不绘制内容的前提下直接返回尺寸。
- 2021.03.07
增加 4 种渐变模式。 - 2021.03.05
删除渐变方向参数。
增加渐变角度参数,支持 360° 渐变。
增加渐变模式参数,支持 4 种模式。
- 2021.03.03
文本色支持渐变。
增加 Style8 。
增加3个渐变方向。 - 2021.03.02
细边框色支持渐变。
增加 Style7 。
增加2个渐变方向。 - 2021.03.01
BTT 的总在最上级别现在跟 ToolTip 一样高了。
解决 BTT 被不明原因置底导致误以为没显示的问题。
增加 Style6 。
文字显示更加居中。 - 2021.02.22
增加返回值与 Transparent 参数。
BTT v2转写【2022.06.18】.ahk
#Requires AutoHotkey v2.0 btttxt:=" ( 使用模板可以轻松创建自己的风格。 欢迎分享,带张截图!!! Use template to easily create your own style. Please share your custom style and include a screenshot. It will help a lot of people. )" ;样式1:用于正则工具完成情况提示 OwnzztooltipStyle1 := {Border:1 , Rounded:2 , Margin:8 , BorderColorLinearGradientStart:0xff3881a7 ;0xffb7407c , BorderColorLinearGradientEnd:0xff3881a7 , BorderColorLinearGradientAngle:45 , BorderColorLinearGradientMode:6 , FontSize:16 , TextColor:0xFFFFFFFF ;0xffd9d9db , BackgroundColor:0xFF000000 ;0xff26293a , FontStyle:"Regular"} ;样式2:用于正则工具控件功能提示 OwnzztooltipStyle2 := {Border:1 , Rounded:8 , TextColor:0xfff4f4f4 , BackgroundColor:0xaa3e3d45 , FontSize:14} Style99 := {Border:20 ; If omitted, 1 will be used. Range 0-20. , Rounded:30 ; If omitted, 3 will be used. Range 0-30. , Margin:30 ; If omitted, 5 will be used. Range 0-30. , TabStops:[50, 80, 100] ; If omitted, [50] will be used. This value must be an array. , BorderColor:0xffaabbcc ; ARGB , BorderColorLinearGradientStart:0xff16a085 ; ARGB , BorderColorLinearGradientEnd:0xfff4d03f ; ARGB , BorderColorLinearGradientAngle:45 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , BorderColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , TextColor:0xff112233 ; ARGB , TextColorLinearGradientStart:0xff00416a ; ARGB , TextColorLinearGradientEnd:0xffe4e5e6 ; ARGB , TextColorLinearGradientAngle:90 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , TextColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , BackgroundColor:0xff778899 ; ARGB , BackgroundColorLinearGradientStart:0xff8DA5D3 ; ARGB , BackgroundColorLinearGradientEnd:0xffF4CFC9 ; ARGB , BackgroundColorLinearGradientAngle:135 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , BackgroundColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , Font:"Font Name" ; If omitted, ToolTip's Font will be used. Can specify the font file path. , FontSize:20 ; If omitted, 12 will be used. , FontRender:5 ; If omitted, 5 will be used. Range 0-5. , FontStyle:"Regular Bold Italic BoldItalic Underline Strikeout"} btt(btttxt,,,,OwnzztooltipStyle1,{Transparent:180,DistanceBetweenMouseXAndToolTip:-100,DistanceBetweenMouseYAndToolTip:-20}) SetTimer OwnzztooltipEnd ,-3000 OwnzztooltipEnd() { btt() ExitApp } ;================================ BTT v2库 ;================================ ;BTT转v2 by城西 2022.6.18 https://github.com/liuyi91 :初步测试支持thqby大佬的 v2 beta4 ;感谢大佬thqby的悉心指点 https://github.com/thqby/ ;BTT作者v1:https://github.com/telppa/BeautifulToolTip btt(Text:="", X:="", Y:="", WhichToolTip:="", BulitInStyleOrStyles:="", BulitInOptionOrOptions:="") { static BTT , Style99 := {Border:20 ; If omitted, 1 will be used. Range 0-20. , Rounded:30 ; If omitted, 3 will be used. Range 0-30. , Margin:30 ; If omitted, 5 will be used. Range 0-30. , TabStops:[50, 80, 100] ; If omitted, [50] will be used. This value must be an array. , BorderColor:0xffaabbcc ; ARGB , BorderColorLinearGradientStart:0xff16a085 ; ARGB , BorderColorLinearGradientEnd:0xfff4d03f ; ARGB , BorderColorLinearGradientAngle:45 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , BorderColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , TextColor:0xff112233 ; ARGB , TextColorLinearGradientStart:0xff00416a ; ARGB , TextColorLinearGradientEnd:0xffe4e5e6 ; ARGB , TextColorLinearGradientAngle:90 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , TextColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , BackgroundColor:0xff778899 ; ARGB , BackgroundColorLinearGradientStart:0xff8DA5D3 ; ARGB , BackgroundColorLinearGradientEnd:0xffF4CFC9 ; ARGB , BackgroundColorLinearGradientAngle:135 ; Mode=8 Angle 0(L to R) 90(U to D) 180(R to L) 270(D to U) , BackgroundColorLinearGradientMode:1 ; Mode=4 Angle 0(L to R) 90(D to U), Range 1-8. , Font:"Font Name" ; If omitted, ToolTip's Font will be used. Can specify the font file path. , FontSize:20 ; If omitted, 12 will be used. , FontRender:5 ; If omitted, 5 will be used. Range 0-5. , FontStyle:"Regular Bold Italic BoldItalic Underline Strikeout"} , Option99 := {TargetHWND:"" ; If omitted, active window will be used. , CoordMode:"Screen Relative Window Client" ; If omitted, A_CoordModeToolTip will be used. , Transparent:"" ; If omitted, 255 will be used. , MouseNeverCoverToolTip:"" ; If omitted, 1 will be used. , DistanceBetweenMouseXAndToolTip:"" ; If omitted, 16 will be used. This value can be negative. , DistanceBetweenMouseYAndToolTip:"" ; If omitted, 16 will be used. This value can be negative. , JustCalculateSize:""} ; Set to 1, no content will be displayed, just calculate size and return. , Style1 := {TextColor:0xffeef8f6 , BackgroundColor:0xff1b8dff , FontSize:14} , Style2 := {Border:1 , Rounded:8 , TextColor:0xfff4f4f4 , BackgroundColor:0xaa3e3d45 , FontSize:14} , Style3 := {Border:2 , Rounded:0 , TextColor:0xffF15839 , BackgroundColor:0xffFCEDE6 , FontSize:14} , Style4 := {Border:10 , Rounded:20 , BorderColor:0xff604a78 , TextColor:0xffF3AE00 , BackgroundColor:0xff6A537F , FontSize:20 , FontStyle:"Bold Italic"} , Style5 := {Border:0 , Rounded:5 , TextColor:0xffeeeeee , BackgroundColorLinearGradientStart:0xff134E5E , BackgroundColorLinearGradientEnd:0xff326f69 , BackgroundColorLinearGradientAngle:0 , BackgroundColorLinearGradientMode:1} , Style6 := {Border:2 , Rounded:5 , TextColor:0xffCAE682 , BackgroundColor:0xff434343 , FontSize:14} , Style7 := {Border:20 , Rounded:30 , Margin:30 , BorderColor:0xffaabbcc , TextColor:0xff112233 , BackgroundColorLinearGradientStart:0xffF4CFC9 , BackgroundColorLinearGradientEnd:0xff8DA5D3 , BackgroundColorLinearGradientAngle:0 , BackgroundColorLinearGradientMode:1 , FontStyle:"BoldItalic"} , Style8 := {Border:3 , Rounded:30 , Margin:30 , BorderColorLinearGradientStart:0xffb7407c , BorderColorLinearGradientEnd:0xff3881a7 , BorderColorLinearGradientAngle:45 , BorderColorLinearGradientMode:1 , TextColor:0xffd9d9db , BackgroundColor:0xff26293a} ; 直接在 static 中初始化 BTT 会报错,所以只能这样写 if !isset(BTT) BTT := BeautifulToolTip() return BTT.ToolTip(Text, X, Y, WhichToolTip,BulitInStyleOrStyles,BulitInOptionOrOptions) ; 如果 Style 是一个内置预设的名称,则使用对应内置预设的值,否则使用 Styles 本身的值。 Options 同理。 ;, BulitInStyleOrStyles="" ? BulitInStyleOrStyles : %BulitInStyleOrStyles% ;, %BulitInOptionOrOptions%="" ? BulitInOptionOrOptions : %BulitInOptionOrOptions%) } Class BeautifulToolTip extends Map { static DebugMode:=0 CaseSense := 'off' __New() { ;=if (!this.pToken) if (!this.HasOwnProp("pToken") or !this.pToken) { this.pToken := Gdip_Startup() if (!this.pToken) { MsgBox("Gdiplus failed to start. Please ensure you have gdiplus on your system", "gdiplus error!", 48) ExitApp } ; 多显示器支持 this.Monitors := MDMF_Enum() ; 获取多显示器各自 DPI 缩放比例 for hMonitor, v in this.Monitors.Clone() { if (hMonitor="TotalCount" or hMonitor="Primary") continue ; https://github.com/Ixiko/AHK-libs-and-classes-collection/blob/e421acb801867edb659a54b7473e6e617f2b267b/libs/g-n/Monitor.ahk ; ahk 源码里 A_ScreenDPI 就是只获取了 dpiX ,所以这里保持一致 osv := StrSplit(A_OSVersion, ".") ; https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_osversioninfoexw if (osv[1] < 6 || (osv[1] == 6 && osv[2] < 3)) ; WIN_8- 。Win7 必须用这种方式,否则会出错 { ;=hDC := DllCall("Gdi32.dll\CreateDC", "Str", hMonitor.name, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr") hDC := DllCall("Gdi32.dll\CreateDC", "Str", v.name, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr") dpiX := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "Int", 88) ; LOGPIXELSX = 88 DllCall("Gdi32.dll\DeleteDC", "Ptr", hDC) } else DllCall("Shcore.dll\GetDpiForMonitor", "Ptr", hMonitor, "Int", 0, "UIntP", &dpiX:=0, "UIntP", &dpiY:=0, "UInt") ;原文这个type没设置数字 this.Monitors[hMonitor].DPIScale := this.NonNull_Ret(dpiX, A_ScreenDPI)/96 } ; 获取整个桌面的分辨率,即使跨显示器 VirtualWidth := SysGet(78) VirtualHeight := SysGet(79) this.DIBWidth := VirtualWidth this.DIBHeight := VirtualHeight ; 获取 ToolTip 的默认字体 this.ToolTipFontName := this.Fnt_GetTooltipFontName() ; create 20 guis for gdi+ ; 最多20个 ToolTip ,与原版对应。 _BTTGUI:=map() loop 20 { ; _BTT1(GUI 名称) 与 _hBTT1(GUI 句柄) 都是临时变量,后者被储存了。 _BTTGUI[A_Index]:=GUI("+E0x80000 -Caption +ToolWindow +LastFound +AlwaysOnTop") jjtem :=_BTTGUI[A_Index].Hwnd ;设置鼠标穿透属性,配合透明度,实现鼠标穿透 让tooltip永远在最上端 +城西 2021.11.25 _BTTGUI[A_Index].Show("NA") WinSetExStyle("+32", "ahk_id " jjtem) ;设置鼠标穿透属性,配合透明度,实现鼠标穿透 让tooltip永远在最上端 +城西 2021.11.25 this["hBTT" A_Index] := _BTTGUI[A_Index].Hwnd , this["hbm" A_Index] := CreateDIBSection(this.DIBWidth, this.DIBHeight) , this["hdc" A_Index] := CreateCompatibleDC() , this["obm" A_Index] := SelectObject(this["hdc" A_Index], this["hbm" A_Index]) , this["G" A_Index] := Gdip_GraphicsFromHDC(this["hdc" A_Index]) , Gdip_SetSmoothingMode(this["G" A_Index], 4) , Gdip_SetPixelOffsetMode(this["G" A_Index], 2) ; 此参数是画出完美圆角矩形的关键 } } else return } ; new 后得到的变量在销毁后会自动跳这里来运行,因此很适合做自动资源回收。 __Delete() { loop 20 { /* ;不知道为什么会出现不能读写能存,改用下面的方式 Gdip_DeleteGraphics(this["G" A_Index]) , SelectObject(this["hdc" A_Index], this["obm" A_Index]) , DeleteObject(this["hbm" A_Index]) , DeleteDC(this["hdc" A_Index]) */ this["G" A_Index]:="" , this["hdc" A_Index]:="" , this["obm" A_Index]:="" , this["hbm" A_Index]:="" , this["hdc" A_Index]:="" } ;Gdip_Shutdown(this.pToken) this.pToken:="" } ; 参数默认全部为空只是为了让清空 ToolTip 的语法简洁而已。 ToolTip(Text:="", X:="", Y:="", WhichToolTip:="", Styles:="", Options:="") { ; 给出 WhichToolTip 的默认值1,并限制 WhichToolTip 的范围为 1-20 this.NonNull(&WhichToolTip, 1, 1, 20) ; 检查并解析 Styles 与 Options 。无论不传参、部分传参、完全传参,此函数均能正确返回所需参数。 if (text !="") O:=this._CheckStylesAndOptions(Styles, Options) Else { O:={} O.Checksum:="" } ; 判断显示内容是否发生变化。由于前面给了 Options 一个默认值,所以首次进来时下面的 O.Options!=SavedOptions 是必然成立的。 FirstCallOrNeedToUpdate:=(Text != (this.HasOwnProp("SavedText" WhichToolTip) ? this["SavedText" WhichToolTip] : "") or O.Checksum != (this.HasOwnProp("SavedText" WhichToolTip) ? this["SavedText" WhichToolTip] : "")) if (Text="") { ; 清空 ToolTip Gdip_GraphicsClear(this["G" WhichToolTip]) UpdateLayeredWindow(this["hBTT" WhichToolTip], this["hdc" WhichToolTip]) ; 清空变量 this["SavedText" WhichToolTip] := "" , this["SavedOptions" WhichToolTip] := "" , this["SavedX" WhichToolTip] := "" , this["SavedY" WhichToolTip] := "" , this["SavedW" WhichToolTip] := "" , this["SavedH" WhichToolTip] := "" , this["SavedTargetHWND" WhichToolTip] := "" , this["SavedCoordMode" WhichToolTip] := "" , this["SavedTransparent" WhichToolTip] := "" return } else if (FirstCallOrNeedToUpdate) ; First Call or NeedToUpdate { ; 加速 ; 获取目标尺寸,用于计算文本大小时加上宽高限制,否则目标为屏幕时,可能计算出超宽超高的大小,导致无法显示。 TargetSize:=this._CalculateDisplayPosition(&X, &Y, "", "", O, GetTargetSize:=1) ; 使得 文字区域+边距+细边框 不会超过目标宽度。 , MaxTextWidth:=TargetSize.W - O.Margin*2 - O.Border*2 ; 使得 文字区域+边距+细边框 不会超过目标高度的90%。 ; 之所以高度限制90%是因为两个原因,1是留出一些上下的空白,避免占满全屏,鼠标点不了其它地方,难以退出。 ; 2是在计算文字区域时,即使已经给出了宽高度限制,且因为自动换行的原因,宽度的返回值通常在范围内,但高度的返回值偶尔还是会超过1行,所以提前留个余量。 , MaxTextHeight:=(TargetSize.H*90)//100 - O.Margin*2 - O.Border*2 ; 为 _TextToGraphics 计算区域提供高宽限制。 , O.Width:=MaxTextWidth, O.Height:=MaxTextHeight ; 计算文字显示区域 TextArea = x|y|width|height|chars|lines , TextArea:=StrSplit(this._TextToGraphics(this["G" WhichToolTip], Text, O, Measure:=1), "|") ; 这里务必向上取整。 ; 向上的原因是,例如 1.2 如果四舍五入为 1,那么最右边的字符可能会显示不全。 ; 取整的原因是,不取整无法画出完美的圆角矩形。 ; 当使用 AutoTrim 选项,即自动将超出范围的文字显示为 “...” 时,此时返回的宽高度值是不包含 “...” 的。 ; 所以加上 “...” 的宽高度后,仍然可能超限。故需要再次检查并限制。 ; 一旦宽高度超过了限制(CreateDIBSection() 时创建的大小),会导致 UpdateLayeredWindow() 画不出图像来。 , TextWidth:=Min(Ceil(TextArea[3]), MaxTextWidth) , TextHeight:=Min(Ceil(TextArea[4]), MaxTextHeight) , RectWidth:=TextWidth+O.Margin*2 ; 文本+边距。 , RectHeight:=TextHeight+O.Margin*2 , RectWithBorderWidth:=RectWidth+O.Border*2 ; 文本+边距+细边框。 , RectWithBorderHeight:=RectHeight+O.Border*2 ; 圆角超过矩形宽或高的一半时,会画出畸形的圆,所以这里验证并限制一下。 , R:=(O.Rounded>Min(RectWidth, RectHeight)//2) ? Min(RectWidth, RectHeight)//2 : O.Rounded if (O.JustCalculateSize!=1) { ; 画之前务必清空画布,否则会出现异常。 Gdip_GraphicsClear(this["G" WhichToolTip]) ; 准备细边框画刷 if (O.BCLGA!="" and O.BCLGM and O.BCLGS and O.BCLGE) ; 渐变画刷 画细边框 pBrushBorder := this._CreateLinearGrBrush(O.BCLGA, O.BCLGM, O.BCLGS, O.BCLGE , 0, 0, RectWithBorderWidth, RectWithBorderHeight) else pBrushBorder := Gdip_BrushCreateSolid(O.BorderColor) ; 纯色画刷 画细边框 if (O.Border>0) switch R { ; 圆角为0则使用矩形画。不单独处理,会画出显示异常的图案。 case "0": Gdip_FillRectangle(this["G" WhichToolTip] ; 矩形细边框 , pBrushBorder, 0, 0, RectWithBorderWidth, RectWithBorderHeight) Default: Gdip_FillRoundedRectanglePath(this["G" WhichToolTip] ; 圆角细边框 , pBrushBorder, 0, 0, RectWithBorderWidth, RectWithBorderHeight, R) } ; 准备文本框画刷 if (O.BGCLGA!="" and O.BGCLGM and O.BGCLGS and O.BGCLGE) ; 渐变画刷 画文本框 pBrushBackground := this._CreateLinearGrBrush(O.BGCLGA, O.BGCLGM, O.BGCLGS, O.BGCLGE , O.Border, O.Border, RectWidth, RectHeight) else pBrushBackground := Gdip_BrushCreateSolid(O.BackgroundColor) ; 纯色画刷 画文本框 switch R { case "0": Gdip_FillRectangle(this["G" WhichToolTip] ; 矩形文本框 , pBrushBackground, O.Border, O.Border, RectWidth, RectHeight) Default: Gdip_FillRoundedRectanglePath(this["G" WhichToolTip] ; 圆角文本框 , pBrushBackground, O.Border, O.Border, RectWidth, RectHeight , (R>O.Border) ? R-O.Border : R) ; 确保内外圆弧看起来同心 } ; 清理画刷 Gdip_DeleteBrush(pBrushBorder) Gdip_DeleteBrush(pBrushBackground) ; 计算居中显示坐标。由于 TextArea 返回的文字范围右边有很多空白,所以这里的居中坐标并不精确。 O.X:=O.Border+O.Margin, O.Y:=O.Border+O.Margin, O.Width:=TextWidth, O.Height:=TextHeight ; 如果显示区域过小,文字无法完全显示,则将待显示文本最后4个字符替换为4个英文省略号,表示显示不完全。 ; 虽然有 GdipSetStringFormatTrimming 函数可以设置末尾显示省略号,但它偶尔需要增加额外的宽度才能显示出来。 ; 由于难以判断是否需要增加额外宽度,以及需要增加多少等等问题,所以直接用这种方式自己实现省略号的显示。 ; 之所以选择替换最后4个字符,是因为一般替换掉最后2个字符,才能确保至少一个省略号显示出来。 ; 为了应对意外情况,以及让省略号更加明显一点,所以选择替换最后4个。 ; 原始的 Text 需要用于显示前的比对,所以不能改原始值,必须用 TempText 。 if (TextArea[5]<StrLen(Text)) TempText:=TextArea[5]>4 ? SubStr(Text, 1 ,TextArea[5]-4) "…………" : SubStr(Text, 1 ,1) "…………" else TempText:=Text ; 写字到框上。这个函数使用 O 中的 X,Y 去调整文字的位置。 this._TextToGraphics(this["G" WhichToolTip], TempText, O) ; 调试用,可显示计算得到的文字范围。 /* ;城西取消 if (this.DebugMode) { pBrush := Gdip_BrushCreateSolid(0x20ff0000) Gdip_FillRectangle(this["G" WhichToolTip], pBrush, O.Border+O.Margin, O.Border+O.Margin, TextWidth, TextHeight) Gdip_DeleteBrush(pBrush) } */ ; 返回文本框不超出目标范围(比如屏幕范围)的最佳坐标。 this._CalculateDisplayPosition(&X, &Y, RectWithBorderWidth, RectWithBorderHeight, O) ; 显示 UpdateLayeredWindow(this["hBTT" WhichToolTip], this["hdc" WhichToolTip] , X, Y, RectWithBorderWidth, RectWithBorderHeight, O.Transparent) ; 因为 BTT 总会在使用一段时间后,被不明原因的置底,导致显示内容被其它窗口遮挡,以为没有正常显示,所以这里提升Z序到最上面! ; 已测试过,此方法效率极高,远超 WinSet, Top 命令。 ; hWndInsertAfter ; HWND_TOPMOST:=-1 ; uFlags ; SWP_NOSIZE:=0x0001 ; SWP_NOMOVE:=0x0002 ; SWP_NOREDRAW:=0x0008 ; SWP_NOACTIVATE:=0x0010 ; SWP_NOOWNERZORDER:=0x0200 ; SWP_NOSENDCHANGING:=0x0400 ; SWP_DEFERERASE:=0x2000 ; SWP_ASYNCWINDOWPOS:=0x4000 DllCall("SetWindowPos", "ptr", this["hBTT" WhichToolTip], "ptr", -1, "int", 0, "int", 0, "int", 0, "int", 0, "uint", 26139) } ; 保存参数值,以便之后比对参数值是否改变 this["SavedText" WhichToolTip] := Text , this["SavedOptions" WhichToolTip] := O.Checksum , this["SavedX" WhichToolTip] := X ; 这里的 X,Y 是经过 _CalculateDisplayPosition() 计算后的新 X,Y , this["SavedY" WhichToolTip] := Y , this["SavedW" WhichToolTip] := RectWithBorderWidth , this["SavedH" WhichToolTip] := RectWithBorderHeight , this["SavedTargetHWND" WhichToolTip] := O.TargetHWND , this["SavedCoordMode" WhichToolTip] := O.CoordMode , this["SavedTransparent" WhichToolTip] := O.Transparent } ; x,y 任意一个跟随鼠标位置 或 使用窗口或客户区模式(窗口大小可能发生改变或者窗口发生移动) ; 或 目标窗口发生变化 或 坐标模式发生变化 ; 或 整体透明度发生变化 这5种情况可能需要移动位置,需要进行坐标计算。 else if ((X="" or Y="") or O.CoordMode!="Screen" or O.TargetHWND!=this.SavedTargetHWND or O.CoordMode!=this.SavedCoordMode or O.Transparent!=this.SavedTransparent) { ; 返回文本框不超出目标范围(比如屏幕范围)的最佳坐标。 this._CalculateDisplayPosition(X, Y, this["SavedW" WhichToolTip], this["SavedH" WhichToolTip], O) ; 判断文本框 显示位置 ; 或 显示透明度 是否发生改变 if (X!=this["SavedX" WhichToolTip] or Y!=this["SavedY" WhichToolTip] or O.Transparent!=this.SavedTransparent) { ; 显示 UpdateLayeredWindow(this["hBTT" WhichToolTip], this["hdc" WhichToolTip] , X, Y, this["SavedW" WhichToolTip], this["SavedH" WhichToolTip], O.Transparent) ; 保存新的位置 this["SavedX" WhichToolTip] := X , this["SavedY" WhichToolTip] := Y , this["SavedTargetHWND" WhichToolTip] := O.TargetHWND , this["SavedCoordMode" WhichToolTip] := O.CoordMode , this["SavedTransparent" WhichToolTip] := O.Transparent } } ret:={Hwnd : this["hBTT" WhichToolTip] , X : X , Y : Y , W : this["SavedW" WhichToolTip] , H : this["SavedH" WhichToolTip]} return ret } ; 为了统一参数的传输,以及特殊模式的设置,修改了 gdip 库的 Gdip_TextToGraphics() 函数。 _TextToGraphics(pGraphics, Text, Options, Measure:=0) { static Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout" ; 设置字体样式 Style := 0 for eachStyle, valStyle in StrSplit(Styles, "|") { if InStr(Options.FontStyle, valStyle) Style |= (valStyle != "StrikeOut") ? (A_Index-1) : 8 } if (FileExist(Options.Font)) ; 加载未安装的本地字体 { hFontCollection := Gdip_NewPrivateFontCollection() hFontFamily := Gdip_CreateFontFamilyFromFile(Options.Font, hFontCollection) } if !isset(hFontFamily ) or !hFontFamily ; 加载已安装的字体 hFontFamily := Gdip_FontFamilyCreate(Options.Font) if !isset(hFontFamily ) or !hFontFamily ; 加载默认字体 hFontFamily := Gdip_FontFamilyCreateGeneric(1) ; 根据 DPI 缩放比例自动调整字号 hFont := Gdip_FontCreate(hFontFamily, Options.FontSize * Options.DPIScale, Style) ; 设置文字格式化样式,LineLimit = 0x00002000 只显示完整的行。 ; 比如最后一行,因为布局高度有限,只能显示出一半,此时就会让它完全不显示。 ; 直接使用 Gdip_StringFormatGetGeneric(1) 包含 LineLimit 设置,同时可以实现左右空白区域最小化。 ; 但这样有个副作用,那就是无法精确的设置文本框的宽度了,同时最右边文字的间距会被压缩。 ; 例如指定宽度800,可能返回的宽度是793,因为右边没有用空白补上。 ; 好处是右边几乎没有空白区域,左边也没有,所以接近完美的实现文字居中了。 ; hStringFormat := Gdip_StringFormatCreate(0x00002000) ; if !hStringFormat hStringFormat := Gdip_StringFormatGetGeneric(1) ; 准备文本画刷 if (Options.TCLGA!="" and Options.TCLGM and Options.TCLGS and Options.TCLGE and Options.Width and Options.Height) ; 渐变画刷 { pBrush := this._CreateLinearGrBrush(Options.TCLGA, Options.TCLGM, Options.TCLGS, Options.TCLGE , this.NonNull_Ret(Options.HasOwnProp("X") ? Options.X : 0, 0), this.NonNull_Ret(Options.HasOwnProp("Y") ? Options.Y : 0, 0) , Options.Width, Options.Height) } else pBrush := Gdip_BrushCreateSolid(Options.TextColor) ; 纯色画刷 ; 检查参数是否齐全 if !(hFontFamily && hFont && hStringFormat && pBrush && pGraphics) { E := !pGraphics ? -2 : !hFontFamily ? -3 : !hFont ? -4 : !hStringFormat ? -5 : !pBrush ? -6 : 0 if pBrush Gdip_DeleteBrush(pBrush) if hStringFormat Gdip_DeleteStringFormat(hStringFormat) if hFont Gdip_DeleteFont(hFont) if hFontFamily Gdip_DeleteFontFamily(hFontFamily) if hFontCollection Gdip_DeletePrivateFontCollection(hFontCollection) return E } TabStops := [] for k, v in Options.TabStops TabStops.Push(v * Options.DPIScale) Gdip_SetStringFormatTabStops(hStringFormat, TabStops) ; 设置 TabStops Gdip_SetStringFormatAlign(hStringFormat, Align:=0) ; 设置左对齐 Gdip_SetTextRenderingHint(pGraphics, Options.FontRender) ; 设置渲染模式 ;=CreateRectF(RC CreateRectF(&RC , Options.HasOwnProp("X") ? this.NonNull_Ret(Options.X, 0) : 0 ; x,y 需要至少为0 , Options.HasOwnProp("Y") ? this.NonNull_Ret(Options.Y, 0) : 0 , Options.Width, Options.Height) ; 宽高可以为空 ;=returnRC := Gdip_MeasureString(pGraphics, Text, hFont, hStringFormat, RC) ; 计算大小 returnRC := Gdip_MeasureString(pGraphics, Text, hFont, hStringFormat, &RC) ; 计算大小 _E:="" ;城西加 if !Measure _E := Gdip_DrawString(pGraphics, Text, hFont, hStringFormat, pBrush, &RC) Gdip_DeleteBrush(pBrush) Gdip_DeleteFont(hFont) Gdip_DeleteStringFormat(hStringFormat) Gdip_DeleteFontFamily(hFontFamily) if Isset(hFontCollection) and hFontCollection Gdip_DeletePrivateFontCollection(hFontCollection) return _E ? _E : returnRC } _CreateLinearGrBrush(Angle, Mode, StartColor, EndColor, x, y, w, h) { ; Mode=8 Angle 0=左到右 90=上到下 180=右到左 270=下到上 ; Mode=3 Angle 0=左到右 90=近似上到下 ; Mode=4 Angle 0=左到右 90=下到上 switch Mode { case 1,3,5,7:pBrush:=Gdip_CreateLinearGrBrush(x, y, x+w, y, StartColor, EndColor) case 2,4,6,8:pBrush:=Gdip_CreateLinearGrBrush(x, y+h//2, x+w, y+h//2, StartColor, EndColor) } switch Mode { case 1,2: Gdip_RotateLinearGrBrushTransform(pBrush, Angle, 0) ; 性能比模式3、4高10倍左右 case 3,4: Gdip_RotateLinearGrBrushTransform(pBrush, Angle, 1) case 5,6: Gdip_RotateLinearGrBrushAtCenter(pBrush, Angle, 0) case 7,8: Gdip_RotateLinearGrBrushAtCenter(pBrush, Angle, 1) ; 可绕中心旋转 } return pBrush } ; 此函数确保传入空值或者错误值均可返回正确值。 _CheckStylesAndOptions(Styles, Options) { O := {} if Styles="" { O:={Border:1,Rounded:3,Margin:5,TabStops:[50],TextColor:0xff575757,BackgroundColor:0xffffffff,Font:this.ToolTipFontName,FontSize:12,FontRender:5,FontStyle:"",BCLGS:"",BCLGE:"",BCLGA:"",BCLGM:"",TCLGS:"",TCLGE:"",TCLGA:"" ,TCLGM:"",BorderColor:0xff575757} } Else { O.Border :=Styles.HasOwnProp("Border") ? this.NonNull_Ret(Styles.Border , 1 , 0 , 20) : 1 ; 细边框 默认1 0-20 , O.Rounded :=Styles.HasOwnProp("Rounded") ? this.NonNull_Ret(Styles.Rounded , 3 , 0 , 30) : 3 ; 圆角 默认3 0-30 , O.Margin :=Styles.HasOwnProp("Margin") ? this.NonNull_Ret(Styles.Margin , 5 , 0 , 30) : 5 ; 边距 默认5 0-30 , O.TabStops :=Styles.HasOwnProp("TabStops") ? this.NonNull_Ret(Styles.TabStops , [50] , "", "") : [50] ; 制表符宽 默认[50] , O.TextColor :=Styles.HasOwnProp("TextColor") ? this.NonNull_Ret(Styles.TextColor , 0xff575757 , "", "") : 0xff575757 ; 文本色 默认0xff575757 , O.BackgroundColor :=Styles.HasOwnProp("BackgroundColor") ? this.NonNull_Ret(Styles.BackgroundColor, 0xffffffff , "", "") : 0xffffffff ; 背景色 默认0xffffffff , O.Font :=Styles.HasOwnProp("Font") ? this.NonNull_Ret(Styles.Font , this.ToolTipFontName, "", "") : this.ToolTipFontName ; 字体 默认与 ToolTip 一致 , O.FontSize :=Styles.HasOwnProp("FontSize") ? this.NonNull_Ret(Styles.FontSize , 12 , "", "") : 12 ; 字号 默认12 , O.FontRender :=Styles.HasOwnProp("FontRender") ? this.NonNull_Ret(Styles.FontRender , 5 , 0 , 5 ) : 5 ; 渲染模式 默认5 0-5 , O.FontStyle :=Styles.HasOwnProp("FontStyle") ? Styles.FontStyle : "" ; 字体样式 默认无 ; 名字太长,建个缩写副本。 , O.BCLGS :=Styles.HasOwnProp("BorderColorLinearGradientStart") ? Styles.BorderColorLinearGradientStart : "" ; 细边框渐变色 默认无 , O.BCLGE :=Styles.HasOwnProp("BorderColorLinearGradientEnd") ? Styles.BorderColorLinearGradientEnd : "" ; 细边框渐变色 默认无 , O.BCLGA :=Styles.HasOwnProp("BorderColorLinearGradientAngle") ? Styles.BorderColorLinearGradientAngle : "" ; 细边框渐变角度 默认无 , O.BCLGM :=Styles.HasOwnProp("BorderColorLinearGradientMode") ? this.NonNull_Ret(Styles.BorderColorLinearGradientMode, "", 1, 8) : "" ; 细边框渐变模式 默认无 1-8 ; 名字太长,建个缩写副本。 , O.TCLGS :=Styles.HasOwnProp("TextColorLinearGradientStart") ? Styles.TextColorLinearGradientStart : "" ; 文本渐变色 默认无 , O.TCLGE :=Styles.HasOwnProp("TextColorLinearGradientEnd") ? Styles.TextColorLinearGradientEnd : "" ; 文本渐变色 默认无 , O.TCLGA :=Styles.HasOwnProp("TextColorLinearGradientAngle") ? Styles.TextColorLinearGradientAngle : "" ; 文本渐变角度 默认无 , O.TCLGM :=Styles.HasOwnProp("TextColorLinearGradientMode") ? this.NonNull_Ret(Styles.TextColorLinearGradientMode, "", 1, 8) : "" ; 文本渐变模式 默认无 1-8 ; 名字太长,建个缩写副本。 , O.BGCLGS :=Styles.HasOwnProp("BackgroundColorLinearGradientStart") ? Styles.BackgroundColorLinearGradientStart : "" ; 背景渐变色 默认无 , O.BGCLGE :=Styles.HasOwnProp("BackgroundColorLinearGradientEnd") ? Styles.BackgroundColorLinearGradientEnd : "" ; 背景渐变色 默认无 , O.BGCLGA :=Styles.HasOwnProp("BackgroundColorLinearGradientAngle") ? Styles.BackgroundColorLinearGradientAngle : "" ; 背景渐变角度 默认无 , O.BGCLGM :=Styles.HasOwnProp("BackgroundColorLinearGradientMode") ? this.NonNull_Ret(Styles.BackgroundColorLinearGradientMode, "", 1, 8) : "" ; 背景渐变模式 默认无 1-8 ; a:=0xaabbccdd 下面是运算规则 ; a>>16 = 0xaabb ; a>>24 = 0xaa ; a&0xffff = 0xccdd ; a&0xff = 0xdd ; 0x88<<16 = 0x880000 ; 0x880000+0xbbcc = 0x88bbcc ;;;, BlendedColor2 := (O.TCLGS and O.TCLGE and O.TCLGD) ? O.TCLGS : O.TextColor ; 使用文本渐变色替换文本色用于混合 , BlendedColor2 := (O.TCLGS and O.TCLGE) ? O.TCLGS : O.TextColor ; 使用文本渐变色替换文本色用于混合 , BlendedColor := ((O.BackgroundColor>>24)<<24) + (BlendedColor2&0xffffff) ; 混合色 背景色的透明度与文本色混合 , O.BorderColor :=Styles.HasOwnProp("BorderColor") ? this.NonNull_Ret(Styles.BorderColor , BlendedColor , "", "") : BlendedColor ; 细边框色 默认混合色 } if Options="" { O.TargetHWND:=WinExist("A") , O.CoordMode:=A_CoordModeToolTip , O.Transparent:=255 , O.MouseNeverCoverToolTip:=1 , O.DistanceBetweenMouseXAndToolTip:=16 , O.DistanceBetweenMouseYAndToolTip:=16 , O.JustCalculateSize:="" } Else { O.TargetHWND :=Options.HasOwnProp("TargetHWND") ? this.NonNull_Ret(Options.TargetHWND , WinExist("A") , "", "") : WinExist("A") ; 目标句柄 默认活动窗口 , O.CoordMode :=Options.HasOwnProp("CoordMode") ? this.NonNull_Ret(Options.CoordMode , A_CoordModeToolTip, "", "") : A_CoordModeToolTip ; 坐标模式 默认与 ToolTip 一致 , O.Transparent :=Options.HasOwnProp("Transparent") ? this.NonNull_Ret(Options.Transparent, 255 , 0 , 255) : 255 ; 整体透明度 默认255 , O.MouseNeverCoverToolTip :=Options.HasOwnProp("MouseNeverCoverToolTip") ? this.NonNull_Ret(Options.MouseNeverCoverToolTip , 1 , 0 , 1 ) : 1 ; 鼠标永不遮挡文本框 , O.DistanceBetweenMouseXAndToolTip :=Options.HasOwnProp("DistanceBetweenMouseXAndToolTip") ? this.NonNull_Ret(Options.DistanceBetweenMouseXAndToolTip, 16, "", "") : 16 ; 鼠标与文本框的X距离 , O.DistanceBetweenMouseYAndToolTip :=Options.HasOwnProp("DistanceBetweenMouseYAndToolTip") ? this.NonNull_Ret(Options.DistanceBetweenMouseYAndToolTip, 16, "", "") : 16 ; 鼠标与文本框的Y距离 , O.JustCalculateSize :=Options.HasOwnProp("JustCalculateSize") ? Options.JustCalculateSize : "" ; 仅计算显示尺寸并返回 } ; 难以比对两个对象是否一致,所以造一个变量比对。 ; 这里的校验因素,必须是那些改变后会使画面内容也产生变化的因素。 ; 所以没有 TargetHWND 和 CoordMode 和 Transparent ,因为这三个因素只影响位置。 for k, v in O.TabStops TabStops .= v "," O.Checksum := O.Border "|" O.Rounded "|" O.Margin "|" TabStops "|" . O.BorderColor "|" O.BCLGS "|" O.BCLGE "|" O.BCLGA "|" O.BCLGM "|" . O.TextColor "|" O.TCLGS "|" O.TCLGE "|" O.TCLGA "|" O.TCLGM "|" . O.BackgroundColor "|" O.BGCLGS "|" O.BGCLGE "|" O.BGCLGA "|" O.BGCLGM "|" . O.Font "|" O.FontSize "|" O.FontRender "|" O.FontStyle return O } ; 此函数确保文本框显示位置不会超出目标范围。 ; 使用 ByRef X, ByRef Y 返回不超限的位置。 _CalculateDisplayPosition(&X, &Y, W, H, Options, GetTargetSize:=0) { Point := Buffer(8, 0) ; 获取鼠标位置 , DllCall("GetCursorPos", "Ptr", Point.Ptr, "Int") , MouseX := NumGet(Point, 0, "Int"), MouseY := NumGet(Point, 4, "Int") ; x,y 即 ToolTip 显示的位置。 ; x,y 同时为空表明完全跟随鼠标。 ; x,y 单个为空表明只跟随鼠标横向或纵向移动。 ; x,y 都有值,则说明被钉在屏幕或窗口或客户区的某个位置。 ; MouseX,MouseY 是鼠标的屏幕坐标。 ; DisplayX,DisplayY 是 x,y 经过转换后的屏幕坐标。 ; 以下过程 x,y 不发生变化, DisplayX,DisplayY 储存转换好的屏幕坐标。 ; 不要尝试合并分支 (X="" and Y="") 与 (A_CoordModeToolTip = "Screen")。 ; 因为存在把坐标模式设为 Window 或 Client 但又同时不给出 x,y 的情况!!!!!! if (X="" and Y="") { ; 没有给出 x,y 则使用鼠标坐标 DisplayX := MouseX , DisplayY := MouseY ; 根据坐标判断在第几个屏幕里,并获得对应屏幕边界。 ; 使用 MONITOR_DEFAULTTONEAREST 设置,可以在给出的点不在任何显示器内时,返回距离最近的显示器。 ; 这样可以修正使用 1920,1080 这种错误的坐标,导致返回空值,导致画图失败的问题。 ; 为什么 1920,1080 是错误的呢?因为 1920 是宽度,而坐标起点是0,所以最右边坐标值是 1919,最下面是 1079。 ;=, hMonitor := MDMF_FromPoint(DisplayX, DisplayY, MONITOR_DEFAULTTONEAREST:=2) , hMonitor := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2) , TargetLeft := this.Monitors[hMonitor].Left , TargetTop := this.Monitors[hMonitor].Top , TargetRight := this.Monitors[hMonitor].Right , TargetBottom := this.Monitors[hMonitor].Bottom , TargetWidth := TargetRight-TargetLeft , TargetHeight := TargetBottom-TargetTop ; 将对应屏幕的 DPIScale 存入 Options 中。 , Options.DPIScale := this.Monitors[hMonitor].DPIScale } ; 已给出 x和y 或x 或y,都会走到下面3个分支去。 else if (Options.CoordMode = "Window" or Options.CoordMode = "Relative") { ; 已给出 x或y 且使用窗口坐标 WinGetPos &WinX, &WinY, &WinW, &WinH,"ahk_id " Options.TargetHWND XInScreen := WinX+X , YInScreen := WinY+Y , TargetLeft := WinX , TargetTop := WinY , TargetWidth := WinW , TargetHeight := WinH , TargetRight := TargetLeft+TargetWidth , TargetBottom := TargetTop+TargetHeight , DisplayX := (X="") ? MouseX : XInScreen , DisplayY := (Y="") ? MouseY : YInScreen , hMonitor := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2) , Options.DPIScale := this.Monitors[hMonitor].DPIScale } else if (Options.CoordMode = "Client") { ; 已给出 x或y 且使用客户区坐标 ClientArea:=Buffer(16, 0) , DllCall("GetClientRect", "Ptr", Options.TargetHWND, "Ptr", ClientArea.Ptr) , DllCall("ClientToScreen", "Ptr", Options.TargetHWND, "Ptr", ClientArea.Ptr) , ClientX := NumGet(ClientArea, 0, "Int") , ClientY := NumGet(ClientArea, 4, "Int") , ClientW := NumGet(ClientArea, 8, "Int") , ClientH := NumGet(ClientArea, 12, "Int") XInScreen := ClientX+ (X ? X : 0) , YInScreen := ClientY+ (X ? y : 0) , TargetLeft := ClientX , TargetTop := ClientY , TargetWidth := ClientW , TargetHeight := ClientH , TargetRight := TargetLeft+TargetWidth , TargetBottom := TargetTop+TargetHeight , DisplayX := (X="") ? MouseX : XInScreen , DisplayY := (Y="") ? MouseY : YInScreen , hMonitor := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2) , Options.DPIScale := this.Monitors[hMonitor].DPIScale } else ; 这里必然 A_CoordModeToolTip = "Screen" { ; 已给出 x或y 且使用屏幕坐标 DisplayX := (X="") ? MouseX : X , DisplayY := (Y="") ? MouseY : Y , hMonitor := MDMF_FromPoint(&DisplayX, &DisplayY, MONITOR_DEFAULTTONEAREST:=2) , TargetLeft := this.Monitors[hMonitor].Left , TargetTop := this.Monitors[hMonitor].Top , TargetRight := this.Monitors[hMonitor].Right , TargetBottom := this.Monitors[hMonitor].Bottom , TargetWidth := TargetRight-TargetLeft , TargetHeight := TargetBottom-TargetTop , Options.DPIScale := this.Monitors[hMonitor].DPIScale } if (GetTargetSize=1) { TargetSize := [] , TargetSize.X := TargetLeft , TargetSize.Y := TargetTop ; 一个窗口,有各种各样的方式可以让自己的高宽超过屏幕高宽。 ; 例如最大化的时候,看起来刚好填满了屏幕,应该是1920*1080,但实际获取会发现是1936*1096。 ; 还可以通过拖动窗口边缘调整大小的方式,让它变1924*1084。 ; 还可以直接在创建窗口的时候,指定一个数值,例如3000*3000。 ; 由于设计的时候, DIB 最大就是多个屏幕大小的总和。 ; 当造出一个超过屏幕大小总和的窗口,又使用了 A_CoordModeToolTip = "Window" 之类的参数,同时待显示文本单行又超级长。 ; 此时 (显示宽高 = 窗口宽高) > DIB宽高,会导致 UpdateLayeredWindow() 显示失败。 ; 所以这里做一下限制。 , TargetSize.W := Min(TargetWidth, this.DIBWidth) , TargetSize.H := Min(TargetHeight, this.DIBHeight) return TargetSize } DPIScale := Options.DPIScale ; 为跟随鼠标显示的文本框增加一个距离,避免鼠标和文本框挤一起发生遮挡。 ; 因为前面需要用到原始的 DisplayX 和 DisplayY 进行计算,所以在这里才增加距离。 , DisplayX := (X="") ? DisplayX+Options.DistanceBetweenMouseXAndToolTip*DPIScale : DisplayX , DisplayY := (Y="") ? DisplayY+Options.DistanceBetweenMouseYAndToolTip*DPIScale : DisplayY ; 处理目标边缘(右和下)的情况,让文本框可以贴边显示,不会超出目标外。 , DisplayX := (DisplayX+W>=TargetRight) ? TargetRight-W : DisplayX , DisplayY := (DisplayY+H>=TargetBottom) ? TargetBottom-H : DisplayY ; 处理目标边缘(左和上)的情况,让文本框可以贴边显示,不会超出目标外。 ; 不建议合并代码,理解会变得困难。 , DisplayX := (DisplayX<TargetLeft) ? TargetLeft : DisplayX , DisplayY := (DisplayY<TargetTop) ? TargetTop : DisplayY ; 处理鼠标遮挡文本框的情况(即鼠标跑到文本框坐标范围内了)。这里要做测试的话,需要测试5种情况。 ; X跟随 Y跟随。 ; X跟随 Y固定。0和1919都要测 ; Y跟随 X固定。0和1079都要测 ; X固定 Y固定。此种情况文本框可被鼠标遮挡,无需测试。 if (Options.MouseNeverCoverToolTip=1 and (X="" or Y="") and MouseX>=DisplayX and MouseY>=DisplayY and MouseX<=DisplayX+W and MouseY<=DisplayY+H) { ; MouseY-H-16 是往上弹,应对在左下角和右下角的情况。 ; MouseY+H+16 是往下弹,应对在右上角和左上角的情况。 ; 这里不要去用 Abs(Options.DistanceBetweenMouseYAndToolTip) 替代 16。因为当前者很大时,显示效果不好。 ; 优先往上弹,如果不超限,则上弹。如果超限则往下弹,下弹超限,则不弹。 DisplayY := MouseY-H-16>=TargetTop ? MouseY-H-16 : MouseY+H+16<=TargetBottom ? MouseY+16 : DisplayY } ; 使用 ByRef 变量特性返回计算得到的 X和Y X := DisplayX , Y := DisplayY } ; https://autohotkey.com/boards/viewtopic.php?f=6&t=4379 ; jballi's Fnt Library Fnt_GetTooltipFontName() { static LF_FACESIZE:=32 ;-- In TCHARS return StrGet(this.Fnt_GetNonClientMetrics()+316+28,LF_FACESIZE) } Fnt_GetNonClientMetrics() { static Dummy15105062 ,SPI_GETNONCLIENTMETRICS:=0x29 ,NONCLIENTMETRICS ;-- Set the size of NONCLIENTMETRICS structure cbSize:=500 if (((GV:=DllCall("GetVersion"))&0xFF . "." . GV>>8&0xFF)>=6.0) ;-- Vista+ cbSize+=4 ;-- Create and initialize NONCLIENTMETRICS structure NONCLIENTMETRICS := Buffer(cbSize, 0) NumPut("UInt", cbSize, NONCLIENTMETRICS, 0) ;-- Get nonclient metrics parameter if !DllCall("SystemParametersInfo" ,"UInt",SPI_GETNONCLIENTMETRICS ,"UInt",cbSize ;,"Ptr",&NONCLIENTMETRICS ,"Ptr",NONCLIENTMETRICS.Ptr ,"UInt",0) return false ;-- Return to sender return NONCLIENTMETRICS.Ptr } ; 变量为空,则使用默认值。变量不为空,则使用变量值。 ; 同时可以检查变量是否超出最大最小范围。 ; 注意,默认值不受最大最小范围的限制。 ; 也就是说 ; 当变量值为"",默认值为8,范围为2-5,此时变量值会是8。 ; 当变量值为10,默认值为8,范围为2-5,此时变量值会是5。 NonNull(&var, DefaultValue, MinValue:="", MaxValue:="") ; 237ms { var:= var="" ? DefaultValue : MinValue="" ? (MaxValue="" ? var : Min(var, MaxValue)) : (MaxValue!="" ? Max(Min(var, MaxValue), MinValue) : Max(var, MinValue)) } ; 与 NonNull 一致,区别是通过 return 返回值,而不是 ByRef。 NonNull_Ret(var, DefaultValue, MinValue:="", MaxValue:="") ; 237ms { return var="" ? DefaultValue : MinValue="" ? (MaxValue="" ? var : Min(var, MaxValue)) : (MaxValue!="" ? Max(Min(var, MaxValue), MinValue) : Max(var, MinValue)) } } ;================================GDIP================================================ Gdip_Startup() { DllCall("LoadLibrary", "str", "gdiplus") si := Buffer(A_PtrSize = 8 ? 24 : 16, 0) NumPut("UInt", 1, si) DllCall("gdiplus\GdiplusStartup", "UPtr*", &pToken:=0, "UPtr", si.Ptr, "UPtr", 0) if (!pToken) { throw Error("Gdiplus failed to start. Please ensure you have gdiplus on your system") } return pToken } MDMF_Enum(HMON := "") { static EnumProc := CallbackCreate(MDMF_EnumProc) static Obj := "Map" static Monitors := {} if (HMON = "") { ; new enumeration Monitors := %Obj%("TotalCount", 0) if !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", ObjPtr(Monitors), "Int") return False } return (HMON = "") ? Monitors : Monitors.Has(HMON) ? Monitors[HMON] : False } CreateDIBSection(w, h, hdc:="", bpp:=32, &ppvBits:=0) { hdc2 := hdc ? hdc : GetDC() bi := Buffer(40, 0) NumPut("UInt", w, bi, 4) , NumPut("UInt", h, bi, 8) , NumPut("UInt", 40, bi, 0) , NumPut("ushort", 1, bi, 12) , NumPut("uInt", 0, bi, 16) , NumPut("ushort", bpp, bi, 14) hbm := DllCall("CreateDIBSection" , "UPtr", hdc2 , "UPtr", bi.Ptr , "UInt", 0 , "UPtr*", &ppvBits , "UPtr", 0 , "UInt", 0, "UPtr") if (!hdc) { ReleaseDC(hdc2) } return hbm } CreateCompatibleDC(hdc:=0) { return DllCall("CreateCompatibleDC", "UPtr", hdc) } Gdip_GraphicsFromHDC(hdc) { DllCall("gdiplus\GdipCreateFromHDC", "UPtr", hdc, "UPtr*", &pGraphics:=0) return pGraphics } Gdip_SetSmoothingMode(pGraphics, SmoothingMode) { if !pGraphics return 2 return DllCall("gdiplus\GdipSetSmoothingMode", "UPtr", pGraphics, "Int", SmoothingMode) } Gdip_SetPixelOffsetMode(graphics, pixelOffsetMode) => DllCall('Gdiplus\GdipSetPixelOffsetMode', 'ptr', graphics, 'ptr', pixelOffsetMode, 'uint') SelectObject(hdc, hgdiobj) { return DllCall("SelectObject", "UPtr", hdc, "UPtr", hgdiobj) } Gdip_DeleteGraphics(pGraphics) { return DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics) } DeleteObject(hObject) { return DllCall("DeleteObject", "UPtr", hObject) } DeleteDC(hdc) { return DllCall("DeleteDC", "UPtr", hdc) } Gdip_Shutdown(pToken) { DllCall("gdiplus\GdiplusShutdown", "UPtr", pToken) if hModule := DllCall("GetModuleHandle", "str", "gdiplus", "UPtr") { DllCall("FreeLibrary", "UPtr", hModule) } return 0 } Gdip_GraphicsClear(pGraphics, ARGB:=0x00ffffff) { If !pGraphics return 2 return DllCall("gdiplus\GdipGraphicsClear", "UPtr", pGraphics, "Int", ARGB) } UpdateLayeredWindow(hwnd, hdc, x:="", y:="", w:="", h:="", Alpha:=255) { if ((x != "") && (y != "")) pt := Buffer(8), NumPut("UInt", x, pt, 0), NumPut("UInt", y, pt, 4) if (w = "") || (h = "") { CreateRect(&winRect:="", 0, 0, 0, 0) ;is 16 on both 32 and 64 DllCall( "GetWindowRect", "UPtr", hwnd, "UPtr", winRect.Ptr ) w := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt") h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt") } return DllCall("UpdateLayeredWindow" , "UPtr", hwnd , "UPtr", 0 , "UPtr", ((x = "") && (y = "")) ? 0 : pt.Ptr , "Int64*", w|h<<32 , "UPtr", hdc , "Int64*", 0 , "UInt", 0 , "UInt*", Alpha<<16|1<<24 , "UInt", 2) } Gdip_BrushCreateSolid(ARGB:=0xff000000) { DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, "UPtr*", &pBrush:=0) return pBrush } Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h) { return DllCall("gdiplus\GdipFillRectangle" , "UPtr", pGraphics , "UPtr", pBrush , "Float", x , "Float", y , "Float", w , "Float", h) } Gdip_FillRoundedRectanglePath(pGraphics, pBrush, X, Y, W, H, R) { DllCall("Gdiplus.dll\GdipCreatePath", "UInt", 0, "PtrP", &pPath:=0) D := (R * 2), W -= D, H -= D DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X, "Float", Y, "Float", D, "Float", D, "Float", 180, "Float", 90) DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X + W, "Float", Y, "Float", D, "Float", D, "Float", 270, "Float", 90) DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X + W, "Float", Y + H, "Float", D, "Float", D, "Float", 0, "Float", 90) DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", pPath, "Float", X, "Float", Y + H, "Float", D, "Float", D, "Float", 90, "Float", 90) DllCall("Gdiplus.dll\GdipClosePathFigure", "Ptr", pPath) RS := DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", pBrush, "Ptr", pPath) DllCall("Gdiplus.dll\GdipDeletePath", "Ptr", pPath) Return RS } Gdip_DeleteBrush(pBrush) { return DllCall("gdiplus\GdipDeleteBrush", "UPtr", pBrush) } Gdip_NewPrivateFontCollection(fontCollection:=0) => DllCall('Gdiplus\GdipNewPrivateFontCollection', 'ptr', fontCollection, 'uint') Gdip_CreateFontFamilyFromFile(FontFile, hFontCollection, FontName:="") { If !hFontCollection Return E := DllCall("gdiplus\GdipPrivateAddFontFile", "ptr", hFontCollection, "str", FontFile) if (FontName="" && !E) { pFontFamily := Buffer(10, 0) DllCall("gdiplus\GdipGetFontCollectionFamilyList", "ptr", hFontCollection, "int", 1, "ptr", pFontFamily.Ptr, "int*", &found:=0) FontName:=Buffer(100, 0) DllCall("gdiplus\GdipGetFamilyName", "ptr", NumGet(pFontFamily, 0, "ptr"), "str", FontName, "ushort", 1033) } If !E DllCall("gdiplus\GdipCreateFontFamilyFromName", "str", FontName, "ptr", hFontCollection, "uint*", &hFontFamily:=0) Return hFontFamily } Gdip_FontFamilyCreate(Font) { DllCall("gdiplus\GdipCreateFontFamilyFromName" , "UPtr", StrPtr(Font) , "UInt", 0 , "UPtr*", &hFamily:=0) return hFamily } Gdip_FontFamilyCreateGeneric(whichStyle) { If (whichStyle=0) DllCall("gdiplus\GdipGetGenericFontFamilyMonospace", "UPtr*", &hFontFamily:=0) Else If (whichStyle=1) DllCall("gdiplus\GdipGetGenericFontFamilySansSerif", "UPtr*", &hFontFamily:=0) Else If (whichStyle=2) DllCall("gdiplus\GdipGetGenericFontFamilySerif", "UPtr*", &hFontFamily:=0) Return hFontFamily } Gdip_FontCreate(hFamily, Size, Style:=0) { DllCall("gdiplus\GdipCreateFont", "UPtr", hFamily, "Float", Size, "Int", Style, "Int", 0, "UPtr*", &hFont:=0) return hFont } Gdip_StringFormatGetGeneric(whichFormat:=0) { If (whichFormat=1) DllCall("gdiplus\GdipStringFormatGetGenericTypographic", "UPtr*", &hStringFormat:=0) Else DllCall("gdiplus\GdipStringFormatGetGenericDefault", "UPtr*", &hStringFormat:=0) Return hStringFormat } Gdip_DeleteStringFormat(hFormat) { return DllCall("gdiplus\GdipDeleteStringFormat", "UPtr", hFormat) } Gdip_DeleteFont(hFont) { return DllCall("gdiplus\GdipDeleteFont", "UPtr", hFont) } Gdip_DeleteFontFamily(hFamily) { return DllCall("gdiplus\GdipDeleteFontFamily", "UPtr", hFamily) } Gdip_DeletePrivateFontCollection(fontCollection) => DllCall('Gdiplus\GdipDeletePrivateFontCollection', 'ptr', fontCollection, 'uint') Gdip_SetStringFormatTabStops(format,tabStops) { firstTabOffset:=0 count:=tabStops.Length buf :=Buffer(4 * count), p :=buf.Ptr loop count p := NumPut('Float',tabStops[A_index],p) return DllCall('Gdiplus\GdipSetStringFormatTabStops', 'ptr', format, 'int', firstTabOffset, 'int', count, 'ptr', buf, 'uint') } Gdip_SetStringFormatAlign(hFormat, Align) { return DllCall("gdiplus\GdipSetStringFormatAlign", "UPtr", hFormat, "Int", Align) } Gdip_SetTextRenderingHint(pGraphics, RenderingHint) { If !pGraphics Return 2 return DllCall("gdiplus\GdipSetTextRenderingHint", "UPtr", pGraphics, "Int", RenderingHint) } CreateRectF(&RectF, x, y, w, h) { RectF := Buffer(16) NumPut("Float", x, RectF, 0), NumPut("Float", y, RectF, 4), NumPut("Float", w, RectF, 8), NumPut("Float", h, RectF, 12) } Gdip_MeasureString(pGraphics, sString, hFont, hFormat, &RectF) { RC := Buffer(16) DllCall("gdiplus\GdipMeasureString" , "UPtr", pGraphics , "UPtr", StrPtr(sString) , "Int", -1 , "UPtr", hFont , "UPtr", RectF.Ptr , "UPtr", hFormat , "UPtr", RC.Ptr , "uint*", &Chars:=0 , "uint*", &Lines:=0) return RC.Ptr ? NumGet(RC, 0, "Float") "|" NumGet(RC, 4, "Float") "|" NumGet(RC, 8, "Float") "|" NumGet(RC, 12, "Float") "|" Chars "|" Lines : 0 } Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, &RectF) { return DllCall("gdiplus\GdipDrawString" , "UPtr", pGraphics , "UPtr", StrPtr(sString) , "Int", -1 , "UPtr", hFont , "UPtr", RectF.Ptr , "UPtr", hFormat , "UPtr", pBrush) } Gdip_CreateLinearGrBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode:=1) { Static Ptr := "UPtr" CreatePointF(&PointF1, x1, y1) CreatePointF(&PointF2, x2, y2) DllCall("gdiplus\GdipCreateLineBrush", Ptr, PointF1.Ptr, Ptr, PointF2.Ptr, "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode, "UPtr*", &pLinearGradientBrush:=0) return pLinearGradientBrush } Gdip_RotateLinearGrBrushTransform(pLinearGradientBrush, Angle, matrixOrder:=0) => DllCall("gdiplus\GdipRotateLineTransform", "UPtr", pLinearGradientBrush, "float", Angle, "int", matrixOrder) Gdip_RotateLinearGrBrushAtCenter(pLinearGradientBrush, Angle, MatrixOrder:=1) { Rect := Gdip_GetLinearGrBrushRect(pLinearGradientBrush) cX := Rect.x + (Rect.w / 2) cY := Rect.y + (Rect.h / 2) pMatrix := Gdip_CreateMatrix() Gdip_TranslateMatrix(pMatrix, -cX , -cY) Gdip_RotateMatrix(pMatrix, Angle, MatrixOrder) Gdip_TranslateMatrix(pMatrix, cX, cY, MatrixOrder) E := Gdip_SetLinearGrBrushTransform(pLinearGradientBrush, pMatrix) Gdip_DeleteMatrix(pMatrix) Return E } MDMF_FromPoint(&X:="", &Y:="", Flag:=0) { if (X = "") || (Y = "") { PT := Buffer(8, 0) DllCall("User32.dll\GetCursorPos", "Ptr", PT.Ptr, "Int") if (X = "") { X := NumGet(PT, 0, "Int") } if (Y = "") { Y := NumGet(PT, 4, "Int") } } return DllCall("User32.dll\MonitorFromPoint", "Int64", (X & 0xFFFFFFFF) | (Y << 32), "UInt", Flag, "Ptr") } GetDC(hwnd:=0) { return DllCall("GetDC", "UPtr", hwnd) } ReleaseDC(hdc, hwnd:=0) { return DllCall("ReleaseDC", "UPtr", hwnd, "UPtr", hdc) } Gdip_SetInterpolationMode(pGraphics, InterpolationMode) { return DllCall("gdiplus\GdipSetInterpolationMode", "UPtr", pGraphics, "Int", InterpolationMode) } Gdip_SetPageUnit(graphics, unit) => DllCall('Gdiplus\GdipSetPageUnit', 'ptr', graphics, 'uint', unit, 'uint') Gdip_SetCompositingQuality(graphics, compositingQuality) => DllCall('Gdiplus\GdipSetCompositingQuality', 'ptr', graphics, 'ptr', compositingQuality, 'uint') GetWindowRect(hwnd, &W, &H) { If !hwnd Return rect := Buffer(16, 0) er := DllCall("dwmapi\DwmGetWindowAttribute" , "UPtr", hWnd ; HWND hwnd , "UInt", 9 ; DWORD dwAttribute (DWMWA_EXTENDED_FRAME_BOUNDS) , "UPtr", rect.Ptr ; PVOID pvAttribute , "UInt", rect.size ; DWORD cbAttribute , "UInt") ; HRESULT If er DllCall("GetWindowRect", "UPtr", hwnd, "UPtr", rect.Ptr, "UInt") r := Object() r.x1 := NumGet(rect, 0, "Int"), r.y1 := NumGet(rect, 4, "Int") r.x2 := NumGet(rect, 8, "Int"), r.y2 := NumGet(rect, 12, "Int") r.w := Abs(max(r.x1, r.x2) - min(r.x1, r.x2)) r.h := Abs(max(r.y1, r.y2) - min(r.y1, r.y2)) W := r.w H := r.h Return r } CreatePointF(&PointF, x, y) { PointF := Buffer(8) NumPut("Float", x, PointF, 0), NumPut("Float", y, PointF, 4) } Gdip_GetLinearGrBrushRect(pLinearGradientBrush) { RectF := Buffer(16, 0) E := DllCall("gdiplus\GdipGetLineRect", "UPtr", pLinearGradientBrush, "UPtr", RectF.Ptr) If (!E) { rData := Object() rData.x := NumGet(RectF, 0, "float") rData.y := NumGet(RectF, 4, "float") rData.w := NumGet(RectF, 8, "float") rData.h := NumGet(RectF, 12, "float") Return rData } Else { Return E } } Gdip_CreateMatrix() { DllCall("gdiplus\GdipCreateMatrix", "UPtr*", &Matrix:=0) return Matrix } Gdip_TranslateMatrix(matrix, offsetX, offsetY, order:=0) => DllCall('Gdiplus\GdipTranslateMatrix', 'ptr', matrix, 'int', offsetX, 'int', offsetY, 'uint', order, 'uint') Gdip_RotateMatrix(matrix, angle, order:=0) => DllCall('Gdiplus\GdipRotateMatrix', 'ptr', matrix, 'int', angle, 'uint', order, 'uint') Gdip_SetLinearGrBrushTransform(pLinearGradientBrush, pMatrix) => DllCall("gdiplus\GdipSetLineTransform", "UPtr", pLinearGradientBrush, "UPtr", pMatrix) Gdip_DeleteMatrix(Matrix) { return DllCall("gdiplus\GdipDeleteMatrix", "UPtr", Matrix) } MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) { Monitors := objfromptraddref(ObjectAddr) Monitors[HMON] := MDMF_GetInfo(HMON) Monitors["TotalCount"]++ if (Monitors[HMON].Primary) { Monitors["Primary"] := HMON } return true } MDMF_GetInfo(HMON) { MIEX := Buffer(40 + (32 << !!1)) NumPut("UInt", MIEX.Size, MIEX, 0) if DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", MIEX.Ptr, "Int") { return {Name: (Name := StrGet(MIEX.Ptr + 40, 32)) ; CCHDEVICENAME = 32 , Num: RegExReplace(Name, ".*(\d+)$", "$1") , Left: NumGet(MIEX, 4, "Int") ; display rectangle , Top: NumGet(MIEX, 8, "Int") ; " , Right: NumGet(MIEX, 12, "Int") ; " , Bottom: NumGet(MIEX, 16, "Int") ; " , WALeft: NumGet(MIEX, 20, "Int") ; work area , WATop: NumGet(MIEX, 24, "Int") ; " , WARight: NumGet(MIEX, 28, "Int") ; " , WABottom: NumGet(MIEX, 32, "Int") ; " , Primary: NumGet(MIEX, 36, "UInt")} ; contains a non-zero value for the primary monitor. } return False } CreateRect(&Rect, x, y, w, h) { Rect := Buffer(16) NumPut("UInt", x, Rect, 0), NumPut("UInt", y, Rect, 4), NumPut("UInt", w, Rect, 8), NumPut("UInt", h, Rect, 12) } ;=======================================Gdip与BTT完===============================================================
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。
评论(0)