The Interface Kit: Listening to the User

The Interface Kit Table of Contents | The Interface Kit Index

# Listening to the User

When the user fiddles with the mouse or keyboard, the Application Server figures out which window the event is intended for, and then forms an interface message that it sends to the BWindow object. The BWindow receives the message in its `DispatchMessage()` function and invokes an interface hook function declared by BWindow or BView. If your application wants to respond to an event it simply implements the appropriate hook function. For example, if you want to watch for mouse down events within a view, you implement `MouseDown()` in your BView subclass.

You can also find out what's going on with the keyboard and mouse through the (global) `get_key_info()` function and the `BView::GetMouse()` function. You typically use these functions within the implementation of an interface hook function to get more information about the keyboard or mouse.

You shouldn't call `get_key_info()` or `BView::GetMouse()` in a loop to track the keyboard or mouse unless you really know what you're doing (some game developers may need to poll). To track the keyboard, you implement BView's `KeyDown()` and `KeyUp()` functions. To track the mouse, implement `MouseDown()`, `MouseMoved()`, and `MouseUp()`.

## Interface Messages

The interface messages are reasonably self-explanatory. See the notes below and the documentation of the individual hook functions for more information.

Message | Hook function
--- | ---
`B_ZOOM` | `BWindow::Zoom()`
B_MINIMIZE | `BWindow::Minimize()`
B_KEY_DOWN | `BView::KeyDown()`
B_KEY_UP | `BView::KeyUp()`
`B_MOUSE_DOWN` | `BView::MouseDown()`
B_MOUSE_UP | `BView::MouseUp()`
`B_MOUSE_MOVED` | `BView::MouseMoved()`
B_VIEW_MOVED | `BView::FrameMoved()`
B_VIEW_RESIZED | `BView::FrameResized()`
B_VALUE_CHANGED | `BScrollBar::ValueChanged()`
B_WINDOW_ACTIVATED | `BWindow::WindowActivated() BView::WindowActivated()`
B_QUIT_REQUESTED | `BLooper::QuitRequested()`
B_WINDOW_MOVED | `BWindow::FrameMoved()`
B_WINDOW_RESIZED | `BWindow::FrameResized()`
B_SCREEN_CHANGED | `BWindow::ScreenChanged()`
B_WORKSPACE_ACTIVATED | `BWindow::WorkspaceActivated()`
`B_WORKSPACES_CHANGED` | `BWindow::WorkspacesChanged()`
`B_PULSE` | `BView::Pulse()`

Notes:

- `B_ZOOM` is usually caused by the user operating the zoom button in the window's title tab.

- `B_MINIMIZE` tells the window to hide/show itself. This message is usually caused by the user double-clicking the window's tab (to hide), and clicking the window token in the application's window list (in the DeskBar).

- `B_KEY_DOWN` and `B_KEY_UP` report that the user pressed and then release a character key. When the key is held down, repeated `B_KEY_DOWN` messages are sent. If a key is mapped to a string of characters, a `B_KEY_DOWN/B_KEY_UP` pair is generated for each character. Pressing a modifier keys on its own doesn't generate key messages.

- `B_MOUSE_DOWN`, `B_MOUSE_MOVED`, and `B_MOUSE_UP` are delivered (as hook functions) to the BView that the mouse is over at the time, regardless of where it started. For example, if the user drags the mouse through your view, you'll get a series of `B_MOUSE_MOVED` messages without getting a `B_MOUSE_UP` or a `B_MOUSE_DOWN`. The `BView::MouseMoved()` function has a code that tells you whether the mouse is just entering, inside, or exiting your view. You can tell your BView to track the mouse even when it's outside your view through `BView::SetMouseEventMask(`).

- A `B_VALUE_CHANGED` message reports that the Application Server changed a value associated with a BScrollBar objects.

- `B_WORKSPACE_ACTIVATED` reports that the active workspace (the one displayed on-screen) has changed. The message is only sent to windows that live in one of the affected workspaces.

- `B_WORKSPACES_CHANGED` notifies the window that the set of workspaces in which it can be displayed has changed.

- A BWindow reinterprets a `B_QUIT_REQUESTED` message, originally defined for the BLooper class in the Application Kit, to mean a user request to close the window. However, it doesn't redeclare the `QuitRequested()` hook function that it inherits from BLooper.

### Tracking the Keyboard

The view that's responsible for handling keyboard events is called as the focus view. The focus view is the view within the active window that's displaying the current selection, or the control that's marked to show that it can be operated from the keyboard. Only one view in the window can be in focus at a time.

You promote a view to focus by calling `BView::MakeFocus()`, and you ask for the current focus through `BWindow::CurrentFocus()`.

#### Focus View Commands

The table below maps a keyboard action to the hook function it invokes or the message it sends. In each of these cases, the function or message is sent to the current focus view (which may change between a key down and a key up)

User action | Function or message
--- | ---
Press a character key | `BView::KeyDown()`
Release a character key | `BView::KeyUp()`
Command+a | `B_SELECT_ALL`
Command+c | `B_SELECT_COPY`
Command+x | `B_SELECT_CUT`
Command+v | `B_SELECT_PASTE`

The focus view needn't respond to all of these functions and commands, but a BView that doesn't respond to any of them should never promote itself to focus.

#### Other Keyboard Commands

There are four other built-in keyboard commands:

User action | Message or Action | Target
--- | --- | ---
Command+w | `B_QUIT_REQUESTED` | The active window.
Command+q | `B_QUIT_REQUESTED` | The active app.
Option+Tab | Changes focus to the next navigable view. | The active window.
Enter | Activates the default button. | The active window.

#### Kinds of Keyboard Messages

`B_KEY_UP` events are always assigned to the view that's in focus when the user releases the key—even if the previous `B_KEY_DOWN` message performed a shortcut, forced keyboard navigation, or was assigned to the default button.

### Message Protocols

The event messages that the Application Server sends to a BWindow usually have more information in them than is passed to the corresponding hook function. For example, while a `B_MOUSE_DOWN` message knows where, when, and which mouse button was pressed (among other things), only the "where" information is passed to the `MouseDown()` function.

You can retrieve the message that initiated a hook function from within the hook function itself by calling BWindow::CurrentMessage() function:

       void MyView::MouseDown(BPoint where)
       {
          BMessage *msg = Window()->CurrentMessage();
          ...

## The User Interface

The Interface Kit provides interface mechanisms that your classes can participate in, if they coordinate with kit-defined code. Two such mechanisms are described below—keyboard navigation and the drag-and-drop delivery of messages.

### Keyboard Navigation

Keyboard navigation is a mechanism for allowing users to manipulate views—especially buttons, check boxes, and other control devices—from the keyboard. It gives users the ability to:

- Move the focus of keyboard actions from view to view within a window by pressing the Tab key.

- Operate the view that's currently in focus by pressing spacebar and Enter (to invoke it) or the arrow keys (to move around inside it).

The first ability—navigation between views—is implemented by the Interface Kit. The second—navigation within a view—is up to individual applications (although the BControl class helps a little), as are most view-specific aspects of the user interface. The only trick, and it's not a difficult one, is to make the two kinds of navigation work together.

To participate in the navigation mechanism, a class derived from BView needs to coordinate three aspects of its code—setting navigation flags, drawing an indication that the BView is in focus, and responding to keyboard events. The following sections discuss each of these elements.

#### Setting Navigation Flags

The `B_NAVIGABLE` flag marks a BView as an eligible target for keyboard navigation. It's one flag in a mask that the BView constructor sets, along with other view attributes. For example:

       MyView::MyView(BRect frame, const char *name,
                                uint32 resizingMode, uint32 flags)
          : BView(frame, name, resizingMode, flags|B_NAVIGIBLE|B_WILL_DRAW)
       {
           . . .
       }

When the user presses the Tab key, the focus moves from one `B_NAVIGIBLE` target to the next, working first down and then across the view hierarchy. That is, if a BView has both `B_NAVIGIBLE` children and `B_NAVIGIBLE` siblings, the children will be targeted before the siblings.

The flag should be removed from the mask when the view is disabled or cannot become the focus view for any reason, and included again when it's re-enabled. The mask can be altered with the `SetFlags()` function:

       if ( /* cannot become the focus view */ )
           SetFlags(Flags() & ~B_NAVIGIBLE);
       else
           SetFlags(Flags() | B_NAVIGIBLE);

Most navigible BViews are control devices and derive from the BControl class. All BControls are navigible by default and BControl has a `SetEnabled()` function that turns the `B_NAVIGIBLE` flag on and off, so this work is already done for objects that inherit from BControl.

You may also want to set a view's `B_NAVIGIBLE_JUMP` flag to permit larger jumps between navigible views. Control+Tab moves the focus from one group of views to another, where the groups are (hopefully) obvious to the user from their arrangement in the window.

`B_NAVIGIBLE_JUMP` marks positions in the view hierarchy for these larger jumps. When the user presses Control+Tab, focus jumps to the next `B_NAVIGIBLE_JUMP` view. If a `B_NAVIGABLE_JUMP` view isn't also `B_NAVIGABLE`, focus moves to the next available `B_NAVIGABLE` view. For example, if a `B_NAVIGABLE_JUMP` parent view is not navigible itself but has navigible children, Control+Tab focusses on the first `B_NAVIGABLE` child.

#### Drawing the Focus Indicator

When the user navigates to a view, the BView needs to draw some sort of visual indication that it's the current focus for keyboard actions. Be-defined views underline text (for example, a button label) when the view is in focus, or draw a rectangular outline of the view. The underline and outline are drawn in the color returned by `keyboard_navigation_color()`. Using this color lends consistency to the user interface.

A BView learns that the focus has changed when its `MakeFocus()` hook function is called. It's up to `MakeFocus()` to ensure that the focus indicator is drawn or erased, depending on the BView's new status. It's usually simplest for `MakeFocus()` to call `Draw()` and have it do the work. For example:

       void MyView::MakeFocus(bool focused)
       {
           if ( focused != IsFocus() ) {
               baseClass::MakeFocus(focused);
               Draw(Bounds());
               Flush();
               . . .
           }
       }

The BControl class has a `MakeFocus()` function that calls `Draw()`, so if your class derives from BControl, all you need to do is implement `Draw()`. `Draw()` can call `IsFocus()` to test the BView's current status. Here's a rough example:

       void MyView::Draw(BRect updateRect)
       {
           rgb_color navigationColor = keyboard_navigation_color();
           BRect r = Bounds()
           r.InsetBy(2.0, 2.0)
           . . .
           rgb_color c = HighColor();
           if ( IsFocus() ) {
               /* draw the indicator */
               SetHighColor(navigationColor);
               StrokeRect(r);
               SetHighColor(c);
           }
           else {
               /* erase the indicator */
               SetHighColor(ViewColor());
               StrokeRect(r);
               SetHighColor(c);
           }
           . . .
       }

#### Handling Keyboard Actions

Finally, your BView may need to override `KeyDown()` to handle the keystrokes that are used to operate the view (for view-internal navigation). Always incorporate the inherited version of `KeyDown()` so that it can take care of navigation between views. For example:

       void MyView::KeyDown(const char *bytes, int32 numBytes)
       {
           switch ( bytes[0] ) {
               case B_ENTER:
               case B_SPACE:
                   /* take action */
                   break;
               case B_UP_ARROW:
               case B_DOWN_ARROW:
               case B_RIGHT_ARROW:
               case B_LEFT_ARROW:
                   /* move within the view */
                   break;
               default:
                   baseClass::KeyDown(bytes, numBytes);
                   break;
           }
       }

Again, the BControl class implements a `KeyDown()` function that invokes the control device when the user presses the space bar or Enter key. If your class derives from BControl and it doesn't have to do any other view-internal navigation, the BControl function may be adequate for your needs.

### Drag and Drop

The BView class supports a drag-and-drop user interface. The user can transfer a parcel of information from one place to another by dragging an image from a source view and dropping it on a destination view—perhaps a view in a different window in a different application.

A source BView initiates dragging by calling `DragMessage()` from within its `MouseDown()` function. The BView bundles all information relevant to the dragging session into a BMessage object and passes it to `DragMessage()`. It also passes an image or a rectangle to represent the data package on-screen. For example:

       void MyView::MouseDown(BPoint point)
       {
           . . .
           if ( aRect.Contains(point) ) {
               BMessage message(SOME_WORDS_OF_ENCOURAGEMENT);
               message.AddString("words", theEncouragingWords);
               DragMessage(&message, aRect);
           }
           . . .
       }

The Application Server then takes charge of the BMessage object and animates the image as the user drags it on-screen. As the image moves across the screen, the views it passes over are informed with `MouseMoved()` function calls. These notifications give views a chance to show the user whether or not they're willing to accept the message being dragged. When the user releases the mouse button, dropping the dragged message, the message is delivered to the BWindow and targeted to the destination BView.

A BView is notified that a message has arrived by a `MessageReceived()` function call. This is the same function that announces the arrival of other messages. By calling `WasDropped()`, you can ask the message whether it was dropped on the view or delivered in some other way. If it was dropped, you can find out where by calling `DropPoint()`. For example:

       void AnotherView::MessageReceived(BMessage *message)
       {
           switch ( Message->what ) {
               . . .
               case SOME_WORDS_OF_ENCOURAGEMENT:
               {
                   char *words;
                   if ( message->FindString("words", &words) != B_OK )
                       return;
                   if ( message->WasDropped() ) {
                       BPoint where = DropPoint();
                       ConvertFromScreen(&where);
                       PleaseInsertTheseWords(words, where);
                   }
                   break;
               }
               . . .
               default:
                   baseClass::MessageReceived(message);
           }
       }

Aside from creating a BMessage object and passing it to `DragMessage()`, or implementing `MouseMoved()` and `MessageReceived()` functions to handle any messages that come its way, there's nothing an application needs to do to support a drag-and-drop user interface. The bulk of the work is done by the Application Server and Interface Kit.

## Character Encoding

The BeOS encodes characters using the UTF-8 transformation of Unicode character values. Unicode is a standard encoding scheme for all the major scripts of the world—including, among others, extended Latin, Cyrillic, Greek, Devanagiri, Telugu, Hebrew, Arabic, Tibetan, and the various character sets used by Chinese, Japanese, and Korean. It assigns a unique and unambiguous 16-bit value to each character, making it possible for characters from various languages to co-exist in the same document. Unicode makes it simpler to write language-aware software (though it doesn't solve all the problems). It also makes a wide variety of symbols available to an application, even if it's not concerned with covering more than one language.

Unicode's one disadvantage is that all characters have a width of 16 bits. Although 16 bits are necessary for a universal encoding system and a fixed width for all characters is important for the standard, there are many contexts in which byte-sized characters would be easier to work with and take up less memory (besides being more familiar and backwards compatible with existing code). UTF-8 is designed to address this problem.

### UTF-8

UTF-8 stands for "UCS Transformation Format, 8-bit form" (and UCS stands for "Universal Multiple-Octet Character Set," another name for Unicode). UTF-8 transforms 16-bit Unicode values into a variable number of 8-bit units. It takes advantage of the fact that for values equal to or less than 0x007f, the Unicode character set matches the 7-bit ASCII character set—in other words, Unicode adopts the ASCII standard, but encodes each character in 16 bits. UTF-8 strips ASCII values back to 8 bits and uses two or three bytes to encode Unicode values over 0x007f.

The high bit of each UTF-8 byte indicates the role it plays in the encoding:

- If the high bit is 0, the byte stands alone and encodes an ASCII value.

- If the high bit is 1, the byte is part of a multiple-byte character representation.

In addition, the first byte of a multibyte character indicates how many bytes are in the encoding: The number of high bits that are set to 1 (before a bit is 0) is the number of bytes it takes to represent the character. Therefore, the first byte of a multibyte character will always have at least two high bits set. The other bytes in a multibyte encoding have just one high bit set.

To illustrate, a character encoded in one UTF-8 byte will look like this (where a '1' or a '0' indicates a control bit specified by the standard and an 'x' is a bit that contributes to the character value):

       0xxxxxxx

A character encoded in two bytes has the following arrangement of bits:

       110xxxxx 10xxxxxx

And a character encoded in three bytes is laid out as follows:

       1110xxxx 10xxxxxx 10xxxxxx

Note that any 16-bit value can be encoded in three UTF-8 bytes. However, UTF-8 discards leading zeroes and always uses the fewest possible number of bytes—so it can encode Unicode values less than 0x0080 in a single byte and values less than 0x0800 in two bytes.

In addition to the codings illustrated above, UTF-8 takes four bytes to translate a Unicode surrogate pair—two conjoined 16-bit values that together encode a character that's not part of the standard. Surrogates are extremely rare.

### ASCII Compatibility

The UTF-8 encoding scheme has several advantages:

- The single byte that encodes an ASCII value can't be confused with a byte that's part of a multiple-byte encoding. You can test a UTF-8 byte for an ASCII value without considering surrounding bytes; if there's a match, you can be sure the byte is the ASCII character. UTF-8 is fully compatible with ASCII.

- The first (or only) byte of a character can't be confused with a byte inside a multibyte sequence. It's simple to find where a character begins. For example, this macro will do it:

          #define BEGINS_CHAR(byte) ((byte & 0xc0) != 0x80)

- The string functions in the standard C library—for example, `strcat()` and `strlen()`—can operate on a UTF-8 string.

  However, it's important to remember that `strlen()` measures the string in bytes, not characters. Some Interface Kit functions, like `GetEscapements()` in the BFont class, ask for a character count; `strlen()` can't provide the answer. Instead, you need to do something like this to count the characters in a string:

          int32 count = 0;
          while ( *p != '0' ) {
              if ( BEGINS_CHAR(*p) )
                  count++;
              p++;
          }

- UTF-8 preserves the numerical ordering of Unicode character values. String comparison functions—such as `strcasecmp()`—will put UTF-8 strings in the correct order.

  However, you should be careful when using the string comparison functions to order a set of UTF-8 strings. Unicode tries for a universal encoding and orders characters in a way that's generically correct, but it may not be correct for specific characters in specific languages. (Because it follows ASCII, UTF-8 is correct for English.)

- For European languages, UTF-8 generally yields more compact data representations than would Unicode. Most of the characters in a string can be encoded in a single byte. In many other cases, UTF-8 is no less compact than Unicode.

### UTF-8 and the BeOS

The BeOS assumes UTF-8 encoding in most cases. For example, a `B_KEY_DOWN` message reports the character that's mapped to the key the user pressed as a UTF-8 value. That value is then passed as a string to `KeyDown()` along with the byte count:

| virtual void `KeyDown(`const char *bytes, int32 numBytes`)`;

You can expect the bytes string to always contain at least one byte. And, of course, you can test it for an ASCII value without caring whether it's UTF-8:

       if ( bytes[0] == B_TAB )
           . . .

Similarly, objects that display text in the user interface—such as window titles and button labels—expect to be passed UTF-8 encoded strings, and hand you a UTF-8 string if you ask for the title or label. These objects display text using a system font—either the system plain font (`be_plain_font`) or the bold font (`be_bold_font`). The BFont class allows other character encodings, which you may need to use in limited circumstances from time to time, but the system fonts are constrained to UTF-8 (`B_UNICODE_UTF8` encoding). The FontPanel preferences application doesn't permit users to change the encoding of a system font.

Unicode and UTF-8 are documented in The Unicode Standard, Version 2.0, published by Addison-Wesley. See that book for complete information on Unicode and for a description of how UTF-8 encodes surrogate pairs.

The Interface Kit Table of Contents | The Interface Kit Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..