Custom Shape Window with Semi-Transparent Drop Shadow

Hi. I’m trying to make a floating window that has rounded corners and a drop shadow and has no system colors, borders or buttons etc. I want it cross platform but am currently just trying to get it to work on Win32. I have used the CustomWindowShape example included with Xojo and have managed to get part of it working (the shape and no system UI) but I can’t get the image I’m using for the window background to reflect the transparency drop shadow gradient of the png file.

Here’s the code (from this forum) to get rid of the system interface that’s working:

[code] #If TargetWin32 then
//////////////////////////////////////////////////////////////////////////////////////////////////
// — Custom shaped window code - by the Xojo community.
// Original code by Julen Ibarretxe Uriguen
// Modified by Bill Gookin
// Lastly modified by Sam Rowlands
//
// This uses code from the Windows Functionality Suite to reduce flicker.
//
// It works by removing the window’s border and title, then telling the OS to mask out
// a certain color. This is faster than using CreateRectRgn, CombineRgn, DeleteObject
//////////////////////////////////////////////////////////////////////////////////////////////////

// --- The following code reduces the border and window title to nothing!
Const WS_BORDER = &H800000
Const WS_CAPTION = &HC00000
Const WS_THICKFRAME = &H40000
Const WS_MINIMIZE = &H20000000
Const WS_MAXIMIZE = &H1000000
Const GWL_EXSTYLE=-20
Const SWP_FRAMECHANGED = &H20
Const WS_EX_LAYERED=&h00080000
Const GWL_STYLE = -16
const WS_SYSMENU = &h00080000

Declare function GetWindowLong lib "User32" Alias "GetWindowLongA" ( hwnd as integer, flags as integer ) as integer
Dim WS_NOBORDER as integer = GetWindowLong(me.Handle, GWL_EXSTYLE) and not WS_CAPTION and not WS_THICKFRAME and not WS_MINIMIZE and not WS_MAXIMIZE and WS_SYSMENU

Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (hwnd As Integer, nIndex As Integer, dwNewLong As Integer) As Integer
call setwindowlong (me.Handle, GWL_STYLE, WS_NOBORDER)
call setwindowlong (me.Handle, GWL_EXSTYLE,WS_EX_LAYERED) // -- Needed to allow the setLayerWindowAttributes to work

Declare Function SetWindowPos Lib "User32" (hwnd as Integer, hWndInstertAfter as Integer, x as Integer, y as Integer, cx as Integer, cy as Integer, flags as Integer) as Integer
call SetWindowPos(me.WinHWND, 0, left, top, width-1, height-1, SWP_FRAMECHANGED) //Apply border reduction

// --- This last declare is the magic one, that masks out via color. The color is in ARGB, instead of RGBA (Xojo)
Declare function SetLayeredWindowAttributes lib "user32" (hWnd as Integer, ColorRef as integer, AlphaCode as byte, dwFlags as integer) as Boolean
call setlayeredwindowattributes(me.Handle, &h00FFFFFF, 0, 1)

'// --- To reduce 'flickering' we'll freeze the display, refresh and then release.
'Soft Declare Function SendMessageA Lib "User32" ( hwnd as Integer, msg as Integer, wParam as Integer, lParam as Integer ) as Integer
'Soft Declare Function SendMessageW Lib "User32" ( hwnd as Integer, msg as Integer, wParam as Integer, lParam as Integer ) as Integer
'
'Const WM_SETREDRAW = &hB
'
'if System.IsFunctionAvailable( "SendMessageW", "User32" ) then
'call sendMessageW( me.handle, WM_SETREDRAW, 0, 0 )
'me.refresh( true )
'call sendMessageW( me.handle, WM_SETREDRAW, 1, 0 )
'
'else
'call sendMessageA( me.handle, WM_SETREDRAW, 0, 0 )
'me.refresh( true )
'call sendMessageA( me.handle, WM_SETREDRAW, 1, 0 )
'
'end if

#endif
[/code]

What I’m left with is a custom shaped window but instead of a semi-transparent drop shadow over the screen behind, it’s a drop shadow over the system color bg. I’ve been at this for hours and have searched the forum and web and can’t seem to work it out.

To clarify: I am using two pics: 1) A png image (for the window background) with a transparent gradient drop shadow. 2) A mask/matte of that image which is just pure black on white but obviously with a gradient drop shadow from black to white around all four sides.

How can I get the drop shadow to work as you’d expect it to?

Thanks!

IIRC, the window mask is a 1-Bit image, either on or off. I don’t know if it’s possible using this technique to do anything other than.

I tried a transparent PNG image as background of the window in the same example project. there does not seem to be a way to preserve partially transparent areas. It’s either transparent, or not. Even with a 32 bit picture instead of 8 in the Open event, the rendition improves but yet no transparency.

There are other ways to create transparent windows that preserve semi-transparent. I noticed that iObit start menu for Windows 8 uses an icon with a semi transparent aura. But I frankly have no idea how they have obtained that.

I found this :
http://www.codeproject.com/Articles/34158/Cool-Semi-transparent-and-Shaped-Dialogs-with-Stan

Look into MacOSLib for a transparent window for Mac.

Thanks, Sam/Michel. It seems so bizarre that in this day and age it is so difficult (or impossible) to create something so basic as a custom shape cross-platform window with a semi-transparent drop-shadow. I could understand if we were living in the dark ages of Windows 98 but this is such a basic UI design these days I can’t believe Xojo hasn’t made it easy to do. Other WIndows applications have it so are they all coded in C/C++ or do they just access declares we are unfamiliar with? Crazy :frowning:

Come on. Cross platform is not easy to do for such features. Each platform has its own way of doing things.

I am sure you will succeed, though.

Yes, I understand that and don’t mind having to access declares for each platform if I have to…but it’s still strange to me that basic UI stuff is such a problem…at least on Win32. Hey ho!

Windows is like an archeological site, where several layers exist which pay tribute to several species of dinosaurs, from the very early age of 3.2 until modern age and Windows 8.1. Each with idiosyncrasies and limitations. The transparent window method using the red layer dates back to the XP era, when such things as drop shadow had not even appeared.

Mac OS X on the other hand, has regularly shed old framework and methods to embrace new technologies. It may explain why sometimes, very modern UI things maybe easier.

To be fair, not everybody does drop shadow on Windows, where Mac OS X has it built in.

Yes. Very true.

I know talking about “other” programs is in bad taste, but with the source code being available you may want to look to see how LiveCode does it as they offer cross-platform alpha-blended custom window shapes (windowShape property)…

Are you sure the source code is available ? There is no mention of it on their site. Beside, I fear looking for one feature in what is probably a massive project will be akin to the proverbial needle in the haystack.

I will look around for a more modern way of getting a shaped window than the existing Windows example. If there is one, it must be posted somewhere.

Thanks Shao but that does sound quite a task. Thanks Michel.

I located the method for shaped windows on the Microsoft Developer Network site :
http://msdn.microsoft.com/en-us/library/aa979235(v=vs.71).aspx

This one is from 2003, but there does not seem to be a newer method. This one uses exactly the same technique as used in the Xojo code example of using the magenta/red color as transparency mask.

That said, I found an intriguing post at http://www.codeproject.com/Articles/14694/Non-transparent-controls-on-a-semi-transparent-win that may provide a way to get the appearance of a semi-transparent drop shadow. Instead of using a semi transparent window, he paints the background of a solid window as it would look as semi transparent, and then places a regular control over it. To the eye of the user, it looks like a semi transparent window with a solid control over it.

Now, back to your concern, Denise, here is what I would try :

  • Create a shaped window the total size of your drop shadow window picture
  • Capture a picture of the screen and draw it onto the backdrop of the window
  • The window picture together with its semi transparent drop shadow is displayed in a canvas stuck over the simulated background

If the screen capture is positioned right, the borders of the shaped window should be invisible, and it will look as if the drop shadow is indeed over the desktop and other apps windows.

Now the issue is that such a trick works only if the drop shadow window is not moved. But it may suffice if you make it modal and not movable.

Interesting link but I unfortunately need a movable floating window with actionable controls. I have a feeling I’ll just have to give up on the idea of a blurred drop shadow around it (on Windows at least) and just stick with the custom shape alone. Thanks again.

Windows does have CD_DROPSHADOW for rectangular windows, but it seems not be applicable to shaped windows. Or regions for that matter, the other way to have shaped windows.
See winapi - Win32: How to make drop shadow honor non-rectangular Layered window? - Stack Overflow

The simplest way is the shaped window alone. Although adding a 2 pixels gradient to grey on the edge of the window could probably smooth it up.

Good luck.

https://github.com/runrev/livecode

A quick look through their code shows that they are creating a region from the image and then using that to create the window shape…

http://msdn.microsoft.com/en-us/library/aa930600.aspx

some source code that could be useful
http://www.ucancode.net/VC_Library_Control_Tool/HRGN-SetWindowPos-SetWindowRgn-Bitmap-File-VC_Example.htm

Ah. I was looking on the livecode.com site. Thank you.

[quote=116940:@Michel Bujardet]Denise Adams I’ll just have to give up on the idea of a blurred drop shadow around it (on Windows at least) and just stick with the custom shape alone.
The simplest way is the shaped window alone. Although adding a 2 pixels gradient to grey on the edge of the window could probably smooth it up.[/quote]

Yesterday I got a closer look at Windows “Aero” drop shadow. In fact, it IS a two pixels wide transparent shadow around windows. No more.

I think obtaining the same effect around a floating shaped window is possible this way, in the Moved event :

  • Grab a picture of the screen under the window (make it invisible while this happens)
  • For the very edge pixels of the window, get the color of their counterpart in the grabbed screen, subtract enough from each color to make it a bit darker, and replace the shape pixels
  • For the 2 pixels away inside the shape, do the same with a darker color

The effect will be a two pixels transparent drop shadow around the edge of the shape. The flicker occasioned by the invisible+screengrab routine should be masked by the landing on the window at its new place when the user has finished moving it.

In order to be able to repeat the operation each time the window is moved, a copy of the original picture that compose the window must be kept.

This looks like quite a bit of work for a 2 pixels drop shadow, but that seems the only way to go at this stage, since Windows obviously cannot manage it for you. Now, is it necessary versus a much simpler light grey opaque shadow is for you to decide.

That’s great. Thank you both for all your help. Right now that does seem an awful lot of work for a small drop shadow effect and I have a mountain of more imortant code to tackle with but when I carve out some time I’ll definitely have another stab at this!

Good news I got it to work thanks to all your help!

    Const GCL_STYLE = -26
    Const CS_DROPSHADOW = &h00020000
    
    Declare Function SetClassLong Lib "User32" Alias "SetClassLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
    call SetClassLong(me.Handle, GCL_STYLE, CS_DROPSHADOW)

If you use this after the SetWindowLong() calls to shape the window then the drop shadow will appear when the window has focus!

If you need to retain the original window style and just add the drop shadow then you’ll need to grab the CS_STYLE value using GetClassLong() before the above call and then add it to the CS_DROPSHADOW when you call SetClassLong().

[quote=117020:@Denise Adams]Good news I got it to work thanks to all your help!

    Const GCL_STYLE = -26
    Const CS_DROPSHADOW = &h00020000
    
    Declare Function SetClassLong Lib "User32" Alias "SetClassLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
    call SetClassLong(me.Handle, GCL_STYLE, CS_DROPSHADOW)

If you use this after the SetWindowLong() calls to shape the window then the drop shadow will appear when the window has focus!

If you need to retain the original window style and just add the drop shadow then you’ll need to grab the CS_STYLE value using GetClassLong() before the above call and then add it to the CS_DROPSHADOW when you call SetClassLong().[/quote]

Congratulations, Denise :slight_smile: