Welcome to the MUI Royale tutorial! This small step-by-step document will guide you through the process of creating your first GUI with MUI Royale in very few steps.
Let us start with the basics: In MUI Royale GUIs are created using XML files that contain a description of a number of windows containing a variety of GUI elements. Here is a minimal GUI description for a MUI Royale GUI that contains a window with a listview, a string gadget, and two buttons in XML format:
<?xml version="1.0" encoding="iso-8859-1"?> <application base="HELLOWORLD"> <window title="Example GUI" muiid="MAIN"> <vgroup> <listview> <column/> </listview> <string/> <hgroup> <button>Add</button> <button>Remove</button> </hgroup> </vgroup> </window> </application> |
The application base is set to HELLOWORLD
. It is very important that you
always define the Application.Base attribute because the string you specify
in this attribute is used by MUI to store the program-specific preferences in an
external file (usually in ENV:MUI
). The <application>
object is
the MUI master object for every application and must only be used once per application.
All other MUI objects are children of Application class.
It is also important to set the attribute Window.MuiID for every window because only windows that have a MUI id remember their position and size. Also, only windows that have Window.MuiID can be snapshot by the user from the window's context menu. Thus, it is very important that you set this attribute for all your windows.
To see how our XML declaration above looks as a MUI GUI, we have to save it to
a file named GUI.xml
and then use the following code to turn it into a
full-blown MUI GUI:
@DISPLAY {Hidden = True} mui.CreateGUI(FileToString("GUI.xml")) Repeat WaitEvent Forever |
Please note that we use the @DISPLAY
preprocessor command to hide the
Hollywood display that will popup by default. We do not want to have this display
because we already have a MUI GUI that shall be the visual representation of our
program. Keep in mind, though, that the Hollywood display is still there. It is
just hidden. Hollywood's conception says that there always must be a valid display.
That is why we cannot remove the Hollywood display completely. All we can do is
to hide it. You can still draw to it, though.
You should also add some information about your program using the @APPAUTHOR
,
@APPCOPYRIGHT
, @APPDESCRIPTION
, @APPTITLE
, and @APPVERSION
preprocessor commands. MUI needs this information for several purposes, e.g. the
information passed in @APPTITLE
is used by the MUI preferences window.
Here is an example declaration of these preprocessor commands:
@APPTITLE "Tutorial" @APPVERSION "$VER: Tutorial 1.0 (27.12.12)" @APPCOPYRIGHT "Copyright ©2012, Andreas Falkenhahn" @APPAUTHOR "Andreas Falkenhahn" @APPDESCRIPTION "The tutorial app from the MUI Royale guide" |
When you run the code above you will notice that the window does not react on clicks on its close gadget. You have to use CTRL-C or Exchange to close the program. Of course, we want our program to be closable via the window's close gadget as well. In order to achieve this, we need to setup a notification on the Window.CloseRequest attribute that will be set whenever the user hits the close gadget. We can setup this notification by modifying our XML code from above like this:
<window title="Example GUI" muiid="MAIN" notify="closerequest"> |
The next thing we have to do is install an event handler callback using the
InstallEventHandler()
Hollywood function because our Hollywood
script needs to be informed every time a MUI Royale event comes in. Thus, we
have to modify our code as follows:
@DISPLAY {Hidden = True} Function p_EventFunc(msg) Switch msg.Class Case "Window": Switch msg.Attribute Case "CloseRequest": End EndSwitch EndSwitch EndFunction InstallEventHandler({MUIRoyale = p_EventFunc}) mui.CreateGUI(FileToString("GUI.xml")) Repeat WaitEvent Forever |
What we have done here is installing the function p_EventFunc
as an
event handler callback that gets executed whenever a MUI Royale event comes
in. When we get such an event, we then have to check which MUI class and attribute
has triggered it. This is done by looking into the msg.Class
and
msg.Attribute
fields of the event message that our callback receives
as its first parameter. In order to react on the Window.CloseRequest
attribute, we thus have to look for an event from class Window
and
attribute CloseRequest
. When we have found such an event, we simply call
the End()
command to terminate our program.
The next thing we want to do is add the functionality that whenever the user presses the "Add" button the text in the string gadget should get added to the listview as the last entry. To do this, we first have to find a way of identifying our MUI gadgets from the Hollywood script. This is done by giving them IDs in the XML declaration. IDs are simply text strings that are used for talking to MUI objects from Hollywood scripts. So let's add some IDs now for all gadgets that we need to talk to. We have to modify our XML declaration like this:
... <listview id="mylistview"> <column/> </listview> <string id="mystring"/> <hgroup> <button id="mybt1">Add</button> <button id="mybt2">Remove</button> </hgroup> ... |
Now we have to setup some notifications so that our event handler callback
does not only inform us about changes in the Window.CloseRequest
attribute but also in the Button.Pressed attribute which is the
attribute that gets set to True
whenever the user hits a button. To listen
to these button clicks we have to modify our code like this:
... <button id="mybt1" notify="pressed">Add</button> <button id="mybt2" notify="pressed">Remove</button> ... |
Now that we have done this we can add some code to our event handler callback
that grabs the contents of the string gadget and adds it to the end of the
list in our listview object. This is done by first calling mui.Get()
on the String.Contents attribute to get the contents of the string
gadget and then running the method Listview.Insert on the listview
gadget using mui.DoMethod() to insert the entry into the
listview. Here is the code that has to be inserted into p_EventFunc
for
this purpose:
Switch msg.Class ... Case "Button": Switch msg.Attribute Case "Pressed": Switch msg.ID Case "mybt1": ; "Add" button was pressed Local s$ = mui.Get("mystring", "contents") mui.DoMethod("mylistview", "insert", "bottom", s$) EndSwitch EndSwitch EndSwitch |
The next thing we want to do is implement the functionality of our "Remove" button. Whenever this button is pressed, we want the active entry to be removed from the listview. We can do this by running the Listview.Remove method on the listview. Hence, we have to modify our code like this:
Switch msg.ID ... Case "mybt2": ; "Remove" button was pressed mui.DoMethod("mylistview", "remove", "active") EndSwitch |
Now we want the active entry of the listview to be automatically displayed in the string gadget. For this purpose we have to setup a notification on the Listview.Active attribute which is triggered whenever the active entry of the listview changes. Thus, we have to modify our XML file like this:
... <listview id="mylistview" notify="active"> <column/> </listview> ... |
In our event handler callback we can implement this functionality quite easily by running the Listview.GetEntry method and then setting the string gadgets contents using the String.Contents attribute. Here is the code for doing that:
Switch msg.Class ... Case "Listview": Switch msg.Attribute Case "Active": Local s$ = mui.DoMethod("mylistview", "getentry", "active") mui.Set("mystring", "contents", s$) EndSwitch EndSwitch |
If you try this code, you will set that the Listview.Active attribute is not only triggered when the user selects a new listview entry with his mouse, but also when entries are removed from the listview and thus cause a new entry becoming the active one.
The next thing we want to do is disable the "Remove" button when there
is no active entry in the listview. We can disable MUI gadgets by setting
the Area.Disabled attribute to True
. As there are no entries in
the listview initially, we have to set Area.Disabled to True
already at the start of our program. So you have to insert this code:
... mui.CreateGUI(FileToString("GUI.xml")) mui.Set("mybt2", "disabled", True) ... |
Now we have to make some modifications to our event handler callback. Whenever we get the notification on Listview.Active we have to check if it is different from the special value "Off". If that is the case, we will enable the "Remove" gadget. The special value "Off" (-1) is returned by Listview.Active whenever there is no active entry in the listview. We have to modify our code like this:
Switch msg.Class ... Case "Listview": Switch msg.Attribute Case "Active": Local s$ = mui.DoMethod("mylistview", "getentry", "active") mui.Set("mystring", "contents", s$) mui.Set("mybt2", "disabled", IIf(msg.triggervalue = -1, True, False)) EndSwitch EndSwitch |
We use the field msg.TriggerValue
here. This always contains
the current value of the attribute that has triggered the event, i.e. in our
case it contains the current value of the Listview.Active attribute.
We could also call mui.Get() manually on Listview.Active
first, but this is not really required because we can simply use the msg.TriggerValue
shortcut.
The last thing we want to do is add a menu to our GUI. All MUI programs should have a menu item that allows the user to customize the program's appearance using the MUI preferences. This is done by running the Application.OpenConfigWindow method on the application object. Also, every MUI program should have the menu items "About MUI" and "About MUI Royale" that inform the user that this program was done using MUI and MUI Royale. You can popup the these windows by running the Application.AboutMUI and Application.AboutMUIRoyale methods on the application object respectively. To add these menus to our program, we use the Menustrip class. Here is the XML code that you have to add before your window declaration:
... <application base="HELLOWORLD"> <menustrip id="mymenustrip"> <menu title="Project"> <item id="menabout" notify="selected">About...</item> <item id="menaboutmui" notify="selected">About MUI...</item> <item id="menaboutmuiroyale" notify="selected"> About MUI Royale...</item> <item/> <item id="menquit" notify="selected" shortcut="Q">Quit</item> </menu> <menu title="Settings"> <item id="menmuiset" notify="selected">MUI...</item> </menu> </menustrip> ... </application> |
After we have created our menustrip object using the XML code above we have to attach this menustrip to our window. This is done by setting the Window.Menustrip attribute to our menustrip object. Here is the XML code for this:
<window title="Example GUI" muiid="MAIN" notify="closerequest" menustrip="mymenustrip"> |
As you can see in the XML code above, we have already added notifications on the Menuitem.Selected attribute to get informed whenever the user selects a menu item. Now we have to add code to our event handler function that takes the appropriate action when a menu item is selected. Before we can do that, however, we need to assign an ID to our application object because we need to use mui.DoMethod() on it. Here is how the XML code needs to be adapted:
<application base="HELLOWORLD" id="app"> |
Now we can write the code for our event handler callback function that handles menu items:
Switch msg.Class ... Case "Menuitem": Switch msg.Attribute Case "Selected": Switch msg.id Case "menabout": mui.Request("Test", "Test program\n" .. "© 2012 by Andreas Falkenhahn", "OK") Case "menaboutmui": mui.DoMethod("app", "aboutmui") Case "menaboutmuiroyale": mui.DoMethod("app", "aboutmuiroyale") Case "menquit": End Case "menmuiset": mui.DoMethod("app", "openconfigwindow") EndSwitch EndSwitch EndSwitch |
Some final touches to our program could be adding online help to our gadgets
by using the Area.ShortHelp attribute and adding all objects to the
keyboard cycle chain so that the GUI can also be controlled via keyboard. We
do not have to add the buttons to our keyboard cycle chain, because this is
always done automatically. We have to add the listview and the string gadgets
manually, though. Adding objects to the keyboard cycle chain is done by using
the Area.CycleChain attribute. Also, it is advised that you listen
to the ShowWindow
and HideWindow
events that can come from
the Exchange program in Commodities and act accordingly. So here is how our
final program looks like with these two last enhancements applied. First the
XML file:
<?xml version="1.0" encoding="iso-8859-1"?> <application base="HELLOWORLD" id="app"> <menustrip id="mymenustrip"> <menu title="Project"> <item id="menabout" notify="selected">About...</item> <item id="menaboutmui" notify="selected">About MUI...</item> <item id="menaboutmuiroyale" notify="selected"> About MUI Royale...</item> <item/> <item id="menquit" notify="selected" shortcut="Q">Quit</item> </menu> <menu title="Settings"> <item id="menmuiset" notify="selected">MUI...</item> </menu> </menustrip> <window title="Example GUI" muiid="MAIN" notify="closerequest" menustrip="mymenustrip"> <vgroup> <listview id="mylistview" notify="active" shorthelp="A listview" cyclechain="true"> <column/> </listview> <string id="mystring" shorthelp="Enter entry name here" cyclechain="true"/> <hgroup> <button id="mybt1" notify="pressed" shorthelp="Add new entry">Add</button> <button id="mybt2" notify="pressed" shorthelp="Remove entry">Remove</button> </hgroup> </vgroup> </window> </application> |
And here is the code for the program logic:
@DISPLAY {Hidden = True} @APPTITLE "Tutorial" @APPVERSION "$VER: Tutorial 1.0 (27.12.12)" @APPCOPYRIGHT "Copyright ©2012, Andreas Falkenhahn" @APPAUTHOR "Andreas Falkenhahn" @APPDESCRIPTION "The tutorial app from the MUI Royale guide" Function p_EventFunc(msg) If msg.Action <> "MUIRoyale" Switch msg.Action Case "HideWindow": mui.Set("app", "iconified", True) Case "ShowWindow": mui.Set("app", "iconified", False) EndSwitch Return EndIf Switch msg.Class Case "Window": Switch msg.Attribute Case "CloseRequest": End EndSwitch Case "Button": Switch msg.Attribute Case "Pressed": Switch msg.ID Case "mybt1": ; "Add" button was pressed Local s$ = mui.Get("mystring", "contents") mui.DoMethod("mylistview", "insert", "bottom", s$) Case "mybt2": ; "Remove" button was pressed mui.DoMethod("mylistview", "remove", "active") EndSwitch EndSwitch Case "Listview": Switch msg.Attribute Case "Active": Local s$ = mui.DoMethod("mylistview", "getentry", "active") mui.Set("mystring", "contents", s$) mui.Set("mybt2", "disabled", IIf(msg.triggervalue = -1, True, False)) EndSwitch Case "Menuitem": Switch msg.Attribute Case "Selected": Switch msg.id Case "menabout": mui.Request("Test", "Test program\n" .. "© 2012 by Andreas Falkenhahn", "OK") Case "menaboutmui": mui.DoMethod("app", "aboutmui") Case "menaboutmuiroyale": mui.DoMethod("app", "aboutmuiroyale") Case "menquit": End Case "menmuiset": mui.DoMethod("app", "openconfigwindow") EndSwitch EndSwitch EndSwitch EndFunction InstallEventHandler({MUIRoyale = p_EventFunc, HideWindow = p_EventFunc, ShowWindow = p_EventFunc}) mui.CreateGUI(FileToString("GUI.xml")) mui.Set("mybt2", "disabled", True) Repeat WaitEvent Forever |
That's it! Now you should be able to create MUI programs to your personal taste. Thank you for reading this tutorial and enjoy the power of MUI with Hollywood and MUI Royale at your hands!