Sign in to follow this  
Followers 0

StringSize - M23 - New version 16 Aug 11

58 posts in this topic




Posted

Hang on a sec... You release plenty of other UDF's for which this is needed (Toast, Extended MsgBox etc.) and never post this??

Is making a GUI the most effective way of doing this? MeasureString is probably a better way to go.

Share this post


Link to post
Share on other sites

Posted

Mat,

You release plenty of other UDF's for which this is needed (Toast, Extended MsgBox etc.) and never post this??

Not quite, the UDF was indeed included in the topics of the UDFs you mention - and in many other topics scattered across the Help sections - I just never released it as a standalone UDF in the Examples section. :)

Not sure I understand your second point. Why else do you need to get the size of a chunk of text if not to put it in a GUI? :idea:

M23

Share this post


Link to post
Share on other sites

Posted

There are plenty of UDF's like this. Even I made one and posted in Examples. I can't find it for you now, though.

Share this post


Link to post
Share on other sites

Posted (edited)

Melba, can this be used to get stringsize in order to resize a "progressOn" window, to fit the strings within it?(currently it has a default size)

I'd like a way to resize the ProgressOn window based on the text within it. Too long strings get cut off.

Edited by Datenshi

Share this post


Link to post
Share on other sites

Posted

There are plenty of UDF's like this. Even I made one and posted in Examples. I can't find it for you now, though.

Here you go.

Share this post


Link to post
Share on other sites

Posted

My point is that always making GUI's is not a particularly glamorous solution. As I have not tried to make the function myself I cannot say if it will be easy to do or not, but making a GUI and then deleting it just to get the size of a label... Sounds more like a workaround to me.

An idea I've just had is possibly a new function for sizing an existing label, then returning the size. That would stop the need for extra GUI's, save on speed, and do what this is probably going to be used for anyway. You only need to input an id for a label and a width, and it will do its stuff and return a height.

As mentioned, I am not 'in the know' and so all this is complete speculation from looking at your code.

Share this post


Link to post
Share on other sites

Posted (edited)

Mat,

Sounds more like a workaround to me

Please do try another way - there is room for all of them. :idea:

Kip,

There are plenty of UDF's like this. Even I made one and posted in Examples

This is not like your UDF - try it and see! :)

M23

Edit: Speeling!

Edited by Melba23

Share this post


Link to post
Share on other sites

Posted

Cheers, ive been using a version of this already, may be differant in the version you posted but note that i found the need to guiswitch to the gui i was creating when adding controls after using stringsize.

Local $HeaderTextSize = _StringSize($sHeaderText, $oSelf.HeaderFontSize, $oSelf.HeaderFontWeight,$oSelf.HeaderFontAttribute, $oSelf.HeaderFontFontName)
	GUISwitch(HWnd($oSelf.GuiHandle))
	$HeaderTextSize[1] += 2
	$hCtrl = _oGUICtrlLabel($sHeaderText, $iLeft, $iTop, $HeaderTextSize[2], $HeaderTextSize[1], BitOR($SS_CENTER,$SS_CENTERIMAGE))

Share this post


Link to post
Share on other sites

Posted

Datenshi,

Apologies, I missed your post earlier in the noise! :)

can this be used to get stringsize in order to resize a "progressOn" window

Of course, but you need to use WinMove and ControlMove to resize the dialog, labels and progress bar. How you do it depends on whether you want to use a very wide dialog with a single line of text or a block of wrapped text with a normal width dialog (see the examples): :idea:

#include "StringSize.au3"

; The long text we want to fit in the dialog
$sText = "I am a very long line and I will not fit within the width of a normal ProgressOn dialog even with a lot of dieting!"

; Wide dialog
$aSize = _StringSize($sText, 11, 800, 0, "Segoe UI")

ProgressOn("Progress Meter", $sText, "0 percent")

WinMove("Progress Meter", "", (@DesktopWidth - ($aSize[2] + 30)) / 2, Default, $aSize[2] + 30)
ControlMove("Progress Meter", "", "[CLASS:msctls_progress32; INSTANCE:1]", Default, Default, $aSize[2] - 15)

For $i = 10 to 100 step 10
    Sleep(1000)
    ProgressSet( $i, $i & " percent")
Next
ProgressSet(100 , "Done", "Complete")
Sleep(500)
ProgressOff()

; Wrapped text
$aSize = _StringSize($sText, 11, 800, 0 , "Segoe UI", 250)

ProgressOn("Progress Meter", $aSize[0], "0 percent")

WinMove("Progress Meter", "", (@DesktopWidth - ($aSize[2] + 30)) / 2, Default, $aSize[2] + 30, $aSize[3] + 80)
ControlMove("Progress Meter", "", "[CLASS:Static; INSTANCE:1]", Default, Default, $aSize[2], $aSize[3])
ControlMove("Progress Meter", "", "[CLASS:msctls_progress32; INSTANCE:1]", Default, $aSize[3] - 5, $aSize[2] - 15)
ControlMove("Progress Meter", "", "[CLASS:Static; INSTANCE:2]", Default, $aSize[3] + 20)

For $i = 10 to 100 step 10
    Sleep(1000)
    ProgressSet( $i, $i & " percent")
Next
ProgressSet(100 , "Done", "Complete")
Sleep(500)
ProgressOff()

I had to guess at the caption font, but the values I chose seem to work well - and the values for resizing the dialog and the controls were chosen empirically as well. :( Why the progress bar needs to be set to a smaller width I have no idea - blame Bill Gates! :)

I hope that is what you wanted. :)

M23

Share this post


Link to post
Share on other sites

Posted

Yoriz,

i found the need to guiswitch to the gui i was creating when adding controls after using stringsize

Sorry about that. :idea: As MAt pointed out, the UDF does create and then destroy a GUI (to get the font object required for sizing) so I imagine this is what is causing the problem.

I have only ever used the UDF BEFORE creating the GUI and any of its controls so I had not noticed this before. Let me think for a while about how we might get around it. :)

M23

Share this post


Link to post
Share on other sites

Posted (edited)

Hey no need to appologise, i just got used to swoppping back :idea: . would this work ?

$hWndPrevious = GUISwitch(WinGetHandle("Program Manager","")) ; at the start of your udf function

If $hWndPrevious Then GUISwitch($hWndPrevious) ; just before returning from your udf function

There may be a nicer way to get the previous window.

Edited by Yoriz

Share this post


Link to post
Share on other sites

Posted

Yoriz,

I do not get the same problem as you when I use StringSize and then try to create more controls in a GUI: :idea:

#include <GUIConstantsEx.au3>
#include "StringSize.au3"

$sText = "pppppppppppp ppppp ppppppppp pppppppppp ppppppppppp ppppppp ppppp ppp"

; Create GUI
$hGUI = GUICreate("Test", 500, 500)

; Create first control
$hButton = GUICtrlCreateButton("Test", 10, 100, 80, 30)

; Run StringSize
$aSize = _StringSize($sText, Default, Default, Default, Default, 200)

; Create second control
$hLabel = GUICtrlCreateLabel($aSize[0], 10, 10, 200, $aSize[3])

GUISetState(@SW_SHOW, $hGUI)

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
	EndSwitch
WEnd

For me that code runs without problem and both controls appear in the GUI.

Your code looks as if you are using AutoItObject - I wonder if that might be causing the problem? I do not know enough about AIO to make any sensible comments about the possibility.

M23

Share this post


Link to post
Share on other sites

Posted

Sorry about the missunderstanding, yes im using oop and its probably not that causing it either, i bet im probably creating guis in a strange way thats causing the problem. :idea:

Share this post


Link to post
Share on other sites

Posted

Yoriz,

Could you post some code that does not work so I could play with it. I already have the AIO UDFs. :idea:

M23

Share this post


Link to post
Share on other sites

Posted

I am using your code kinda backwards - I know my width maximum (160) and I know my height maximum (55), so I run my string through _StringSize with the font size set too high and decrease by one, and compare $array[3] to 55. If it is less than 55, we're gold, ExitLoop and go. Sometimes it hangs though, here is an example:

For $a = 24 To 1 Step -1
	$findIt = _StringSize("CAT5E Crossover Coupler CAT5E Crossover Coupler", $a, Default, Default, "Arial", 160)
	If $findIt[3] <= 55 Then
		MsgBox(0, "font size to use: " & $a)
		ExitLoop
	EndIf
Next

The above works, but run them together as "CAT5E Crossover CouplerCAT5E Crossover Coupler" and it hangs. Got a solution up your sleeve?

Thanks, wonderful UDF as always!

Ian

Share this post


Link to post
Share on other sites

Posted

llewxam,

Got a solution up your sleeve?

Yes, I messed up the error-checking return code - which I had already realised while I was away last week. :idea:

The UDF should return an error if it cannot fit a word into the minimum width required - which should happen a lot in your case! I messed up the original error return code so you did not get an error returned and the UDF just ran on and on........

I have amended the UDF in the first post to get over this - but you will now need to amend your code to look for the @error returns which you will now get:

For $a = 24 To 1 Step -1
	ConsoleWrite($a & " - ")
    $findIt = _StringSize("CAT5E Crossover CouplerCAT5E Crossover Coupler", $a, Default, Default, "Arial", 160)
	If Not @error Then
		ConsoleWrite($findIt[3] & @CRLF)
		If $findIt[3] <= 55 Then
			MsgBox(0, "", "font size to use: " & $a)
			ExitLoop
		EndIf
	Else
		ConsoleWrite("error " & @error & @CRLF)
    EndIf
Next

The ConsoleWrites are obviously there to show you what is going on - you can delete them when you have seen what is going on:

24 - error 4
23 - error 4
22 - error 4
21 - error 4
20 - error 4
19 - error 4
18 - error 4
17 - 129
16 - 124
15 - 119
14 - 67
13 - 61
12 - 58
11 - 52

Sorry about that - I will try to do better next time! :)

M23

Share this post


Link to post
Share on other sites

Posted

Sorry about that - I will try to do better next time! :idea:

hahaha!!

I actually kinda switched things around to get around the issue differently, but will update the UDF abyway just in case. What I did was:

$fontSize = 0
Do
	$findFontSize += 1
	$findit = _StringSize($PlabelName, $findFontSize, Default, Default, "Arial", 160)
Until $findit[3] > 55
$fontSize = $findFontSize - 1

Thankfully most of the terms used in the app aren't very large, but I want to make it not puke if at all possible :)

Thanks again, again

Ian

Share this post


Link to post
Share on other sites

Posted

llewxam,

That will work because by going up in point value you should not hit an error state before finding a valid size.

But as a matter of principle, it is a good idea to do some errorchecking whan you call complex UDFs (especially when you write them!). :idea:

M23

Share this post


Link to post
Share on other sites

Posted

Nice approach :), saved to toolbox for later use :idea: ...

Share this post


Link to post
Share on other sites

Posted

There is an error with changing font size:

#include <StringSize.au3>

$String='Long string, very very long'
$a=_StringSize($String, 18)

#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#Region ### START Koda GUI section ### Form=

$Form1 = GUICreate("Form1", $a[2], $a[3])
GUISetFont(18)
$Label1 = GUICtrlCreateLabel($a[0], 0, 0, $a[2], $a[3])
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

While 1
	$nMsg = GUIGetMsg()
	Switch $nMsg
		Case $GUI_EVENT_CLOSE
			Exit

	EndSwitch
WEnd

Solve:

GuiSetFont doesn't accept keyword Default for Fontname, there needs empty string "".

Corrected UDF code:

#include-once

; #INDEX# ============================================================================================================
; Title .........: _StringSize
; AutoIt Version : v3.2.12.1 or higher
; Language ......: English
; Description ...: Returns size of rectangle required to display string - width can be chosen
; Remarks .......:
; Note ..........:
; Author(s) .....: Melba23
; ====================================================================================================================

;#AutoIt3Wrapper_au3check_parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6

; #CURRENT# ==========================================================================================================
; _StringSize: Returns size of rectangle required to display string - maximum permitted width can be chosen
; ====================================================================================================================

; #INTERNAL_USE_ONLY#=================================================================================================
; _StringSize_Error: Returns from error condition after DC and GUI clear up
; ====================================================================================================================

; #FUNCTION# =========================================================================================================
; Name...........: _StringSize
; Description ...: Returns size of rectangle required to display string - maximum permitted width can be chosen
; Syntax ........: _StringSize($sText[, $iSize[, $iWeight[, $iAttrib[, $sName[, $iWidth]]]]])
; Parameters ....: $sText   - String to display
;                  $iSize   - [optional] Font size in points - default AutoIt GUI default
;                  $iWeight - [optional] Font weight (400 = normal) - default AutoIt GUI default
;                  $iAttrib - [optional] Font attribute (0-Normal, 2-Italic, 4-Underline, 8 Strike - default AutoIt
;                  $sName   - [optional] Font name - default AutoIt GUI default
;                  $iWidth  - [optional] Width of rectangle - default is unwrapped width of string
; Requirement(s) : v3.2.12.1 or higher
; Return values .: Success - Returns array with details of rectangle required for text:
;                  |$array[0] = String formatted with @CRLF at required wrap points
;                  |$array[1] = Height of single line in selected font
;                  |$array[2] = Width of rectangle required to hold formatted string
;                  |$array[3] = Height of rectangle required to hold formatted string
;                  Failure - Returns 0 and sets @error:
;                  |1 - Incorrect parameter type (@extended = parameter index)
;                  |2 - Failure to create GUI to test label size
;                  |3 - DLL call error - extended set to indicate which
;                  |4 - Font too large for chosen width - longest word will not fit
; Author ........: Melba23
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: Yes
;=====================================================================================================================
Func _StringSize($sText, $iSize = Default, $iWeight = Default, $iAttrib = Default, $sName = Default, $iWidth = 0)

	Local $avSize_Info[4], $aRet, $iLine_Width = 0, $iLast_Word, $iWrap_Count
	Local $hLabel_Handle, $hFont, $hDC, $oFont, $tSize = DllStructCreate("int X;int Y")

	; Correction $sName:
	If Number($sName) = -1 Then $sName=""; If -1 or Default used, set to empty string.

	; Check parameters are correct type
	If Not IsString($sText) Then Return SetError(1, 1, 0)
	If Not IsNumber($iSize) And $iSize <> Default   Then Return SetError(1, 2, 0)
	If Not IsInt($iWeight)  And $iWeight <> Default Then Return SetError(1, 3, 0)
	If Not IsInt($iAttrib)  And $iAttrib <> Default Then Return SetError(1, 4, 0)
	If Not IsString($sName) And $sName <> Default   Then Return SetError(1, 5, 0)
	If Not IsNumber($iWidth) Then Return SetError(1, 6, 0)

	; Create GUI to contain test labels, set to required font parameters
	Local $hGUI = GUICreate("", 1200, 500, 10, 10)
		If $hGUI = 0 Then Return SetError(2, 0, 0)
		GUISetFont($iSize, $iWeight, $iAttrib, $sName)

	; Store unwrapped text
	$avSize_Info[0] = $sText

	; Ensure EoL is @CRLF and break text into lines
	If StringInStr($sText, @CRLF) = 0 Then StringRegExpReplace($sText, "[\x0a|\x0d]", @CRLF)
	Local $asLines = StringSplit($sText, @CRLF, 1)

	; Draw label with unwrapped lines to check on max width
	Local $hText_Label = GUICtrlCreateLabel($sText, 10, 10)
	Local $aiPos = ControlGetPos($hGUI, "", $hText_Label)

	GUISetState(@SW_HIDE)

	GUICtrlDelete($hText_Label)

	; Store line height for this font size after removing label padding (always 8)
	$avSize_Info[1] = ($aiPos[3] - 8)/ $asLines[0]
	; Store width and height of this label
	$avSize_Info[2] = $aiPos[2]
	$avSize_Info[3] = $aiPos[3] - 4 ; Reduce margin

	; Check if wrapping is required
	If $aiPos[2] > $iWidth And $iWidth > 0 Then

		; Set returned text element to null
		$avSize_Info[0] = ""

		; Set width element to max allowed
		$avSize_Info[2] = $iWidth

		; Set line count to zero
		Local $iLine_Count = 0

		; Take each line in turn
		For $j = 1 To $asLines[0]

			; Size this line unwrapped
			$hText_Label = GUICtrlCreateLabel($asLines[$j], 10, 10)
			$aiPos = ControlGetPos($hGUI, "", $hText_Label)
			GUICtrlDelete($hText_Label)

			; Check wrap status
			If $aiPos[2] < $iWidth Then
				; No wrap needed so count line and store
				$iLine_Count += 1
				$avSize_Info[0] &= $asLines[$j] & @CRLF
			Else
				; Wrap needed so need to count wrapped lines

				; Create label to hold line as it grows
				$hText_Label = GUICtrlCreateLabel("", 0, 0)
				; Initialise Point32 method
				$hLabel_Handle = ControlGetHandle($hGui, "", $hText_Label)
				; Get DC with selected font
				$aRet = DllCall("User32.dll", "hwnd", "GetDC", "hwnd", $hLabel_Handle)
				If @error Then _StringSize_Error(3, 1, $hLabel_Handle, 0, $hGUI)
				$hDC = $aRet[0]
				$aRet = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hLabel_Handle, "int", 0x0031, "wparam", 0, "lparam", 0) ; $WM_GetFont
				If @error Then _StringSize_Error(3, 2, $hLabel_Handle, $hDC, $hGUI)
				$hFont = $aRet[0]
				$aRet = DllCall("GDI32.dll", "hwnd", "SelectObject", "hwnd", $hDC, "hwnd", $hFont)
				If @error Then _StringSize_Error(3, 3, $hLabel_Handle, $hDC, $hGUI)
				$oFont = $aRet[0]
				If $oFont = 0 Then _StringSize_Error(3, 4, $hLabel_Handle, $hDC, $hGUI)

				; 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($asLines[$j])

						; Is this just past a word ending?
						If StringMid($asLines[$j], $i, 1) = " " Then $iLast_Word = $i - 1
						; Increase line by one character
						Local $sTest_Line = StringMid($asLines[$j], 1, $i)
						; Place line in label
						GUICtrlSetData($hText_Label, $sTest_Line)

						; Get line length into size structure
						$iSize = StringLen($sTest_Line)
						DllCall("GDI32.dll", "int", "GetTextExtentPoint32", "hwnd", $hDC, "str", $sTest_Line, "int", $iSize, "ptr", DllStructGetPtr($tSize))
						If @error Then _StringSize_Error(3, 5, $hLabel_Handle, $hDC, $hGUI)
						$iLine_Width = DllStructGetData($tSize, "X")

						; If too long exit the loop
						If $iLine_Width >= $iWidth - Int($iSize / 2) Then ExitLoop
					Next

					; End of the line of text?
					If $i > StringLen($asLines[$j]) Then
						; Yes, so add final line to count
						$iWrap_Count += 1
						; Store line
						$avSize_Info[0] &= $sTest_Line & @CRLF
						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
							_StringSize_Error(4, 0, $hLabel_Handle, $hDC, $hGUI)
						EndIf
						; Store line up to end of last word
						$avSize_Info[0] &= StringLeft($sTest_Line, $iLast_Word) & @CRLF
						; Strip string to point reached
						$asLines[$j] = StringTrimLeft($asLines[$j], $iLast_Word)
						; Trim leading whitespace
						$asLines[$j] = StringStripWS($asLines[$j], 1)
						; Repeat with remaining characters in line
					EndIf

				WEnd

				; Add the number of wrapped lines to the count
				$iLine_Count += $iWrap_Count

				; Clean up
				DllCall("User32.dll", "int", "ReleaseDC", "hwnd", $hLabel_Handle, "hwnd", $hDC)
				If @error Then _StringSize_Error(3, 6, $hLabel_Handle, $hDC, $hGUI)
				GUICtrlDelete($hText_Label)

			EndIf

		Next

		; Convert lines to pixels and add reduced margin
		$avSize_Info[3] = ($iLine_Count * $avSize_Info[1]) + 4

	EndIf

	; Clean up
	GUIDelete($hGUI)

	; Return array
	Return $avSize_Info

EndFunc ; => _StringSize

; #INTERNAL_USE_ONLY#============================================================================================================
; Name...........: _StringSize_Error
; Description ...: Returns from error condition after DC and GUI clear up
; Syntax ........: _StringSize_Error($iError, $iExtended, $hLabel_Handle, $hDC, $hGUI)
; Parameters ....: $iError  - required error value to return
;                  $iExtended - required extended value to return
;                  $hLabel_Handle, $hDC, $hGUI - variables as set in _StringSize function
; Author ........: Melba23
; Modified.......:
; Remarks .......: This function is used internally by _StringSize
; ===============================================================================================================================
Func _StringSize_Error($iError, $iExtended, $hLabel_Handle, $hDC, $hGUI)

	; Release DC if created
	DllCall("User32.dll", "int", "ReleaseDC", "hwnd", $hLabel_Handle, "hwnd", $hDC)
	; Delete GUI
	GUIDelete($hGUI)
	; Return with extended set
	Return SetError($iError, $iExtended, 0)

EndFunc ; => _StringSize_Error

Share this post


Link to post
Share on other sites

Posted

xrewndel,

Thank you for that. ;)

I have amended the code in the first post to correct the error - although I have not done it quite as you suggested. :)

M23

Share this post


Link to post
Share on other sites

Posted

Wow this is a really great script! Is there any way to add an option for max height?

Share this post


Link to post
Share on other sites

Posted

kickarse.

Glad you like it. :x

Is there any way to add an option for max height?

Not as such, but as the UDF calculates the height of a line and the overall string when wrapped, I can see several ways to code a wrapper which deals with heights (see llewxam's posts above for an example).

Could you be a bit more specific about what you want to do? I can then see how we might go about it. :P

M23

Share this post


Link to post
Share on other sites

Posted

I imagine (As with message boxes and such) that there is a maximum height that you want. Beyond that, it should report an error or use scrollbars etc. etc.

Using MsgBox with a ridiculously large string results in you not being able to move the window. That's not good.

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0

  • Recently Browsing   0 members

    No registered users viewing this page.