GuiCtrlTips.ahk:
Code: Select all
#Requires AutoHotkey v2.0
; ======================================================================================================================
; Release date: 2023-05-28
; ======================================================================================================================
; Add tooltips to your Gui controls.
; Tooltips are managed per Gui, so you first have to create a new instance of the class for the Gui, e.g.:
; MyGui := Gui()
; MyGui.Tips := GuiCtrlTips(MyGui)
; Then you can create your controls and add tooltips, e.g.:
; MyBtn := MyGui.AddButton(...)
; MyGui.Tips.SetTip(MyBtn, "My Tooltip!")
; You can activate, deactivate, or change the delay times for all tooltips at any time by calling the corresponding
; methods.
; To remove a tooltip from a single control pass an empty text, e.g.:
; MyGui.Tips.SetTip(MyBtn, "")
; Text and Picture controls require the SS_NOTIFY (+0x0100) style or a 'Click' event function.
; ----------------------------------------------------------------------------------------------------------------------
; Tooltip control: https://learn.microsoft.com/en-us/windows/win32/controls/tooltip-control-reference
; ======================================================================================================================
Class GuiCtrlTips {
Static TOOLINFO {
Get {
Static SizeOfTI := 24 + (A_PtrSize * 6)
Local TI := Buffer(SizeOfTI, 0)
NumPut("UInt", SizeOfTI, TI)
Return TI
}
}
; -------------------------------------------------------------------------------------------------------------------
Static ToolTipFont {
Get {
Static SizeOfLFW := 92 ; LOGFONTW structure
Static SizeOfNCM := 44 + (SizeOfLFW * 5) ; NONCLIENTMETRICSW structure
Static OffStatusFont := 40 + (SizeOfLFW * 3) ; lfStatusFont
Static LOGFONTW := 0
If !IsObject(LOGFONTW) { ; first call
Local NCM := Buffer(SizeOfNCM, 0)
NumPut("UInt", SizeOfNCM, NCM)
DllCall("SystemParametersInfoW", "UInt", 0x0029, "UInt", 0, "Ptr", NCM.Ptr, "UInt", 0) ; SPI_GETNONCLIENTMETRICS
LOGFONTW := Buffer(SizeOfLFW, 0)
DllCall("RtlMoveMemory", "Ptr", LOGFONTW.Ptr, "Ptr", NCM.Ptr + OffStatusFont, "Ptr", SizeOfLFW)
}
Local LF := Buffer(SizeOfLFW, 0)
DllCall("RtlMoveMemory", "Ptr", LF.Ptr, "Ptr", LOGFONTW.Ptr, "Ptr", SizeOfLFW)
Return LF
}
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-addtool
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-setmaxtipwidth
; https://learn.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-tttoolinfow
__New(GuiObj, UseAhkStyle := True, UseComboEdit := True) {
Local Flags, HGUI, HTIP, TI
If !(GuiObj Is Gui)
Throw TypeError(A_ThisFunc . ": Expected a Gui object!", -1 "GuiObj")
HGUI := GuiObj.Hwnd
; Create the TOOLINFO structure
Flags := 0x11 ; TTF_SUBCLASS | TTF_IDISHWND
TI := GuiCtrlTips.TOOLINFO
NumPut("UInt", Flags, "UPtr", HGUI, "UPtr", HGUI, TI, 4) ; uFlags, hwnd, uID
; Create a tooltip control for this Gui
If !(HTIP := DllCall("CreateWindowEx", "UInt", 0, "Str", "tooltips_class32", "Ptr", 0, "UInt", 0x80000003
, "Int", 0x80000000, "Int", 0x80000000, "Int", 0x80000000, "Int", 0x80000000
, "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "UPtr"))
Throw Error(A_ThisFunc . ": Could not create a tooltip control", -1)
If (UseAhkStyle)
DllCall("Uxtheme.dll\SetWindowTheme", "Ptr", HTIP, "Ptr", 0, "Str", " ")
SendMessage(0x0418, 0, A_ScreenWidth, HTIP) ; TTM_SETMAXTIPWIDTH
; SendMessage(0x0432, 0, TI.Ptr, HTIP) ; TTM_ADDTOOLW <--- doesn't seem required any more
This.DefineProp("HTIP", {Get: (*) => HTIP})
This.DefineProp("HGUI", {Get: (*) => HGUI})
This.DefineProp("UAS", {Get: (*) => !!UseAhkStyle})
This.DefineProp("UCE", {Get: (*) => !!UseComboEdit})
This.Ctrls := Map()
}
; -------------------------------------------------------------------------------------------------------------------
__Delete() {
If This.HasProp("HTIP")
DllCall("DestroyWindow", "Ptr", This.HTIP)
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-activate
Activate() {
SendMessage(0x0401, True, 0, This.HTIP) ; TTM_ACTIVATE
Return True
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-activate
Deactivate() {
SendMessage(0x0401, False, 0, This.HTIP) ; TTM_ACTIVATE
Return True
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-settipbkcolor
SetBkColor(Color) {
If IsInteger(Color) {
Color := ((Color & 0x0000FF) << 16) | (Color & 0x00FF00) | ((Color & 0xFF0000) >> 16)
SendMessage(0x0413, Color, 0, This.HTIP)
}
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-setdelaytime
; Flag - one of the string keys defined in Flags
; MilliSecs - time in millisecons, pass -1 to reset to the default
SetDelayTime(Flag, MilliSecs) {
Static Flags := {AUTOMATIC: 0, AUTOPOP: 2, INITIAL: 3, RESHOW: 1}
If !Flags.HasProp(Flag) || !IsInteger(MilliSecs)
Return False
If (MilliSecs < -1)
MilliSecs := -1
SendMessage(0x0403, Flags.%Flag%, MilliSecs, This.HTIP)
Return True
}
; -------------------------------------------------------------------------------------------------------------------
; Any value passed in Bold and Italic will set the related font option
SetFont(FontSize?, FontName?, Bold?, Italic?) {
Static LOGPIXELSY := 0, PrevFont := 0
If (LOGPIXELSY = 0) { ; first call
Local HDC := DllCall("GetDC", "Ptr", 0, "UPtr")
LOGPIXELSY := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 90, "Int") ; LOGPIXELSY
DllCall("ReleaseDC", "Ptr", 0, "Ptr", HDC)
}
Local LOGFONT := GuiCtrlTips.ToolTipFont
If IsSet(FontSize) && IsNumber(FontSize)
NumPut("Int", -Round(FontSize * LOGPIXELSY / 72), LOGFONT)
If IsSet(Bold)
NumPut("Int", 700, LOGFONT, 16)
If IsSet(Italic)
NumPut("UChar", 1, LOGFONT, 20)
If IsSet(FontName)
StrPut(FontName, LOGFONT.Ptr + 28, 32)
Local HFONT := DllCall("CreateFontIndirectW", "Ptr", LOGFONT.Ptr, "UPtr")
SendMessage(0x0030, HFONT, 1, This.HTIP)
If PrevFont
DllCall("DeleteObject", "Ptr", PrevFont)
PrevFont := HFONT
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-setmargin
SetMargins(L := 0, T := 0, R := 0, B := 0) {
RC := Buffer(16, 0)
NumPut("Int", L, "Int", T, "Int", R, "Int", B, RC)
SendMessage(0x041A, 0, RC.Ptr, This.HTIP)
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-addtool
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-deltool
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-updatetiptext
SetTip(GuiCtrl, TipText, CenterTip := False) {
Local Flags, HCTL, TI
; Check the passed GuiCtrl
If !(GuiCtrl Is Gui.Control) || (GuiCtrl.Gui.Hwnd != This.HGUI)
Return False
If (GuiCtrl.Type = "ComboBox") && This.UCE ; use the Edit control of the Combobox
HCTL := DllCall("FindWindowExW", "Ptr", GuiCtrl.Hwnd, "Ptr", 0, "Ptr", 0, "Ptr", 0, "UPtr")
Else
HCTL := GuiCtrl.Hwnd
; Create the TOOLINFO structure
Flags := 0x11 | (CenterTip ? 0x02 : 0x00) ; TTF_SUBCLASS | TTF_IDISHWND [| TTF_CENTERTIP]
TI := GuiCtrlTips.TOOLINFO
NumPut("UInt", Flags, "UPtr", This.HGUI, "UPtr", HCTL, TI, 4) ; cbSize, uFlags, hwnd, uID
If (TipText = "") {
If This.Ctrls.Has(HCTL) {
SendMessage(0x0433, 0, TI.Ptr, This.HTIP) ; TTM_DELTOOLW
This.Ctrls.Delete(HCTL)
}
Return True
}
If !This.Ctrls.Has(HCTL) {
SendMessage(0x0432, 0, TI.Ptr, This.HTIP) ; TTM_ADDTOOLW
This.Ctrls[HCTL] := True
}
; Set / Update the tool's text.
NumPut("UPtr", StrPtr(TipText), TI, 24 + (A_PtrSize * 3)) ; lpszText
SendMessage(0x0439, 0, TI.Ptr, This.HTIP) ; TTM_UPDATETIPTEXTW
Return True
}
; -------------------------------------------------------------------------------------------------------------------
; https://learn.microsoft.com/en-us/windows/win32/controls/ttm-settiptextcolor
SetTxColor(Color) {
If IsInteger(Color) {
Color := ((Color & 0x0000FF) << 16) | (Color & 0x00FF00) | ((Color & 0xFF0000) >> 16)
SendMessage(0x0414, Color, 0, This.HTIP)
}
}
}
; ======================================================================================================================
Code: Select all
#Requires AutoHotkey v2.0
; ======================================================================================================================
; Release date: 2023-05-28
; ======================================================================================================================
#Include GuiCtrlTips.ahk
MainGui := Gui( , "ToolTips")
MainGui.Tips := GuiCtrlTips(MainGui) ; create a GuiCtrlTips instance
MainGui.Tips.SetBkColor(0x404040) ; set the background colour
MainGui.Tips.SetTxColor(0xFFFFFF) ; set the text colour
MainGui.Tips.SetMargins(4, 4, 4, 4) ; set the tect margins
MainGui.Tips.SetFont(24, "Arial", , 1) ; set the font
MainGui.MarginX := 10
MainGui.MarginY := 10
MainBtn1 := MainGui.AddButton("w200 h20", "Button1")
MainBtn1.OnEvent("Click", ToggleBtn1)
MainGui.Tips.SetTip(MainBtn1, "Click me first")
Btn1TT := True
MainBtn2 := MainGui.AddButton("w200 h20", "Button2")
MainGui.Tips.SetTip(MainBtn2, "I'm button 2!`r`nI show a multi-line tooltip.`r`nLet's see, how long it is shown!")
MainBtn3 := MainGui.AddButton("w200 h20", "Show 10 seconds!")
MainBtn3.OnEvent("Click", (*) => MainGui.Tips.SetDelayTime("AUTOPOP", 10000))
MainBtn4 := MainGui.AddButton("w200 h20", "Default time!")
MainBtn4.OnEvent("Click", (*) => MainGui.Tips.SetDelayTime("AUTOPOP", -1))
MainTxt5 := MainGui.AddText("w200 h20 +0x0100 Border", "Text")
MainGui.Tips.SetTip(MainTxt5, "Gotcha!")
MainBtn6 := MainGui.AddButton("w200 h20 ym", "Button6")
MainBtn7 := MainGui.AddButton("w200 h20", "Button7")
MainBtn8 := MainGui.AddButton("w200 h20", "Avtivate!")
MainBtn8.OnEvent("Click", (*) => MainGui.Tips.Activate())
MainBtn9 := MainGui.AddButton("w200 h20", "Deactivate!")
MainBtn9.OnEvent("Click", (*) => MainGui.Tips.Deactivate())
MainEdt0 := MainGui.AddEdit("w200 h20", "Edit")
MainGui.Tips.SetTip(MainEdt0, "Finally we have an Edit!", True)
MainCBB1 := MainGui.AddComboBox("xm w410", ["Red", "Green", "Blue"])
MainGui.Tips.SetTip(MainCBB1, "That's the tooltip of a ComboBox")
MainGui.Show()
; ----------------------------------------------------------------------------------------------------------------------
ToggleBtn1(*) {
Global Btn1TT := !Btn1TT
MainGui.Tips.SetTip(MainBtn1, Btn1TT ? "Click me first" : "")
}
Edit: Sources on GitHub