Moderators Melba23 Posted December 14, 2008 Moderators Share Posted December 14, 2008 (edited) Edit 17 Dec 08: UDF amended to use _WinAPI_GetTextExtentPoint32 (many thanks to Zedna for the suggestion) which removes the need to distinguish mono/prop fonts and to measure each character in the relevant font.There have been a number of threads recently about sizing a label or GUI to fit a given text exactly. The sort of thing that the Windows MsgBox does so nicely to the text it holds.I have joined in many of these threads as I have been looking for something like this for quite a while. But all the various suggested solutions were only really valid for non-wrapped text - while I wanted something that would accept a maximum width limitation so that the resulting label/GUI was not as wide as the screen! I did not want to have to guess at line lengths to fit - too often it did not work or looked pretty awful. After numerous attempts, including SWAG*ing the line length and using GDI to draw the lines (some of which have appeared on the forums), I believe I have found a good working solution. By which I mean I have not been able to break it yet!The function takes 4 parameters: the text to display, the font size and face, and the max width. It returns an array which gives the size of the label needed to hold the text and the height of a single line in that font size and face.expandcollapse popup; #include-once #include <WindowsConstants.au3> #include <SendMessage.au3> #include <WinAPI.au3> ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~ ; Example Global $hTest_GUI, $iTest_Width $hTest_GUI = GUICreate("Size Label Test", 600, 800) GUISetState() $iTest_Width = 350 _Labels() $iTest_Width = 500 _Labels() $iTest_Width = 150 _Labels() GUIDelete($hTest_GUI) Exit ; -------- Func _Labels() Local $sMsg $sMsg = "This is a long line - this is a long line - this is a long line!" & @CRLF & @CRLF $sMsg &= "This is an even longer long line - this is an even longer long line - this is an even longer long line!" & @CRLF & @CRLF $sMsg &= "And this is a little one" _Choose_Font($sMsg) $sMsg = "This is a short line" & @CRLF & @CRLF $sMsg &= "This is a very long line indeed - this is a very long line indeed - this is a very long line indeed - this is a very long line indeed - " $sMsg &= "this is a very long line indeed - this is a very long line indeed - this is a very long line indeed - and this is the end!" _Choose_Font($sMsg) $sMsg = "This line contains a long word - generalisation" & @CRLF & @CRLF $sMsg &= "If the width is narrow and the font size large, it may not fit in the label" & @CRLF & @CRLF $sMsg &= "In that case, the label will not be displayed and the error will be set to 2 to indicate that there is a font size / width mismatch" & @CRLF & @CRLF $sMsg &= "There are now a number of blank lines" & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF $sMsg &= "But the UDF still manages to cope, wrapping the lines as required to fit into the width requested and determining the correct height" _Choose_Font($sMsg) $sMsg = "This is only a short line" & @CRLF & @CRLF $sMsg &= "This is only a short line" & @CRLF & @CRLF $sMsg &= "This is only a short line" & @CRLF & @CRLF $sMsg &= "This is only a short line" _Choose_Font($sMsg) EndFunc ; ------- Func _Choose_Font($sMsg) Local $sFont $sFont = "Segoe UI" _Choose_Size($sMsg, $sFont) $sFont = "Tahoma" _Choose_Size($sMsg, $sFont) $sFont = "Arial" _Choose_Size($sMsg, $sFont) $sFont = "Microsoft Sans Serif" _Choose_Size($sMsg, $sFont) $sFont = "Comic Sans MS" _Choose_Size($sMsg, $sFont) $sFont = "Courier New" _Choose_Size($sMsg, $sFont) $sFont = "Lucida Console" _Choose_Size($sMsg, $sFont) EndFunc ; ------- Func _Choose_Size($sMsg, $sFont) Local $iSize $iSize = 9 _Size_Label($sMsg, $iSize, $sFont) $iSize = 10 _Size_Label($sMsg, $iSize, $sFont) $iSize = 11 _Size_Label($sMsg, $iSize, $sFont) $iSize = 12 _Size_Label($sMsg, $iSize, $sFont) $iSize = 13 _Size_Label($sMsg, $iSize, $sFont) $iSize = 14 _Size_Label($sMsg, $iSize, $sFont) $iSize = 15 _Size_Label($sMsg, $iSize, $sFont) EndFunc ; ------- Func _Size_Label($sMsg, $iSize, $sFont) Local $aLabel_Info[4], $error, $hLabel GUISetFont($iSize, 400, 0, $sFont, $hTest_GUI) $aLabel_Info =_Label_Size($sMsg, $iSize, $sFont, $iTest_Width) $error = @error If $error Then ConsoleWrite($sFont & " " & $iSize & " : Error = " & @error & @CRLF) Sleep(1000) Return Else ConsoleWrite($sFont & " " & $iSize & @CRLF) $hLabel = GUICtrlCreateLabel($sMsg, 10, 10, $aLabel_Info[2], $aLabel_Info[3]) If $iTest_Width < 200 Then GUICtrlSetBkColor(-1, 0xFFFF00) ElseIf $iTest_Width < 400 Then GUICtrlSetBkColor(-1, 0x80FF80) Else GUICtrlSetBkColor(-1, 0x80FFFF) EndIf Sleep(1000) GUICtrlDelete($hLabel) EndIf EndFunc ; End of example ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~ ; #FUNCTION# ======================================================================================= ; Name............: _Label_Size ; Description ....: Returns size of label required for a text, even if wrapped to a set width ; Syntax..........: _Label_Size($sText, $iFont_Size, $sFont_Name[, $iWidth]) ; Parameters .....: $sText -> Text to display with @CRLF line endings ; $iFont_Size -> Font size in points ; $sFont_Name -> Font name ; $iWidth -> Max width of the label - default is width of desktop ; Requirement(s)..: v3.2.12.1 or higher ; Return values ..: Success: Returns array with details of label required for text ; $array[0] = Number of unwrapped lines in text ; $array[1] = Height of single line of text ; $array[2] = Width of label required to hold text ; $array[3] = Height of label required to hold text ; Failure: Returns 0 ; - Error 1 - Failure to create GUI to test label size ; - Error 2 - Font too large for width - longest word will not fit ; Author .........: Melba23 ; Example.........; Yes ;=================================================================================================== Func _Label_Size($sText, $iFont_Size, $sFont_Name, $iWidth = @DesktopWidth) Local $hWnd, $hFont, $hDC, $tSize, $hGUI, $hText_Label, $sTest_Line Local $iStart_Code, $iEnd_Code, $iFont_Width, $iLine_Count, $iLine_Width, $iWrap_Count, $iLast_Word Local $aLines[1], $aLabel_Info[4], $aPos[4], $aInfo[3] ; Create GUI $hGUI = GUICreate("", 800, 1000, 800, 10) If $hGUI = 0 Then Return SetError(1, 0, 0) GUISetFont($iFont_Size, 400, 0, $sFont_Name) ; Break text into lines $aLines = StringSplit($sText, @CRLF, 1) ; Store number of lines $aLabel_Info[0] = $aLines[0] ; Draw label with unwrapped lines to check on max width $hText_Label = GUICtrlCreateLabel($sText, 10, 10) $aPos = ControlGetPos($hGUI, "", $hText_Label) GUICtrlDelete($hText_Label) ; Store line height for this font size after removing label padding (always 8) $aLabel_Info[1] = ($aPos[3] - 8)/ $aLines[0] $aLabel_Info[2] = $aPos[2] $aLabel_Info[3] = $aPos[3] ; Now see if wrapping is required If $aPos[2] > $iWidth Then ; Set width element to max allowed $aLabel_Info[2] = $iWidth ; Set line count to zero $iLine_Count = 0 ; Take each line in turn For $j = 1 To $aLines[0] ; Size this line $hText_Label = GUICtrlCreateLabel($aLines[$j], 10, 10) $aPos = ControlGetPos($hGUI, "", $hText_Label) GUICtrlDelete($hText_Label) ; Check wrap status If $aPos[2] < $iWidth Then ; No wrap needed so count line and move on $iLine_Count += 1 Else ; Wrap needed so need to count wrapped lines ; Create label $hText_Label = GUICtrlCreateLabel("", 0, 0) ; Initialise Point32 method $hWnd = ControlGetHandle($hGui, "", $hText_Label) $hFont = _SendMessage($hWnd, $WM_GETFONT) $hDC = _WinAPI_GetDC($hWnd) _WinAPI_SelectObject($hDC, $hFont) ; Zero counter $iWrap_Count = 0 While 1 ; Set line width to 0 $iLine_Width = 0 ; Initialise pointer for end of word $iLast_Word = 0 For $i = 1 To StringLen($aLines[$j]) ; Is this just past a word ending? If StringMid($aLines[$j], $i, 1) = " " Then $iLast_Word = $i - 1 ; Increase line by one character $sTest_Line = StringMid($aLines[$j], 1, $i) ; Place line in label GUICtrlSetData($hText_Label, $sTest_Line) ;ConsoleWrite($sTest_Line & @CRLF) ; Get line length $tSize = _WinAPI_GetTextExtentPoint32($hDC, $sTest_Line) $iLine_Width = DllStructGetData($tSize, "X") ;ConsoleWrite($iLine_Width & @CRLF) ; If too long exit the loop If $iLine_Width > $iWidth Then ExitLoop Next ; End of the line of text? If $i > StringLen($aLines[$j]) Then ; Yes, so add final line to count $iWrap_Count += 1 ExitLoop Else ; No, but add line just completed to count $iWrap_Count += 1 ; Check at least 1 word completed or return error If $iLast_Word = 0 Then GUIDelete($hGUI) Return SetError(2, 0, 0) EndIf ; Strip string to point reached $aLines[$j] = StringTrimLeft($aLines[$j], $iLast_Word) ; Trim leading whitespace $aLines[$j] = StringStripWS($aLines[$j], 1) ; Repeat bulid with remaining characters in line EndIf WEnd ; Add the number of wrapped lines to the count $iLine_Count += $iWrap_Count ; Clean up _WinAPI_SelectObject($hDC, $hFont) _WinAPI_ReleaseDC($hWnd, $hDC) GUICtrlDelete($hText_Label) EndIf Next ; Convert lines to pixels and add normal padding $aLabel_Info[3] = ($iLine_Count * $aLabel_Info[1]) + 8 EndIf ; Clean up GUIDelete($hGUI) ; Return array Return $aLabel_Info EndFunc; => _Label_Size Edited December 17, 2008 by Melba23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Valuater Posted December 14, 2008 Share Posted December 14, 2008 Respect here! Imagine my dilemma expandcollapse popup#include <XSkin.au3> ; folder of skin $Skin_Folder = @ScriptDir & "\Skins\Universal" $XSkinGui = XSkinGUICreate("My GUI", 400, 450, $Skin_Folder) $button_1 = XSkinButton("Button 1", 145, 100, 100, 35, "Set_Tray") GUISetState() While 1 MouseOver() Sleep(10) WEnd Func _Labels() Local $sMsg $sMsg = "This is a long line - this is a long line - this is a long line" & @CRLF & @CRLF $sMsg &= "This is an even longer long line - this is an even longer long line - this is an even longer long line" & @CRLF & @CRLF $sMsg &= "And this is a little one" XSkinMsgBox("XSkin", $sMsg) $sMsg = "This is a short line" & @CRLF & @CRLF $sMsg &= "This is a very long line indeed - this is a very long line indeed - this is a very long line indeed - this is a very long line indeed - " $sMsg &= "this is a very long line indeed - this is a very long line indeed - this is a very long line indeed - and this is the end!" XSkinMsgBox("XSkin", $sMsg) $sMsg = "This line contains a long word - generalisation" & @CRLF & @CRLF $sMsg &= "If the width is narrow and the font size large, it may not fit in the label" & @CRLF & @CRLF $sMsg &= "In that case, the label will not be displayed and the error will be set to 2 to indicate that there is a font size / width mismatch" & @CRLF & @CRLF $sMsg &= "There are now a number of blank lines" & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF & @CRLF $sMsg &= "But the UDF still manages to cope, wrapping the lines as required to fit into the width requested and determining the correct height" XSkinMsgBox("XSkin", $sMsg) $sMsg = "This is only a short line" & @CRLF & @CRLF $sMsg &= "This is only a short line" & @CRLF & @CRLF $sMsg &= "This is only a short line" & @CRLF & @CRLF $sMsg &= "This is only a short line" XSkinMsgBox("XSkin", $sMsg) $sMsg = "This is a long line - this is a long line - this is a long line" XSkinTrayBox("XSkin", $sMsg) EndFunc ;==>_Labels Func Set_Tray() _Labels() EndFunc ;==>Set_Tray I needed not only the GUI size and label size... but also the relational button location I think your UDF could be more beneficial to others with this info... rather than create a GUI to test in 8) Link to comment Share on other sites More sharing options...
AlmarM Posted December 14, 2008 Share Posted December 14, 2008 Wauw! Nice done mate AlmarM Minesweeper A minesweeper game created in autoit, source available. _Mouse_UDF An UDF for registering functions to mouse events, made in pure autoit. 2D Hitbox Editor A 2D hitbox editor for quick creation of 2D sphere and rectangle hitboxes. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted December 16, 2008 Author Moderators Share Posted December 16, 2008 @Valuater and AlmarM, Thank you for the kind comments. Valuater, I am afraid that as I do not use XSkin (a lacuna on my part I accept!) I do not understand what you mean by "relational button location". The UDF returns the size of the label needed - can you not work out the locations of other controls in the GUI from that? Could you please rephrase the requirement - only too happy to oblige if I can. M23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Zedna Posted December 16, 2008 Share Posted December 16, 2008 Search forum for _WinAPI_GetTextExtentPoint32 For example here: http://www.autoitscript.com/forum/index.ph...st&p=550588 Resources UDF ResourcesEx UDF AutoIt Forum Search Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted December 17, 2008 Author Moderators Share Posted December 17, 2008 @Zedna,Thanks for the pointer to _WinAPI_GetTextExtentPoint32. There are so many functions in that helpfile that it is sometimes easy to overlook the obvious! But then that is why we have a forum, is it not?Improved version of the UDF now in first post.Děkuju moc,M23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now