Войти

Показать полную графическую версию : определение z-порядка для для гуи элемента


Ботяра
20-02-2023, 11:23
Существует ли в природе метод, позволяющий для каждого конкретного гуи элемента, узнать его z-порядковый номер? Может быть какие-нибудь апи есть?...Уточню некоторые детали. Если я создаю гуи элемент, то каждый последующий элемент по умолчанию будет находится поверх предыдущего, это в нормальном случае, но если я задействую функцию GUICtrlSetState($ctrl, $GUI_ONTOP) для этого элемента, то он отправится в самый низ(как ни странно, но в автоите у меня это работает именно так, - вниз а не вверх). И вот в этом случае, хотелось бы иметь возможность настраивать взаимное положение элементов в зависимости от ситуации, а для этого нужно знать, z-порядок для каждого элемента в любой момент времени.

iglezz
21-02-2023, 21:48
Ботяра,
Тут всё немного сложнее, чем кажется на первый взгляд.

Z-order направлен по вымышленной оси от экрана наружу (к пользователю) (z-order (https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features#z-order)).
Дочерние окна (в терминологии WinAPI, кнопки и прочие штуки -- окна определённого класса) при содании добавляются в конец z-order (CreateWindowEx (https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa#remarks)). Т.е. первый элемент -- самый верхний в порядке, второй ниже по иерархии, третий еще ниже и т.д.

Windows по умолчанию так и отрисовывает - сначала самый верхний первый, потом второй, ...
Для правильной отрисовки (с обрезкой перекрывающихся частей окон) требуется присвоить накладывающимся элементам стиль WS_CLIPSIBLINGS (Child Window Update Region (https://learn.microsoft.com/en-us/windows/win32/gdi/child-window-update-region), последний абзац).
Альтернативный способ (со своими особенностями) - присвоить родительскому окну расширенный стиль WS_EX_COMPOSITED.

В скрипте-примере:
- получение массива дочерних окон в z-порядке через _WinAPI_GetTopWindow, _WinAPI_GetWindow(.., GW_HWNDNEXT)
- изменение z-order через _WinAPI_SetWindowPos
- быстрая отрисовка при массовом изменении окон (здесь для функции сброса) через _WinAPI_BeginDeferWindowPos, _WinAPI_DeferWindowPos, _WinAPI_EndDeferWindowPos

AutoItSetOption("GUIOnEventMode", 1)

#include <WinAPI.au3>
#include <WinAPISysWin.au3>
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>

Main()


Func Main()
Global $hGUI, $hLabels[5], $idButtons[3][4], $hControls[256]
Global Enum $MTOP, $MHIGHER, $MLOWER, $MBOTTOM

$hGUI = GUICreate("Z-order playground", 600, 600)
GUISetOnEvent($GUI_EVENT_CLOSE, "EV_Exit")

GUICreateControls()

While 1
Sleep(100)
WEnd
EndFunc


Func GUICreateControls()
Local $GW, $GH, $tRECT, $top

$tRECT = _WinAPI_GetClientRect($hGUI)
$GW = DllStructGetData($tRECT, "Right")
$GH = DllStructGetData($tRECT, "Bottom")

$hLabels[0] = GUICtrlCreateLabel_My(0, 10, 10, 230, 55, 0xFFAAAA)
$hLabels[1] = GUICtrlCreateLabel_My(1, 35, 40, 190, 70, 0xAAFFAA)
$hLabels[2] = GUICtrlCreateLabel_My(2, 60, 45, 150, 90, 0xAAAAFF)

$hLabels[3] = GUICtrlCreateLabel_My(3, 10, 140, $GW/2-20, $GH-150, 0xFFFFFF)
$hLabels[4] = GUICtrlCreateLabel_My(4, 10+$GW/2, 140, $GW/2-20, $GH-150, 0xFFFFFF)

For $i = 0 To 2
$top = 10+$i*30
$idButtons[$i][0] = GUICtrlCreateButton("top", 250, $top, 60, 24)
GUICtrlSetOnEvent(-1, "EV_ChangeZOrder")
$idButtons[$i][1] = GUICtrlCreateButton("higher", 250+65*1, $top, 60, 24)
GUICtrlSetOnEvent(-1, "EV_ChangeZOrder")
$idButtons[$i][2] = GUICtrlCreateButton("lower", 250+65*2, $top, 60, 24)
GUICtrlSetOnEvent(-1, "EV_ChangeZOrder")
$idButtons[$i][3] = GUICtrlCreateButton("bottom", 250+65*3, $top, 60, 24)
GUICtrlSetOnEvent(-1, "EV_ChangeZOrder")
Next

GUICtrlCreateButton("RESET", $GW-70, 10, 60, 24)
GUICtrlSetOnEvent(-1, "GUIResetControls")

$hControls = GUIGetChildsList($hGUI)

UpdateWindowInfo()
UpdateWindowInfo2()

GUISetState(@SW_SHOW)
EndFunc


Func GUIResetControls()
Static Local $swp_flags = BitOr($SWP_NOMOVE, $SWP_NOSIZE)
Local $HDWP

$HDWP = _WinAPI_BeginDeferWindowPos(UBound($hControls))
$HDWP = _WinAPI_DeferWindowPos($HDWP, $hControls[0], $HWND_TOP, 0,0,0,0, $swp_flags)
For $i = 1 To UBound($hControls) - 1
$HDWP = _WinAPI_DeferWindowPos($HDWP, $hControls[$i], $hControls[$i-1], 0,0,0,0, $swp_flags)
Next
_WinAPI_EndDeferWindowPos($HDWP)

UpdateWindowInfo()
UpdateWindowInfo2()
EndFunc


Func GUICtrlCreateLabel_My($idx = "@", $x = 0, $y = 0, $w = 0, $h = 0, $bkcolor = $GUI_BKCOLOR_DEFAULT)
Local $id = GUICtrlCreateLabel("", $x, $y, $w, $h, $WS_CLIPSIBLINGS)
Local $hwnd = GUICtrlGetHandle($id)
Local $text = StringFormat("[%i] 0x%08X", $idx, $hwnd) & @crlf
_WinAPI_SetWindowText($hwnd, $text & $text & $text & $text & $text)
GUICtrlSetBkColor(-1, $bkcolor)
GUICtrlSetFont(-1, 9, 400, 0, "Consolas")
Return $hwnd
EndFunc


Func GUIGetChildsList($parent)
Local $count = 0, $array[256]

$array[0] = _WinAPI_GetTopWindow($hGUI)
While True
$count += 1
$array[$count] = _WinAPI_GetWindow($array[$count-1], $GW_HWNDNEXT)
If $array[$count] = 0 Then ExitLoop
WEnd

ReDim $array[$count]

Return $array
EndFunc


Func FindInArray($value, $array)
For $i = 0 To UBound($array) - 1
if $value = $array[$i] Then Return $i
Next

Return -1
EndFunc


Func UpdateWindowInfo()
Local $text, $sfmt = "%-12s = 0x%08X\n"

$text = StringFormat("[3] 0x%08X", $hLabels[3]) & @crlf & @crlf
$text &= StringFormat($sfmt, "hGUI", $hGUI)
$text &= StringFormat($sfmt, "parent c", _WinAPI_GetParent($hLabels[1]))
$text &= StringFormat($sfmt, "top", _WinAPI_GetTopWindow($hGUI))
$text &= StringFormat($sfmt, "first c", _WinAPI_GetWindow($hLabels[1], $GW_HWNDFIRST))
$text &= StringFormat($sfmt, "last", _WinAPI_GetWindow(_WinAPI_GetTopWindow($hGUI), $GW_HWNDLAST))
$text &= StringFormat($sfmt, "last c", _WinAPI_GetWindow($hLabels[1], $GW_HWNDLAST))
$text &= StringFormat($sfmt, "next c(1)", _WinAPI_GetWindow($hLabels[1], $GW_HWNDNEXT))

_WinAPI_SetWindowText($hLabels[3], $text)
EndFunc


Func UpdateWindowInfo2()
Local $ctl = GUIGetChildsList($hGUI)
Local $text = StringFormat("[4] 0x%08X\n\n%s\n", $hLabels[4],"Z-order:")

For $i = 0 To UBound($ctl) - 1
$text &= $ctl[$i] _
& (FindInArray($ctl[$i], $hLabels)>=0 ? (" :: " & FindInArray($ctl[$i], $hLabels)) : "" ) _
& ($i = 0 ? " :: first" : "" ) _
& @crlf
Next

_WinAPI_SetWindowText($hLabels[4], $text)
EndFunc


Func GUIControlChangeZOrder($hwnd, $action)
Static Local $swp_flags = BitOr($SWP_NOMOVE, $SWP_NOSIZE)

Switch $action
Case $MTOP
_WinAPI_SetWindowPos($hwnd, $HWND_TOP, 0,0,0,0, $swp_flags)

Case $MHIGHER
If $hwnd <> _WinAPI_GetTopWindow($hGUI) Then
_WinAPI_SetWindowPos(_WinAPI_GetWindow($hwnd, $GW_HWNDPREV), $hwnd, 0,0,0,0, $swp_flags)
EndIf

Case $MLOWER
If $hwnd <> _WinAPI_GetWindow($hwnd, $GW_HWNDLAST) Then
_WinAPI_SetWindowPos($hwnd, _WinAPI_GetWindow($hwnd, $GW_HWNDNEXT), 0,0,0,0, $swp_flags)
EndIf

Case $MBOTTOM
_WinAPI_SetWindowPos($hwnd, $HWND_BOTTOM, 0,0,0,0, $swp_flags)

EndSwitch

UpdateWindowInfo()
UpdateWindowInfo2()
EndFunc


Func EV_ChangeZOrder()
For $ctl = 0 To 2
For $action = 0 To 3
If $idButtons[$ctl][$action] = @GUI_CtrlId Then ExitLoop 2
Next
Next

GUIControlChangeZOrder($hLabels[$ctl], $action)
EndFunc


Func EV_Exit()
GUIDelete($hGUI)
Exit
EndFunc

Ботяра
22-02-2023, 10:48
В скрипте-примере:
- получение массива дочерних окон в z-порядке через _WinAPI_GetTopWindow, _WinAPI_GetWindow(.., GW_HWNDNEXT)
- изменение z-order через _WinAPI_SetWindowPos
- быстрая отрисовка при массовом изменении окон (здесь для функции сброса) через _WinAPI_BeginDeferWindowPos, _WinAPI_DeferWindowPos, _WinAPI_EndDeferWindowPos »

Спасибо! Это то, что надо. Все работает!




© OSzone.net 2001-2012