SetButtonPixmap() ?

BlitzMax Forums/BlitzMax GUI Programming/SetButtonPixmap() ?

I'm trying to find a way to put a pixmap on a button without losing the text. I've got part way there, but it's not quite right. Firstly, the image doesn't cover the whole button and secondly, the button doesn't show text as well.

According to the MSDN, it should do if I only SendMessage BM_BITMAP and don't SetWindowLong BS_BITMAP but if I do that, I'm back to just text and no image.

Here's what I got ( feel free to use and abuse if you want this half-finished piece of not-entirely-working junk built around half-understood information from the MSDN and stolen snippets of code from around here. )


Extern "Win32"
	
	Function SetWindowLong:Int(HWND:Int,nIndex:Int,dwNewLong:Int) = "SetWindowLongA@12"
	Function GetWindowLong:Int(HWND:Int,nIndex:Int) = "GetWindowLongA@8"
	Function DrawMenuBar:Int(hMenu:Int)
	
	'Function SetWindowLongPtr(hWnd:Int,nIndex:Int,lNewLong:Int)= "SetWindowLongA@12"
	'Function GetWindowLongPtr:Int(hWnd:Int,nIndex:Int)= "GetWindowLongA@8"
	
	Function GetDesktopWindow:Int()"win32"
	Function GetWindowRect:Int(hWindow:Int,r:Int Ptr)"win32"
	
	Function TextOutA(HDC:Int,nXStart:Int,nYStart:Int,lpString$z,cbString:Int)
	Function ReleaseDC(hWND:Int,hDC:Int)
	
	Function CreateBitmap(nWidth:Int,nHeight:Int,cPlanes:Int,cBitsPerPixel:Int,lpvBits:Byte Ptr)
	
	Function SetFocus:Int(hWND:Int) = "SetFocus@4"
	
	Function SendMessage:Int(hWnd:Int,Msg:Int,wParam:Int,lParam:Int) = "SendMessageA"
	
End Extern
Const GWL_STYLE:Int=-16
Const BM_SETIMAGE:Int=247
Const BS_BITMAP:Int=128
Const BS_ICON:Int=64
Const IMAGE_BITMAP:Int=0

Function SetButtonPixmap(Button:TGadget,Pixmap:TPixmap)
	Local ButtonhWND:Int=QueryGadget(Button,QUERY_HWND)
	Local Flags:Int=GetWindowLong(ButtonhWND,GWL_STYLE)
	Local Img:Int
	If Pixmap
		If Pixmap.format<>PF_RGBA8888
			Pixmap=Pixmap.Convert(PF_RGBA8888)
		End If
		If ~ (BS_BITMAP & flags) 
			SetWindowLong(ButtonhWND,GWL_STYLE,flags+BS_BITMAP)
		Else
			If (BS_BITMAP & flags)
				SetWindowLong(ButtonhWND,GWL_STYLE,flags-BS_BITMAP)
				Return
			End If
		End If
		Img:Int=CreateBitmap(Pixmap.width,Pixmap.height,1,32,Pixmap.pixels)
		DebugLog "IMG "+Img
		Local oldimage:Int=SendMessage(ButtonhWND,BM_SETIMAGE,IMAGE_BITMAP,Img)
		DebugLog "OLDIMAGE "+OldImage
		If OldImage<>Null
			DeleteObject(oldimage)
		End If
	End If
End Function


Some of the Constants and Externs aren't needed, but I don't want to piddle about removing the ones I use for other things because I'm tired and I'll probably remove one you need by mistake.

I'm not sure, but I think Windows has different command button controls. The one used by BlitzMax GUI is the system command button, and this kind of button can't have any image attached to it. there's a general command button (I think it could be selected with a simple creation flag) that allows pictures (in the middle of the button I think).

Non-rinky-dink version. You could easily convert this to use images:
Const GWL_STYLE:Int=-16
Const BM_SETIMAGE:Int=247
Const BS_BITMAP:Int=128
Const BS_ICON:Int=64
Const IMAGE_BITMAP:Int=0

Function SetButtonColor(button:TGadget,r,g,b)
	hbutton=QueryGadget(button,QUERY_HWND)
	SetWindowLong hbutton,GWL_STYLE,GetWindowLong(hbutton,GWL_STYLE)|BS_BITMAP
	w=GadgetWidth(button)-4
	h=GadgetHeight(button)-4
	pixels:TBank=CreateBank(w*h*4)
	For y=0 To h-1
		For x=0 To w-1
			PokeByte pixels,4*(y*w+x)+0,r
			PokeByte pixels,4*(y*w+x)+1,g
			PokeByte pixels,4*(y*w+x)+2,b
			PokeByte pixels,4*(y*w+x)+3,0
		Next
	Next
	i=CreateBitmap(w,h,1,32,pixels.buf())
	If Not i Return
	oldimage=SendMessageA(hbutton,BM_SETIMAGE,IMAGE_BITMAP,i)
	If oldimage DeleteObject(oldimage)
	Return True
End Function


This is what I use:
SuperStrict

?win32
Extern "Win32"
	Function SetWindowLong:Int(HWND:Int,nIndex:Int,dwNewLong:Int) = "SetWindowLongA@12"
	Function GetWindowLong:Int(HWND:Int,nIndex:Int) = "GetWindowLongA@8"
	Function _LoadImage:Int(hInstance:Int,lpcstr:Byte Ptr,a:Int,b:Int,c:Int,d:Int) = "LoadImageA@24"
End Extern

Const GWL_STYLE:Int		= -16
Const GWL_HINSTANCE:Int  = -6

Const BM_SETIMAGE:Int	= 247

Const BS_ICON:Int		= 64
Const BS_BITMAP:Int		= 128

Const IMAGE_BITMAP:Int	= 0
Const IMAGE_ICON:Int	= 1

Const LR_LOADFROMFILE:Int = 16
Const LR_LOADTRANSPARENT:Int = 32
Const LR_DEFAULTSIZE:Int	= 64
?

Function SetButtonIcon(button:TGadget,url:String)
	
	?win32
	If TWin32Gadget(button).class <> GADGET_BUTTON Then Return

	Local hwnd:Int	= QueryGadget(Button,QUERY_HWND)
	Local mode:Int = IMAGE_BITMAP
	Local style:Int = BS_BITMAP
	
	If ExtractExt(url).ToLower() = "ico"
		mode = IMAGE_ICON
		style = BS_ICON
	EndIf
	
	Local hInstance:Int = GetWindowLong(hwnd,GWL_HINSTANCE)
	Local hIcon:Int = _LoadImage(hInstance, url.ToCString(), mode, 0, 0, LR_LOADFROMFILE | LR_LOADTRANSPARENT | LR_DEFAULTSIZE )

	If hIcon = 0 Then Return
	SetWindowLong(hwnd,GWL_STYLE,GetWindowLong(hwnd,GWL_STYLE) | style)

	Local old:Int = SendMessageA(hwnd,BM_SETIMAGE,mode,hIcon)
	If old Then	DeleteObject(old)
	?
	
End Function

Loads a .bmp or .ico file and applies it to the button. If you use .ico files, it will use the transparency mask as well.

I'm not sure if it's possible to get both text and icon, any combo I tried gives either one or the other, regardless of what msdn says.