Chapter 14: Introducing Icons

Over the past few chapters, we’ve seen how to create a window and get it to open on the desktop. Whilst windows are extremely useful in their own right, they can often become a lot more useful when combined with icons.

The Programmer’s Reference Manual defines an icon as “a rectangular area of a window’s workspace” which – while accurate – is perhaps a little vague. In fact, many familiar pieces of the RISC OS desktop are icons: Figure 14.1 shows four windows (from Paint, Ovation Pro and UnitConv) which are composed entirely using them.

Figure 14.1: Icons come in all shapes and sizes

Icons can contain text, or a sprite, or both text and a sprite. They can have a border, or go without; if they do have a border, it can take a range of 3D effects if required. Icons can respond to mouse clicks – to change their appearance, notify the application, or both – and writable icons can accept and display keyboard input from the user. In many cases, this functionality is provided automatically by the Wimp, without the application needing to do anything.

A simple icon

There are two ways to create an icon on RISC OS. One is to add its details to the end of a wimp_window structure and have the Wimp_CreateWindow SWI process them: this is what the wimp_window.icon_count and wimp_window.icons[] elements in the wimp_window structure, which we glossed over in Section 12.6, are for. The other is to assemble the same set of details and pass them to the Wimp_CreateIcon SWI after the window has been created.

For now, we’re going to concentrate on the latter method, because this will allow us to examine the various components in detail and to see how they all interact with each other. In practice, most applications will use the former method in conjunction with a piece of software called a template editor – this approach allows many windows and dialogue boxes to be laid out in an almost WYSIWYG manner, much as one might put together an image in Draw. The window and icon designs can then be saved out as a template file which the Wimp can load and turn into a set of wimp_window structures. All the complexity that we’re about to encounter is still present, however, so it’s essential to know how it works – for now we’ll do things ‘the hard way’ and see exactly what’s going on. Once the fundamentals are in place, we can move on to template files in a later chapter.

Probably the simplest form of icon that we can create is a basic text icon. In a similar way to windows, icons can be created by filling in a structure and passing a pointer to it to the Wimp_CreateIcon SWI – this SWI then adds the icon to its target window, so that it will be there when the window is opened. Let’s start by adding a new win_create_icon() to our win.c file:

static wimp_i win_create_icon(void)
{
        wimp_icon_create icon_definition;

        icon_definition.w = win_handle;
        icon_definition.icon.extent.x0 = 100;
        icon_definition.icon.extent.y0 = -300;
        icon_definition.icon.extent.x1 = 300;
        icon_definition.icon.extent.y1 = -100;
        icon_definition.icon.flags = wimp_ICON_TEXT | wimp_ICON_BORDER |
                        wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
                        (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT);
        strncpy(icon_definition.icon.data.text, "Icon", 12);

        return wimp_create_icon(&icon_definition);
}

In a similar manner to that in which win_initialise() called Wimp_CreateWindow, this code initialises a block of memory by declaring a wimp_icon_create structure and then fills it in with the complete details of the icon that we wish to create. Once the structure contents is ready, a pointer is passed to wimp_create_icon() – which causes the Wimp to create an icon from the information contained within. In the same way that wimp_create_window() returns a wimp_w window handle, wimp_create_icon() returns a wimp_i icon handle to to identify the icon within the window. Another similarity is that the structure passed to wimp_create_icon() is no longer required as soon as the SWI returns; as with the window_definition variable in win_initialise(), icon_definition is a local variable which will disappear as soon as the win_create_icon() function exits.

The function has been declared as static because it’s closely tied to the window defined within win.c and won’t be of interest to other parts of the code. With a suitable function prototype added at the top of the file, we can then add a line to call win_create_icon() from the end of win_initialise(). The complete code can be found in Download 14.1. It will be useful as a base from which to experiment with the values used for creating the icon as we start to explain what’s going on.

Download 14.1
The source code and files in this example are made available under Version 1.2 of the European Union Public Licence.

When the code is compiled and run, the window that opens will have gained a square box containing the word “Icon” as shown in Figure 14.2.

Figure 14.2: Our application’s window with a simple text icon added

The wimp_icon_create structure, with which we declare the icon_definition variable in our new function, is defined by OSLib as follows and contains two elements:

struct wimp_icon_create {
        wimp_w          w;
        wimp_icon       icon;
};

typedef struct wimp_icon_create wimp_icon_create;

Rather like a set of Russion Dolls, one of the elements – wimp_icon_create.icon – is itself a structure. It has the same type as the wimp_window.icons[] array in the wimp_window structure, and contains the actual icon definition. To enable Wimp_CreateIcon to know which window to create the new icon in, the other element of wimp_icon_create is a wimp_w window handle stored in wimp_icon_create.w.

        icon_definition.w = win_handle;

The icon that we’re creating is to go in the window that has just been defined and created, so we assign icon_definition.w to have the wimp_w handle stored in the global win_handle variable. If we were going to make win_create_icon() a general-purpose function, we would probably want to pass this window handle in as a parameter – since we’ll only be using it to create icons in the one window, directly using the global variable is fine for now.

The other element of the structure, icon_definition.icon, is a wimp_icon structure defined by OSLib as:

struct wimp_icon {
        os_box          extent;
        wimp_icon_flags flags;
        wimp_icon_data  data;
};

typedef struct wimp_icon wimp_icon;

This structure is the icon equivalent of the wimp_window structure for windows, although it’s a lot shorter: unlike a window, icons only have a handful of pieces of information associated with them.

The icon’s extent

The first piece of information that the wimp_icon structure contains is wimp_icon.extent – an os_box structure. We’ve met os_box before, most recently in Section 12.2: it’s a structure containing two pairs of x,y coordinates which locate the icon within its window’s work area. We looked briefly at the dimensions of icons back in Chapter 6, when we created an icon on the iconbar. The iconbar is a very specific case, however – we now need to consider things more generally.

Unlike windows, whose visible area extents are specified in terms of the overall screen coordinates as we saw in Section 12.2, icons live within windows and so their extents are given in terms of their parent window’s work area. The origin of a window’s work area is up to the developer but, as we’ve seen,it’s conventional to locate (0,0) at the top-left. Our window’s visible area is 400 × 400 OS Units square, so with both scroll offsets set to zero the work area that’s initially visible is 0 to 399 in the x direction, and 0 to −399 in the y direction.

To make an icon that’s 200 × 200 OS Units square in the centre of this initial visible area, the minimum and maximum x coordinates of the icon will be at 100 and 300 OS Units. Similarly, the minimum and maximum y coordinates will be at −300 and −100 OS Units respectively. As with window coordinates, the minimum coordinates are inclusive whilst the maximum ones are exclusive to the icon’ area. This is shown graphically in Figure 14.3.

Figure 14.3: An icon’s location is defined in terms of its parent window

In the case of our icon defintion, we set the four values icon_definition.icon.extent.x0, icon_definition.icon.extent.y0, icon_definition.icon.extent.x1 and icon_definition.icon.extent.y1 to the values 100, −300, 300 and −100 respectively.

icon_definition.icon.extent.x0 = 100;
icon_definition.icon.extent.y0 = -300;
icon_definition.icon.extent.x1 = 300;
icon_definition.icon.extent.y1 = -100;

The icon’s appearance

With the icon’s extent established, the next part of the definition to consider are the icon flags. Like the wimp_window_flags that we met earlier, wimp_icon_flags consists of a 32-bit word split into a number of distinct parts which – taken together – define a lot about the icon. The first thing to decide is what kind of icon we’re looking at, and this is defined by three flag bits within the word:

#define wimp_ICON_TEXT                  ((wimp_icon_flags) 0x1u)
#define wimp_ICON_SPRITE                ((wimp_icon_flags) 0x2u)
#define wimp_ICON_INDIRECTED            ((wimp_icon_flags) 0x100u)

Our icon definition set the icon_definition.icon.flags element to

        icon_definition.icon.flags = wimp_ICON_TEXT | wimp_ICON_BORDER |
                wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
                (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT);

which we can see has the wimp_ICON_TEXT bit set. This means that the icon we’ve created is a text icon: it contains a single piece of text, which is simply the word “Icon”.

A number of other icon flags control the appearance and layout of the icon:

#define wimp_ICON_BORDER                ((wimp_icon_flags) 0x4u)
#define wimp_ICON_HCENTRED              ((wimp_icon_flags) 0x8u)
#define wimp_ICON_VCENTRED              ((wimp_icon_flags) 0x10u)
#define wimp_ICON_FILLED                ((wimp_icon_flags) 0x20u)
#define wimp_ICON_RJUSTIFIED            ((wimp_icon_flags) 0x200u)
#define wimp_ICON_HALF_SIZE             ((wimp_icon_flags) 0x800u)

Our icon has the wimp_ICON_HCENTRED and wimp_ICON_VCENTRED flags set, which means that the text appears centred within the icon’s extent – as was seen in Figure 14.2. By setting the wimp_ICON_VCENTRED on its own, wimp_ICON_HCENTRED and wimp_ICON_VCENTRED together, or wimp_ICON_RJUSTIFIED and wimp_ICON_VCENTRED together, it is possible to left align, centre or right align the text respectively as shown in Figure 14.4. The wimp_ICON_VCENTRED flag, whilst ostensibly controlling vertical alignment, has little effect on text icons.

Figure 14.4: The icon’s text can be aligned left, centre or right

The final flag set in our icon definition is wimp_ICON_BORDER, which causes the Wimp to draw an outline around the icon’s extent. If the flag is omitted than, as can be seen in Figure 14.5, the outline is omitted.

Figure 14.5: Icons can either have a border, or go without

Since the wimp_ICON_HALF_SIZE flag only applies to icons containing sprites – which we’ll meet soon – this only leaves one flag to consider: wimp_ICON_FILLED. When set, this allows the icon’s background to be filled with a solid colour instead of the window’s background being allowed to show through. Before this makes any sense, however, we need to consider the colours used for the icon.

Unlike the wimp_window_flags, which consisted entirely of flag bits, the value of wimp_icon_flags contains a mixture of single-bit flags and groups of bits which form numeric values. When we set icon_definition.icon.flags in win_create_icon(), we’re combining the four individual flags – wimp_ICON_TEXT, wimp_ICON_BORDER, wimp_ICON_HCENTRED and wimp_ICON_VCENTRED – with a numeric colour value of wimp_COLOUR_BLACK.

To do this, the value wimp_COLOUR_BLACK is shifted left by wimp_ICON_FG_COLOUR_SHIFT, which is defined to be 24 by OSLib:

#define wimp_ICON_FG_COLOUR_SHIFT       (24)
#define wimp_ICON_FG_COLOUR             ((wimp_icon_flags) 0xF000000u)

#define wimp_ICON_BG_COLOUR_SHIFT       (28)
#define wimp_ICON_BG_COLOUR             ((wimp_icon_flags) 0xF0000000u)

The four bits from 24 to 27 of wimp_icon_flags are taken by the Wimp to indicate the foreground colour of the icon, whilst the bits from 28 to 31 are taken as the background colour if one is needed – the corresponding shift constant is wimp_ICON_BG_COLOUR_SHIFT. The colours are given as wimp_colour values – the same one that we met back in Section 12.3 for defining window colours.

OSLib also provides two bit-mask values, defined as wimp_ICON_FG_COLOUR and wimp_ICON_BG_COLOUR to indicate the bits in the flag field which represent the colours.

Some examples showing the effect of applying different foreground colours can be seen in Figure 14.6.

Figure 14.6: Icons can appear in any of the Wimp colours

To be able to specify a background colour for the icon, it must be filled so that the background shows up – this can be achieved by setting the wimp_ICON_FILLED flag in icon_definition.icon.flags within win_create_icon(). If the flag definition is changed to

icon_definition.icon.flags = wimp_ICON_TEXT |
                wimp_ICON_BORDER | wimp_ICON_FILLED |
                wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
                (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
                (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);

and then compiled and run, then the icon created should have a filled white background as seen in Figure 14.7.

Figure 14.7: Icons can be filled with a solid background colour

The icon data

The final part of the wimp_icon structure is the icon data held in wimp_icon.data. The wimp_icon_data type is a union occupying 12 bytes of memory, which OSLib defines as follows:

union wimp_icon_data {
        char                    text[12];
        char                    sprite[12];
        char                    text_and_sprite[12];
        struct {
                char            *text;
                char            *validation;
                int             size;
        } indirected_text;
        struct {
                osspriteop_id   id;
                osspriteop_area *area;
                int             size;
        } indirected_sprite;
        struct {
                char            *text;
                char            *validation;
                int             size;
        } indirected_text_and_sprite;
};

typedef union wimp_icon_data wimp_icon_data;

Whilst this might look confusing at first glance, there are actually six distinct parts to the union corresponding to the six ‘types’ of icon which are available on RISC OS. It is possible to create text icons, sprite icons or text and sprite icons; each of these three variants can then be either indirected or non-indirected, which give us the six options.

As we saw earlier, our icon is a text icon, because it has the wimp_ICON_TEXT flag bit set: this means that only the wimp_icon_data.text[] element at the top of the union definition applies. This is simply a char array with 12 entries: for a non-indirected text icon, the Wimp treats the 12 bytes of icon data as holding between 0 and 12 characters of text to be displayed in the icon.

In our win_create_icon() function, we set it up by copying in a string from a location pointed to by the text parameter:

strncpy(icon_definition.icon.data.text, text, 12);

As with a number of other, similar text buffers in other parts of the system, the Wimp treats the icon data for a non-indirected text icon a little surprisingly. If the length of the icon text is between 0 and 11 characters, it should be a control terminated string with a control character (such as a '\0') after the last character. If the text is 12 characters long, however, it should be unterminated. This makes the behaviour of strncpy() ideal for populating it, because should the function reach the end of the destination buffer before it reaches the end of the string to copy, the buffer is left without a terminator.

Sprite icons

If a text icon contains a piece of text, a sprite icon contains – unsurprisingly – a sprite. We’ve already met sprite icons, in fact, as the iconbar icon that we created back in Chapter 6 was one. All of the formatting options that we’ve encountered so far apply equally to both text icons and sprite icons, so we can change our icon into a sprite icon simply by updating the win_create_icon() function as follows:

static wimp_i win_create_icon(void)
{
        wimp_icon_create icon_definition;

        icon_definition.w = win_handle;
        icon_definition.icon.extent.x0 = 100;
        icon_definition.icon.extent.y0 = -300;
        icon_definition.icon.extent.x1 = 300;
        icon_definition.icon.extent.y1 = -100;
        icon_definition.icon.flags = wimp_ICON_SPRITE |
                        wimp_ICON_BORDER | wimp_ICON_FILLED |
                        wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
                        (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
                        (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
        strncpy(icon_definition.icon.data.sprite, "directory", 12);

        return wimp_create_icon(&icon_definition);
}

Aside from changing the wimp_ICON_TEXT into wimp_ICON_SPRITE when setting icon_definition.icon.flags, the only change is to the line using strncpy() to initialise icon_definition.icon.data – it now copies data into the wimp_icon_data.sprite part of the union.

Since this is now a sprite icon, we should change to initialising the wimp_icon_data.sprite[] element instead of wimp_icon_data.text[] – it’s the same memory location underneath, but keeps the code clearer. Instead of a piece of text to be displayed, the icon data now contains the name of the sprite which is to be shown – again up to 12 characters long, and terminated by a control character (in other words '\0') if it contains 11 characters or less. We’ve also changed the text that’s copied in: now we’re using the name of a sprite which we know exists in the Wimp’s sprite area.

If the changes, which can be found in full in Download 14.2, are compiled and run, the window in Figure 14.8 should be seen. The icon now contains a standard directory icon centred in its box, still with the same black border and white background.

Figure 14.8: Sprite icons display a sprite instead of text

Download 14.2
The source code and files in this example are made available under Version 1.2 of the European Union Public Licence.

All of the formatting options which we applied to the text icon can also be used on this sprite icon. The sprite can be aligned horizontally to the left, right or centre based on the settings of the wimp_ICON_HCENTRED, wimp_ICON_VCENTRED and wimp_ICON_RJUSTIFIED flags. It can have a border if wimp_ICON_BORDER is set, and it can be filled if wimp_ICON_FILLED is set. The foreground and background colours can also be changed.

Since this is a sprite icon, the wimp_ICON_HALF_SIZE flag can also be used to change its appearance. If set, the Wimp scales the sprite to half-size when displaying it, which is used by the Filer to produce ‘small’ icons when small sprites aren’t available. Its effect can be seen in Figure 14.9.

Figure 14.9: The sprites in sprite icons can be shown at half size

Text and sprite icons

The final type of icon is the text and sprite icon; unfortunately, there are no prizes for guessing what this kind of icon does. It can be created by setting both the wimp_ICON_TEXT and wimp_ICON_SPRITE flags in the definition, like this:

static wimp_i win_create_icon(void)
{
        wimp_icon_create icon_definition;

        icon_definition.w = win_handle;
        icon_definition.icon.extent.x0 = 100;
        icon_definition.icon.extent.y0 = -300;
        icon_definition.icon.extent.x1 = 300;
        icon_definition.icon.extent.y1 = -100;
        icon_definition.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
                        wimp_ICON_BORDER | wimp_ICON_FILLED |
                        wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
                        (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) |
                        (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT);
        strncpy(icon_definition.icon.data.text_and_sprite, "directory", 12);

        return wimp_create_icon(&icon_definition);
}

The only other change to the definition is that we’re now initialising the wimp_icon_data.text_and_sprite[] element of the icon_definition.icon.data union. A full set of code can be found in Download 14.3.

Download 14.3
The source code and files in this example are made available under Version 1.2 of the European Union Public Licence.

When the changed code is compiled and run, the result should be as shown in Figure 14.10. The Wimp is treating the string held in icon_definition.icon.data.text_and_sprite as both the sprite name and the text to be displayed in the icon – this is a big limitation, as there are few occasions where this is likely to be useful.

Figure 14.10: Combining both text and a sprite into the same icon

The alignment of the icon’s contents is still controlled by the wimp_ICON_HCENTRED, wimp_ICON_VCENTRED and wimp_ICON_RJUSTIFIED flags, but with this kind of icon they are used together to specify one of eight arrangements as shown in Figure 14.11.

Figure 14.11: The eight ways in which text and sprite icons can be arranged

The final ‘oddity’ of text and sprite icons is that whatever the state of the wimp_ICON_FILLED flag, the text background is always filled. This can be seen in Figure 14.12, where the icon on the left is filled and the icon on the right isn’t – a contrasting colour has been used to make the effect stand out clearly.

Figure 14.12: The text always has a filled background in text and sprite icons

Clearly the icons that we’ve seen up to now are limited in their potential uses: aside from the fact that text and sprite icons must contain sprites with names that exactly match their text, the limitation of twelve characters for simple text icons is quite a restriction. Fortunately there’s a solution available, and in the next chapter we’ll learn what it is when we meet indirected icons.

In the meantime, now would be a good time to go back and play with all of the options that we’ve met in this chapter until you’re completely comfortable with them and what they do. Icons are central to the RISC OS Wimp, and the following chapters will be much easier to follow if the basics have been mastered!