i always wanted to know, but was afaid to ask

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

i always wanted to know, but was afaid to ask

10 Jul 2019, 17:22

ToolTipGui - how to embed a GUI into a tooltip window.?
I have been looking for an example of ToolTipGui, which is in line of TooTipOpt.
something like (pseudo-code, totally not working, maybe a little bit):

Code: Select all

;-------------------------------------------------------------------------------
; ToolTipGui.ahk
;-------------------------------------------------------------------------------



#NoEnv
SetBatchLines, -1

    new ToolTipGui.Options := [["#", "Center"], ["x"], ["y"]]

    ; make Points array
    p0 := {x: 100, y: 100}
    p1 := {x: 500, y: 500}
    p2 := {x: 500, y: 100}
    p3 := {x: 100, y: 500}
    Points := [p0, p1, p2, p3]

    ; test
    ToolTip, Points

    loop 100 {
        p0.x ++, p0.y --, p1.x += 2, p1.y -= 3
        ToolTip, Points
        sleep 50 ; ~5 sec total time
    }

ExitApp ; end of auto-execute section



ToolTipGui(Options := "", Name := "", hwnd := "") {
    if (hwnd = "")
        _TTG("Gui", "", "")
        _TTHook()
}


_TTHook() {
    static hook := 0
    if !hook
        hook := DllCall("SetWindowsHookExW", "int", 4
            , "ptr", RegisterCallback("_TTWndProc"), "ptr", 0
            , "uint", DllCall("GetCurrentThreadId"), "ptr")
}


_TTWndProc(nCode, _wp, _lp) {
    Critical 999
   ;lParam  := NumGet(_lp+0*A_PtrSize)
   ;wParam  := NumGet(_lp+1*A_PtrSize)
    uMsg    := NumGet(_lp+2*A_PtrSize, "uint")	
    hwnd    := NumGet(_lp+3*A_PtrSize)
    if (nCode >= 0 && (uMsg = 1081 || uMsg = 1036)) {
        _hack_ = ahk_id %hwnd%
        WinGetClass wclass, %_hack_%
        if (wclass = "tooltips_class32") {
            ;~ ToolTipColor(,, hwnd)
            ;~ ToolTipFont(,, hwnd)
        }
    }
    return DllCall("CallNextHookEx", "ptr", 0, "int", nCode, "ptr", _wp, "ptr", _lp, "ptr")
}

_TTG(Cmd, Arg1, Arg2 := "") {
    static htext := 0, hgui := 0
    if !htext {
        Gui _TTG: Add, Text, +hwndhtext
        Gui _TTG: +hwndhgui +0x40000000
    }
    Gui _TTG: %Cmd%, %Arg1%, %Arg2%
    if (Cmd = "Gui") {
        Gui _TTG: Add, ListView
        return ErrorLevel
    }
}
This is obviously not working (for starters, ToolTipGui in NOT a class), but it is supposed to resemble Lexikos' ToolTipOpt()

Background: I want to experiment with using the TooTip command to show a custom GUI.
What I want to improve on: my old debugging helpers, namely TV_show and LV_show.
There is no problem that I found yet, but it is noticeable that TV_show is struggling with performance, when compared to LV_show.
My current knowledge is limited, The helpers do well in my use-cases (debugging does not need to be faster, I want to be able to read the output :D ,
But there is this question that I never dared to ask in public: Does anybody understand this hint/note by Lexikos in this thread? https://www.autohotkey.com/boards/viewtopic.php?p=27472#p27472
Lexikos wrote:Notes:
It's possible to embed a GUI into a tooltip window.
TV_show
LV_show
In case this topic makes sense to anybody and that person is able to tell me, that I am completely mistaken, please come forward to tell me.

Thanks for reading! :)
User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: i always wanted to know, but was afaid to ask

11 Jul 2019, 04:42

Theoretically, if you know tooltip's HWND (which you find in the _TTWndProc() function according to lexikos' code), then you can use the SetParent() API to attach a GUI to the tooltip window.
I said theoretically - there may be other tidbits to take care of.
Part of my AHK work can be found here.
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: i always wanted to know, but was afaid to ask

11 Jul 2019, 05:15

Thx, this looks promising, theoretically this is giving me the first hurdle.

I have also this script from teadrinker:

Code: Select all

text := "Hello, World!"
sym := Chr(1)
Loop 300
   text .= (!mod(A_Index, 40) ? "`n" : "") . sym
ToolTip % text
hTT := WinExist("ahk_class tooltips_class32 ahk_pid" . DllCall("GetCurrentProcessId"))
WinGetPos,,, W, H, ahk_id %hTT%

Gui, Child: New, +Parent%hTT% +ToolWindow -Caption
Gui, Add, Edit, % "x10 y10 w" W - 40, AutoHotkey
Gui, Add, Button, w100 gCloseTT, Close ToolTip
Gui, Show, % "x10 y20 w" W - 20 " h" H - 30
Return

CloseTT:
   WinClose, ahk_id %hTT%
   Gui, Child: Destroy
   ExitApp
this shows a way to attach a child-Gui.

Once or if I understand what lexikos' ToolTipOpt is doing or how it is doing it ...
I could be able to combine both hints. Your hint is very helpful on my way to understand all the functions in ToolTipOpt.
Thank you again :)
User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: i always wanted to know, but was afaid to ask

11 Jul 2019, 08:22

Lexikos hooks the Window Procedure of the tooltip window and changes certain attributes from within the new Window Procedure.

I'm not familiar with the relatively new GUI command options such as +Parent (been trying to stick to old, common AHK1.0 style) but I suppose that could work too. Just make sure you modify GUI's parent each time a new tooltip is displayed since their HWND may not be the same.
It'll take some trial and error. I've been on Linux for a while already and AHK doesn't work very well under Wine so can't perform relevant tests here.
Part of my AHK work can be found here.
teadrinker
Posts: 4334
Joined: 29 Mar 2015, 09:41
Contact:

Re: i always wanted to know, but was afaid to ask

11 Jul 2019, 17:23

@wolf_II
I experimented a bit with Lexicos' code. I don't know whether this is what you need.

Code: Select all

CoordMode, ToolTip
options := { fontOptions: "s36"
           , fontFamily:  "calibri" }
           
ToolTipOptions(options)
ToolTip Hello`, World!, 500, 200, 1
Sleep, 500

for k, v in { fontOptions: "s24"
            , fontFamily:  "Courier New"
            , textColor:   "blue"
            , backColor:   "red" }
   options[k] := v
ToolTip, AutoHotkey, 500, 290, 2
Sleep, 500

ToolTipOptions()
ToolTip, Usual ToolTip, 500, 350, 3
Sleep, 500

for k, v in { fontOptions: "s16"
            , fontFamily:  "Arial"
            , textColor:   "00675C"
            , backColor:   "FFA500"
            , childGui: {fn: Func("CreateGui"), x: 10, y: 5} } ; y — coordinate relative to the bottom of the ToolTip text
   options[k] := v
   
ToolTipOptions(options)
ToolTip, ToolTip with GUI, 500, 400, 4

CreateGui(hParent) {
   Gui, New, +hwndhGui +Parent%hParent% +ToolWindow -Caption
   Gui, Color, 6B90D4
   Gui, Font, s12, Calibri
   Gui, Add, Edit, w350 h50, Editable Text
   Gui, Add, Button, x+-100 y+7 w100 h24 gExitFunc, Exit
   Gui, Show, Hide
   Return hGui
}

ExitFunc() {
   ExitApp
}

ToolTipOptions(options := "", wp := "", lp := "") {
   static WH_CALLWNDPROC := 4, WM_SETFONT := 0x30, WM_GETFONT := 0x31
        , TTM_UPDATETIPTEXT := 0x400 + (A_IsUnicode ? 57 : 12)
        , TTM_SETTIPBKCOLOR := 0x413, TTM_SETTIPTEXTCOLOR := 0x414
        , TTM_GETTEXT := 0x400 + (A_IsUnicode ? 56 : 11)
        , TTM_SETMARGIN := 0x41A, DT_CALCRECT := 0x400, hHook, opt
        
   if (options = "" && hHook)
      SetHook(hHook), hHook := opt := ""
   else if !lp {
      opt := options
      ( hHook = "" && hHook := SetHook(WH_CALLWNDPROC, A_ThisFunc, false) )
   }
   else {
      msg := NumGet(lp + A_PtrSize*2, "UInt")
      if (msg = TTM_UPDATETIPTEXT) {
         lastFound := WinExist()
         hwnd := NumGet(lp + A_PtrSize*3)
         WinExist("ahk_id" . hwnd)
         if (opt.textColor != "" || opt.backColor != "" || opt.childGui) {
            VarSetCapacity(empty, 2, 0)
            DllCall("UxTheme\SetWindowTheme", "Ptr", hwnd, "Ptr", 0, "Ptr", &empty)
            if (opt.backColor != "")
               SendMessage, TTM_SETTIPBKCOLOR, GetOptions("color", opt.backColor)
            if (opt.textColor != "")
               SendMessage, TTM_SETTIPTEXTCOLOR, GetOptions("color", opt.textColor)
         }
         if (opt.fontOptions || opt.fontFamily) {
            hFont := GetOptions("font", opt.fontOptions, opt.fontFamily)
            SendMessage, WM_SETFONT, hFont
         }
         if opt.childGui {
            X := opt.childGui.x ? opt.childGui.x : 5
            Y := opt.childGui.y ? opt.childGui.y : 5
            hGui := opt.childGui.fn.Call(hwnd)
            VarSetCapacity(buff, 1024, 0)
            VarSetCapacity(TOOLINFO, sz := 4*6 + A_PtrSize*6, 0)
            NumPut(sz, TOOLINFO, "UInt")
            NumPut(&buff, TOOLINFO, 4*6 + A_PtrSize*3)
            SendMessage, TTM_GETTEXT, 1024 >> !!A_IsUnicode, &TOOLINFO
            text := StrGet(&buff)
            hDC := DllCall("GetDC", "Ptr", hwnd, "Ptr")
            SendMessage, WM_GETFONT
            hObj := DllCall("SelectObject", "Ptr", hDC, "Ptr", ErrorLevel, "Ptr")
            VarSetCapacity(RECT, 16, 0)
            height := DllCall("DrawText", "Ptr", hDC, "Str", text, "Int", StrLen(text), "Ptr", &RECT, "UInt", DT_CALCRECT)
            height *= 1.04, width := NumGet(RECT, 8, "UInt")
            DllCall("SelectObject", "Ptr", hDC, "Ptr", hObj)
            DllCall("ReleaseDC", "Ptr", hwnd, "Ptr", hDC)
            WinGetPos,,, W, H, % "ahk_id" hGui
            ( X + W + 5 > width && NumPut(X + W + 5 - width, RECT, 8) )
            NumPut(Y + H + 10, RECT, 12, "UInt")
            SendMessage, TTM_SETMARGIN,, &RECT
            Gui, % hGui ": Show", % "NA x" X " y" height + Y
         }
         WinExist("ahk_id" . lastFound)
      }
      Return DllCall("CallNextHookEx", "Ptr", 0, "Int", options, "UInt", wp, "Ptr", lp)
   }
}

GetOptions(cmd, arg1, arg2 := "") {
   static hGui, hText, WS_CHILD := 0x40000000, WM_GETFONT := 0x31, WM_CTLCOLORSTATIC := 0x138
   if !hGui {
      Gui, New, +hwndhGui +%WS_CHILD%
      Gui, Add, Text, hwndhText
   }
   Gui, %hGui%: %cmd%, % arg1, % arg2
   if (cmd = "font") {
      GuiControl, %hGui%:Font, %hText%
      SendMessage, WM_GETFONT,,,, ahk_id %hText%
      Return ErrorLevel
   }
   if (cmd = "color") {
      hdc := DllCall("GetDC", "Ptr", hText, "Ptr")
      SendMessage, WM_CTLCOLORSTATIC, hdc, hText,, ahk_id %hGui%
      clr := DllCall("GetBkColor", "Ptr", hdc, "UInt")
      DllCall("ReleaseDC", "Ptr", hText, "Ptr", hdc)
      return clr
   }
}

SetHook(hook, fn := "", isGlobal := true) {
   static exitFunc
   if !fn {
      DllCall("UnhookWindowsHookEx", "Ptr", hook)
      OnExit(exitFunc, 0)
      exitFunc := ""
   }
   else {
      hHook := DllCall("SetWindowsHookEx", "Int", hook, "Ptr", RegisterCallback(fn, "Fast", 3)
                                         , "Ptr", DllCall("GetModuleHandle", "UInt", 0, "Ptr")
                                         , "UInt", isGlobal ? 0 : DllCall("GetCurrentThreadId"), "Ptr")
      ( exitFunc && OnExit(exitFunc, 0) )
      exitFunc := Func(A_ThisFunc).Bind(hHook, "", "")
      OnExit(exitFunc)
      Return hHook
   }
}
Last edited by teadrinker on 11 Jul 2019, 18:52, edited 2 times in total.
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: i always wanted to know, but was afaid to ask

11 Jul 2019, 18:35

I don't know whether this is what you need.
Thank you, I don't know yet, my brain melted just now. But I can start to see how this is working ...
No wait, all I can see is spaghetti (A description on my brain, not of your or Lexikos' code. This is difficult to wrap my head around.
It will take a while to get used to thinking about hooks.

Thank you for giving me more examples to study, :) That will help I think. :thumbup:

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: DataLife, GEOVAN and 80 guests