To do the demo you need this: http://www.lloydsplace.com/BG.png
You have two main issues that must be overcome:
Firstly, the PNG image decoder that ships with PureBasic is currently incompatible with the UpdateLayeredWindow API. So you must decode the image using another tool. I've supplied you with an arrow from my quiver, so that's out of the way.
Secondly, sad to report, Microsoft has made windows using the UpdateLayeredWindow API unable to host child windows. This is due to the fact that it uses a different, per-pixel update routine to draw the window and it doesn't process #WM_PAINT messages like a normal window does. So, while the child controls exist, they are never drawn.
The UpdateLayeredWindow approach has one major advantage over other forms of skinning or achieving transparency in that you can create dropshadows for the window edges and they will render correctly. In fact, it's the only technology on Windows I know of that will achieve this. So, we must find a way to make it work.
Here we go, this is our plan:
Make a Layered window with dropshadowed edges for use as a window border only.
Make another toplevel window to host the gadgets and keep it centered on the first window at all times.
As hackitistically perverted as this idea is, it actually is one of the options suggested by Microsoft engineers as a solution to this known limitation.
On to the hack!
Code: Select all
;=========================================
; Danger, netmaestro software ahead
;=========================================
ProcedureDLL CatchPNG(ImageNumber, Address, Length)
CompilerIf Defined(GdiplusStartupInput, #PB_Structure) = 0
Structure GdiplusStartupInput
GdiPlusVersion.l
*DebugEventCallback.Debug_Event
SuppressBackgroundThread.l
SuppressExternalCodecs.l
EndStructure
CompilerEndIf
Structure StreamObject
block.l
*bits
stream.ISTREAM
EndStructure
Protected lib
lib = OpenLibrary(#PB_Any, "gdiplus.dll")
If Not lib
ProcedureReturn -1
EndIf
input.GdiplusStartupInput
input\GdiPlusVersion = 1
CallFunction(lib, "GdiplusStartup", @*token, @input, #Null)
If *token
stream.streamobject
Stream\block = GlobalAlloc_(#GHND, Length)
Stream\bits = GlobalLock_(Stream\block)
CopyMemory(address, stream\bits, Length)
If CreateStreamOnHGlobal_(stream\bits, 0, @Stream\stream) = #S_OK
CallFunction(lib, "GdipCreateBitmapFromStream", Stream\stream , @*image)
Stream\stream\Release()
GlobalUnlock_(Stream\bits)
GlobalFree_(Stream\block)
Else
CallFunction(lib, "GdiplusShutdown", *token)
ProcedureReturn 0
EndIf
If *image
CallFunction(lib, "GdipGetImageWidth", *image, @Width.l)
CallFunction(lib, "GdipGetImageHeight", *image, @Height.l)
If imagenumber = #PB_Any
return_imagenumber = CreateImage(#PB_Any, Width, Height, 32)
Else
CreateImage(return_imagenumber, Width, Height, 32, #PB_Image_Transparent)
EndIf
hDC = StartDrawing(ImageOutput(return_imagenumber))
CallFunction(lib, "GdipCreateFromHDC", hdc, @*gfx)
CallFunction(lib, "GdipDrawImageRectI", *gfx, *image, 0, 0, Width, Height)
StopDrawing()
CallFunction(lib, "GdipDeleteGraphics", *gfx)
CallFunction(lib, "GdipDisposeImage", *image)
CallFunction(lib, "GdiplusShutdown", *token)
CloseLibrary(0)
ProcedureReturn return_imagenumber
Else
ProcedureReturn -1
EndIf
Else
ProcedureReturn -1
EndIf
EndProcedure
Procedure WinProc(hwnd, msg, wparam, lparam)
result = #PB_ProcessPureBasicEvents
Select msg
Case #WM_MOVE, #WM_ACTIVATE, #WM_ACTIVATEAPP, #WM_GETICON
ResizeWindow(0,WindowX(1)+96, WindowY(1)+30, #PB_Ignore, #PB_Ignore)
Case #WM_LBUTTONDOWN
If EventlParam()>>16<21
SendMessage_(hwnd, #WM_NCLBUTTONDOWN, #HTCAPTION, 0)
EndIf
EndSelect
ProcedureReturn result
EndProcedure
CatchPNG(0, ?bkimg, ?endbk-?bkimg)
OpenWindow(1, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_BorderLess)
OpenWindow(0, 0, 0, 300, 200, "", #PB_Window_BorderLess, WindowID(1) )
SetWindowLong_(WindowID(1), #GWL_EXSTYLE, GetWindowLong_(WindowID(1), #GWL_EXSTYLE) | #WS_EX_LAYERED)
SetWindowLong_(WindowID(0), #GWL_EXSTYLE, GetWindowLong_(WindowID(0), #GWL_EXSTYLE) | #WS_EX_LAYERED)
SetLayeredWindowAttributes_(WindowID(0), 0, 255, #LWA_ALPHA) ; Not necessary but just to show it's ok to do it
SetWindowColor(0, RGB(131,193,178)) ; Same color as PNG
hdc = StartDrawing(ImageOutput(0))
sz.SIZE
sz\cx = ImageWidth(0)
sz\cy = ImageHeight(0)
ContextOffset.POINT
BlendMode.BLENDFUNCTION
BlendMode\SourceConstantAlpha = 255
BlendMode\AlphaFormat = 1
UpdateLayeredWindow_(WindowID(1), 0, 0, @sz, hDC, @ContextOffset, 0, @BlendMode, 2)
StopDrawing()
SetWindowCallback(@WinProc(),1)
ButtonGadget(1,20,20,100,30,"Press to exit...")
Repeat
Select WaitWindowEvent()
Case #PB_Event_Gadget
Break
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
DataSection
bkimg:
IncludeBinary "BG.png"
endbk:
EndDataSection