Aros/Developer/Zune

From Wikibooks, open books for an open world
Jump to: navigation, search
Navbar for the Aros wikibook
Aros User Docs
Aros User Docs
Aros User FAQs
Aros User Applications
Aros User DOS Shell
Aros/User/AmigaLegacy
Aros Dev Docs
Aros Developer Docs
Porting Software from AmigaOS/SDL
For Zune Beginners
Zune .MUI Classes
For SDL Beginners
Aros Developer BuildSystem
Specific platforms
68k Support
PPC Power Architecture Support
Arm Raspberry Pi Support
Android support
Linux and FreeBSD Support
Windows Mingw and MacOSX Support
Aros x86 Installing
Aros x86 Audio/Video Support
Aros x86 Network Support
Aros x86 Complete System HCL
Aros Storage Support IDE SATA etc
Aros Poseidon USB Support
x86-64 Support
misc
Aros Public License


Introduction[edit]

version muimaster.library is 19 for 3.8 
                          is 20 for 3.9
                          is    for 4.0  dynamic tabs simplification 


Zune is an object-oriented GUI toolkit. It is nearly a clone (at both API and Look&Feel level) of MUI, a well-known Amiga shareware product by Stefan Stuntz. Therefore MUI developers will feel at home here, while others will discover the concepts and qualities that Zune shares with MUI.

The programmer has a much easier time to design its GUI: no need for hardcoded values, Zune is font-sensitive, and adapts to any window size due to its layout system. He/she mostly needs to only specify the semantic of its GUI to Zune, which will arrange the low-level details automatically.

Zune is based on the BOOPSI system, the framework inherited from AmigaOS (TM) for object-oriented programming in C. Zune classes does not derive from existing BOOPSI gadget classes; instead, the Notify class (base class of the Zune hierarchy) derives from the BOOPSI root class.

  • Many AROS GUI components (windows, requesters, gadgets, images) are now programmed in an object-oriented way, using the BOOPSI/Zune toolkit.
  • Every such component is created as an object, not as a pointer to a system structure. That means no more filling structures with data.
  • Objects are manipulated using methods and by passing tags via the SetAttrs() function.

Creating Prefs from scratch with using our Zune Prefs classes.

A good introduction to the BOOPSI system is the Chapter 12, "BOOPSI - Object-oriented Intuition" on-line here. Here BOOPSI NewObject() has been replaced by MUI_NewObject(), DisposeObject() becomes MUI_DisposeObject(), etc


Prerequisites[edit]

Some knowledge of OOP object-oriented programming is more than welcome.

Knowing AROS APIs and concepts like taglists and BOOPSI is essential. As Zune is a MUI clone, all the documentation pertaining to MUI is applicable to Zune. In particular, the latest available MUI developer kit and MUI autodocs can be found at here. In this LHA archive, 2 documents are warmly recommended:

MUIdev.guide, the MUI programmer documentation
and
PSI.c source code, demonstrating Zune practices like OOP and dynamic object creation 



Zune basics and conventions[edit]

MUI (= Zune) sticks a prefix on the start to signify what is being access/changed:

  • MUIA_ attribute
  • MUIM_ method
  • MUIV_ special values


BOOPSI Primer/Concepts[edit]

Class[edit]

A class is defined by its name, its parent class and a dispatcher.

name: either a string for the public classes, so that they may be used by any program in the system, or none if its a private class used only by a single application.

parent class: all BOOPSI classes are forming a hierarchy rooted at the class aptly named rootclass. It allows each subclass to implement its own version of specific parent operation, or to fall back on the one provided by its parent. Also known as base class or super class.

dispatcher: it gives access to all operations (called methods) provided by this class, ensuring that each operation is handled by the proper code or passed to its super class.

BOOPSI type for a class is Class * also known as IClass.

Object[edit]

An object is an instance of class: each object has its specific data, but all objects of the same class share the same behavior. An object has several classes if we count the parents of its true class (the most derived one) up to the rootclass.

BOOPSI type for an object is Object *. It has no field you can directly access.

When a Zune object is created and destroyed several methods are called:

OM_NEW
MUIM_Setup
MUIM_AskMinMax
[ window is opened here ]
MUIM_Show
MUIM_Draw
MUIM_Hide
[ window is closed here ]
MUIM_Cleanup
OM_DISPOSE

If the documentation says that something is only valid between setup and cleanup it means that the first place where you can use it is the setup method and you aren't allowed to use it after cleanup.

Attribute[edit]

An attribute is related to the instance data of each object: you can not access these data directly, you can only set or get the attributes provided by an object to modify its internal state. An attribute is implemented as a Tag (ULONG value or'ed with TAG_USER).

GetAttr() and SetAttrs() are used to modify an object's attributes.

Attributes can be one or more of the following:

Initialization-settable (I) : the attribute can be given as parameter at the object creation.

Settable (S) : You can set this attribute at any time (or at least, not only creation).

Gettable (G) : You can get the value of this attribute.

Method[edit]

A BOOPSI method is a function which receives as parameters an object, a class and a message:

object: the object you act on

class: the considered class for this object.

message: contains a method ID which determines the function to call within a dispatcher, and is followed by its parameters.

To send a message to an object, use DoMethod(). It will use the true class first. If the class implements this method, it will handle it. Else it will try its parent class, until the message is handled or the rootclass is reached (in this case, the unknown message is silently discarded).

Summary, before a method can act on an object, it needs a BOOPSI message

    Function                     BOOPSI Method

    NewObject()                  OM_NEW
    DisposeObject()              OM_DISPOSE
    SetAttrs()/SetGadgetAttrs()  OM_SET
    GetAttr()                    OM_GET


BOOPSI Examples[edit]

Let's see basic examples of this OOP framework:

Getting an attribute We'll query a MUI String object for its content:

void f(Object *string)
{
    IPTR result;
 
    GetAttr(string, MUIA_String_Contents, &result);
    printf("String content is: %s\n", (STRPTR)result);
}

Object * is the type of BOOPSI objects. IPTR must be used for the type of the result, which can be an integer or a pointer. An IPTR is always written in memory, so using a smaller type would lead to memory corruption! Here we query a MUI String object for its content: MUIA_String_Contents, as any other attribute, is a ULONG (it's a Tag) Zune applications use more often the get() and XGET() macros instead:

get(string, MUIA_String_Contents, &result);
 
result = XGET(string, MUIA_String_Contents);

Setting an attribute Let's change the content of our string:

SetAttrs(string, MUIA_String_Contents, (IPTR)"hello", TAG_DONE);

Pointers parameters must be casted to IPTR to avoid warnings. After the object parameter, a taglist is passed to SetAttrs and thus must end with TAG_DONE. You'll find the set() macro useful:

set(string, MUIA_String_Contents, (IPTR)"hello");

But it's only with SetAttrs() that you can set several attributes at once:

SetAttrs(string,
         MUIA_Disabled, TRUE,
         MUIA_String_Contents, (IPTR)"hmmm...",
         TAG_DONE);

Calling a method Let's see the most called method in a Zune program, the event processing method called in your main loop:

result = DoMethod(obj, MUIM_Application_NewInput, (IPTR)&sigs);

Parameters are not a taglist, and thus don't end with TAG_DONE. You have to cast pointers to IPTR to avoid warnings.

"Hello world" example sourcecode[edit]

Screenshot 'Hello World'


// gcc hello.c -lmui
#include <exec/types.h>
#include <libraries/mui.h>
 
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/muimaster.h>
#include <clib/alib_protos.h>
 
int main(void)
{
    Object *wnd, *app, *but;
 
    // GUI creation
    app = ApplicationObject,
        SubWindow, wnd = WindowObject,
            MUIA_Window_Title, "Hello world!",
            WindowContents, VGroup,
                Child, TextObject,
                    MUIA_Text_Contents, "\33cHello world!\nHow are you?",
                    End,
                Child, but = SimpleButton("_Ok"),
                End,
            End,
        End;
 
    if (app != NULL)
    {
        ULONG sigs = 0;
 
        // Click Close gadget or hit Escape to quit
        DoMethod(wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
                 (IPTR)app, 2,
                 MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
 
        // Click the button to quit
        DoMethod(but, MUIM_Notify, MUIA_Pressed, FALSE,
                 (IPTR)app, 2,
                 MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
 
        // Open the window
        set(wnd, MUIA_Window_Open, TRUE);
 
        // Check that the window opened
        if (XGET(wnd, MUIA_Window_Open))
        {
            // Main loop
            while((LONG)DoMethod(app, MUIM_Application_NewInput, (IPTR)&sigs)
                  != MUIV_Application_ReturnID_Quit)
            {
                if (sigs)
                {
                    sigs = Wait(sigs | SIGBREAKF_CTRL_C);
                    if (sigs & SIGBREAKF_CTRL_C)
                        break;
                }
            }
        }
        // Destroy our application and all its objects
        MUI_DisposeObject(app);
    }
 
    return 0;
}


Remarks/General[edit]

We don't manually open libraries, it's done automatically for us.

GUI creation[edit]

We use a macro-based language to easily build our GUI. A Zune application has always 1 and only 1 Application object:

app = ApplicationObject,

An application can have 0, 1 or more Window objects. Most often a single one:

SubWindow, wnd = WindowObject,

Be nice, give a title to the window:

MUIA_Window_Title, "Hello world!",

A window must have 1 and only 1 child, usually a group. This one is vertical, that means that its children will be arranged vertically:

WindowContents, VGroup,

A group must have at least 1 child, here it's just a text:

Child, TextObject,

Zune accepts various escape codes (here, to center the text) and newlines:

MUIA_Text_Contents, "\33cHello world!\nHow are you?",

An End macro must match every xxxObject macro (here, TextObject):

End,

Let's add a second child to our group, a button! With a keyboard shortcut o indicated by an underscore:

Child, but = SimpleButton("_Ok"),

Finish the group:

End,

Finish the window:

End,

Finish the application:

End;

So, who still needs a GUI builder? :-)

Error handling[edit]

If any of the object in the application tree can't be created, Zune destroys all the objects already created and application creation fails. If not, you have a fully working application:

if (app != NULL)
{
...

When you're done, just call MUI_DisposeObject() on your application object to destroy all the objects currently in the application, and free all the resources:

: ...

MUI_DisposeObject(app);
}


Notifications[edit]

Notifications are the simplest way to react on events. The principle? We want to be notified when a certain attribute of a certain object is set to a certain value:

DoMethod(wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,

Here we'll listen to the MUIA_Window_CloseRequest of our Window object and be notified whenever this attribute is set to TRUE. So what happens when a notification is triggered? A message is sent to an object, here we tell our Application to return MUIV_Application_ReturnID_Quit on the next event loop iteration:

(IPTR)app, 2,
MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

As we can specify anything we want here, we have to tell the number of extra parameters we are supplying to MUIM_Notify: here, 2 parameters.

For the button, we listen to its MUIA_Pressed attribute: it's set to FALSE whenever the button is being released (reacting when it's pressed is bad practice, you may want to release the mouse outside of the button to cancel your action - plus we want to see how it looks when it's pressed). The action is the same as the previous, send a message to the application:

DoMethod(but, MUIM_Notify, MUIA_Pressed, FALSE,
(IPTR)app, 2,
MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);


Opening the window[edit]

Windows aren't open until you ask them to:

set(wnd, MUIA_Window_Open, TRUE);

If all goes well, your window should be displayed at this point. But it can fail! So don't forget to check by querying the attribute, which should be TRUE:

if (XGET(wnd, MUIA_Window_Open))


Main loop[edit]

Let me introduce you my lil' friend, the ideal Zune event loop:

ULONG sigs = 0;

Don't forget to initialize the signals to 0 ... The test of the loop is the MUIM_Application_NewInput method:

...
while((LONG) DoMethod(app, MUIM_Application_NewInput, (IPTR)&sigs)
 != MUIV_Application_ReturnID_Quit)

It takes as input the signals of the events it has to process (result from Wait(), or 0), will modify this value to place the signals Zune is waiting for (for the next Wait()) and will return a value. This return value mechanism was historically the only way to react on events, but it was ugly and has been deprecated in favor of custom classes and object-oriented design.

The body of the loop is quite empty, we only wait for signals and handle Ctrl-C to break out of the loop:

    {
    if (sigs)
        {
        sigs = Wait(sigs | SIGBREAKF_CTRL_C);
        if (sigs & SIGBREAKF_CTRL_C)
             break;
        }
    }


Conclusion[edit]

This program gets you started with Zune, and allows you to toy with GUI design, but not more.


Notification actions[edit]

Notification allows you to respond to events your application/gui or any other object might cause. Due to the attribute and method based nature of Zune, and a few special attributes, most applications can be almost completely automated through the use of Notification(s).

As seen in hello.c, you use MUIM_Notify to call a method if a certain condition happens. If you want your application to react in a specific way to events, you can use one of these schemes:

  • Set An attribute. You can automate the passing of values from gadgets,
etc to other gadgets. Setting values may also trigger other notification
events.
  • Invoke a method, for example:
MUIM_Application_ReturnID: you can ask your application to return an
arbitrary ID on the next loop iteration, and check for the value in
the loop. This is the dirty old way of doing things.
MUIM_CallHook, to call a standard Amiga callback hook: this is an
average choice, not object-oriented but not that ugly either.
custom method: the method belongs to one of your custom class. It
is the best solution as it supports object-oriented design in
applications. It needs you to create custom classes so it may not
the easiest for beginners or people in a hurry.


Zune Examples/Tutorials[edit]

  • Being an AmigaOS3.x MUI clone, all AmigaOS3.x MUI code serves as example code for using AROS Zune.
  • Shinkuro's Cross-Platform Guide gives information about compatible programming on Amiga-related platforms (Amiga, AROS, MorphOS) and MUI
  • AROS itself contains lots of Zune examples. Download AROS sources and AROS contrib sources archives, take a look at the sourcecode of Zune programs. A good starting point are the Zune test programs, located in the SYS:Tests/Zune folder of AROS' binary distributions (e.g. the nightly build) - their sources can be found in the "test/Zune" folder of the AROS source archive.


Some other good examples are here:


Zune Reference[edit]

  • MUI Reference information from AmigaOS documentation can be found e.g. utilitybase.com/forum/... or at utilitybase.com/ref, it has a section on MUI. (Note: Information at that site is a little outdated, but still very helpful.) (2011-10-31: utilitybase.com currently down.)

Tools[edit]

MUIBuilder[edit]

A native build and sourceforge svn repository has been started and Aros/Developer/ZuneFurther documentation can be read. v3 is W.I.P. so it is not recommended for now.

This thread discussed using the m68k build and diff file which modifications were necessary to make it build-able under AROS.

Summary

  • added "#define MUI_OBSOLETE" to get some deprecated but needed defines
  • changed/added some #include
  • changed variable name Object to obj because Object is a type
  • changed the Hook for the callback function
  • last not least: added main.c which calls the functions to open/close the GUI.

Just go ahead and create your GUIS under any version of AROS or under amiga 68k version and do some modifications (which would be necessary for MorphOS/AmigaOS3/AmigaOS4, too).

ChocolateCastle[edit]

The binary which makes creating MUI classes and applications easier. Programmers using this system often avoid writing custom classes, being discouraged by a lot of boring and schematical typing. A significant part of a typical custom class may be generated automatically. This is usually 2 to 5 kB of source code. Automating this work speeds programming up and helps avoiding simple typing errors.


Errors[edit]

Message  
Solution SetAttrs() is vararg version of SetAttrsA() and expects a complete taglist so you must add TAG_DONE and not TAG_END. 
Message  
Solution 
Message  undefined symbols ... KeyString
Solution finally replaced KeyString(0,256,'f') by MUIA_CreateObject(MUIO_String,"f",256)
Message  undefined symbols... GetAttr
Solution you need:  #include <proto/intuition.h>
Message  
Solution 
Message  
Solution  #define MUI_OBSOLETE