mpower

$WS_EX_LAYERED - I'm confused!

8 posts in this topic

#1 ·  Posted (edited)

Hi guys,

I've been trying to create a working slide-out panel GUI but I am getting confused with $WS_EX_LAYERED.

Reading here: "https://www.autoitscript.com/autoit3/docs/functions/GUICreate.htm#Extended Style Table"

It says " With $WS_EX_LAYERED it is possible to have a transparent picture on a background picture defined in the parent window." - Perfect that's what I need. So I use this style and manage to get my GUI to have a transparent PNG background (see attached png file for the example bg).

Now, the problem I have here is that after setting the background I go on to create some controls, but they are not showing up! What gives?

My code is here, what am I doing wrong? I'd really like to wrap my head around using PNG's as GUI elements (background and controls). 

#include-once
#NoTrayIcon
#AutoIt3Wrapper_Outfile=Example.exe
#AutoIt3Wrapper_Compression=4
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_UseX64=n
#include <GDIPlus.au3>
#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <WinAPIEx.au3>

Global $bAero
Global Const $hDwmApiDll = DllOpen("dwmapi.dll"), $sChkAero = DllStructCreate("int;")
DllCall($hDwmApiDll, "int", "DwmIsCompositionEnabled", "ptr", DllStructGetPtr($sChkAero))
$bAero = DllStructGetData($sChkAero, 1)
If Not $bAero Then ;check for Aero as transparent png GUI requires Aero
    MsgBox(16, 'Sorry', 'Sorry, Aero must be enabled for this application to work!')
    Exit
EndIf

$hGUI = GUICreate("", 715, 683, -1, 0, $WS_POPUP, $WS_EX_LAYERED + $WS_EX_TOPMOST, WinGetHandle(AutoItWinGetTitle()))
;remove application taskbar icon
$hIcon = _WinAPI_GetClassLongEx($hGUI, $GCL_HICON)
_WinAPI_DestroyIcon($hIcon)
_WinAPI_SetClassLongEx($hGUI, $GCL_HICON, 0)
_WinAPI_SetClassLongEx($hGUI, $GCL_HICONSM, 0)

;set the png file as the gui background
_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\panel.png')
SetBitmap($hGUI, $hImage)
_GDIPlus_Shutdown()

;create some controls - WHICH DONT SHOW UP :(
GUISetFont(18, 100, Default, 'Segoe UI', $hGUI, 5)
GUICtrlCreateLabel('Quicksearch:', 10, 25)
GUICtrlCreateInput('', 150, 20, 100, 35)
GUICtrlCreateLabel('Customise', 525, 25)

GUISetState(@SW_SHOW, $hGUI)

While 1
    $msg = GUIGetMsg(1)
    Switch $msg[1]
    Case $hGUI
        Switch $msg[0]
            Case $GUI_EVENT_CLOSE, $GUI_EVENT_SECONDARYDOWN ;let's exit on right click (or esc or alt+f4)
                Exit
        EndSwitch
    EndSwitch
WEnd

Func SetBitmap($hGUI, $hImage, $iOpacity = 255)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", 1)
    _WinAPI_UpdateLayeredWindow($hGUI, $hGUI, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc

Somewhat related, the other thing I have found on the same Docs page it says "$WS_EX_LAYERED Creates a layered window. Note that this cannot be used for child windows."

But the funny thing is it follows with an example that explicitly uses $WS_EX_LAYERED on a child GUI:

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

Example()

Func Example()
    Local $sFilePath = "..\GUI\logo4.gif"

    ; Create a GUI with various controls.
    Local $hGUI = GUICreate("Example", 400, 100)
    GUICtrlCreatePic("..\GUI\msoobe.jpg", 0, 0, 400, 100)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    Local $hChild = GUICreate("", 169, 68, 20, 20, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $hGUI)

    ; Create a picture control with a transparent image.
    GUICtrlCreatePic($sFilePath, 0, 0, 169, 68)

    ; Display the child GUI.
    GUISetState(@SW_SHOW)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

        EndSwitch
    WEnd

    ; Delete the previous GUIs and all controls.
    GUIDelete($hGUI)
    GUIDelete($hChild)
EndFunc   ;==>Example

EDIT: oops accidentally posted the wrong code for my section (it was the docs example) - now its my code.

post-75824-0-00893100-1426199855_thumb.p

Edited by mpower

Share this post


Link to post
Share on other sites



 

If a picture is set as a background picture the other controls will overlap, so it is important to disable the pic control: GUICtrlSetState(-1, $GUI_DISABLE).

It's in the help file for GUICtrlCreatePic


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

It's in the help file for GUICtrlCreatePic

 

But I don't have a Pic control? The PNG image is drawn directly onto the GUI without a control as far as I can tell:

Func SetBitmap($hGUI, $hImage, $iOpacity = 255)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", 1)
    _WinAPI_UpdateLayeredWindow($hGUI, $hGUI, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc
Edited by mpower

Share this post


Link to post
Share on other sites

Sorry, I was looking at your second example and not the first.

What exactly is it that you're trying to achieve with your panel.png? If all you want is a see through GUI, you can always try using WinSetTrans to do that, if you're doing the whole GUI.

BTW, that example in the help file is demonstrating how to make a GUI with transparency by using a technique that makes the GUI transparent, it works because the pixel color in the 0,0 location on the image is used as the transparency indicator. Anywhere that color is in the image will cause the GUI to be transparent in that location. The logo4.gif has dark blue in that location, so the entire background of the GUI is transparent wherever that blue color is, in this case anywhere there isn't text.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

BrewManNH, I am trying to make a GUI which has a drop shadow by using the PNG I have made (if there is another way to do this please let me know). I am also planning on adding other PNG images to go on top of the GUI to serve as GUI elements.

 

-------------

The example I brought up from the Docs page (https://www.autoitscript.com/autoit3/docs/functions/GUICreate.htm) was to point out the contradictory statements made in the Docs of: "$WS_EX_LAYERED Creates a layered window. Note that this cannot be used for child windows." 

As you can see the example from Docs page clearly is applying $WS_EX_LAYERED style to a child window:

Local $hChild = GUICreate("", 169, 68, 20, 20, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $hGUI)

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

I'm not quite interesting in this topic, but I think I can provide some information.

- First, about your last example:

Local $hChild = GUICreate("", 169, 68, 20, 20, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $hGUI)

AutoIt do some hack with those Windows style, so some statement you've read can be incorrect in some situation. 

From Win32 point of view, a WS_EX_LAYERED flag doesn't have any effect to a child window. So with a MDI child, its still true.

But AutoIt cannot provide a true MDI implementation (yes, it is complex, so AutoIt's dev decide to provide a simulated MDI child, using top-level window but move/minimize it when the main MDI host window is moved/minimized. So any 'MDI child' in AutoIt (created by GUICreate) is not a child window, but a top level window. They can have WS_EX_LAYERED flag, as all other top-level windows.

- Second, from Windows 8, WS_EX_LAYERED also support child window, so if you target a Windows 8 platform or later, you can freely use that flag with any window.

- Make dropshadow is hard part. You can use WS_EX_LAYERED and set a image as your GUI shape, but all your controls in the GUI becomes useless. Easiest solution is have a normal interactive GUI window, and create a/several decorater window which is WS_EX_LAYERED and do the shadow job. When your main GUI move or minimize then move/minimize your decorater window(s) along with. Its still a lot of work. Almost programs with custom shadow use this method (MS Office 2013 as an example)

Edited by binhnx

99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Thanks binhnx, for that explanation regarding simulated MDI child windows, now it makes sense. I think the wording on the Docs page could be changed to remove the confusion.

I have managed to get somewhere though :). Whilst you can't add controls to the Parent GUI, you can add controls to the child GUI. They show fine and work as expected!

Here is a working example, much better than my previous crude work. :D

#include-once
#NoTrayIcon
#AutoIt3Wrapper_Outfile=Example.exe
#AutoIt3Wrapper_Compression=4
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_UseX64=n
#include <GDIPlus.au3>
#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <WinAPIEx.au3>

Global $bAero, $hidden = False
Global Const $hDwmApiDll = DllOpen("dwmapi.dll"), $sChkAero = DllStructCreate("int;")
DllCall($hDwmApiDll, "int", "DwmIsCompositionEnabled", "ptr", DllStructGetPtr($sChkAero))
$bAero = DllStructGetData($sChkAero, 1)
If Not $bAero Then ;check for Aero as transparent png GUI requires Aero
    MsgBox(16, 'Sorry', 'Sorry, Aero must be enabled for this application to work!')
    Exit
EndIf

$hGUI = GUICreate("", 715, 683, -1, 0, $WS_POPUP, $WS_EX_LAYERED + $WS_EX_TOPMOST, WinGetHandle(AutoItWinGetTitle()))
$hIcon = _WinAPI_GetClassLongEx($hGUI, $GCL_HICON)
_WinAPI_DestroyIcon($hIcon)
_WinAPI_SetClassLongEx($hGUI, $GCL_HICON, 0)
_WinAPI_SetClassLongEx($hGUI, $GCL_HICONSM, 0)
_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\panel.png')
SetBitmap($hGUI, $hImage)
_GDIPlus_Shutdown()

$hGUI_child = GUICreate("", 715-39, 683 - 26, 22,  3, $WS_POPUP, $WS_EX_MDICHILD + $WS_EX_TOPMOST, $hGUI)
GUISetBkColor(0xFFFFFF)
GUISetFont(18, 100, Default, 'Segoe UI', $hGUI_child, 5)
GUICtrlCreateLabel('Example lbl:', 10, 25)
GUICtrlCreateInput('', 150, 20, 100, 35)
GUICtrlCreateLabel('Example lbl', 525, 25)
$testbutton = GUICtrlCreateButton('A test button', 100, 250)

$hGUI_child2 = GUICreate("", 112, 50, 299, 645, $WS_POPUP, $WS_EX_LAYERED + $WS_EX_MDICHILD + $WS_EX_TOPMOST, $hGUI)
GUISetCursor(0, 1, $hGUI_child2)
_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\roundbn.png')
SetBitmap($hGUI_child2, $hImage)
_GDIPlus_Shutdown()

GUISetState(@SW_SHOW, $hGUI)
GUISetState(@SW_SHOW, $hGUI_child)
GUISetState(@SW_SHOW, $hGUI_child2)

While 1
    $msg = GUIGetMsg(1)
    Switch $msg[1]
    Case $hGUI_child
        Switch $msg[0]
            Case $GUI_EVENT_CLOSE, $GUI_EVENT_SECONDARYDOWN ;let's exit on right click (or esc or alt+f4)
                Exit
            Case $testbutton
                MsgBox(0,'Button','You clicked a button!', 0, $hGUI_child)
        EndSwitch
    Case $hGUI_child2
        Switch $msg[0]
            Case $GUI_EVENT_PRIMARYDOWN
                If Not $hidden Then
                    For $i = -1 to -663 Step - 1
                        WinMove($hGUI, "", Default, $i)
                    Next
                    $hidden = True
                Else
                    For $i = -664 to -4 Step 1
                        WinMove($hGUI, "", Default, $i)
                    Next
                    $hidden = False
                EndIf
        EndSwitch
    EndSwitch
WEnd

Func SetBitmap($hGUI, $hImage, $iOpacity = 255)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", 1)
    _WinAPI_UpdateLayeredWindow($hGUI, $hGUI, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc
Edited by mpower

Share this post


Link to post
Share on other sites

AutoIt cannot provide a true MDI implementation (yes, it is complex, so AutoIt's dev decide to provide a simulated MDI child, using top-level window but move/minimize it when the main MDI host window is moved/minimized.

I'd also like to thank binhnx for his summary of the key aspects of layered windows.

I've had instances of GUI elements becoming disassociated with the main gui ... especially when the entire screen is rotated from horizontal to vertical on one of the Win 8 tablets.  I need to look at the whole issue again in light of this clearer explanation.

Thanks, binhnx!

 

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