Blitz:MUI

From Amiga Coding
Revision as of 13:16, 25 January 2018 by Daedalus (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

MUI is a third party GUI toolkit which was designed to bring a more modern GUI system to the Amiga. It's designed to modular, object-orientated, and to take care of the actual laying out and display of the window so your program doesn't have to. A number of other third party toolkits were also available, but MUI is by far the most popular, and the only one to be compatible across Amiga OS and all its derivatives. As a result, practically everyone has it installed on their machines. Amiga OS 3 users can download MUI from Aminet, Amiga OS 4 users have MUI included by default, and MorphOS uses MUI as its main toolkit. AROS uses a clone of MUI called Zune which is broadly source compatible with MUI, so your 68k MUI programs made with Blitz should work fine on 68k AROS.

When MUI was originally introduced, it was criticized for being heavy on resources; however, those were the days when many people still used unexpanded Amigas with 68000 or 68020 CPUs and often with only 1 or 2 MB of RAM. Nowadays it's not really a concern with most users having expanded machines, and while it's heavier on resources than GadTools, it's still far lighter than almost any toolkit on any other platform.

MUI in Blitz

Several userlibs are available for Blitz Basic which expand the internal command set, most of which are included with Blitz itself or in the Blitz Support Suite (which you should download if you're only using a basic Blitz Basic 2.1 setup). One of these adds many commands for conveniently accessing MUI without having to worry about the library calls necessary for such an interface, and allowing you to use MUI in a similar way to GadTools. It includes commands for setting up and using many common MUI elements, as well as some generic commands that allow you to use any other MUI elements, custom classes etc. directly, just like C.

Unfortunately, this userlib isn't perfect and there are some bugs in it that mean for some tasks you should really do it manually rather than using the built-in commands. Where I have found this to be the case I've eventually found a workaround, and in these cases I'll provide that workaround in these notes and examples.

Getting Started

To use MUI in Blitz, you should add the resident file MUI to the Compiler Settings. This contains all the standard structs and constants that MUI uses (and it uses a lot!). MUI had a different approach compared to GadTools; instead of creating a window and defining all the properties of all the elements by hand, MUI looks after all of this for you: window size and position, gadget size and layout, refreshing the window contents etc. All you have to do is decide on a general layout and create the relevant groups and objects, although in most cases it's also possible to manually override MUI's control.

To start, all MUI applications require some special initialization code. This opens the MUI libraries, identifies your program to MUI and loads in any MUI-specific settings required (window size etc.) This is all handled automatically whenever you compile a MUI program, however Blitz's MUI library provides some commands for setting particular properties of your program. These commands are demonstrated in this example:

MUIApplicationTitle "MUITest" ; This sets the name of your application as it is seen by MUI and the Commodities library

MUIApplicationVersion "$VER: MUITest 1.2 (23.09.2015)" ; Sets the version information for MUI and the Commodities library

MUIApplicationCopyright "(c)2015 Rob Cranley" ; Sets a copyright message, again, as seen by MUI and the Commodities library

MUIApplicationAuthor "Rob Cranley" ; Sets the author information

MUIApplicationDescription "MUI Test Program" ; Sets the description as seen by MUI and the Commodities Exchange
 
MUIApplicationBase "TEST1" ; Sets a unique identifier of your program for MUI

These fields are not required, and if omitted will be left blank or given default values. However, it is recommended to set them at the start of any MUI programs.

Defining the User Interface

MUI works a little differently to Intuition and GadTools in that MUI looks after the window and gadget sizes and positions itself. All you have to do is define the general layout in terms of either horizontal or vertical groups of objects and how they relate to each other, and MUI will look after the rest. At least one overall group is required, and this can contain any number of objects, including sub groups of any size or type. A simple group can be create with the following code:

; Constants for object IDs
#continuebutton = 1
#quitbutton     = 2
#buttongroup    = 3

; Create the button objects
MUIKeyButton #continuebutton, "Continue", "c"
MUIKeyButton #quitbutton, "Quit", "q"

; Add the buttons to a horizontal group definition with ID #buttongroup
MUIAddObjsHGroup #buttongroup, #continuebutton, #quitbutton

; Create the group object for group ID #buttongroup
MUICreateHGroup #buttongroup

This will create two buttons, one labeled Continue, the other labeled Quit. The MUIKeyButton statement sets up a button that can also be activated by a keypress, in this case c for the continue button and q for the quit button. (There is also a statement for creating a button without a keyboard shortcut called MUISimpleButton, but I've found that to be buggy so I recommend using MUIKeyButton, setting the shortcut string to "" if no shortcut is required.)

A horizontal group is then created, so the buttons will appear side-by-side. This group can be attached to a window as-is, or can be used as an object in another group:

; More object constants
#label1    = 4
#string1   = 5
#vspace1   = 6
#vspace2   = 7
#maingroup = 8

; Create a label for the string gadget
MUILabel #label1, "Enter your name", #MUIO_Label_Centered

; Create an empty string gadget with maximum length of 100 characters
MUIString #string1, "", 100

; Create two vertical spacers
MUIVSpace #vspace1, 0
MUIVSpace #vspace2, 0

; Now, add everything so far to a vertical group object with ID #maingroup
MUIAddObjsVGroup #maingroup, #vspace1, #label1, #vspace2, #buttongroup

; Create the vertical group object
MUICreateVGroup #maingroup

This vertical group contains two vertical spacers which starts at 0 pixels but can be stretched. This allows the window to be resized vertically, as the string, label and buttons all have a fixed height. The label and string gadget are included, as is the button group from the previous example. This gives the two buttons side by side at the bottom of the window.

Opening a Window

Creating the Window Object

Once the interface's elements are defined, you are ready to open a window with these elements attached. All the objects required should be placed in an overall group which is then applied to the window being opened.

Two commands are required to create a complete window object, as follows:

#mainwindow = 10

MUICreateWindow #mainwindow, "My MUI Test", "MAIN", #maingroup
MUIAddSubWindow #mainwindow

The first line creates the window object with the ID #mainwindow. The title of this window is set as "My MUI Test", and "MAIN" is set as a unique identifier for MUI. Finally, the #maingroup argument is the ID of the group to attach to the window. The window will be sized appropriately for this group.

The second line adds the just-created window object to your application. Without it, your window doesn't exist as far as MUI is concerned.

Creating the Application

Once all your windows are declared as above, it's time to create the application object using the MUICreateApplication statement. This is a simple command that returns False (0) if it fails. Always check this, just in case there is some problem creating the application! For example:

If MUICreateApplication=False Then NPrint "Unable to create application!":End

Optionally you can also assign an ID to the application object for referencing your program itself later on. It gives your program an object ID for use with event handling, methods and some other features. This is handled by the MUIApplicationObj command:

; Application object ID constant
#mainapplication = 11
MUIApplicationObj #mainapplication

Opening and Closing the Window

Once the window object and application are created, it can then be opened and closed whenever you need it. This is done simply using the MUIOpenWindow command to open the window with the given ID, and the MUICloseWindow command to close the window with the given ID. Unlike the standard Window command, MUIOpenWindow must be treated as a function. It returns True (1) if it was successful and False (0) if not.

The full window code can be seen in this example:

; Window object ID constant
#mainwindow      = 10
; Application object ID constant
#mainapplication = 11

MUICreateWindow #mainwindow, "My MUI Test", "MAIN", #maingroup
MUIAddSubWindow #mainwindow

If MUICreateApplication=False Then NPrint "Unable to create application!":End
MUIApplicationObj #mainapplication

succ = MUIOpenWindow(#mainwindow)

If succ = False Then NPrint "Unable to open window!":End

; Put your main loop here

MUICloseWindow #mainwindow

Event Handling

MUI has its own way of handling events which must be used for a program using MUI, although it is similar to the standard Intuition event handling. With MUI, you need to explicitly declare each event that you wish to detect with an ID number you define. Then in your main loop you can look for those defined IDs using MUI-specific Event statements. After that it's more or less the same as standard Intuition - a Select/Case block can be used to decide on the correct action to take.

Declaring Events

Each individual event must be declared with MUI as a Notification. This declaration defines the object that produces the event (e.g. a gadget), a definition of the condition required to trigger the event, an ID number to identify the event, and a definition of what to do with the event when it occurs. MUI has the ability to pass an event to any object which allows for some clever automation between objects, for example, setting the value of a number gadget based on a slider or making groups visible or invisible based on the status of a checkbox, all without needing to manually carry out these actions in your code.

For now though, we'll just work about passing the event onto the application so you can see it. The simplest way to do this is with the MUINotifyApp statement, which declares an event that is to be passed to the application object for processing (that means your program deals with it). Each event required will need its own Notify, although multiple notifies can use the same ID if desired when two events produce the same outcome, for example a Cancel gadget and the window's close gadget can share a notify ID.

An example of usage is:

#ev_close = -1 ; Event ID constant

MUINotifyApp #mainwindow, #MUIA_Window_CloseRequest, 1, #ev_close

This sets up a notification that comes from the object ID #mainwindow (our main window), and the condition to trigger the event is that #MUIA_Window_CloseRequest is 1. #MUIA_Window_CloseRequest is a special constant defined by MUI that corresponds to an attribute of the window that specifies if the close gadget has been pressed. #ev_close is a constant that we have defined as an ID for the close event, and is the value that is reported to the application whenever this notify is triggered.

To set up notifications for the two gadgets in the earlier example, the following code can be used:

#ev_continue ; Event ID constant for continue button

MUINotifyApp #continuebutton,#MUIA_Pressed,0,#ev_continue
MUINotifyApp #quitbutton,#MUIA_Pressed,0,#ev_close

Note that the event trigger is that the attribute of the button (#MUIA_Pressed) is 0 rather than 1. This means the event is only triggered when the user lifts off the mouse button after clicking the gadget. Checking this for a value of 1 instead will cause the event to trigger as soon as the user presses the button down, which isn't normal behavior.

Note also that the Quit button sends the same event code, #ev_close, since we want it to perform the same function as the window's close gadget.

Detecting Events

Detecting MUI events is similar to the standard Intuition events, and two functions are available for this: MUIEvent (equivalent of Event) and MUIWaitEvent (equivalent of WaitEvent). MUIWaitEvent is the preferred option as it puts your program to sleep while waiting on something to happen, whereas MUIEvent does not sleep and returns a 0 instead if nothing is happening. In reality though, MUIWaitEvent appears to be bugged and behaves identically to MUIEvent, so the difference is moot here... Anyway, both commands return 0 if there is no event waiting in the queue, or the ID of the Notify if an event has occurred. A simple Select/Case block can then be used to carry out the relevant action. For example:

quit.b = False
Repeat
  ev.l = MUIWaitEvent
  If ev
    NPrint "Event occurred: ", ev
    Select ev
      Case #ev_continue
        NPrint "Continue Pressed!"
      Case #ev_close
        NPrint "Quitting..."
        quit = True
    End Select
  End If
Until quit
End

Complete Example Code

BlitzMUITest.png
The following code combines all the steps above into a simple, working program. The image on the right shows an example of the window produced by this code. Note that this screenshot is from OS4 and both OS4 and MUI can be easily customized so it may look quite different on other computers.
; Set some application properties
MUIApplicationTitle "MUITest"
MUIApplicationVersion "$VER: MUITest 1.2 (23.09.2015)"
MUIApplicationCopyright "(c)2015 Rob Cranley"
MUIApplicationAuthor "Rob Cranley"
MUIApplicationDescription "MUI Test Program"
MUIApplicationBase "TEST1"

; Constants For Object IDs
#continuebutton = 1
#quitbutton     = 2
#buttongroup    = 3


; More object constants
#label1    = 4
#string1   = 5
#vspace1   = 6
#vspace2   = 7
#maingroup = 8


#ev_close = -1 ; Event ID constant
#ev_continue ; Event ID constant for continue button

; Window object ID constant
#mainwindow      = 10
; Application object ID constant
#mainapplication = 11


; Create the button objects
MUIKeyButton #continuebutton, "Continue", "c"
MUIKeyButton #quitbutton, "Quit", "q"

; Add the buttons to a horizontal group definition with ID #buttongroup
MUIAddObjsHGroup #buttongroup, #continuebutton, #quitbutton

; Create the group object for group ID #buttongroup
MUICreateHGroup #buttongroup


; Create a label for the string gadget
MUILabel #label1, "Enter your name", #MUIO_Label_Centered

; Create an empty string gadget with maximum length of 100 characters
MUIString #string1, "", 100

; Create two vertical spacers
MUIVSpace #vspace1, 0
MUIVSpace #vspace2, 0

; Now, add everything so far to a vertical group object with ID #maingroup
MUIAddObjsVGroup #maingroup, #vspace1, #label1, #string1, #vspace2, #buttongroup

; Create the vertical group object
MUICreateVGroup #maingroup



MUICreateWindow #mainwindow, "My MUI Test", "MAIN", #maingroup
MUIAddSubWindow #mainwindow

If MUICreateApplication=False Then NPrint "Unable to create application!":End
MUIApplicationObj #mainapplication

succ = MUIOpenWindow(#mainwindow)

If succ = False Then NPrint "Unable to open window!":End


MUINotifyApp #mainwindow, #MUIA_Window_CloseRequest, 1, #ev_close

MUINotifyApp #continuebutton,#MUIA_Pressed,0,#ev_continue
MUINotifyApp #quitbutton,#MUIA_Pressed,0,#ev_close

quit.b = False
Repeat
  ev.l = MUIWaitEvent
  If ev
    NPrint "Event occurred: ", ev
    Select ev
      Case #ev_continue
        NPrint "Continue Pressed!"
      Case #ev_close
        NPrint "Quitting..."
        quit = True
    End Select
  End If
Until quit
MUICloseWindow #mainwindow
End