Jump to content

How can you make a layered window clickable?


Go to solution Solved by WildByDesign,

Recommended Posts

Posted

I'm doing some testing right now with applying WS_EX_LAYERED to a "Scrollbar" window that is being used as a SBS_SIZEBOX / SBS_SIZEGRIP for the GUI.

I am using _WinAPI_SetLayeredWindowAttributes() to remove the background color of the "Scrollbar" window, leaving only the dots. That part is working successfully. But the problem is that it seems using WS_EX_LAYERED makes it so that the "Scrollbar" window is no longer clickable to resize the GUI and makes the hover cursor fail to show directly over it.

If anybody has any ideas for making a layered window clickable, please let me know. This is mostly just for testing and seeing what other possibilities there are for this type of thing. I appreciate your time. Thank you. :)

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIGdi.au3>
#include <WinAPIRes.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>

Opt("MustDeclareVars", 1)

Global $g_hGui, $g_hSizebox, $g_hOldProc, $g_hStatus, $g_iHeight, $g_aText, $g_aRatioW, $g_iBkColor, $g_iTextColor, $g_hDots

Example()

;==============================================
Func Example()

    _GDIPlus_Startup()

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10

    $g_hGui = GUICreate("SBS_SIZEBOX / SBS_SIZEGRIP Testing", 400, 200, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX, $WS_SIZEBOX))
    GUISetBkColor(0x202020)

    $g_hSizebox = _WinAPI_CreateWindowEx(0, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, 400 - 15, 200 - 15, 15, 15, $g_hGui) ; $SBS_SIZEBOX or $SBS_SIZEGRIP
    ;$g_hSizebox = _WinAPI_CreateWindowEx($WS_EX_TOPMOST, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, 300 - 15, 200 - 15, 15, 15, $g_hGui) ; $SBS_SIZEBOX or $SBS_SIZEGRIP

    ; Add WS_EX_LAYERED extended style
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_EXSTYLE, BitOR(_WinAPI_GetWindowLong($g_hSizebox, $GWL_EXSTYLE), $WS_EX_LAYERED))
    _WinAPI_SetWindowPos($g_hSizebox, 0, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOZORDER, $SWP_FRAMECHANGED))
    ;_WinAPI_SetWindowPos($g_hSizebox, $HWND_TOP, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOREDRAW, $SWP_NOSIZE))

    ; Subclass the sizebox (by changing the window procedure associated with the Scrollbar class)
    ;Local $hProc = DllCallbackRegister('ScrollbarProc', 'lresult', 'hwnd;uint;wparam;lparam')
    ;$g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hProc))

    _WinAPI_SetWindowTheme($g_hSizebox, "DarkMode_Explorer", "ScrollBar")

    Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZENWSE)
    _WinAPI_SetClassLongEx($g_hSizebox, -12, $hCursor) ; $GCL_HCURSOR = -12

    SizeboxResize()
    GUISetState()

    Sleep(2000)

    ; Make surrounding background color around dots transparent
    Local $iColor = 0x383838
    _WinAPI_SetLayeredWindowAttributes($g_hSizebox, $iColor, 255)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

        EndSwitch
    WEnd

    _GDIPlus_BitmapDispose($g_hDots)
    _GUICtrlStatusBar_Destroy($g_hStatus)
    _WinAPI_DestroyCursor($hCursor)
    ; _WinAPI_DeleteObject($g_hBrush)
    ;_WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    ;DllCallbackFree($hProc)
    _GDIPlus_Shutdown()
EndFunc   ;==>Example

Func SizeboxResize()
    Local $aSize = WinGetClientSize($g_hGui)
    WinMove($g_hSizebox, "", $aSize[0] - 15, $aSize[1] - 15)
EndFunc   ;==>SizeboxResize

;==============================================
Func ScrollbarProc($hWnd, $iMsg, $wParam, $lParam) ; Andreik

    If $iMsg = $WM_PAINT Then
        Local $tPAINTSTRUCT
        Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
        Local $iWidth = DllStructGetData($tPAINTSTRUCT, 'rPaint', 3) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 1)
        Local $iHeight = DllStructGetData($tPAINTSTRUCT, 'rPaint', 4) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 2)
        Local $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC)
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $g_hDots, 0, 0, $iWidth, $iHeight)
        _GDIPlus_GraphicsDispose($hGraphics)
        _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
        Return 0
    EndIf
    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>ScrollbarProc

 

Posted

The WM_NCHITTEST message could be what you want.
For a basic implementation, check the position and return HTCLIENT (1) ( to accept a click, and HTTRANSPARENT (-1) to pass it along.
You get the X and Y from the lParam.

You can read more here: https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest

  • Solution
Posted (edited)
4 hours ago, genius257 said:

The WM_NCHITTEST message could be what you want.
For a basic implementation, check the position and return HTCLIENT (1) ( to accept a click, and HTTRANSPARENT (-1) to pass it along.
You get the X and Y from the lParam.

Thank you. I ended up learning a lot from your suggestion. I hadn't done anything with _WinAPI_LoWord() or _WinAPI_HiWord() before and had zero understanding of it. So I spent some time learning it and was able to get this working successfully with your idea to use the WM_NCHITTEST message. So this was a great learning experience for me and I appreciate it. :)

However, unfortunately, I found the WM_NCHITTEST message to be too expensive on resources for this purpose. So not only did I learn from your suggestion, but I also pushed on further to see if I could achieve this in a more efficient way.

For clarification, the intent here is for something like that Control Viewer script which does not have a Statusbar, but does have a Scrollbar (grip) window. I needed to find a way to make the background of it transparent.

Anyway, I did find a way to achieve the goal in WM_PAINT by keeping the background transparent, having reasonable efficiency and matching DPI scale as well.

I did a 2 second Sleep just to show the Before and After.

#include <WinAPIGdi.au3>
#include <WinAPISysWin.au3>
#include <WinAPIRes.au3>
#include <WindowsStylesConstants.au3>
#include <APIResConstants.au3>
#include <APIThemeConstants.au3>
#include <AutoItConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <GUIConstantsEx.au3>
#include <WinAPITheme.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>

; Initialize System DPI awareness
DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

Opt("MustDeclareVars", 1)

Global $g_hGui, $g_hSizebox, $g_hOldProc, $g_iGripSize

Global Const $SP_GRIPPER = 3

Example()

;==============================================
Func Example()

    Local Const $SBS_SIZEBOX = 0x08, $SBS_SIZEGRIP = 0x10

    $g_hGui = GUICreate("SBS_SIZEBOX / SBS_SIZEGRIP Testing", 500, 300, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX, $WS_SIZEBOX))
    GUISetBkColor(0x191919)

    ; Get gripper size (different size per DPI)
    Local $hTheme = _WinAPI_OpenThemeData($g_hGui, 'Status')
    Local $tSIZE = _WinAPI_GetThemePartSize($hTheme, $SP_GRIPPER, 0, Null, Null, $TS_TRUE)
    $g_iGripSize = $tSIZE.X
    _WinAPI_CloseThemeData($hTheme)

    ; Create SizeBox window
    $g_hSizebox = _WinAPI_CreateWindowEx(0, "Scrollbar", "", $WS_CHILD + $WS_VISIBLE + $SBS_SIZEBOX, 400 - $g_iGripSize, 200 - $g_iGripSize, $g_iGripSize, $g_iGripSize, $g_hGui) ; $SBS_SIZEBOX or $SBS_SIZEGRIP

    ; Set dark theme
    _WinAPI_SetWindowTheme($g_hSizebox, "DarkMode_Explorer")

    ; Set cursor
    Local $hCursor = _WinAPI_LoadCursor(0, $OCR_SIZENWSE)
    _WinAPI_SetClassLongEx($g_hSizebox, -12, $hCursor) ; $GCL_HCURSOR = -12

    GUIRegisterMsg($WM_SIZE, _WM_SIZE)

    SizeboxResize()
    GUISetState()

    Sleep(2000)

    ; Subclass the sizebox (by changing the window procedure associated with the Scrollbar class)
    Local $hProc = DllCallbackRegister('ScrollbarProc', 'lresult', 'hwnd;uint;wparam;lparam')
    $g_hOldProc = _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, DllCallbackGetPtr($hProc))

    _WinAPI_RedrawWindow($g_hGui)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

        EndSwitch
    WEnd

    _WinAPI_DestroyCursor($hCursor)
    _WinAPI_SetWindowLong($g_hSizebox, $GWL_WNDPROC, $g_hOldProc)
    DllCallbackFree($hProc)
EndFunc   ;==>Example

Func _WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    SizeboxResize()
EndFunc

Func SizeboxResize()
    Local $aSize = WinGetClientSize($g_hGui)
    WinMove($g_hSizebox, "", $aSize[0] - $g_iGripSize, $aSize[1] - $g_iGripSize)
EndFunc   ;==>SizeboxResize

;==============================================
Func ScrollbarProc($hWnd, $iMsg, $wParam, $lParam)

    Switch $iMsg
        Case $WM_ERASEBKGND
            Return 1 ; Prevent background erasing to avoid flickering

        Case $WM_PAINT
            Local $tPAINTSTRUCT
            Local $hDC = _WinAPI_BeginPaint($hWnd, $tPAINTSTRUCT)
            Local $iWidth = DllStructGetData($tPAINTSTRUCT, 'rPaint', 3) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 1)
            Local $iHeight = DllStructGetData($tPAINTSTRUCT, 'rPaint', 4) - DllStructGetData($tPAINTSTRUCT, 'rPaint', 2)

            ; Paint SizeGrip only when window is not maximized
            Local $hTheme = _WinAPI_OpenThemeData(_WinAPI_GetParent($hWnd), 'Status')
            Local $tRectGrip = _WinAPI_CreateRectEx(0, 0, $iWidth, $iHeight)
            If Not BitAND(WinGetState(_WinAPI_GetParent($hWnd)), $WIN_STATE_MAXIMIZED) Then
                _WinAPI_DrawThemeBackground($hTheme, $SP_GRIPPER, 0, $hDC, $tRectGrip)
            EndIf

            _WinAPI_CloseThemeData($hTheme)

            _WinAPI_EndPaint($hWnd, $tPAINTSTRUCT)
            Return 0
    EndSwitch

    Return _WinAPI_CallWindowProc($g_hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc   ;==>ScrollbarProc

 

Edited by WildByDesign

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
×
×
  • Create New...