-
Notifications
You must be signed in to change notification settings - Fork 72
/
BalloonTip.ahk
182 lines (176 loc) · 8.56 KB
/
BalloonTip.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/*
Use:
BalloonTip(sTitle = "", sText = "", hlicon=0, TitleCodePage = "", TextCodePage = "", Clickable=1, Timeout = 10000, MinTimeDisp = 200, RefreshRate = 100)
- sTitle: Title of the tooltip. Leave it empty for no title.
- sText: Text of the tooltip. Cannot be empty.
- hlicon: Icon displayed left to the title (0: None, 1:Info, 2: Warning, 3: Error, >3: assumed to be an hIcon.)
- TitleCodePage: Code page used for the title (empty: ascii, U8: UTF8, U16: UTF16) Note that if U16 is to be used, you must use the address of the variable containing the title for sTitle instead of the variable itself (by means of &Var instead of Var).
- TextCodePage: Same as TitleCodePage, for the text.
- Clickable: Determines the balloontip behavior when clicked (0: clicks are forwarded through the balloontip, 1: the balloontip disappears when clicked (flickers the active win), 2: the balloontip gets focused on click, 3: Same as 1, plus the balloontip displays a close button, 4: the balloontip gets focused on click and has a working close button). Note that on 98 and previous, the close button won't show and the balloontip may not show properly when using 0.
- Timeout: Delay before the balloontip disappears (in ms).
- MinTimeDisp: Delay during which the balloontip cannot disappear, except when control focus changes or active window loses focus. After this delay, moving the window or the caret will destroy the balloontip.
- RefreshRate: Delay between two checks of caret position, active win and focused control.
Entering no parameters at all (or simply an empty string as sText parameter) hides the balloontip unconditionally.
#include BalloonTip.ahk
#SingleInstance, Force
F1::
BalloonTip()
sleep 200 ;Waits for the previous tooltip to disappear and the previous window to retrieve focus, if Clickable is >0
i++
BalloonTip("Tooltip nбу" i, "A" A_Tab "few`n" A_Space A_Space "words.", 1)
KeyWait F1
return
F2::
Transform, U8Str, Unicode ;Retrieves the UTF8-formatted clipboard content.
BalloonTip()
sleep 200
i++
BalloonTip(U8Str, U8Str, 1, "U8", "U8")
KeyWait F2
return
F3::
Transform, U8Str, Unicode ;Retrieves the UTF8-formatted clipboard content.
;Transforms it into unicode
nSize := DllCall("MultiByteToWideChar", "Uint", 65001, "Uint", 0, "Uint", &U8Str, "int", -1, "Uint", 0, "int", 0)
VarSetCapacity(sU16Title, nSize * 2)
DllCall("MultiByteToWideChar", "Uint", 65001, "Uint", 0, "Uint", &U8Str, "int", -1, "Uint", &sU16Title, "int", nSize)
VarSetCapacity(sU16Text, nSize * 2)
DllCall("MultiByteToWideChar", "Uint", 65001, "Uint", 0, "Uint", &U8Str, "int", -1, "Uint", &sU16Text, "int", nSize)
BalloonTip()
sleep 200
i++
BalloonTip(&sU16Title, &sU16Text, 1, "U16", "U16")
KeyWait F3
return
F4::
BalloonTip()
KeyWait F4
Return
*/
BalloonTip(sTitle = "", sText = "", hlicon=0, TitleCodePage = "", TextCodePage = "", Clickable=1, Timeout = 10000, MinTimeDisp = 200, RefreshRate = 100)
{
Static hwnd, ActiveWinID, ActiveCtrl, CtrlContent, CaretX, CaretY, MinTime, ClickableSave
If !sText
Goto _DestroyBalloonTip
ActiveWinID := WinExist("A")
SetTimer, _DestroyBalloonTip, Off
Gosub _DestroyBalloonTip
ClickableSave := Clickable
ControlGetFocus, ActiveCtrl, Ahk_ID %ActiveWinID%
If !ActiveCtrl
Return
MinTime = 1
ControlGetText, CtrlContent, %ActiveCtrl%, Ahk_ID %ActiveWinID%
coordmode, caret, screen
CaretX := A_CaretX
CaretY := A_CaretY
hwnd := DllCall("CreateWindowEx", "Uint", (Clickable ? 0x80088 : 0x80028), "str", "tooltips_class32", "str", "", "Uint", (Clickable>2 ? 0xC3 : 0x42), "int", 0, "int", 0, "int", 0, "int", 0, "Uint", 0, "Uint", 0, "Uint", 0, "Uint", 0)
VarSetCapacity(ti, 40, 0)
ti := Chr(40)
DllCall("ntdll\RtlFillMemoryUlong", "Uint", &ti + 4, "Uint", 4, "Uint", 0x20) ; TTF_TRACK
If !TitleCodePage
{
If (StrLen(sTitle)>99)
sTitle := SubStr(sTitle, 1, 98) "бн"
DllCall("SendMessage", "Uint", hWnd, "Uint", 1056, "Uint", hlicon, "Uint", &sTitle) ; TTM_SETTITLE ; 0: None, 1:Info, 2: Warning, 3: Error. n > 3: assumed to be an hIcon.
}
Else If (TitleCodePage = "U8")
{
;Retrieve size of the title in unicode
nSize := DllCall("MultiByteToWideChar", "Uint", 65001, "Uint", 0, "Uint", &sTitle, "int", -1, "Uint", 0, "int", 0)
VarSetCapacity(sU16, nSize * 2)
DllCall("MultiByteToWideChar", "Uint", 65001, "Uint", 0, "Uint", &sTitle, "int", -1, "Uint", &sU16, "int", nSize)
;Cut the string at the proper size by writing an end char in memory.
If (nSize>100) ;100=string+end char
NumPut(0x2026, &sU16+196, "UInt") ;2026 is "бн" .UInt is 32bits. The remaining bits will be filled with 0, creating the end char.
DllCall("SendMessage", "Uint", hWnd, "Uint", 1057, "Uint", hlicon, "Uint", &sU16)
}
Else If (TitleCodePage = "U16")
{
;The variable sTitle contains the address of the U16-formatted title
;Cut the string if too long
Loop
{
If (A_Index>100)
{
NumPut(0x2026, sTitle+196, "UInt") ;2026 is "бн" .UInt is 32bits. The remaining bits will be filled with 0, creating the end char.
Break
}
If !NumGet(sTitle+0, 2*(A_Index-1), "UShort")
Break
}
DllCall("SendMessage", "Uint", hWnd, "Uint", 1057, "Uint", hlicon, "Uint", sTitle)
}
If !TextCodePage
DllCall("ntdll\RtlFillMemoryUlong", "Uint", &ti +36, "Uint", 4, "Uint", &sText)
Else If (TextCodePage = "U8")
{
;Retrieve size of the text in unicode
nSize := DllCall("MultiByteToWideChar", "Uint", 65001, "Uint", 0, "Uint", &sText, "int", -1, "Uint", 0, "int", 0)
VarSetCapacity(sU16, nSize * 2)
;Converts U8 to unicode
DllCall("MultiByteToWideChar", "Uint", 65001, "Uint", 0, "Uint", &sText, "int", -1, "Uint", &sU16, "int", nSize)
DllCall("ntdll\RtlFillMemoryUlong", "Uint", &ti +36, "Uint", 4, "Uint", &sU16)
}
Else If (TextCodePage = "U16")
DllCall("ntdll\RtlFillMemoryUlong", "Uint", &ti +36, "Uint", 4, "Uint", sText)
DllCall("SendMessage", "Uint", hWnd, "Uint", 1028, "Uint", 0, "Uint", &ti) ; TTM_ADDTOOL
WinGetPos, WinX, WinY, , , Ahk_ID %ActiveWinID%
ControlGetPos, CtrlX, CtrlY, CtrlW, CtrlH, %ActiveCtrl%, Ahk_ID %ActiveWinID%
; TTM_TRACKPOSITION
If ((CaretY < CtrlY+WinY) || (CaretY > CtrlY+CtrlH-11+WinY) || (CaretX < CtrlX+WinX) || (CaretX > CtrlX+CtrlW-4+WinX))
DllCall("SendMessage", "Uint", hWnd, "Uint", 1042, "Uint", 0, "Uint", (CtrlX+4+WinX & 0xFFFF)|(CtrlY+CtrlH-9+WinY & 0xFFFF)<<16)
Else
DllCall("SendMessage", "Uint", hWnd, "Uint", 1042, "Uint", 0, "Uint", (CaretX+4 & 0xFFFF)|(CaretY+11 & 0xFFFF)<<16)
DllCall("SendMessage", "Uint", hWnd, "Uint", 1048, "Uint", 0, "Uint", A_ScreenWidth) ; TTM_SETMAXTIPWIDTH (allows A_Tab to be displayed)
If !TextCodePage
DllCall("SendMessage", "Uint", hWnd, "Uint", 1036, "Uint", 0, "Uint", &ti) ; TTM_UPDATETIPTEXT
Else
DllCall("SendMessage", "Uint", hWnd, "Uint", 1081, "Uint", 0, "Uint", &ti) ; TTM_UPDATETIPTEXTW
DllCall("SendMessage", "Uint", hWnd, "Uint", 1041, "Uint", 1, "Uint", &ti) ; TTM_TRACKACTIVATE
;WinShow, Ahk_ID %hwnd%
SetTimer, _UpdateBalloonTip, %RefreshRate%
If MinTimeDisp
SetTimer, _MinTimeDisp, -%MinTimeDisp%
If Timeout
SetTimer, _DestroyBalloonTip, -%Timeout%
Return
_MinTimeDisp:
MinTime =
Return
_UpdateBalloonTip:
F := WinExist("A")
If (F = ActiveWinID)
{
ControlGetFocus, F, Ahk_ID %ActiveWinID%
If (F = ActiveCtrl)
{
ControlGetText, F, %F%, Ahk_ID %ActiveWinID%
coordmode, caret, screen
If ((A_CaretX = CaretX) && (A_CaretY = CaretY) && (CtrlContent = F))
Return
Else If MinTime
{
CaretX := A_CaretX
CaretY := A_CaretY
CtrlContent := F
WinGetPos, WinX, WinY, , , Ahk_ID %ActiveWinID%
ControlGetPos, CtrlX, CtrlY, CtrlW, CtrlH, %ActiveCtrl%, Ahk_ID %ActiveWinID%
If ((CaretY < CtrlY+WinY) || (CaretY > CtrlY+CtrlH-11+WinY) || (CaretX < CtrlX+WinX) || (CaretX > CtrlX+CtrlW-4+WinX))
DllCall("SendMessage", "Uint", hWnd, "Uint", 1042, "Uint", 0, "Uint", (CtrlX+4+WinX & 0xFFFF)|(CtrlY+CtrlH-9+WinY & 0xFFFF)<<16)
Else
DllCall("SendMessage", "Uint", hWnd, "Uint", 1042, "Uint", 0, "Uint", (CaretX+4 & 0xFFFF)|(CaretY+11 & 0xFFFF)<<16)
Return
}
}
}
Else If ((F = hwnd) && InStr("2|4", ClickableSave))
Return
_DestroyBalloonTip:
SetTimer, _UpdateBalloonTip, Off
SetTimer, _MinTimeDisp, Off
SetWinDelay, -1
DllCall("SendMessage", "Uint", hWnd, "Uint", 0x10, "Uint", 0)
hwnd =
Return
}