If you ever use a tooltip in a timer to show text next to the cursor, you will likely have noticed flickering of the tooltip window. The function below FFToolTip() addresses that problem. It functions much like the ToolTip command but without the flickering.
I provided two versions: a simpler one for single-monitor configurations and a more complex one for multi-monitor configurations. The later relies on the MDMF library, an updated version of which is included below. Of course, you can use the multi-monitor version for a single-monitor configuration.
For reference, Learning One posted a similar function here.
Here is the function for single-monitor configurations:
Code: Select all
; ===============================================================================================================================
; FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1)
; Function: Creates a tooltip window anywhere on the screen. Unlike the built-in ToolTip command, calling this function
; repeatedly will not cause the tooltip window to flicker. Otherwise, it behaves the same way. Use this function
; without the first three parameters, i.e. FFToolTip(), in order to hide the tooltip.
; Parameters: Text - The text to display in the tooltip. To create a multi-line tooltip, use the linefeed character (`n) in
; between each line, e.g. Line1`nLine2. If blank or omitted, the existing tooltip will be hidden.
; X - The x position of the tooltip. This position is relative to the active window, the active window's client
; area, or the entire screen depending on the coordinate mode (see the CoordMode command). In the default
; mode, the coordinates that are relative to the active window.
; Y - The y position of the tooltip. See the above X parameter for more information. If both the X and Y
; coordinates are omitted, the tooltip will be shown near the mouse cursor.
; WhichToolTip - A number between 1 and 20 to indicate which tooltip window to operate upon. If unspecified, the
; default is 1.
; Return values: None
; Global vars: None
; Dependencies: None
; Tested with: AHK 1.1.30.01 (A32/U32/U64)
; Tested on: Win 7 (x64)
; Written by: iPhilip
; ===============================================================================================================================
; MSDN Links:
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getcursorpos - GetCursorPos function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-clienttoscreen - ClientToScreen function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-movewindow - MoveWindow function
; ===============================================================================================================================
FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1) {
static ID := [], Xo, Yo, W, H, SavedText
, PID := DllCall("GetCurrentProcessId")
, _ := VarSetCapacity(Point, 8)
if (Text = "") { ; Hide the tooltip
ToolTip, , , , WhichToolTip
ID.Delete(WhichToolTip)
} else if not ID[WhichToolTip] { ; First call
ToolTip, %Text%, X, Y, WhichToolTip
ID[WhichToolTip] := WinExist("ahk_class tooltips_class32 ahk_pid " PID)
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else if (Text != SavedText) { ; The tooltip text changed
ToolTip, %Text%, X, Y, WhichToolTip
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else { ; The tooltip is being repositioned
if (Flag := X = "" || Y = "") {
DllCall("GetCursorPos", "Ptr", &Point, "Int")
MouseX := NumGet(Point, 0, "Int")
MouseY := NumGet(Point, 4, "Int")
}
;
; Convert input coordinates to screen coordinates
;
if (A_CoordModeToolTip = "Window") {
WinGetPos, WinX, WinY, , , A
X := X = "" ? MouseX + 16 : X + WinX
Y := Y = "" ? MouseY + 16 : Y + WinY
} else if (A_CoordModeToolTip = "Client") {
NumPut(X, Point, 0, "Int"), NumPut(Y, Point, 4, "Int")
DllCall("ClientToScreen", "Ptr", WinExist("A"), "Ptr", &Point, "Int")
X := X = "" ? MouseX + 16 : NumGet(Point, 0, "Int")
Y := Y = "" ? MouseY + 16 : NumGet(Point, 4, "Int")
} else { ; A_CoordModeToolTip = "Screen"
X := X = "" ? MouseX + 16 : X
Y := Y = "" ? MouseY + 16 : Y
}
;
; Deal with the bottom and right edges of the screen
;
if Flag {
X := X + W >= A_ScreenWidth ? A_ScreenWidth - W - 1 : X
Y := Y + H >= A_ScreenHeight ? A_ScreenHeight - H - 1 : Y
if (MouseX >= X && MouseX <= X + W && MouseY >= Y && MouseY <= Y + H)
X := MouseX - W - 3, Y := MouseY - H - 3
}
;
; If necessary, store the coordinates and move the tooltip window
;
if (X != Xo || Y != Yo) {
Xo := X, Yo := Y
DllCall("MoveWindow", "Ptr", ID[WhichToolTip], "Int", X, "Int", Y, "Int", W, "Int", H, "Int", false, "Int")
}
}
}
Code: Select all
#NoEnv
SetBatchLines, -1
ToolTipText := "I urge you`nto please`nnotice when`nyou are`nhappy...`n-Kurt Vonnegut"
MsgBox,
(
First, let's show a tooltip as it tracks with mouse movement
using the built-in method.`n
Notice the flickering of the tooltip window.`n
Click OK to start.
)
SetTimer, Timer1, 10
MsgBox
SetTimer, Timer1, Off
ToolTip
MsgBox,
(
Now, let's show a tooltip as it tracks with mouse movement
using the FFToolTip() function.`n
Notice the absence of any flickering and the behavior of the
tooltip window near the bottom-right corner of the screen.`n
Click OK to start.
)
SetTimer, Timer2, 10
MsgBox Click OK to exit.
SetTimer, Timer2, Off
FFToolTip()
ExitApp
Timer1:
ToolTip, %ToolTipText%
Return
Timer2:
FFToolTip(ToolTipText)
Return
Esc::ExitApp
; ===============================================================================================================================
; FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1)
; Function: Creates a tooltip window anywhere on the screen. Unlike the built-in ToolTip command, calling this function
; repeatedly will not cause the tooltip window to flicker. Otherwise, it behaves the same way. Use this function
; without the first three parameters, i.e. FFToolTip(), in order to hide the tooltip.
; Parameters: Text - The text to display in the tooltip. To create a multi-line tooltip, use the linefeed character (`n) in
; between each line, e.g. Line1`nLine2. If blank or omitted, the existing tooltip will be hidden.
; X - The x position of the tooltip. This position is relative to the active window, the active window's client
; area, or the entire screen depending on the coordinate mode (see the CoordMode command). In the default
; mode, the coordinates that are relative to the active window.
; Y - The y position of the tooltip. See the above X parameter for more information. If both the X and Y
; coordinates are omitted, the tooltip will be shown near the mouse cursor.
; WhichToolTip - A number between 1 and 20 to indicate which tooltip window to operate upon. If unspecified, the
; default is 1.
; Return values: None
; Global vars: None
; Dependencies: None
; Tested with: AHK 1.1.30.01 (A32/U32/U64)
; Tested on: Win 7 (x64)
; Written by: iPhilip
; ===============================================================================================================================
; MSDN Links:
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getcursorpos - GetCursorPos function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-clienttoscreen - ClientToScreen function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-movewindow - MoveWindow function
; ===============================================================================================================================
FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1) {
static ID := [], Xo, Yo, W, H, SavedText
, PID := DllCall("GetCurrentProcessId")
, _ := VarSetCapacity(Point, 8)
if (Text = "") { ; Hide the tooltip
ToolTip, , , , WhichToolTip
ID.Delete(WhichToolTip)
} else if not ID[WhichToolTip] { ; First call
ToolTip, %Text%, X, Y, WhichToolTip
ID[WhichToolTip] := WinExist("ahk_class tooltips_class32 ahk_pid " PID)
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else if (Text != SavedText) { ; The tooltip text changed
ToolTip, %Text%, X, Y, WhichToolTip
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else { ; The tooltip is being repositioned
if (Flag := X = "" || Y = "") {
DllCall("GetCursorPos", "Ptr", &Point, "Int")
MouseX := NumGet(Point, 0, "Int")
MouseY := NumGet(Point, 4, "Int")
}
;
; Convert input coordinates to screen coordinates
;
if (A_CoordModeToolTip = "Window") {
WinGetPos, WinX, WinY, , , A
X := X = "" ? MouseX + 16 : X + WinX
Y := Y = "" ? MouseY + 16 : Y + WinY
} else if (A_CoordModeToolTip = "Client") {
NumPut(X, Point, 0, "Int"), NumPut(Y, Point, 4, "Int")
DllCall("ClientToScreen", "Ptr", WinExist("A"), "Ptr", &Point, "Int")
X := X = "" ? MouseX + 16 : NumGet(Point, 0, "Int")
Y := Y = "" ? MouseY + 16 : NumGet(Point, 4, "Int")
} else { ; A_CoordModeToolTip = "Screen"
X := X = "" ? MouseX + 16 : X
Y := Y = "" ? MouseY + 16 : Y
}
;
; Deal with the bottom and right edges of the screen
;
if Flag {
X := X + W >= A_ScreenWidth ? A_ScreenWidth - W - 1 : X
Y := Y + H >= A_ScreenHeight ? A_ScreenHeight - H - 1 : Y
if (MouseX >= X && MouseX <= X + W && MouseY >= Y && MouseY <= Y + H)
X := MouseX - W - 3, Y := MouseY - H - 3
}
;
; If necessary, store the coordinates and move the tooltip window
;
if (X != Xo || Y != Yo) {
Xo := X, Yo := Y
DllCall("MoveWindow", "Ptr", ID[WhichToolTip], "Int", X, "Int", Y, "Int", W, "Int", H, "Int", false, "Int")
}
}
}
Code: Select all
; ===============================================================================================================================
; FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1)
; Function: Creates a tooltip window on any screen in a multiple monitor environment. Unlike the built-in ToolTip command,
; calling this function repeatedly will not cause the tooltip window to flicker. Furthermore, if one or both of
; the coordinates is not specified, the tooltip window will not hide the bottom-right corner of any of the
; monitors. Use this function without the first three parameters, i.e. FFToolTip(), in order to hide the tooltip.
; Parameters: Text - The text to display in the tooltip. To create a multi-line tooltip, use the linefeed character (`n) in
; between each line, e.g. Line1`nLine2. If blank or omitted, the existing tooltip will be hidden.
; X - The x position of the tooltip. This position is relative to the active window, the active window's client
; area, or the entire screen depending on the coordinate mode (see the CoordMode command). In the default
; mode, the coordinates that are relative to the active window.
; Y - The y position of the tooltip. See the above X parameter for more information. If both the X and Y
; coordinates are omitted, the tooltip will be shown near the mouse cursor.
; WhichToolTip - A number between 1 and 20 to indicate which tooltip window to operate upon. If unspecified, the
; default is 1.
; Return values: None
; Global vars: None
; Dependencies: MDMF Library (https://www.autohotkey.com/boards/viewtopic.php?p=266388#p266388)
; Tested with: AHK 1.1.30.01 (A32/U32/U64)
; Tested on: Win 7 (x64)
; Written by: iPhilip
; ===============================================================================================================================
; MSDN Links:
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getcursorpos - GetCursorPos function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-clienttoscreen - ClientToScreen function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-movewindow - MoveWindow function
; ===============================================================================================================================
FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1) {
static ID := [], Xo, Yo, W, H, SavedText, Monitors
, PID := DllCall("GetCurrentProcessId")
, _ := VarSetCapacity(Point, 8)
if not Monitors
Monitors := MDMF_Enum()
if (Text = "") { ; Hide the tooltip
ToolTip, , , , WhichToolTip
ID.Delete(WhichToolTip)
} else if not ID[WhichToolTip] { ; First call
ToolTip, %Text%, X, Y, WhichToolTip
ID[WhichToolTip] := WinExist("ahk_class tooltips_class32 ahk_pid " PID)
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else if (Text != SavedText) { ; The tooltip text changed
ToolTip, %Text%, X, Y, WhichToolTip
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else { ; The tooltip is being repositioned
if (Flag := X = "" || Y = "") {
DllCall("GetCursorPos", "Ptr", &Point, "Int")
MouseX := NumGet(Point, 0, "Int"), MouseY := NumGet(Point, 4, "Int")
hMonitor := MDMF_FromPoint(X = "" ? MouseX : X, Y = "" ? MouseY : Y)
MonitorRight := Monitors[hMonitor].Right
MonitorBottom := Monitors[hMonitor].Bottom
}
;
; Convert input coordinates to screen coordinates
;
if (A_CoordModeToolTip = "Window") {
WinGetPos, WinX, WinY, , , A
X := X = "" ? MouseX + 16 : X + WinX
Y := Y = "" ? MouseY + 16 : Y + WinY
} else if (A_CoordModeToolTip = "Client") {
NumPut(X, Point, 0, "Int"), NumPut(Y, Point, 4, "Int")
DllCall("ClientToScreen", "Ptr", WinExist("A"), "Ptr", &Point, "Int")
X := X = "" ? MouseX + 16 : NumGet(Point, 0, "Int")
Y := Y = "" ? MouseY + 16 : NumGet(Point, 4, "Int")
} else { ; A_CoordModeToolTip = "Screen"
X := X = "" ? MouseX + 16 : X
Y := Y = "" ? MouseY + 16 : Y
}
;
; Deal with the bottom and right edges of the current monitor
;
if Flag {
X := X + W >= MonitorRight ? MonitorRight - W - 1 : X
Y := Y + H >= MonitorBottom ? MonitorBottom - H - 1 : Y
if (MouseX >= X && MouseX <= X + W && MouseY >= Y && MouseY <= Y + H)
X := MouseX - W - 3, Y := MouseY - H - 3
}
;
; If necessary, store the coordinates and move the tooltip window
;
if (X != Xo || Y != Yo) {
Xo := X, Yo := Y
DllCall("MoveWindow", "Ptr", ID[WhichToolTip], "Int", X, "Int", Y, "Int", W, "Int", H, "Int", false, "Int")
}
}
}
; ----------------------------------------------------------------------------------------------------------------------
; Name ..........: MDMF - Multiple Display Monitor Functions
; Description ...: Various functions for multiple display monitor environments
; Tested with ...: AHK 1.1.30.01 (A32/U32/U64) and 2.0-a100-52515e2 (U32/U64)
; Original Author: just me (https://www.autohotkey.com/boards/viewtopic.php?f=6&t=4606)
; Mod Author ....: iPhilip
; Changes .......: Modified MDMF_Enum() so that it works under both AHK v1 and v2.
; ................ Modified MDMF_EnumProc() to provide Count and Primary keys to the Monitors array.
; ................ Modified MDMF_FromHWND() to allow flag values that determine the function's return value if the
; ................ window does not intersect any display monitor.
; ................ Modified MDMF_FromPoint() to allow the cursor position to be returned ByRef if not specified and
; ................ allow flag values that determine the function's return value if the point is not contained within
; ................ any display monitor.
; ................ Modified MDMF_FromRect() to allow flag values that determine the function's return value if the
; ................ rectangle does not intersect any display monitor.
;................. Modified MDMF_GetInfo() with minor changes.
; ----------------------------------------------------------------------------------------------------------------------
;
; ======================================================================================================================
; Multiple Display Monitors Functions -> msdn.microsoft.com/en-us/library/dd145072(v=vs.85).aspx =======================
; ======================================================================================================================
; Enumerates display monitors and returns an object containing the properties of all monitors or the specified monitor.
; ======================================================================================================================
MDMF_Enum(HMON := "") {
Static CallbackFunc := Func(A_AhkVersion < "2" ? "RegisterCallback" : "CallbackCreate")
Static EnumProc := CallbackFunc.Call("MDMF_EnumProc")
Static Monitors := {}
If (HMON = "") ; new enumeration
Monitors := {Count: 0}
If (Monitors.MaxIndex() = "") ; enumerate
If !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", &Monitors, "Int")
Return False
Return (HMON = "") ? Monitors : Monitors.HasKey(HMON) ? Monitors[HMON] : False
}
; ======================================================================================================================
; Callback function that is called by the MDMF_Enum function.
; ======================================================================================================================
MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) {
Monitors := Object(ObjectAddr)
Monitors[HMON] := MDMF_GetInfo(HMON)
Monitors.Count++
If (Monitors[HMON].Primary)
Monitors.Primary := HMON
Return True
}
; ======================================================================================================================
; Retrieves the display monitor that has the largest area of intersection with a specified window.
; The following flag values determine the function's return value if the window does not intersect any display monitor:
; MONITOR_DEFAULTTONULL = 0 - Returns NULL.
; MONITOR_DEFAULTTOPRIMARY = 1 - Returns a handle to the primary display monitor.
; MONITOR_DEFAULTTONEAREST = 2 - Returns a handle to the display monitor that is nearest to the window.
; ======================================================================================================================
MDMF_FromHWND(HWND, Flag := 0) {
Return DllCall("User32.dll\MonitorFromWindow", "Ptr", HWND, "UInt", Flag, "Ptr")
}
; ======================================================================================================================
; Retrieves the display monitor that contains a specified point.
; If either X or Y is empty, the function will use the current cursor position for this value and return it ByRef.
; The following flag values determine the function's return value if the point is not contained within any
; display monitor:
; MONITOR_DEFAULTTONULL = 0 - Returns NULL.
; MONITOR_DEFAULTTOPRIMARY = 1 - Returns a handle to the primary display monitor.
; MONITOR_DEFAULTTONEAREST = 2 - Returns a handle to the display monitor that is nearest to the window.
; ======================================================================================================================
MDMF_FromPoint(ByRef X := "", ByRef Y := "", Flag := 0) {
If (X = "") || (Y = "") {
VarSetCapacity(PT, 8, 0)
DllCall("User32.dll\GetCursorPos", "Ptr", &PT, "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")
}
; ======================================================================================================================
; Retrieves the display monitor that has the largest area of intersection with a specified rectangle.
; Parameters are consistent with the common AHK definition of a rectangle, which is X, Y, W, H instead of
; Left, Top, Right, Bottom.
; The following flag values determine the function's return value if the rectangle does not intersect any
; display monitor:
; MONITOR_DEFAULTTONULL = 0 - Returns NULL.
; MONITOR_DEFAULTTOPRIMARY = 1 - Returns a handle to the primary display monitor.
; MONITOR_DEFAULTTONEAREST = 2 - Returns a handle to the display monitor that is nearest to the window.
; ======================================================================================================================
MDMF_FromRect(X, Y, W, H, Flag := 0) {
VarSetCapacity(RC, 16, 0)
NumPut(X, RC, 0, "Int"), NumPut(Y, RC, 4, "Int"), NumPut(X + W, RC, 8, "Int"), NumPut(Y + H, RC, 12, "Int")
Return DllCall("User32.dll\MonitorFromRect", "Ptr", &RC, "UInt", Flag, "Ptr")
}
; ======================================================================================================================
; Retrieves information about a display monitor.
; ======================================================================================================================
MDMF_GetInfo(HMON) {
NumPut(VarSetCapacity(MIEX, 40 + (32 << !!A_IsUnicode)), MIEX, 0, "UInt")
If DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", &MIEX, "Int")
Return {Name: (Name := StrGet(&MIEX + 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
}
Code: Select all
#NoEnv
SetBatchLines, -1
ToolTipText := "I urge you`nto please`nnotice when`nyou are`nhappy...`n-Kurt Vonnegut"
MsgBox,
(
First, let's show a tooltip as it tracks with mouse movement
using the built-in method.`n
Notice the flickering of the tooltip window.`n
Click OK to start.
)
SetTimer, Timer1, 10
MsgBox
SetTimer, Timer1, Off
ToolTip
MsgBox,
(
Now, let's show a tooltip as it tracks with mouse movement
using the FFToolTip() function.`n
Notice the absence of any flickering and the behavior of the
tooltip window near the bottom-right corner of the screen.`n
Click OK to start.
)
SetTimer, Timer2, 10
MsgBox Click OK to exit.
SetTimer, Timer2, Off
FFToolTip()
ExitApp
Timer1:
ToolTip, %ToolTipText%
Return
Timer2:
FFToolTip(ToolTipText)
Return
Esc::ExitApp
; ===============================================================================================================================
; FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1)
; Function: Creates a tooltip window on any screen in a multiple monitor environment. Unlike the built-in ToolTip command,
; calling this function repeatedly will not cause the tooltip window to flicker. Furthermore, if one or both of
; the coordinates is not specified, the tooltip window will not hide the bottom-right corner of any of the
; monitors. Use this function without the first three parameters, i.e. FFToolTip(), in order to hide the tooltip.
; Parameters: Text - The text to display in the tooltip. To create a multi-line tooltip, use the linefeed character (`n) in
; between each line, e.g. Line1`nLine2. If blank or omitted, the existing tooltip will be hidden.
; X - The x position of the tooltip. This position is relative to the active window, the active window's client
; area, or the entire screen depending on the coordinate mode (see the CoordMode command). In the default
; mode, the coordinates that are relative to the active window.
; Y - The y position of the tooltip. See the above X parameter for more information. If both the X and Y
; coordinates are omitted, the tooltip will be shown near the mouse cursor.
; WhichToolTip - A number between 1 and 20 to indicate which tooltip window to operate upon. If unspecified, the
; default is 1.
; Return values: None
; Global vars: None
; Dependencies: MDMF Library (https://www.autohotkey.com/boards/viewtopic.php?p=266388#p266388)
; Tested with: AHK 1.1.30.01 (A32/U32/U64)
; Tested on: Win 7 (x64)
; Written by: iPhilip
; ===============================================================================================================================
; MSDN Links:
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getcursorpos - GetCursorPos function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-clienttoscreen - ClientToScreen function
; https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-movewindow - MoveWindow function
; ===============================================================================================================================
FFToolTip(Text:="", X:="", Y:="", WhichToolTip:=1) {
static ID := [], Xo, Yo, W, H, SavedText, Monitors
, PID := DllCall("GetCurrentProcessId")
, _ := VarSetCapacity(Point, 8)
if not Monitors
Monitors := MDMF_Enum()
if (Text = "") { ; Hide the tooltip
ToolTip, , , , WhichToolTip
ID.Delete(WhichToolTip)
} else if not ID[WhichToolTip] { ; First call
ToolTip, %Text%, X, Y, WhichToolTip
ID[WhichToolTip] := WinExist("ahk_class tooltips_class32 ahk_pid " PID)
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else if (Text != SavedText) { ; The tooltip text changed
ToolTip, %Text%, X, Y, WhichToolTip
WinGetPos, , , W, H, % "ahk_id " ID[WhichToolTip]
SavedText := Text
} else { ; The tooltip is being repositioned
if (Flag := X = "" || Y = "") {
DllCall("GetCursorPos", "Ptr", &Point, "Int")
MouseX := NumGet(Point, 0, "Int"), MouseY := NumGet(Point, 4, "Int")
hMonitor := MDMF_FromPoint(X = "" ? MouseX : X, Y = "" ? MouseY : Y)
MonitorRight := Monitors[hMonitor].Right
MonitorBottom := Monitors[hMonitor].Bottom
}
;
; Convert input coordinates to screen coordinates
;
if (A_CoordModeToolTip = "Window") {
WinGetPos, WinX, WinY, , , A
X := X = "" ? MouseX + 16 : X + WinX
Y := Y = "" ? MouseY + 16 : Y + WinY
} else if (A_CoordModeToolTip = "Client") {
NumPut(X, Point, 0, "Int"), NumPut(Y, Point, 4, "Int")
DllCall("ClientToScreen", "Ptr", WinExist("A"), "Ptr", &Point, "Int")
X := X = "" ? MouseX + 16 : NumGet(Point, 0, "Int")
Y := Y = "" ? MouseY + 16 : NumGet(Point, 4, "Int")
} else { ; A_CoordModeToolTip = "Screen"
X := X = "" ? MouseX + 16 : X
Y := Y = "" ? MouseY + 16 : Y
}
;
; Deal with the bottom and right edges of the current monitor
;
if Flag {
X := X + W >= MonitorRight ? MonitorRight - W - 1 : X
Y := Y + H >= MonitorBottom ? MonitorBottom - H - 1 : Y
if (MouseX >= X && MouseX <= X + W && MouseY >= Y && MouseY <= Y + H)
X := MouseX - W - 3, Y := MouseY - H - 3
}
;
; If necessary, store the coordinates and move the tooltip window
;
if (X != Xo || Y != Yo) {
Xo := X, Yo := Y
DllCall("MoveWindow", "Ptr", ID[WhichToolTip], "Int", X, "Int", Y, "Int", W, "Int", H, "Int", false, "Int")
}
}
}
; ----------------------------------------------------------------------------------------------------------------------
; Name ..........: MDMF - Multiple Display Monitor Functions
; Description ...: Various functions for multiple display monitor environments
; Tested with ...: AHK 1.1.30.01 (A32/U32/U64) and 2.0-a100-52515e2 (U32/U64)
; Original Author: just me (https://www.autohotkey.com/boards/viewtopic.php?f=6&t=4606)
; Mod Author ....: iPhilip
; Changes .......: Modified MDMF_Enum() so that it works under both AHK v1 and v2.
; ................ Modified MDMF_EnumProc() to provide Count and Primary keys to the Monitors array.
; ................ Modified MDMF_FromHWND() to allow flag values that determine the function's return value if the
; ................ window does not intersect any display monitor.
; ................ Modified MDMF_FromPoint() to allow the cursor position to be returned ByRef if not specified and
; ................ allow flag values that determine the function's return value if the point is not contained within
; ................ any display monitor.
; ................ Modified MDMF_FromRect() to allow flag values that determine the function's return value if the
; ................ rectangle does not intersect any display monitor.
;................. Modified MDMF_GetInfo() with minor changes.
; ----------------------------------------------------------------------------------------------------------------------
;
; ======================================================================================================================
; Multiple Display Monitors Functions -> msdn.microsoft.com/en-us/library/dd145072(v=vs.85).aspx =======================
; ======================================================================================================================
; Enumerates display monitors and returns an object containing the properties of all monitors or the specified monitor.
; ======================================================================================================================
MDMF_Enum(HMON := "") {
Static CallbackFunc := Func(A_AhkVersion < "2" ? "RegisterCallback" : "CallbackCreate")
Static EnumProc := CallbackFunc.Call("MDMF_EnumProc")
Static Monitors := {}
If (HMON = "") ; new enumeration
Monitors := {Count: 0}
If (Monitors.MaxIndex() = "") ; enumerate
If !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", &Monitors, "Int")
Return False
Return (HMON = "") ? Monitors : Monitors.HasKey(HMON) ? Monitors[HMON] : False
}
; ======================================================================================================================
; Callback function that is called by the MDMF_Enum function.
; ======================================================================================================================
MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) {
Monitors := Object(ObjectAddr)
Monitors[HMON] := MDMF_GetInfo(HMON)
Monitors.Count++
If (Monitors[HMON].Primary)
Monitors.Primary := HMON
Return True
}
; ======================================================================================================================
; Retrieves the display monitor that has the largest area of intersection with a specified window.
; The following flag values determine the function's return value if the window does not intersect any display monitor:
; MONITOR_DEFAULTTONULL = 0 - Returns NULL.
; MONITOR_DEFAULTTOPRIMARY = 1 - Returns a handle to the primary display monitor.
; MONITOR_DEFAULTTONEAREST = 2 - Returns a handle to the display monitor that is nearest to the window.
; ======================================================================================================================
MDMF_FromHWND(HWND, Flag := 0) {
Return DllCall("User32.dll\MonitorFromWindow", "Ptr", HWND, "UInt", Flag, "Ptr")
}
; ======================================================================================================================
; Retrieves the display monitor that contains a specified point.
; If either X or Y is empty, the function will use the current cursor position for this value and return it ByRef.
; The following flag values determine the function's return value if the point is not contained within any
; display monitor:
; MONITOR_DEFAULTTONULL = 0 - Returns NULL.
; MONITOR_DEFAULTTOPRIMARY = 1 - Returns a handle to the primary display monitor.
; MONITOR_DEFAULTTONEAREST = 2 - Returns a handle to the display monitor that is nearest to the window.
; ======================================================================================================================
MDMF_FromPoint(ByRef X := "", ByRef Y := "", Flag := 0) {
If (X = "") || (Y = "") {
VarSetCapacity(PT, 8, 0)
DllCall("User32.dll\GetCursorPos", "Ptr", &PT, "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")
}
; ======================================================================================================================
; Retrieves the display monitor that has the largest area of intersection with a specified rectangle.
; Parameters are consistent with the common AHK definition of a rectangle, which is X, Y, W, H instead of
; Left, Top, Right, Bottom.
; The following flag values determine the function's return value if the rectangle does not intersect any
; display monitor:
; MONITOR_DEFAULTTONULL = 0 - Returns NULL.
; MONITOR_DEFAULTTOPRIMARY = 1 - Returns a handle to the primary display monitor.
; MONITOR_DEFAULTTONEAREST = 2 - Returns a handle to the display monitor that is nearest to the window.
; ======================================================================================================================
MDMF_FromRect(X, Y, W, H, Flag := 0) {
VarSetCapacity(RC, 16, 0)
NumPut(X, RC, 0, "Int"), NumPut(Y, RC, 4, "Int"), NumPut(X + W, RC, 8, "Int"), NumPut(Y + H, RC, 12, "Int")
Return DllCall("User32.dll\MonitorFromRect", "Ptr", &RC, "UInt", Flag, "Ptr")
}
; ======================================================================================================================
; Retrieves information about a display monitor.
; ======================================================================================================================
MDMF_GetInfo(HMON) {
NumPut(VarSetCapacity(MIEX, 40 + (32 << !!A_IsUnicode)), MIEX, 0, "UInt")
If DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", &MIEX, "Int")
Return {Name: (Name := StrGet(&MIEX + 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
}
AHK v2 versions
I hope you find it useful.
- iPhilip