Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoHotkey: Dynamic resize control based on text

Consider the following snippet:

FormatTime, time,
Gui, Add, Text, vcTime, 
GuiControl, , cTime, % time 
Gui, Show, NoActivate Center AutoSize

AutoSize is based on the initial value from Gui, Add, Text, vcTime and not on the new value set by GuiControl, , cTime, % time. Depending on months etc., %time% length can vary. How can I automatically resize the window to adapt to updated values of %time%? `

like image 380
antonio Avatar asked Mar 07 '23 15:03

antonio


1 Answers

AutoSize is actually based on the current control sizes when Gui Show is called. The issue is that the "blank" subcommand to GuiControl for text controls does not automatically resize the control; it just changes the text and you still have to call GuiControl Move with a new size yourself. So, in your example, if you replace AutoSize with w200 the text will still be cut off at the same point.

As far as I know, there's not really a "built-in" automatic way to resize the text control based on the new text. The closest way is to use AHK's initial size calculation when creating a text control: create a new text control with the desired text, use GuiControlGet to get the size of the new control, and finally set the size of the original control to that size using GuiControl Move. Here's an example function which does this, adapted from here:

SetTextAndResize(controlHwnd, newText, fontOptions := "", fontName := "") {
    Gui 9:Font, %fontOptions%, %fontName%
    Gui 9:Add, Text, R1, %newText%
    GuiControlGet T, 9:Pos, Static1
    Gui 9:Destroy

    GuiControl,, %controlHwnd%, %newText%
    GuiControl Move, %controlHwnd%, % "h" TH " w" TW
}

Which would fit into your example like so:

FormatTime, time,
Gui, Add, Text, HwndTimeHwnd vcTime,
SetTextAndResize(TimeHwnd, time)
Gui, Show, NoActivate Center AutoSize

Now whenever you use SetTextAndResize instead of just setting the text you can use Gui Show, AutoSize to automatically resize the window correctly. Note that if you changed the font with Gui Font before adding the text control you would have to pass those same options to SetTextAndResize.

Alternatively, I looked at how AHK itself calculates the initial size of a text control for Gui Add, Text when none is provided and found it uses the Windows API function DrawText with DT_CALCRECT. Here's another implementation of SetTextAndResize I wrote using this directly:

SetTextAndResize(controlHwnd, newText) {
    dc := DllCall("GetDC", "Ptr", controlHwnd)

    ; 0x31 = WM_GETFONT
    SendMessage 0x31,,,, ahk_id %controlHwnd%
    hFont := ErrorLevel
    oldFont := 0
    if (hFont != "FAIL")
        oldFont := DllCall("SelectObject", "Ptr", dc, "Ptr", hFont)

    VarSetCapacity(rect, 16, 0)
    ; 0x440 = DT_CALCRECT | DT_EXPANDTABS
    h := DllCall("DrawText", "Ptr", dc, "Ptr", &newText, "Int", -1, "Ptr", &rect, "UInt", 0x440)
    ; width = rect.right - rect.left
    w := NumGet(rect, 8, "Int") - NumGet(rect, 0, "Int")

    if oldFont
        DllCall("SelectObject", "Ptr", dc, "Ptr", oldFont)
    DllCall("ReleaseDC", "Ptr", controlHwnd, "Ptr", dc)

    GuiControl,, %controlHwnd%, %newText%
    GuiControl Move, %controlHwnd%, % "h" h " w" w
}

I'm not sure how it compares to the first method in terms of performance, but one advantage is that it gets the font based on the control itself rather than having to provide it to the function yourself.

like image 130
Josh Brobst Avatar answered May 01 '23 12:05

Josh Brobst