4.1 Tutorial

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!


Show TOC