BOOPSI Gadget help strings in user interfaces

  • up
    45%
  • down
    55%

1. Introduction

The complexity of the user interface of a program can increase along with the features of the program itself, and finding a healthy balance of usability and functionality is sometimes more difficult than you might think.

Most modern gadget toolkits will include various ways to aid the user in navigating around the interface to find the features they want. One common way of doing this is via a system of help messages which appear when hovering the mouse over a particular gadget. Whatever you want to call these, they can be a valuable way to determine the function of a particular interface component. AmigaOS4 is no different in this regard, and allows the interface programmer to supply "GadgetHelp" strings.

2. Background

When the current BOOPSI classes were first introduced (they were collectively called "ClassAct" back then), these too allowed a basic help feature. Some work was done on these over the years, but the most significant upgrades to this system have been done with AmigaOS4.

Traditionally, GadgetHelp strings were supplied as arrays of "struct HintInfo" pointers, which worked well, but was rather cumbersome in use, especially in the case of Localisation. In order to change the actual help strings to suit whichever "Locale" the user may have selected meant changing the pointer to the correct string inside the array of HintInfo. With the advent of more dynamic interfaces, this process did not lend itself well to being able to change according to the context. A good example here would be a ListBrowser gadget with many items in its list. The user would simply be shown one help string for the entire list, which may not be ideal. A ClickTab gadget would be another example where changing the help string might be useful when hovering over each tab, especially when many tabs are open, and the actual text for the tab is being compressed to squeeze them into the window layout.

3. Help string dynamics

AmigaOS4.1 has since made these arrays of HintInfo obsolete, and instead supplied a new tag for every gadget type called "GA_HintInfo". Like all other object based components, BOOPSI gadgets can accept this tag at creation time (OM_NEW), or it can be changed during program execution (OM_SET). If your program uses "Catalogs" to localise the interface texts, then you can simply supply the string with the GA_HintInfo tag directly, no more poking into arrays of structures. A typical code fragment may look something like this;

  1. mybutton = IIntuition->NewObject( NULL, "button.gadget",
  2. GA_ID, OKBUTTON,
  3. GA_RelVerify, TRUE,
  4. GA_Text, GetCatalogString( MSG_MYBUTTON_TEXT ),
  5. GA_HintInfo, GetCatalogString( MSG_HINTINFO_MYBUTTON ),
  6. TAG_DONE );

The above code uses a GetCatalogString function to fetch a string from the loaded catalog suitable for the users preferences. It sets the string shown in the button, plus it sets the HintInfo string which is shown as a help "bubble" when hovering over the button. This makes it much easier to set localised help texts, plus it has the benefit of being able to be changed at any time programmatically. Some interfaces may change the button text during the life of the gadget, and now it becomes an easy task to change the help text too.

4. A deeper look

No extra work (apart from supplying the GA_HintInfo tag) is required to support this new feature, the window.class object still requires the WINDOW_GadgetHelp tag, and ideally this should be set via a user preference (more on this later). The WINDOW_HintInfo tag can simply be ignored or NULL now.
This, of course, assumes AmigaOS 4.1 Update 3 to work correctly, so the general rule so stay compliant with earlier versions is to supply the array of HintInfo as usual, as this will provide the fixed help texts for gadgets under versions prior to Update 3. Then you can supply texts via GA_HintInfo, and Update 3 will over-ride the HintInfo array strings with this new one supplied. Earlier versions will simply ignore the GA_HintInfo strings, so this is safe to use, even for users of < Update 3.

The ListBrowser gadget is also able to supply a help text for each node in the list separately. This can be especially useful to give the user an indication of what that node represents. This is handled with a "hook" that is called by the gadget, like so:

  1. LONG HintInfoHook( struct Hook *hook, Object *o, struct Node *lbn )

The various parameters are explained in the ListBrowser autodoc. The one parameter of special interest is the 'lbn' value. This is a pointer to the listbrowser node that the mouse pointer was over when the hook was called. You may call GetListBrowserNodeAttrs on this pointer to get information about the node, and this can assist you in determining the correct help text to return. Simply return a pointer to the help string as a LONG, and that will be shown to the user. Typical examples of this kind of functionality might be a list of files, and the help text could display what type it is, or the file properties.

AmiUpdate uses this exact feature to inform the user about shown updates in its main list.

The ListBrowser gadget needs to be told which hook to use, and this is done with the LISTBROWSER_HintInfoHook tag. Simply supply a filled in struct Hook pointer that specifies your hook function in its h_Entry field.

The ClickTab gadget also has the ability to display a helptext applicable to the tab the pointer was over at the time. This is set with the TNA_HintInfo tag when creating the ClickTab node. You may also change this attribute at any time, so if the tab text, or the tabs representation changes, the program can change the help text too. Again, details of this is included in the ClickTab autodoc.

The same feature is also applicable to the SpeedBar gadget.

5. Summary

So now we have looked at how easy it is to set up help texts for all BOOPSI gadgets, we need to think about the user.

Help texts are great for learning your way around an interface and learning what all of its features do, but experienced users may find these help "bubbles" annoying, and obstructing the interface use, so how can the programmer help?

Well, obviously, offer the use of help texts as an option in your program preferences or options. The help texts can be turned on and off with a simple call:

  1. /* val can be TRUE or FALSE to enable or disable help texts respectively */
  2. IIntuition->SetAttrs( mywindowobject, WINDOW_HintInfo, val, TAG_DONE );

You may set this attribute at any time during the life of the window object, and your users will thank you for the ability to turn them off. Making them enabled by default is probably the best way to ease the users grasp of your program, and the user can turn them off later when they are ready.

We hope you enjoyed this small look at interface help texts, and hope that its contents may be of use to you in your own programs.

Tags: 

Blog post type: 

Comments

whose's picture

Nice and comprehensive read about the latest incarnation of the OS4 bubble help system. All OS4-developers should take a read, definetly!

Thx Rigo!

Coder Insane-Software

www.insane-software.de

trixie's picture

Well written and informative! I'll add just one little comment to encourage more future-proof programming:

(In section 4)

Simply supply a filled in struct Hook pointer that specifies your hook function in its h_Entry field.

... or even better, use AllocSysObjectTags() to allocate and initialize the hook:

  1. lbHintInfoHook = IExec->AllocSysObjectTags(ASOT_HOOK,
  2. ASOHOOK_Entry, HintInfoHook,
  3. ASOHOOK_Subentry, NULL,
  4. ASOHOOK_Data, NULL,
  5. TAG_DONE);

... then add the hook to the ListBrowser:

  1. IIntuition->SetAttrs(mylbobject,
  2. LISTBROWSER_HintInfoHook, lbHintInfoHook,
  3. TAG_DONE);

... and dispose of the hook when your program finishes:

  1. IExec->FreeSysObject(ASOT_HOOK, lbHintInfoHook);

AmigaOne X5000-020 / 2GB RAM / Sapphire Pulse Radeon RX 560 / AmigaOS 4.1 Final Edition Update 2

TSK's picture

Nice and good read.

Is this GetCatalogString( MSG_MYBUTTON_TEXT ) function your own app specific function ? Using locale library it would be: ILocale->GetCatalogStr(catalog, stringNum, defaultString);

Rigo's picture

Well, it's actually pseudo-code. Its just there to demonstrate that a function exists to get a string from a catalog, which it obviously conveys quite well. How the application programmer goes about it, is up to them.