The command set originates from the original BlitzPlus "Gadget" based names providing a healthy amount of backward compatability for users wishing to port code developed in BlitzPlus.
The code beginning at "While True" is the main event loop of the program, it repeats forever waiting for an Event to happen, printing the event's details to the output window and ending if the event has the ID of EVENT_WINDOWCLOSE.
' createwindow.bmx Strict Local window:TGadget window=CreateWindow("My Window",40,40,320,240) While True WaitEvent Print CurrentEvent.ToString() Select EventID() Case EVENT_WINDOWCLOSE End End Select WendTry moving and sizing the window while the program is running and you will see the details of the events that occur printed in the debug window of BlitzMax.
Try also modifying the "window=" line in the program with:
window=CreateWindow("My Window",40,40,320,240,Null,WINDOW_TITLEBAR)which will create a Window with only a title bar and no status or menu bars. See the documentation for CreateWindow for more details of the Window styles supported by MaxGui.
The MaxGui documentation contains working examples for each of commands that create gadgets including CreateButton, CreateCanvas, CreateComboBox, CreateHTMLView, CreateLabel, CreateListBox, CreateMenu, CreatePanel, CreateProgBar, CreateSlider, CreateTabber, CreateTextField, CreateToolBar and CreateTreeView.
A Gadget in MaxGui is given pixel coordinates when it is created that positions it in it's parent's client area. The client area of a Window gadget is inside the window's borders not including the space used by any Status or Menu bars.
If a Window is resized MaxGui uses any children's layout settings to reposition them within the new size of it's client area.
The SetGadgetLayout is used to control how the Gadget is repositioned when it's parent is resized.
By considering each edge of the Gadget independently, the MaxGui layout engine repositions each gadget according to each edge's rule where the rule is
EDGE_CENTERED - The edge remains a fixed distance from the center of the parent
EDGE_ALIGNED - The edge is a locked distance away from it's parent's corresponding edge
EDGE_RELATIVE - The edge remains a proportional distance from the parent's 2 edges
The following code demonstrates a common case of creating a panel in a window the size of the window's client area and locking the distance of each edge to the correspong edge of the parent window (EDGE_ALIGNED rule). The result is that the panel is automatically sized by MaxGui when the window is resized filling the entire client area.
Strict Local window:TGadget Local panel:TGadget window=CreateWindow("My Window",40,40,320,240) panel=CreatePanel(0,0,ClientWidth(window),ClientHeight(window),window) SetGadgetColor panel,200,100,100 SetGadgetLayout panel,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED While True WaitEvent Print CurrentEvent.ToString() Select EventID() Case EVENT_WINDOWCLOSE End End Select WendBy removing the line of code with the SetGadetLayout command you can view the default EDGE_CENTERED rule in effect on all edges with the panel retaining it's size due to all it's edges being kept a fixed distance from the center of the window.
To view rule EDGE_RELATIVE in effect modify the SetGadgetLayout command to read "SetGadgetLayout panel,EDGE_RELATIVE,EDGE_RELATIVE,EDGE_RELATIVE,EDGE_RELATIVE".
But wait, there is no discernable change in the resizing behavior between rule EDGE_ALIGNED and rule EDGE_RELATIVE???
Unless the panel is created inset from the window client such as the result of the following modification:
panel=CreatePanel(10,10,ClientWidth(window)-20,ClientHeight(window)-20,window)you will note the edges are infact being kept a proportional distance from the parent's 2 edges when the window is resized.
The fact is that EDGE_ALIGNED and EDGE_RELATIVE have the same effect on Gadget edges when the edges are created on the borders of their parent's client area.
In general the MaxGUI module should perform identically on Windows, Apple and Linux machines. However some care and attention is required when testing in regards to how your application will look on each platform.
The following are a few issues that if considered should hopefully enable you to publish programs that look and function well on a wide variety of systems.
Recomended gadget dimensions.
Shorter buttons are possible in Windows but they will become cropped on Apple systems so it is recomended that when using the default font buttons should be created with a height of 24 pixels.
Again, using the default font settings, the above rule should produce good results on all platforms. Often labels will be placed to the left or right of TextFields, in which case they should be set 4 pixels lower down the screen in order to correctly align with TextFields.
When creating a form with multiple TextField gadgets start each row 30 pixels below the y position of the previous gadget's positions.
Use the WINDOW_CLIENTCOORDS flag when dependent on client size.
If you are using a fixed size window specify it's size based on the required client area using the WINDOW_CLIENTCOORDS style.
Specifying a window's size in the default frame coordinates will result in a client area that may be different on different machines due to a number of factors including both the version of the computer's operating system and certain user preference outside your control.
As an alternative to developing applications that have a single main loop that waits then processes events from an eventqueue the AddHook command allows a callback function to be used to process events as they happen.
This is important for two type of events.
EVENT_GADGETACTION events produced by Slider gadgets with the original SLIDER_SCROLLBAR style and EVENT_WINDOWRESIZE events that occur when the user is resizing a Window both require a hooked type of event processing for best results. In both these situations most OS's go into a "modal" loop that means a WaitEvent command will only return once the user releases the mouse button but with Event hooks the code can be called directly from within the OS's own modal loop.
The other type of event is EVENT_GADGETPAINT which is generated whenever the operating system needs to redraw a Canvas gadget. When hooked, drawing code can be executed at the time of the event rather than later when the OS returns control to the main program, at which time it may have already drawn other content above the Canvas.
The following code is a simple example of using Max2D to render dynamically into a Canvas Gadget...
' createcanvas.bmx Strict Global GAME_WIDTH=320 Global GAME_HEIGHT=240 ' create a centered window with client size GAME_WIDTH,GAME_HEIGHT Local wx=(GadgetWidth(Desktop())-GAME_WIDTH)/2 Local wy=(GadgetHeight(Desktop())-GAME_HEIGHT)/2 Local window:TGadget=CreateWindow("My Canvas",wx,wy,GAME_WIDTH,GAME_HEIGHT,0,WINDOW_TITLEBAR|WINDOW_CLIENTCOORDS) ' create a canvas for our game Local canvas:TGadget=CreateCanvas(0,0,320,240,window) ' create an update timer CreateTimer 60 While WaitEvent() Select EventID() Case EVENT_TIMERTICK RedrawGadget canvas Case EVENT_GADGETPAINT Local g=CanvasGraphics(canvas) SetGraphics g SetOrigin 160,120 SetLineWidth 5 Cls Local t=MilliSecs() DrawLine 0,0,120*Cos(t),120*Sin(t) DrawLine 0,0,80*Cos(t/60),80*Sin(t/60) Flip Case EVENT_WINDOWCLOSE FreeGadget canvas End Case EVENT_APPTERMINATE End End Select WendThe above code creates a timer that causes an event to occur at 60hz (ticks per second).
Each time an EVENT_TIMERTICK is received by the program's main event loop it calls RedrawGadget which tells the system to redraw our canvas gadget.
The code following the Case EVENT_GADGETPAINT above is where the actual drawing takes place.
- of interest -
If you remove two lines of the above code that contain the "RedrawGadget canvas" and "Case EVENT_GADGETPAINT" the program still appears to function normally.
This is because MaxGUI infact allows you to draw to a canvas when ever you want. Drawing in response to an EVENT_GADGETPAINT however is better behavior as no drawing code is executed if the canvas is obscured or the application has a low refresh rate in which case it will be still asked to redraw itself on the event an obscured canvas is revealed by window movement from above.
As mentioned above the best way to manage redrawing a Canvas gadget is with an EventHook.
The following example uses an EventHook to handle events at the time of occurance rather than from the WaitEvent() queue. You will notice at the bottom of the program that infact the main loop does nothing but WaitEvent and all the event handling is done inside the TApplet Type.
' rendering a canvas using an EventHook based Applet Type Strict Type TApplet Field window:TGadget Field canvas:TGadget Field timer:TTimer Method Render() Local x,y,width,height,t,i width=GraphicsWidth() height=GraphicsHeight() t=MilliSecs() SetBlend ALPHABLEND SetViewport 0,0,width,height SetClsColor 0,0,0 SetOrigin width/2,height/2 Cls For x=-width/2 To width/2 Step 2 y=Sin(t*0.3+x)*height/2 DrawLine 0,0,x,y Next End Method Method OnEvent(event:TEvent) Select event.id Case EVENT_WINDOWCLOSE End Case EVENT_TIMERTICK RedrawGadget canvas Case EVENT_GADGETPAINT SetGraphics CanvasGraphics(canvas) Render Flip End Select End Method Function eventhook:Object(id,data:Object,context:Object) Local event:TEvent Local app:TApplet event=TEvent(data) app=TApplet(context) app.OnEvent event End Function Method Create:TApplet(name$) Local a:TApplet Local w,h window=CreateWindow(name,20,20,512,512) window.SetTarget Self w=ClientWidth(window) h=ClientHeight(window) canvas=CreateCanvas(0,0,w,h,window) canvas.SetLayout 1,1,1,1 canvas.SetTarget Self timer=CreateTimer(100) AddHook EmitEventHook,eventhook,Self Return Self End Method End Type AutoMidHandle True Local applet:TApplet applet=New TApplet.Create("Render Applet") While True WaitEvent Wend
Unfortunately I have obfiscated (made obscure) the function of the event hook by wrapping it in an Applet object and I have started using big words in my sentences.
To be as concise as possible, the TApplet Type declares a Class that features an OnEvent Method that with some magic in it's constructor (the Create() Method) provides an object oriented way for our program to collect events.
Our TApplet implements the rendering sequence as featured in the previous example but because it does so based on a Hook responds in a more well behaved manner when the OS wishes to redraw the desktop when it is made visible by some user action or told to refresh with the RedrawGadget command.
The following are a few issues that you may encounter when porting code from BlitzPlus.
The codes returned by the WaitEvent and EventID commands are not the same as BlitzPlus. See the Event Objects docs for a list of the constants used in MaxGUI. Numerical values have not been documented in order to encourage users to use the correct EVENT_NAMES.
New events in MaxGUI include
EVENT_HOTKEYHIT - see SetHotKeyEvent
EVENT_WINDOWACCEPT - a file has been dragged onto the window
EVENT_GADGETPAINT - a canvas gadget needs redrawing
EVENT_GADGETSELECT - the selected state of a TextArea has changed
EVENT_GADGETMENU - the user has clicked the right button on this gadget
EVENT_GADGETOPEN - a node in a TreeViewNode has been expanded
EVENT_GADGETCLOSE - a node in a TreeViewNode has been collapsed
EVENT_GADGETDONE - an HTMLView has finished loading a page
TextAreas no longer generate Key Up, Key Down or Key Stroke events.
Instead the new SetGadgetFilter command can be used to install a callback routine that is called each time the user presses a key with the callback able to accept or reject each key by returning either True or False respectively.
The three platform specific modules that ship with MaxGui are Win32MaxGui.mod, CocoaMaxGui.mod and FLTKMaxGui.mod which provide native implementations of MaxGui for Windows, Apple MacOS and Linux platforms respectively.
The new Event.mod and EventQueue.mod modules are extensions to the BlitzMax system. BlitzMax events provide the necessary mechanics of communicating with a MaxGui based application as well as providing all BlitzMax applications with events such as EVENT_APPSUSPEND, EVENT_APPRESUME and EVENT_APPTERMINATE.
The commands WaitEvent, PeekEvent and PollEvent are the primary commands for receiving Events in BlitzMax programs and PostEvent can be used for generating Events.
The Type TGadget is used in MaxGui to represent all 18 Gadget types including:
GADGET_DESKTOP GADGET_WINDOW GADGET_BUTTON GADGET_PANEL GADGET_TEXTFIELD GADGET_TEXTAREA GADGET_COMBOBOX GADGET_LISTBOX GADGET_TOOLBAR GADGET_TABBER GADGET_TREEVIEW GADGET_HTMLVIEW GADGET_LABEL GADGET_SLIDER GADGET_PROGBAR GADGET_MENUITEM GADGET_NODE GADGET_CANVAS
The TMaxGUIDriver Type defined in maxgui.driver represents the gadget factory which is created at startup to produce platform specific versions of TGadget on demand.
The type TGadget is actually an abstract class with TWin32Gadget, TCocoaGadget and TFLTKGadget being the actual types produced depending on the current TMaxGUIDriver.
The types TIconStrip, TGUIFont and TGadgetItem also provide an abstract interface to the platform specific implementations meaning that like TGadget itself, they are not intended to be extended by the user.
It is expected that most users wanting to create custom gadgets that work well across all platforms will find the Panel and Canvas gadgets provide adequate Event reporting and rendering functionality from which to produce their own uber-gadgets.
The cross platform nature of MaxGUI means there are implications for those wishing to extend functionality by taking advantage of platform specific features that have no logical equivalent on other platforms.
Those wishing to do so are encouraged to implement such features in separate modules rather than making modifications to the BRL modules which are intended only to support the core cross platform MaxGUI functionality.
This of course does not mean the the current specification of MaxGUI is set in stone, rather, that those wanting to interface at a lower level with the Operating System of their choice do so in the privacy of their own modules rather than becoming muddied in the source of the lowlevel platform specific BRL modules.