Class ToolTipOptions - 2024-03-17

Post your working scripts, libraries and tools.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Class ToolTipOptions - 2024-03-17

Post by just me » 30 Jan 2023, 10:20

Just for fun!

This class was inspired by @lexikos' ToolTipFont / ToolTipColor - options for the ToolTip command.
Different from that it's using Global Subclassing for the ToolTip controls created by the script.

How to use:
  • You must call ToolTipOptions.Init() to initialize the subclassing before you can set options.
  • Now you can call any of the other methods to set the options you want to be used by newly created tooltips. You'll find the descriptions within the class code.
  • All changes will be applied only to newly created tooltips.
  • If you don't want to use options any more, call ToolTipOptions.Reset() to remove the subclassing.
Enjoy!

Change history:

ToolTipOptions.ahk (2024-03-17):

Code: Select all

; ======================================================================================================================
; ToolTipOptions        -  additional options for ToolTips
;
; Tooltip control       -> https://learn.microsoft.com/en-us/windows/win32/controls/tooltip-control-reference
; TTM_SETMARGIN         = 1050
; TTM_SETTIPBKCOLOR     = 1043
; TTM_SETTIPTEXTCOLOR   = 1044
; TTM_SETTITLEW         = 1057
; WM_SETFONT            = 0x30
; SetClassLong()        -> https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclasslongw
; ======================================================================================================================
Class ToolTipOptions {
   ; -------------------------------------------------------------------------------------------------------------------
   Static HTT := DllCall("User32.dll\CreateWindowEx", "UInt", 8, "Str", "tooltips_class32", "Ptr", 0, "UInt", 3
                       , "Int", 0, "Int", 0, "Int", 0, "Int", 0, "Ptr", A_ScriptHwnd, "Ptr", 0, "Ptr", 0, "Ptr", 0)
   Static SWP := CallbackCreate(ObjBindMethod(ToolTipOptions, "_WNDPROC_"), , 4) ; subclass window proc
   Static OWP := 0                                                               ; original window proc
   Static ToolTips := Map()
   ; -------------------------------------------------------------------------------------------------------------------
   Static BkgColor := ""
   Static TktColor := ""
   Static Icon := ""
   Static Title := ""
   Static HFONT := 0
   Static Margins := ""
   ; -------------------------------------------------------------------------------------------------------------------
   Static Call(*) => False ; do not create instances
   ; -------------------------------------------------------------------------------------------------------------------
   ; Init()          -  Initialize some class variables and subclass the tooltip control.
   ; -------------------------------------------------------------------------------------------------------------------
   Static Init() {
      If (This.OWP = 0) {
         This.BkgColor := ""
         This.TktColor := ""
         This.Icon := ""
         This.Title := ""
         This.Margins := ""
         If (A_PtrSize = 8)
            This.OWP := DllCall("User32.dll\SetClassLongPtr", "Ptr", This.HTT, "Int", -24, "Ptr", This.SWP, "UPtr")
         Else
            This.OWP := DllCall("User32.dll\SetClassLongW", "Ptr", This.HTT, "Int", -24, "Int", This.SWP, "UInt")
         OnExit(ToolTipOptions._EXIT_, -1)
         Return This.OWP
      }
      Else
         Return False
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ;  Reset()        -  Close all existing tooltips, delete the font object, and remove the tooltip's subclass.
   ; -------------------------------------------------------------------------------------------------------------------
   Static Reset() {
      If (This.OWP != 0) {
         For HWND In This.ToolTips.Clone()
            DllCall("DestroyWindow", "Ptr", HWND)
         This.ToolTips.Clear()
         If This.HFONT
            DllCall("DeleteObject", "Ptr", This.HFONT)
         This.HFONT := 0
         If (A_PtrSize = 8)
            DllCall("User32.dll\SetClassLongPtrW", "Ptr", This.HTT, "Int", -24, "Ptr", This.OWP, "UPtr")
         Else
            DllCall("User32.dll\SetClassLongW", "Ptr", This.HTT, "Int", -24, "Int", This.OWP, "UInt")
         This.OWP := 0
         Return True
      }
      Else
         Return False
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetColors()     -  Set or remove the text and/or the background color for the tooltip.
   ; Parameters:
   ;     BkgColor    -  color value like used in Gui.BackColor(...)
   ;     TxtColor    -  see above.
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetColors(BkgColor := "", TxtColor := "") {
      This.BkgColor := BkgColor = "" ? "" : BGR(BkgColor)
      This.TxtColor := TxtColor = "" ? "" : BGR(TxtColor)
      BGR(Color, Default := "") { ; converts colors to BGR
         ; HTML Colors (BGR)
         Static HTML := {AQUA:   0xFFFF00, BLACK: 0x000000, BLUE:   0xFF0000, FUCHSIA: 0xFF00FF, GRAY:  0x808080,
                         GREEN:  0x008000, LIME:  0x00FF00, MAROON: 0x000080, NAVY:    0x800000, OLIVE: 0x008080,
                         PURPLE: 0x800080, RED:   0x0000FF, SILVER: 0xC0C0C0, TEAL:    0x808000, WHITE: 0xFFFFFF,
                         YELLOW: 0x00FFFF}
         If HTML.HasProp(Color)
            Return HTML.%Color%
         If (Color Is String) && IsXDigit(Color) && (StrLen(Color) = 6)
            Color := Integer("0x" . Color)
         If IsInteger(Color)
            Return ((Color >> 16) & 0xFF) | (Color & 0x00FF00) | ((Color & 0xFF) << 16)
         Return Default
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetFont()       -  Set or remove the font used by the tooltip.
   ; Parameters:
   ;     FntOpts     -  font options like Gui.SetFont(Options, ...)
   ;     FntName     -  font name like Gui.SetFont(..., Name)
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetFont(FntOpts := "", FntName := "") {
      Static HDEF := DllCall("GetStockObject", "Int", 17, "UPtr") ; DEFAULT_GUI_FONT
      Static LOGFONTW := 0
      If (FntOpts = "") && (FntName = "") {
         If This.HFONT
            DllCall("DeleteObject", "Ptr", This.HFONT)
         This.HFONT := 0
         LOGFONTW := 0
      }
      Else {
         If (LOGFONTW = 0) {
            LOGFONTW := Buffer(92, 0)
            DllCall("GetObject", "Ptr", HDEF, "Int", 92, "Ptr", LOGFONTW)
         }
         HDC := DllCall("GetDC", "Ptr", 0, "UPtr")
         LOGPIXELSY := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 90, "Int")
         DllCall("ReleaseDC", "Ptr", HDC, "Ptr", 0)
         If (FntOpts != "") {
            For Opt In StrSplit(RegExReplace(Trim(FntOpts), "\s+", " "), " ") {
               Switch StrUpper(Opt) {
                  Case "BOLD":      NumPut("Int", 700, LOGFONTW, 16)
                  Case "ITALIC":    NumPut("Char",  1, LOGFONTW, 20)
                  Case "UNDERLINE": NumPut("Char",  1, LOGFONTW, 21)
                  Case "STRIKE":    NumPut("Char",  1, LOGFONTW, 22)
                  Case "NORM":      NumPut("Int", 400, "Char", 0, "Char", 0, "Char", 0, LOGFONTW, 16)
                  Default:
                     O := StrUpper(SubStr(Opt, 1, 1))
                     V := SubStr(Opt, 2)
                     Switch O {
                        Case "C":
                           Continue ; ignore the color option
                        Case "Q":
                           If !IsInteger(V) || (Integer(V) < 0) || (Integer(V) > 5)
                              Throw ValueError("Option Q must be an integer between 0 and 5!", -1, V)
                           NumPut("Char", Integer(V), LOGFONTW, 26)
                        Case "S":
                           If !IsNumber(V) || (Number(V) < 1) || (Integer(V) > 255)
                              Throw ValueError("Option S must be a number between 1 and 255!", -1, V)
                           NumPut("Int", -Round(Integer(V + 0.5) * LOGPIXELSY / 72), LOGFONTW)
                        Case "W":
                           If !IsInteger(V) || (Integer(V) < 1) || (Integer(V) > 1000)
                              Throw ValueError("Option W must be an integer between 1 and 1000!", -1, V)
                           NumPut("Int", Integer(V), LOGFONTW, 16)
                        Default:
                           Throw ValueError("Invalid font option!", -1, Opt)
                     }
                  }
               }
            }
         NumPut("Char", 1, "Char", 4, "Char", 0, LOGFONTW, 23) ; DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS
         NumPut("Char", 0, LOGFONTW, 27) ; FF_DONTCARE
         If (FntName != "")
            StrPut(FntName, LOGFONTW.Ptr + 28, 32)
         If !(HFONT := DllCall("CreateFontIndirectW", "Ptr", LOGFONTW, "UPtr"))
            Throw OSError()
         If This.HFONT
            DllCall("DeleteObject", "Ptr", This.HFONT)
         This.HFONT := HFONT
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetMargins()    -  Set or remove the margins used by the tooltip
   ; Parameters:
   ;     L, T, R, B  -  left, top, right, and bottom margin in pixels.
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetMargins(L := 0, T := 0, R := 0, B := 0) {
      If ((L + T + R + B) = 0)
         This.Margins := 0
      Else {
         This.Margins := Buffer(16, 0)
         NumPut("Int", L, "Int", T, "Int", R, "Int", B, This.Margins)
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetTitle()      -  Set or remove the title and/or the icon displayed on the tooltip.
   ; Parameters:
   ;     Title       -  string to be used as title.
   ;     Icon        -  icon to be shown in the ToolTip.
   ;                    This can be the number of a predefined icon (1 = info, 2 = warning, 3 = error
   ;                    (add 3 to display large icons on Vista+) or a HICON handle.
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetTitle(Title := "", Icon := "") {
      Switch {
         Case (Title = "") && (Icon != ""):
            This.Icon := Icon
            This.Title := " "
         Case (Title != "") && (Icon = ""):
            This.Icon := 0
            This.Title := Title
         Default:
            This.Icon := Icon
            This.Title := Title
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; For internal use only!
   ; -------------------------------------------------------------------------------------------------------------------
   Static _WNDPROC_(hWnd, uMsg, wParam, lParam) {
      ; WNDPROC -> https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc
      Switch uMsg {
         Case 0x0411: ; TTM_TRACKACTIVATE - just handle the first message after the control has been created
            If This.ToolTips.Has(hWnd) && (This.ToolTips[hWnd] = 0) {
               If (This.BkgColor != "")
                  SendMessage(1043, This.BkgColor, 0, hWnd)                ; TTM_SETTIPBKCOLOR
               If (This.TxtColor != "")
                  SendMessage(1044, This.TxtColor, 0, hWnd)                ; TTM_SETTIPTEXTCOLOR
               If This.HFONT
                  SendMessage(0x30, This.HFONT, 0, hWnd)                   ; WM_SETFONT
               If (Type(This.Margins) = "Buffer")
                  SendMessage(1050, 0, This.Margins.Ptr, hWnd)             ; TTM_SETMARGIN
               If (This.Icon != "") || (This.Title != "")
                  SendMessage(1057, This.Icon, StrPtr(This.Title), hWnd)   ; TTM_SETTITLE
               This.ToolTips[hWnd] := 1
            }
         Case 0x0001: ; WM_CREATE
            DllCall("UxTheme.dll\SetWindowTheme", "Ptr", hWnd, "Ptr", 0, "Ptr", StrPtr(""))
            This.ToolTips[hWnd] := 0
         Case 0x0002: ; WM_DESTROY
            This.ToolTips.Delete(hWnd)
      }
      Return DllCall(This.OWP, "Ptr", hWnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam, "UInt")
   }
   ; -------------------------------------------------------------------------------------------------------------------
   Static _EXIT_(*) {
      If (ToolTipOptions.OWP != 0)
         ToolTipOptions.Reset()
   }
}

Some sample:

Code: Select all

#Include ToolTipOptions.ahk
; ----------------------------------------------------------------------------------------------------------------------
ToolTipOptions.Init()
; ----------------------------------------------------------------------------------------------------------------------
ToolTipOptions.SetFont("s48 underline italic", "Consolas")
ToolTipOptions.SetMargins(12, 12, 12, 12)
ToolTipOptions.SetTitle("Title" , 4)
ToolTipOptions.SetColors("Green", "White")
ToolTip("Hello world!")
; ----------------------------------------------------------------------------------------------------------------------
Return
; ----------------------------------------------------------------------------------------------------------------------
Esc::ExitApp
Esc::ExitApp
^+s::ToolTip("Hello world!")        ; show a ToolTip
^+a::ToolTip("Another Tooltip!")    ; show another ToolTip
^+h::ToolTip()                      ; destroy the ToolTip
^+f::ToolTipOptions.SetFont()       ; remove the font
^+x::ToolTipOptions.Reset()         ; remove the subclassing
^+y::ToolTipOptions.Init()          ; add the sublassing again
Last edited by just me on 17 Mar 2024, 08:28, edited 2 times in total.

paveld
Posts: 6
Joined: 01 Oct 2020, 05:28

Re: Class ToolTipOptions

Post by paveld » 20 Feb 2023, 05:04

GREAT

hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: Class ToolTipOptions

Post by hasantr » 05 Mar 2023, 21:01

Thanks. This will be very useful to us.

LAPIII
Posts: 667
Joined: 01 Aug 2021, 06:01

Re: Class ToolTipOptions

Post by LAPIII » 14 Apr 2023, 06:30

What would a script look like if I wanted to make a tooltip in an app with a hotkey?

EDIT: I made a library of the class and I tried:

Code: Select all

#Requires Autohotkey v2.0+
#HotIf WinActive("ahk_exe msedge.exe")
#Include <ToolTipOptions>
CoordMode "ToolTip", "Window" 
ToolTipOptions.Init()

ToolTipOptions.SetFont("s12 underline italic bold", "Ink Free")
ToolTipOptions.SetMargins(12, 12, 12, 12)
ToolTipOptions.SetColors("Teal", "White")
ToolTip("Hello world!", 1104, 0)
The library works but the script doesn't.

User avatar
boiler
Posts: 16705
Joined: 21 Dec 2014, 02:44

Re: Class ToolTipOptions

Post by boiler » 15 Apr 2023, 06:28

LAPIII wrote: What would a script look like if I wanted to make a tooltip in an app with a hotkey?
Open a new thread in Ask for Help (v2) if you need help on how to create a hotkey. This is thread is not the place for learning AHK basics.

LAPIII wrote: EDIT: I made a library of the class and I tried:
Your script ends before it has a chance to display the ToolTip. Adding a hotkey or a MsgBox are a couple of ways you could prevent the script from immediately ending, allowing you to see the ToolTip. Again, your questions are not related to the library, so ask for help in a new thread if you need help on how to construct a script.

theyakutoo
Posts: 14
Joined: 09 Sep 2023, 11:15

Re: Class ToolTipOptions

Post by theyakutoo » 10 Sep 2023, 00:53

It's doing strange things when using multiple tooltips. On exit, it's looping, showing a windows center screen so fast they don't fade in all the way.

Enabling the commented exit loop resolves the issue by closing the tips before the class does it's exit routine.

Warning: It seems to be exponential... enabling 5 tips may require waiting quite some time... 2 is enough to see the issue.

Code: Select all

#Requires AutoHotkey >=2.0- <2.1
#Singleinstance force

#Include ToolTipOptions.ahk

; ----------------------------------------------------------------------------------------------------------------------

ToolTipOptions.Init()
ToolTipOptions.SetMargins(0, 0, 0, 0)
ToolTipOptions.SetTitle()
ToolTipOptions.SetFont("s12", "Consolas")


TID:=1	,	ToolTipOptions.SetColors("black", "white")	,	ToolTip("TIP " a_index,10,TID*30,TID)
TID:=2	,	ToolTipOptions.SetColors("black", "green")	,	ToolTip("TIP " a_index,10,TID*30,TID)
;TID:=3	,	ToolTipOptions.SetColors("black", "yellow")	,	ToolTip("TIP " a_index,10,TID*30,TID)
;TID:=4	,	ToolTipOptions.SetColors("black", "maroon")	,	ToolTip("TIP " a_index,10,TID*30,TID)
;TID:=5	,	ToolTipOptions.SetColors("black", "red")	,	ToolTip("TIP " a_index,10,TID*30,TID)

return

; ----------------------------------------------------------------------------------------------------------------------

Esc:: 
{

;	loop 20
;		ToolTip("",,,a_index)

	ExitApp

}

just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class ToolTipOptions - 2023-09-10

Post by just me » 10 Sep 2023, 07:08

Thanks for reporting. Elements from the ToolTipOptions.ToolTips Map object were deleted in _WNDPROC_() while enumerating the object in Reset(). Should be fixed now.

theyakutoo
Posts: 14
Joined: 09 Sep 2023, 11:15

Re: Class ToolTipOptions - 2023-09-10

Post by theyakutoo » 10 Sep 2023, 23:28

It works! Thanks!

theyakutoo
Posts: 14
Joined: 09 Sep 2023, 11:15

Re: Class ToolTipOptions - 2023-09-10

Post by theyakutoo » 07 Oct 2023, 06:04

Error when opening start menu. Sometimes it takes a few tries to get the error.

image.png
image.png (11.35 KiB) Viewed 1354 times

Code: Select all

#Requires AutoHotkey >=2.0- <2.1
#Singleinstance force

#Include ToolTipOptions.ahk

ToolTipOptions.Init()
ToolTipOptions.SetMargins(0, 0, 0, 0)
ToolTipOptions.SetTitle()
ToolTipOptions.SetFont("s10", "Consolas")
ToolTipOptions.SetColors("black", "red")

while 1 {
	sleep 11
	title		:= WinGetTitle("A")
	tooltip(title)
}

Esc::ExitApp

User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Class ToolTipOptions - 2023-09-10

Post by rommmcek » 07 Oct 2023, 07:07

This issue seems nothing to do with Class ToolTipOptions.
Try your loop w/o this Class.
If you slow down a loop there is no problem in any case.

P.s.: AutoHotkey v1 seems not to have such an issue, therefore it might be a v2 bug.

User avatar
andymbody
Posts: 856
Joined: 02 Jul 2017, 23:47

Re: Class ToolTipOptions - 2023-09-10

Post by andymbody » 07 Oct 2023, 08:42

theyakutoo wrote:
07 Oct 2023, 06:04
Error when opening start menu. Sometimes it takes a few tries to get the error.
I have not looked over this thread completely, so I don't know if this is the same issue I ran into with another script.

But I found that the taskbar receives active focus briefly when minimizing or closing windows. And at that moment no title can be found because the taskbar has no title associated with it. The same thing happens when showing the desktop. See if this is the case here.

I added a try/catch to my project to avoid the error when no title can be found

See if this resolves it. I included a different message so you will be aware when the issue happens.
Or you can simply test to see if active window class is Shell_TrayWnd or WorkerW, and only get title when it is not

Code: Select all

errorCount := 0
while 1 {
	sleep 11
	try
	{
		title := WinGetTitle("A")
		tooltip(title)
	}
	catch
	{
		tooltip("no title found := " . ++errorCount)
	}
}

Andy

theyakutoo
Posts: 14
Joined: 09 Sep 2023, 11:15

Re: Class ToolTipOptions - 2023-09-10

Post by theyakutoo » 07 Oct 2023, 10:00

Using try solved the issue.

Thanks guys!

User avatar
kunkel321
Posts: 957
Joined: 30 Nov 2015, 21:19

Re: Class ToolTipOptions - 2023-09-10

Post by kunkel321 » 16 Mar 2024, 09:44

I just wanted to say thanks to Just Me, for making and sharing this. It has been integrated into a couple of my regularly-used scripts. It's nice for those of us with failing eyesight. I've started using it in place of MsgBox (MsgBox's font is too small.)

FYI for noobs (like me): If you use variables for the colors, and if you use just the 6-character hex code, be sure to include the zero-x at the beginning, like:

Code: Select all

ToolTipOptions.SetColors("0x" BackColor, "0x" FontColor)
ste(phen|ve) kunkel

just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class ToolTipOptions - 2024-03-17

Post by just me » 17 Mar 2024, 08:34

Hi @kunkel321,
thanks! I changed the class to support 6-xdigit color strings. (I mistakenly assumed that AHK v2 doesn't support such color strings any more.)
Continue to have fun with AHK!

Post Reply

Return to “Scripts and Functions (v2)”