Impressions of the GUI module

BlitzMax Forums/BlitzMax Programming/Impressions of the GUI module

Good:
-I was glad to see that trackbar controls made it in, as a variation of a "slider".
-Comboboxes have an "editable" style, which is a big plus.
-Windows have a "hidden" flag, another good improvement.
-Much better menu commands thatn BlitzPlus.
-Having a GadgetPaint() event is very good.

Needs work:
-No toggle button. Ouch.
-No image button.
-Combobox drop-down heights still can't be altered, nor can the control height (which is easy to do with Windows API).
-Listboxes don't have a FullRowSelect flag, and can't have columns or checkboxes.
-No StatusBar fields. (sounds like this has been fixed already)
-Bug in toolbar routine spaces toolbar buttons further and further each time a separator is encountered. This can even be seen in the IDE.

The GUI module is moving in the right direction, and makes several major improvements to the BlitzPlus GUI. Hopefully it will continue to develop. I'm not getting BlitzMax yet, but am keeping an eye on it to see how it develops in the near future.

+ I much prefer the OOP nature which is on offer now.
So, instead of:
mygadget=CreateGadget(blah)
..
FreeGadget mygadget

I can do:
mygadget:TGadget=CreateGadget(blah)
..
mygadget.Free


+ I also like the much easier to remember official constants such as EVENT_WINDOWCLOSE. Beats $4000 or whatever it was.

+ Another biggie is tabbing between gadgets which is now in place. No more beeps like what happened in BlitzPlus.

- I agree with your comments about 'no toggle buttons' and 'no statusbar fields'. I hope to see them added in later.


The greatest achievement though, is the fact that you can drop your BlitzPlus code straight into max (with minor modifications).

Image button (and toggle button) code:
' Custom Image Button 
' ===================
' A custom "gadget" that can be used like web gadgets, 
' i.e. showing an alternate image on mouseover, and
' on single click cycling through a series of options.
'
' Originally created by Beaker
' Improved upon by Mark Tiffany

Strict 
Type TApplet 

	Method OnEvent(Event:TEvent) Abstract

	Method New()
		AddHook EmitEventHook,eventhook,Self
	End Method

	Function eventhook:Object(id,data:Object,context:Object)
		Local	event:TEvent
		Local	app:TApplet
		event=TEvent(data)
		If Not event Then Return
		app=TApplet(context)
		app.OnEvent event	
		Return data
	End Function

End Type


Type TImageButton Extends TApplet
	Field index
	Field lastind
	Field depressed
	Field mouseover

	Field pix:TPixmap[]
	Field rollpix:TPixmap[]
	Field pan:TGadget
		
	Method OnEvent(Event:TEvent)
		'make sure this was intended for us!
		If event.source<>pan Then Return
		
		Select event.id
			Case EVENT_MOUSEDOWN
				If event.data=1
					Local i:Int=(index + 1) Mod lastind
					' set the image to the new image or rollover image
					If rollpix[i]=Null Then
						SetPanelPixmap pan,pix[i]	
					Else
						SetPanelPixmap pan,rollpix[i]									
					End If
					depressed=True					
				EndIf
		
			Case EVENT_MOUSEUP
				If event.data=1 And depressed And mouseover Then
					index = (index + 1) Mod lastind
					' set the image to the new image or rollover image
					If rollpix[index]=Null Or event.x<0 Or event.x>pan.width Or event.y<0 Or event.y>pan.height Then
						SetPanelPixmap pan,pix[index]	
					Else
						SetPanelPixmap pan,rollpix[index]									
					End If
					
					'and emit an event for someone else to catch
					Local ev:TEvent = New TEvent.Create(EVENT_GADGETACTION,Self,index,event.mods,event.x,event.y)
					ev.Emit
				EndIf
				
			Case EVENT_MOUSEENTER
				If rollpix[index] <> Null
					SetPanelPixmap pan,rollpix[index]
				EndIf
				mouseover=True
				
			Case EVENT_MOUSELEAVE
				SetPanelPixmap pan,pix[index]				
				mouseover=False
				
		End Select
		' Print event.tostring() ' for debugging
	End Method

	Method Create:TImageButton(images$[],rollover$[],x,y,w,h,group:TGadget,style=0)
		index = 0
		lastind = images.length
		pix = New TPixmap[lastind]
		For Local f = 0 Until lastind
			pix[f] = LoadPixmap(images[f])
			' if load fails, then give up on creating the gadget
			If pix[f]=Null Then Return Null
		Next
		pan = CreatePanel(x,y,w,h,group,style|PANEL_ACTIVE)
		SetPanelPixmap pan,pix[0]	

		rollpix = New TPixmap[lastind]
		If rollover <> Null
			For Local f = 0 Until rollover.length
				rollpix[f] = LoadPixmap(rollover[f])
				' if load fails, then give up on creating the gadget
				If rollpix[f]=Null Then Return Null
			Next
		EndIf
		Return Self
	End Method
	
End Type

Function CreateImageButton:TImageButton(image$[],rollover$[],x,y,w,h,group:TGadget,style=0)
	Return New TImageButton.Create(image$,rollover$,x,y,w,h,group,style)
End Function

' create test GUI
Local window:TGadget = CreateWindow("My Window",40,40,320,240)

Local lbl1:TGadget = CreateLabel("This gadget cycles on single click through 3 values.",30,10,250,15,window)
Local butt1:TImageButton = CreateImageButton(["test1.png","test2.png","test3.png"],["","",""],30,30,28,28,window,PANEL_BORDER)

Local lbl2:TGadget = CreateLabel("This gadget is highlighted on mouse over.",30,60,250,15,window)
Local butt2:TImageButton = CreateImageButton(["test1.png","test2.png"],["test1h.png","test2h.png"],30,80,26,26,window)
Local txt2:TGadget = CreateTextField(100,80,60,30,window)
SetGadgetText txt2,0

' main loop
While True
	WaitEvent() 
	Select EventID()
		Case EVENT_WINDOWCLOSE
			End
		Case EVENT_GADGETACTION
			Select EventSource()
				Case butt2 ; SetGadgetText txt2,EventData()
			End Select
	End Select
Wend

End


That's not a button.

There isn't even a button appearing when i run it :\

You might need some images.

It still isn't a button, it's just an image being switched around.

Like these naff ones that I used in tweaking Beaker's code...



Note that this is probably best viewed as a demo of how you can roll your own gadgets, not as a full blown web style image button thing to abuse like any other built in gadget.

Thanks. I wasn't aware it was possible to draw an image on the screen. Your contribution to this discussion of windows controls has been relevant and helpful. Whereas I pointed out that a certain type of control is not supported, you responded by posting a bunch of garbage about drawing images.

Oh yes. All the power of Max2d in yer window.

halo - why the bad attitude? You complain about our contribution, and yet I fail to see how "relevant", "helpful", or productive yours is.

Being rude, will get you far.

I fail to see the difference between an image of a button and a real button. When it works it works. I use buttons and other custom gadgets like that nearly as long as I have B+ ..

I personally like the GUI module, though it does still have some bugs to be worked out.

I think its pretty neat, It has a few bugs but I mean' its a first release, so theres bound to be a few overlooked things about it. Good work BRL!

I fail to see the difference between an image of a button and a real button.

The advantage to using the real OS's built-in GUI as opposed to a custom GUI is that you don't need to worry about all those little GUI standards and useful features.
Windows, for instance, will automatically handle caps lock, Insert, Home, End, Page up, Page down, arrow keys, text highlighting, copy/cut and paste, shift+arrow to highlight, Ctrl+arrow key, accessing window menus by pressing Alt and the first letter of their title, using window menus and other GUI objects with the keyboard, and more.
On top of that, the user's theme/colour settings are dealt with, so that your program can look however the user wants it to look (in conjunction with the rest of the operating system).

And really, when you think about how hard it would be to research and add all those features to a custom GUI that some users will expect, it is actually easier to use the real thing.

Sorry if I misinerpreted your post, by the way :)

The user-inteference (layoutwise) is also something I fear.. what if I create a normal/standard 32x24 button with a word on it.. the word really fits my button well. Now someone with a lame-ass blurry monitor wants to have a big font... yep.. the word doesn't fit the button anymore. If you do your own GUI you have at least the guanrantee that things will appear the best way.

However, I ofcourse understand your point. It'd still be nice to make own gadgets that are really native in some way. Something I like of my own buttonsystem for B+ is that it supports rightclicking on it. In my case it returns the name of the button, if the button-name is 'Save' then leftclick returns the string "Lsave" and rightclick returns "Rsave".

It's nonetheless an interesting discussion whether a GUI should be 'unique' or 'adapted from the OS'. In the latter case you have to assume that the OS is actually good.., and I do have some remarks on the windows layout now and then. :P

Look at those Photoshop buttons in that movable bar with drawoptions.. clickable images they are .. does anyone care about that?

What really is the difference between an image button and a flipped image? An image is an image. Unless you mean, some kind of native button `surround` with image fill?

I tried the GUI demo, it seems pretty good so far.

An image button is a button control with a bitmap drawn in the center. It's easy to send a message to windows to add a bitmap, but then you break cross-platform compatibility. I use them for color dialogs by setting the button with a solid color bitmap.

No StatusBar fields

Owners of BlitzMax (so not Halo:) should check out birdie.maxguiex.

halo - why the bad attitude?

Lol! You're kidding, right?

If you do your own GUI you have at least the guanrantee that things will appear the best way.

Except on lame ass blurry monitors, where you wouldn't be able to see @#!*.

Presenting the user with an image instead of a button seems like an ambiguity to me, unless it's in a toolbar...

I use them for color dialogs. When the user presses the button, a color dialog is opened. The button is then set to a solid color image of the selected color.

I also use them with an arrow image sometimes, like to move terrain layers up and down in the stack.

Here's some code to do it:
Function SetButtonImage(button,image)
hbutton=QueryObject(button,1)
flags=GetWindowLong(hbutton,GWL_STYLE)
If image
	If Not (BS_BITMAP And flags)
		SetWindowLong hbutton,GWL_STYLE,flags+BS_BITMAP
		EndIf
	Else
	If (BS_BITMAP And flags) SetWindowLong hbutton,GWL_STYLE,flags-BS_BITMAP
	Return
	EndIf
w=ImageWidth(image)
h=ImageHeight(image)
ibuffer=ImageBuffer(image)
LockBuffer ibuffer
pixels=CreateBank(w*h*4)
For y=0 To h-1
	For x=0 To w-1
		PokeInt pixels,4*(y*w+x)+0,ReadPixelFast(x,y,ibuffer)
		Next
	Next
UnlockBuffer ibuffer
i=CreateBitmap(w,h,1,32,pixels)
FreeBank pixels
If Not i Return
oldimage=SendMessage(hbutton,BM_SETIMAGE,IMAGE_BITMAP,i)
If oldimage DeleteObject(oldimage)
Return True
End Function


Function SetButtonColor(button,r,g,b)
hbutton=QueryObject(button,1)
flags=GetWindowLong(hbutton,GWL_STYLE)
If Not (BS_BITMAP And flags)
	SetWindowLong hbutton,GWL_STYLE,flags+BS_BITMAP
	EndIf
w=GadgetWidth(button)-4
h=GadgetHeight(button)-4
pixels=CreateBank(w*h*4)
For y=0 To h-1
	For x=0 To w-1
		PokeInt pixels,4*(y*w+x)+0,RGB(r,g,b)
		Next
	Next
i=CreateBitmap(w,h,1,32,pixels)
FreeBank pixels
If Not i Return
oldimage=SendMessage(hbutton,BM_SETIMAGE,IMAGE_BITMAP,i)
If oldimage DeleteObject(oldimage)
Return True
End Function


oops

All I see are regular buttons, checkboxes, and radio buttons. A toggle button is a regular button that stays pushed when you hit it.

Here is code to set the height of a combobox control. I don't know how to set the height of the dropdown list:
SendMessage hwnd,339,-1,height


oops

SetButtonState only works on a checkbox.

I don't see any demo by "James" anywhere. Can you tell me exactly where this demo is? The docs contain a button example, but it only shows a push button, checkbox, and radio button.

apologies, it keeps it "selected" not "depressed", without the XP skinning on my old clunker it was looking "depressed". With XP skinning, it is clearly only "selected" :c(

Stupidly, the gadget paint event only gets triggered for a canvas.

That is my first impression after playing around.

I always use a panel to create an OpenGL context. Then I set the window callback function to a PureBasic routine that catches any WM_PAINT events. Maybe you can set the window callback to your own function in BMAX?

Yeah it is possible to do that, I wrote a max win32 module myself (unfinished) and it was entirely possible to do it. Just seems stupid to build a layer ontop of the Os, and then not give access to the inner workings allowing people to extend the official module with PROPER custom gadgets (opengl draw gadgets are a massive waste of system resources).

Anyway of course I need to look into it more, but that was my first reaction. I was thinking of making a GDI painting module to sit next to MaxGUI, allowing you to paint any gadget (win32 only). So sorta needed a user friendly way to intercept the painting on all gadgets.

ANYWAY AGAIN, lol will look more when get more time.

Maybe you can set the window callback to your own function in BMAX?
Yes you can. Here is another one too.

Shagwana, unfortunatly that isnt actualy hooking the window proc. It only lets you hook into events that bltizmax has been predetermined to tell you.

See here:
http://www.blitzbasic.com/Community/posts.php?topic=53212

After working with the GUI module for a while, I'm very impressed and happy with it. So far it has been able to do everything I have wanted it to. I'm very pleased.

The best thing about MaxGUI as opposed to B+ is that MaxGUI is still being improved. B+ didn't see too much of that really.