Modify

Opened 5 years ago

Last modified 5 years ago

#2786 new Bug

MDI childs don't adjust to parent windows client rect #2

Reported by: Synix <cross.fire@…> Owned by:
Milestone: Component: AutoIt
Version: 3.3.8.1 Severity: None
Keywords: $WS_EX_MDICHILD, $WS_POPUP Cc:

Description

#1013 was about child windows of parent GUIs with $WS_CAPTION style. This is fixed and works fine.

If the parent GUI has the style $WS_POPUP and no caption, the offset of the MDI child window differs with the different border styles and can not be properly aligned on top of the parent GUI. This is a major problem if you e.g. want to work with layered child windows.

I have an example code to show this, with a potential workaround, that at least does work for my Win7 and Win8 machine:

#include <GUIConstants.au3>
Global $aSM_CYBORDER = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYBORDER)
Global $aSM_CYEDGE = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYEDGE) ;3-D counterpart of SM_CYBORDER
Global $aSM_CYDLGFRAME = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYDLGFRAME) ;same as SM_CYFIXEDFRAME
Global $aSM_CYFRAME = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYFRAME) ;same as SM_CYSIZEFRAME
;~ Exit MsgBox(0, "", $aSM_CYBORDER[0] & " - " & $aSM_CYEDGE[0] & " - " & $aSM_CYDLGFRAME[0] & " - " & $aSM_CYFRAME[0])

For $i = 1 To 2
	$bOffsetFix = ($i = 1 ? False : True)
	MsgBox(0, "Offset Fix", $bOffsetFix)
	;$WS_DLGFRAME + $WS_BORDER = $WS_CAPTION
	;$WS_CAPTION always works fine with offset=0
	_GUIs($bOffsetFix, 0)
	_GUIs($bOffsetFix, $WS_DLGFRAME) ;works fine with offset=0
	_GUIs($bOffsetFix, $WS_BORDER)
	_GUIs($bOffsetFix, $WS_THICKFRAME)
	_GUIs($bOffsetFix, BitOR($WS_THICKFRAME, $WS_DLGFRAME))
	_GUIs($bOffsetFix, BitOR($WS_THICKFRAME, $WS_BORDER))
Next

Func _GUIs($bOffsetFix, $iBorder)
	Local $iW = 220, $iH = 220, $iOffset = ($bOffsetFix ? _OffsetFix($iBorder) : 0)

	$hGUI = GUICreate('MDI Example', $iW, $iH, -1, -1, BitOR($WS_POPUP, $iBorder))
	GUISetBkColor(0x000000)
	$hChild = GUICreate('', $iW, $iH,  $iOffset, $iOffset, $WS_POPUP, $WS_EX_MDICHILD, $hGUI)
	GUISetBkColor(0xdddddd)
	GUICtrlCreateLabel('Child GUI offset:  ' & ($iOffset >= 0 ? '+' : '') & $iOffset & ' px' & @CRLF & _
				'Main GUI style:' & @TAB & '   $WS_POPUP             ' & _
				(_StyleInStyle($iBorder, $WS_CAPTION) ?  @CRLF & @TAB & @TAB & ' +$WS_CAPTION' : '') & _
				(_StyleInStyle($iBorder, $WS_BORDER) ? @CRLF & @TAB & @TAB & ' +$WS_BORDER' : '') & _
				(_StyleInStyle($iBorder, $WS_THICKFRAME) ? @CRLF & @TAB & @TAB & ' +$WS_THICKFRAME' : '') & _
				(_StyleInStyle($iBorder, $WS_DLGFRAME) ? @CRLF & @TAB & @TAB & ' +$WS_DLGFRAME' : '') & _
				@CRLF & @CRLF & 'Press [Esc]', 10, 10)
	GUISetState(@SW_SHOW, $hGUI)
	GUISetState(@SW_SHOW, $hChild)
	While GUIGetMsg() <> -3
	WEnd
	GUIDelete($hChild)
	GUIDelete($hGUI)
EndFunc

Func _OffsetFix($iBorder)
	Local $iRet = 0
	If Not _StyleInStyle($iBorder, $WS_CAPTION) Then
		If Not _StyleInStyle($iBorder, $WS_DLGFRAME) Then $iRet += $aSM_CYDLGFRAME[0] ;+3
		If _StyleInStyle($iBorder, $WS_BORDER) Then $iRet -= $aSM_CYBORDER[0] ;-1
		If _StyleInStyle($iBorder, $WS_THICKFRAME) Then
			$iRet -= $aSM_CYFRAME[0] ;-8
			If _StyleInStyle($iBorder, $WS_DLGFRAME) Then
				$iRet += $aSM_CYDLGFRAME[0] ;+3
			Else
				$iRet += $aSM_CYBORDER[0] ;+1
			EndIf
		EndIf
	EndIf
	Return $iRet
EndFunc

Func _StyleInStyle($iSrc, $iSearch)
	Return BitAND($iSrc, $iSearch) = $iSearch
EndFunc

In #1866 someone reported this issue and it got rejected. Though it wasn't a great example with all the different border styles demonstrated. So I follow the comment of trancexx from that ticket: "If anyone sees any sense in this then please make another report."
Please try to fix this internally, so an MDI child always aligns correctly with its parent.

Thanks in advance :)

Attachments (0)

Change History (5)

comment:1 Changed 5 years ago by Synix <cross.fire@…>

I forgot to mention: This issue is still present in v3.3.12.0

comment:2 Changed 5 years ago by Synix <cross.fire@…>

Update

$WS_CAPTION does not always work fine (when using $WS_THICKFRAME), see updated example with new offset fix:

#include <GUIConstants.au3>
Global $aSM_CYBORDER = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYBORDER)
Global $aSM_CYEDGE = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYEDGE) ;3-D counterpart of SM_CYBORDER
Global $aSM_CYDLGFRAME = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYDLGFRAME) ;same as SM_CYFIXEDFRAME
Global $aSM_CYFRAME = DllCall('user32.dll', 'int', 'GetSystemMetrics', 'int', $SM_CYFRAME) ;same as SM_CYSIZEFRAME
;~ Exit MsgBox(0, "", $aSM_CYBORDER[0] & " - " & $aSM_CYEDGE[0] & " - " & $aSM_CYDLGFRAME[0] & " - " & $aSM_CYFRAME[0])

For $i = 1 To 2
	$bOffsetFix = ($i = 1 ? False : True)
	MsgBox(0, "Offset Fix", $bOffsetFix)
	;$WS_DLGFRAME + $WS_BORDER = $WS_CAPTION
	;$WS_CAPTION always works fine with offset=0, except together with $WS_THICKFRAME
	_GUIs($bOffsetFix, 0)
	_GUIs($bOffsetFix, $WS_DLGFRAME) ;works fine with offset=0
	_GUIs($bOffsetFix, $WS_BORDER)
	_GUIs($bOffsetFix, $WS_THICKFRAME)
	_GUIs($bOffsetFix, BitOR($WS_THICKFRAME, $WS_DLGFRAME))
	_GUIs($bOffsetFix, BitOR($WS_THICKFRAME, $WS_BORDER))
	_GUIs($bOffsetFix, BitOR($WS_THICKFRAME, $WS_DLGFRAME, $WS_BORDER)) ;= $WS_CAPTION & $WS_THICKFRAME
Next

Func _GUIs($bOffsetFix, $iBorder)
	Local $iW = 220, $iH = 220, $iOffset = ($bOffsetFix ? _OffsetFix($iBorder) : 0)

	$hGUI = GUICreate('MDI Example', $iW, $iH, -1, -1, BitOR($WS_POPUP, $iBorder))
	GUISetBkColor(0x000000)
	$hChild = GUICreate('', $iW, $iH,  $iOffset, $iOffset, $WS_POPUP, $WS_EX_MDICHILD, $hGUI)
	GUISetBkColor(0xdddddd)
	GUICtrlCreateLabel('Child GUI offset:  ' & ($iOffset >= 0 ? '+' : '') & $iOffset & ' px' & @CRLF & _
				'Main GUI style:' & @TAB & '   $WS_POPUP             ' & _
				(_StyleInStyle($iBorder, $WS_CAPTION) ?  @CRLF & @TAB & @TAB & ' +$WS_CAPTION' : '') & _
				(_StyleInStyle($iBorder, $WS_BORDER) ? @CRLF & @TAB & @TAB & ' +$WS_BORDER' : '') & _
				(_StyleInStyle($iBorder, $WS_THICKFRAME) ? @CRLF & @TAB & @TAB & ' +$WS_THICKFRAME' : '') & _
				(_StyleInStyle($iBorder, $WS_DLGFRAME) ? @CRLF & @TAB & @TAB & ' +$WS_DLGFRAME' : '') & _
				@CRLF & @CRLF & 'Press [Esc]', 10, 10)
	GUISetState(@SW_SHOW, $hGUI)
	GUISetState(@SW_SHOW, $hChild)
	While GUIGetMsg() <> -3
	WEnd
	GUIDelete($hChild)
	GUIDelete($hGUI)
EndFunc

Func _OffsetFix($iBorder)
	Local $iRet = 0
	If _StyleInStyle($iBorder, $WS_THICKFRAME) Then
		$iRet -= $aSM_CYFRAME[0] / 2 ;-(8/2)
		If _StyleInStyle($iBorder, $WS_DLGFRAME) Or _StyleInStyle($iBorder, $WS_BORDER) Then $iRet -= $aSM_CYBORDER[0] ;+1
	ElseIf Not _StyleInStyle($iBorder, $WS_CAPTION) Then
		If Not _StyleInStyle($iBorder, $WS_DLGFRAME) Then $iRet += $aSM_CYDLGFRAME[0] ;+3
		If _StyleInStyle($iBorder, $WS_BORDER) Then $iRet -= $aSM_CYBORDER[0] ;-1
	EndIf
	Return $iRet
EndFunc

Func _StyleInStyle($iSrc, $iSearch)
	Return BitAND($iSrc, $iSearch) = $iSearch
EndFunc

comment:3 Changed 5 years ago by qwert

I would like to confirm that this IS a problem in 3.3.12.0

I've spent many hours trying to get a handle on this. I made a post back in July ("Child window elements become disassociated from parent"), but only today noticed this bug report.

For my application, it somehow seems to be associated the Windows standby mode. I say that because I almost always see the effect when coming out of standby on Windows 8. The child elements are off by a couple hundred pixels, but move "in parallel" when I drag the parent window.

This is a MAJOR PROBLEM that needs a solution or at least a workaround. I'll examine the methods above in detail and try to implement a temporary fix. But I look forward to hearing that there's a solution in the works.

comment:4 Changed 5 years ago by Synix <cross.fire@…>

I also noticed that the offset of an MDI child changes completely, if the child has other styles set than in my example. $WS_CAPTION on the MDI child will cause an offset different from $WS_POPUP. Setting the MDI childs style to 0 will again behave differently.

comment:5 Changed 5 years ago by Synix <cross.fire@…>

Updated my code to fix positioning of windows with captions etc.
The function returns an array with x and y offset values. It's not quite perfect and the bottom half is still a sloppy solution for popupwindows, but it all works pretty well already.

Func _WinMDIGetOffset($hParent, $iStyleChild)
	;returns an array with offset values to fix positioning of an MDI child
	Local $aRet[2] = [0, 0], $iStyleParent

	$iStyleParent = _WinAPI_GetWindowLong($hParent, $GWL_STYLE)
	If BitAND($iStyleChild, $WS_BORDER) = $WS_BORDER Or Not (BitAND($iStyleChild, $WS_POPUP) = $WS_POPUP) Then
		;add each two horizontal/vertical border sizes of the child window
		$aRet[0] += _WinAPI_GetSystemMetrics($SM_CXBORDER) * 2
		$aRet[1] += _WinAPI_GetSystemMetrics($SM_CYBORDER) * 2
		If BitAND($iStyleParent, $WS_BORDER) = $WS_BORDER Or Not (BitAND($iStyleParent, $WS_POPUP) = $WS_POPUP) Then
			;subtract the left and top border of the parent window
			$aRet[0] -= _WinAPI_GetSystemMetrics($SM_CXBORDER)
			$aRet[1] -= _WinAPI_GetSystemMetrics($SM_CYBORDER)
		EndIf
	EndIf
	If BitAND($iStyleChild, $WS_DLGFRAME) = $WS_DLGFRAME Or Not (BitAND($iStyleChild, $WS_POPUP) = $WS_POPUP) Then
		;add each two horizontal/vertical border sizes of the child window
		$aRet[0] += _WinAPI_GetSystemMetrics($SM_CXDLGFRAME) * 2
		$aRet[1] += _WinAPI_GetSystemMetrics($SM_CYDLGFRAME) * 2
		If BitAND($iStyleParent, $WS_DLGFRAME) = $WS_DLGFRAME Or Not (BitAND($iStyleParent, $WS_POPUP) = $WS_POPUP) Then
			;subtract the left and top border of the parent window
			$aRet[0] -= _WinAPI_GetSystemMetrics($SM_CXDLGFRAME)
			$aRet[1] -= _WinAPI_GetSystemMetrics($SM_CYDLGFRAME)
		EndIf
	EndIf
	If BitAND($iStyleChild, $WS_CAPTION) = $WS_CAPTION Then
		$aRet[1] -= _WinAPI_GetSystemMetrics($SM_CYCAPTION)
	EndIf
	If BitAND($iStyleChild, $WS_POPUP) = $WS_POPUP And Not (BitAND($iStyleChild, $WS_CAPTION) = $WS_CAPTION) Then
		;child window is a popup without a caption
		If BitAND($iStyleParent, $WS_THICKFRAME) = $WS_THICKFRAME Then
			$aRet[0] -= _WinAPI_GetSystemMetrics($SM_CXFRAME) / 2
			$aRet[1] -= _WinAPI_GetSystemMetrics($SM_CYFRAME) / 2
			If BitAND($iStyleParent, $WS_DLGFRAME) = $WS_DLGFRAME Or BitAND($iStyleParent, $WS_BORDER) = $WS_BORDER Then
				$aRet[0] -= _WinAPI_GetSystemMetrics($SM_CXBORDER)
				$aRet[1] -= _WinAPI_GetSystemMetrics($SM_CYBORDER)
			EndIf
		ElseIf Not (BitAND($iStyleParent, $WS_CAPTION) = $WS_CAPTION) Then
			If Not (BitAND($iStyleParent, $WS_DLGFRAME) = $WS_DLGFRAME) Then
				$aRet[0] += _WinAPI_GetSystemMetrics($SM_CXDLGFRAME)
				$aRet[1] += _WinAPI_GetSystemMetrics($SM_CYDLGFRAME)
			EndIf
			If BitAND($iStyleParent, $WS_BORDER) = $WS_BORDER Then
				$aRet[0] -= _WinAPI_GetSystemMetrics($SM_CXBORDER)
				$aRet[1] -= _WinAPI_GetSystemMetrics($SM_CYBORDER)
			EndIf
		EndIf
	EndIf

	Return $aRet
EndFunc

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as new The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.