Jump to content

Recommended Posts

Posted

Hello @ioa747,

Have you seen any crashing/runtime errors while developing this? I've been using MetroUDF 5.1 for my GUI builder and if there is any subscript error Metro will crash before writing the subscript error to the console.

If there is a subscript error will uc_framework display it or will it have a "rc 0x00..." error.

I think we are really close to replacing Koda with these modern frameworks!

I've looked at your code a little bit, and it is very organized. I like how the controls are modular.

What is detecting the hits and hovers, user32.dll?

Thanks for everything,

0xC0ffee

Posted (edited)

Hi 0xC0FFEE, thanks for your kind words!

Regarding errors:
The framework uses Maps for properties.
If you try to access a non-existent map, AutoIt will crash, unless you provide error handling.
I am trying to implement MapExists checks, and error checks in the main engine (__UC_Main_MsgHandler and _UC_Properties)
to prevent these errors at runtime.
(I'm sure I missed some, but I'll fix them as I find them)

For critical operations, I use the Call() method with the documented error codes (0xDEAD / 0xBEEF).
This way, if a function is missing or a parameter is invalid, the framework logs the specific error point to the console 
 

Hit Detection:
You are right, it relies on user32.dll messages (WM_LBUTTONDOWN, WM_MOUSEMOVE, etc.).
The framework essentially acts as a router. It listens for these messages via GUIRegisterMsg,
checks which control is under the cursor using the properties stored in our Maps, and then triggers the appropriate _UC_[Type]_Draw or event function.

The hovers for the time are managed by the function
__UC_Main_MsgHandler

Keeps track of $hLastChild (the previous control) and $hWnd (the current control).

 

What I'm trying to achieve now is to avoid pointless redraw.
for example with techniques like the one below.
 

Func _UC_Toggle_WM_MOUSEMOVE($idDummy, $hWnd, $iX, $iY)
    #forceref $hWnd, $iX, $iY
    Static $sFlag = 0
    Local $m = _UC_Properties($idDummy, Default, Default, "UC_Toggle.au3")
    __DW("_UC_Toggle_WM_MOUSEMOVE :: $idDummy=" & $idDummy & " :: $m.State=" & $m.State & " :: $sFlag=" & $sFlag & @CRLF, 1, ">> UC_Toggle.au3")

    If Not MapExists($m, "State") Then Return 1 = __DW("_UC_Toggle_WM_MOUSEMOVE :: Not Map Exists $m.State" & @CRLF, 1, "!! UC_Toggle.au3")

    ; 🚧
    If $sFlag = $idDummy & "-" & $m.State Then    ; 🚧 as new flag e.g. '4-2'
        __DW("_UC_Toggle_WM_MOUSEMOVE :: $sFlag=" & $sFlag & " => Return" & @CRLF, 1, "-> UC_Toggle.au3")
        Return
    EndIf

    If $m.State = 0 Then Return ; If is Disabled
    If $m.State = 1 Then ; If is normal
        $m.State = 2 ; Hover

        __DW('_UC_Toggle_WM_MOUSEMOVE :: -> New $sFlag from:' & $sFlag & "  to:" & $idDummy & "-" & $m.State & @CRLF, 1, "--> UC_Toggle.au3")

        $sFlag = $idDummy & "-" & $m.State    ; 🚧
        _UC_Properties($idDummy, $m, Default, "UC_Toggle.au3")
    EndIf
EndFunc   ;==>_UC_Toggle_WM_MOUSEMOVE

(I'll go to the early approach, and see how I can integrate it into __UC_Main_MsgHandler, directly)

I have updated https://github.com/ioa747/UC_Framework
and added the  Example4.au3  with a 'chattery' debugging output,  for a full recording of the program flow


I'm a little pressed for time right now, as soon as I find some free time I'll work on it more extensively.
Thank you for your understanding and support. :)

Edited by ioa747

I know that I know nothing

Posted

Hi @ioa747,

I took a look at Example4.au3 and the code was very clean! There was no messy GUIRegisterMsg or WM_NOTIFY code visible. :thumbsup: I didn't see any flicker either.

Heads-up for Maps because I am new to using them. Maybe this could help:

I've found that MapExist is not as helpful as these other techniques.
 

  1. Check if a Map has anything in it
    Global $g_Settings[]
    
    If Not UBound($g_Settings) then Return
    
    ; Code Returns early if there are no Keys in the Map

     

  2. Check if a Key exists and has value
    ; Use IsMap or the next line might lead to a runtime error
    If Not IsMap($m) Then Return
    
    ; Keys that don't exist will return Null
    If $m.State = Null Then Return

 

 

Do you know any way of drawing a fuzzy shadow with GDI+ under a button? The only way seems to have the window manager drop the shadow under GUI windows (like your buttons).

Posted

Hi! Thanks for the feedback and the heads-up!

You are totally right about IsMap().
It's a lifesaver against runtime crashes when passing variables around.
I prefer MapExists() over checking for Null just to be formal, but your approach is definitely faster to write!

As for the fuzzy shadow with GDI+, you hit a soft spot!
Since GDI+ doesn't natively support Gaussian Blur filters,
relying on the OS window manager drops shadows on the entire $hWnd rect, which ruins custom or rounded shapes.

From my perspective , the correct approach is
using a PathGradientBrush on a GraphicsPath around the button to fade a dark color to 0% alpha at the edges (creates a nice soft glow/shadow).

I might experiment with the PathGradient approach in future updates! 


Thanks for interactive :)

I know that I know nothing

Posted
2 minutes ago, ioa747 said:

What is your opinion on the topic of drawing a shadow under a button?

Oh i looked into it and it's complicated.
I like the idea of shadows, but you can't really draw the shadow outside your control.
Best way I've found so far, is padding your control size for the shadow, but that introduces unexpected problems, if the coder don't know this and tries to move or resize the control.
I'm considering if I should try it for my own custom controls, but so far it's not a priority, because of the downsides 😅

Posted (edited)

You hit the nail on the head!
Including the shadow completely breaks the predictability of the layout for the end user
(alignment issues, inaccurate hit testing on the transparent shadow area, and resizing headaches).

I can see why keeping it flat and clean is the smartest priority right now!

Just thinking about how to code that padding logic makes me feel desperate 😅

Edited by ioa747

I know that I know nothing

Posted (edited)

:idea:  The best logic would be an independent function that wouldn't affect the control

If we put it underneath, the child window's clipping rect/parent background would just cut the shadow off.

The ultimate trick would be the exact opposite.
Put the translucent layer right on top of the control, using $WS_EX_TRANSPARENT so it becomes a "ghost" window
(zero mouse capture, clicks pass right through to the button).

Then, using GDI+ Regions with an Exclude mode, we can punch a "hole" into that top layer exactly where the button is.
We draw the fuzzy shadow only on the outside edges, leaving the button area untouched and perfectly crisp.

A bit complex to orchestrate, but theoretically, it completely bypasses the clipping limitations!

Edited by ioa747

I know that I know nothing

Posted

Hi everyone,

Regarding drawing custom blur shadows under controls...

genius257  rightly pointed out back then that padding the control itself to fit a shadow creates layout and sizing headaches for the developer.

At that point, I shared a theoretical idea:
What if we put a transparent "ghost" window over the control, punch a hole in it using GDI+ regions, and draw the shadow only on the outer edges?


Well, that thought wouldn't let me rest.
After a lot of experimentation with the Win32 API and GDI+, I finally added this "piece of wood" to 0xC0FFEE's fire!

It's not perfect and can certainly be improved, as it's a first approach.
The key is that it's universal and works automatically for all controls.
 

How the architecture of _UC_Shadow_Overlay works internally

; #FUNCTION# ====================================================================================================================
; Name...........: _UC_Shadow_Overlay
; Description ...: Creates and manages a layered window overlay to render a drop shadow effect for a UC_control.
; Syntax.........: _UC_Shadow_Overlay($idCtrl, $iDirection, $iDistance, $bOutline, $iInflate, $iOpacity, $iMethod)
; Parameters ....: $idCtrl     - The ID of the control to apply the shadow to.
;                  $iDirection - [optional] The angle of the shadow in degrees (0-360). (Default = 150)
;                  $iDistance  - [optional] The distance of the shadow from the control in pixels. (Default = 10)
;                  $bOutline   - [optional] Boolean to toggle outline rendering logic. (Default = True)
;                  $iInflate   - [optional] Pixel value to expand or shrink the shadow area. (Default = 0)
;                  $iOpacity   - [optional] Shadow opacity percentage (0-100).
;                                Set to 0 for automatic adaptive opacity based on
;                                parent background brightness (Light=1%, Dark=7%). (Default = 0)
;                  $iMethod    - [optional] Rendering method (1 = Multi-layer blur, 2 = Offset spread). (Default = 2)
; Return values .: Success - Returns the handle (HWND) of the created shadow overlay window.
;                  Failure - Returns False and sets @error if the control properties are not found.
; ===============================================================================================================================
Func _UC_Shadow_Overlay($idCtrl, $iDirection = Default, $iDistance = Default, $bOutline = Default, $iInflate = Default, $iOpacity = Default, $iMethod = Default)

The shadow window is actually created on top of the control in Z-Order.

The ghost layer, by combining $WS_EX_LAYERED and $WS_EX_TRANSPARENT, makes this top layer completely invisible on mouseovers.
Clicks go straight through it to the button below!

The exception hole, using GDI+ regions with exception mode, a perfect "hole" is opened in this top layer exactly where the control is.
This keeps the control rendering 100% clean.

To implement this, I also had to deal with some deep Win32 timing issues.
For example, changing the background theme in bulk caused a small rendering delay due to Alpha Blending calculations.
Using _WinAPI_LockWindowUpdate() helped quite a bit

Also, to avoid the "ghost shadows" that remain on the screen when a control is hidden programmatically
(since hidden windows do not trigger WM_PAINT),
I implemented a custom state manager _UC_SetState($idCtrl, $iState)
so that this can be done internally by UC_Framework

It was not a major breakthrough,
just a small piece of code added to the collective knowledge, bypassing the typical cut and fill limitations of custom UI frameworks.
 

Thanks to 0xC0FFEE for the initial inspiration and genius257 for pointing out the points of caution!
 

I haven't integrated it into all the controls, I put it experimentally in
UC_Toggle, UC_Slider, UC_Button
 

the implementation of the idea, you can find it at
https://github.com/ioa747/UC_Framework

main.zip

 

Please, every comment is appreciated!
leave your comments and experiences here!
Thank you very much  :)

I know that I know nothing

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...