Museum

Home

Lab Overview

Retrotechnology Articles

Online Manuals

⇒ The Interface Kit: Responding to the User

Media Vault

Software Library

Restoration Projects

Artifacts Sought

The Interface Kit: Responding to the User


The Interface Kit: Responding to the User

The BWindow and BView classes together define a structure for responding to user actions on the keyboard and mouse. These actions generate interface messages that are delivered to BWindow objects. The BWindow distributes responsibility for the messages it receives to other objects, typically BViews.


Interface Messages

Eighteen interface messages are currently defined. Two of them command the window to do something in particular:

  • A B_ZOOM instruction tells the window to zoom to a larger size--or to return to its normal size having previously been zoomed larger. The message is typically caused by the user operating the zoom button in the window's title tab.

  • A B_MINIMIZE instruction tells the window to remove itself from the screen so that only a token is left to represent it--or to restore itself to the screen, having previously been minimized. This message is typically caused by the user double-clicking the window tab (or invoking the token).

    In the current incarnation of the Be user interface, the window token is a menu item under the application name in the track bar. Applications don't have access to the item, which is partially dimmed when the window is minimized,

All other interface messages report events--something that happened, rather than something that the application must do. In most cases, the message merely reports what the user did on the keyboard or mouse. However, in some cases, the event may reflect the way the Application Server interpreted or handled a user action. The server might respond directly to the user and pass along a message that indicates what it did--moved a window or changed a value, for example. In a few cases, the message may even reflect what the application thinks the user intended--that is, an application might interpret one or more generic user actions as a more specific event.

The following five messages report atomic user actions on the keyboard and mouse:

  • A B_KEY_DOWN message reports character input from the keyboard. It's typically sent when the user presses a character key. After the initial key-down event (and a brief threshold), most keys generate repeated B_KEY_DOWN messages--as long as the user continues to hold the key down and doesn't press another key. Only character keys produce the event; the modifier keys--Shift, Control, Caps Lock, and so on--may affect the character that's reported for another key, but don't generate keyboard input on their own.

    If a key is mapped to a string of characters, one B_KEY_DOWN message is generated for each character. In effect, one event is mapped to a series of messages.

    Similarly, B_KEY_DOWN messages may also report a string of characters consolidated by an input method. Input methods are used where the keyboard can't be directly mapped to a full set of characters, even with the Option modifier and dead keys, either because the full set is too large or because the choice of character depends on context (or typically both). For example, input methods permit users to type languages like Japanese and Chinese from a standard keyboard. As the user types phonetically, the input method translates the typing to a set of candidate strings. The user picks a string from the list, which is then reported in a series of B_KEY_DOWN message, one for each character.

  • A B_KEY_UP message reports that the user released the character key; a normal keystroke produces B_KEY_DOWN and B_KEY_UP messages in quick succession. If the user holds a repeating key down, it produces a series of B_KEY_DOWN messages, but only one B_KEY_UP. If a key is mapped to a string of characters, it produces a B_KEY_DOWN message for each character, followed by a matching series of B_KEY_UP messages, one for each character.

  • A B_MOUSE_DOWN message reports that the user pressed one of the mouse buttons while the cursor was over the content area of a window. The message is generated only for the first button the user presses--that is, only if no other mouse buttons are down at the time.

  • A B_MOUSE_UP message reports that the user released the mouse button. The message is produced only for the last button the user releases--that is, only if no other mouse button remains down.

  • A B_MOUSE_MOVED message captures some small portion of the cursor's movement into, within, or out of a window. If the cursor isn't over a window, it's movement isn't reported. (All interface messages are associated with windows.) Repeated messages are generated as the user moves the mouse.

The five messages above are all directed at particular views--the view where the cursor is located or where typed input appears. Three others also concern views:

  • A B_VIEW_MOVED message is sent when a view is moved within its parent's coordinate system. This can be a consequence of a programmatic action or of the parent view being automatically resized. If the parent view is being continuously resized because the user is resizing the window, repeated mouse-moved events may be reported.

  • A B_VIEW_RESIZED message is delivered when a view is resized, perhaps because the program resized it or possibly as an automatic consequence of the window being resized. If the resizing is continuous, because the user is resizing the window, repeated view-resized events are reported.

  • A B_VALUE_CHANGED message reports that the Application Server changed a value associated with an object. Currently, a value-changed event occurs only for BScrollBar objects. Repeated messages are sent as the user manipulates a scroll bar.

A few messages concern events that affect the window itself:

  • A B_WINDOW_ACTIVATED message reports an activation event. This event occurs when a window becomes the active window and again when it gives up that status. The single action of clicking a window to make it active might result in four messages--one for the window that gains active-window status and another for the window that relinquishes it, plus B_MOUSE_DOWN and B_MOUSE_UP messages.

  • A B_QUIT_REQUESTED message is interpreted by a BWindow object as a request to close the window. Quit-requested events occur when the user clicks a window's close button, or when the system perceives some other reason to request the window to quit.

  • A B_WINDOW_MOVED message records the new location of a window that has been moved, either programmatically or by the user. When the user drags a window, repeated messages are generated, each one capturing a small portion of the window's continuous movement. Only one window-moved event is reported when the program moves a window.

  • A B_WINDOW_RESIZED message reports that a window has been resized, again either programmatically or by the user. The message is generated repeatedly as the user resizes the window, but only once each time the application resizes it.

A few messages report changes to the on-screen environment for a window:

  • A B_SCREEN_CHANGED message reports that the configuration of the screen--the size of the pixel grid it displays or the color space of the frame buffer--has changed. Such changes may require the window to take compensatory measures.

  • A B_WORKSPACE_ACTIVATED message reports that the active workspace (the one displayed on-screen) has changed. All windows that live in the previously active workspace and in the one that has been newly activated are notified of the change.

  • A B_WORKSPACES_CHANGED message notifies the window that the set of workspaces in which it can be displayed has changed.

Finally, there's one message that doesn't derive from a user action:

  • Periodic B_PULSE messages are posted at regularly spaced intervals, like a steady heartbeat. Pulses don't involve any communication between the application and the Server. They're generated as long as no other events are pending, but only if the application asks for them.

An application doesn't have to wait for a message to discover what the user is doing on the keyboard and mouse. A global function, get_key_info(), takes a snapshot of the current state of the keyboard. BView's GetMouse() checks on the state of the mouse buttons and the location of the cursor.


Hook Functions for Interface Messages

Interface messages are generated and delivered to the application as the user acts. Keyboard messages are delivered to the current active window; mouse messages are sent to the window where the cursor is located. The BWindow object handles some of these messages itself, and passes others to the appropriate BView object.

An interface message is dispatched by calling a virtual function that's matched to the message. The chart below lists the virtual functions, and the base classe where each is declared.

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

B_MOUSE_UP messages aren't dispatched by calling a virtual function. A BView can determine when a mouse button goes up by calling GetMouse() from within its MouseDown() function. As it reports information about the location of the cursor and the state of the mouse buttons, GetMouse() removes mouse messages from the BWindow's message queue, so the same information won't be reported twice.

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.


Dispatching

Notice, from the chart above, that the BWindow class declares the functions that handle instructions and events directed at the window itself. FrameMoved() is called when the user moves the window, FrameResized() when the user resizes it, WindowActivated() when it becomes, or ceases to be, the active window, Zoom() when it should zoom larger, and so on.

Although the BWindow handles some interface messages, the most common ones--those reporting direct user actions on the keyboard and mouse--are handled by BViews. When the BWindow receives a keyboard or mouse message, it must decide which view is responsible.

This decision is relatively easy for messages reporting mouse events. The cursor points to the affected view:

  • When the user presses a mouse button, the BWindow calls the MouseDown() virtual function of the view under the cursor.

  • When the user moves the mouse, it calls the MouseMoved() function of each view the cursor travels through.

  • When the user releases the mouse, it will, in a future release, call the MouseUp() function both of the view that was under the cursor when the mouse button went down and of the view that is currently under the cursor (if it's a different view in the same window). (MouseUp() is not currently called.)

However, there's no cursor attached to the keyboard, so the BWindow object must keep track of the view that's responsible for messages reporting key-down and key-up events. That view is known as the focus view.

The Focus View

The focus view is whatever view happens to be displaying the current selection (possibly an insertion point) within the window, or whatever check box, button, or other gadget is currently marked to show that it can be operated from the keyboard.

The focus view is expected to respond to the user's keyboard actions when the window is the active window. When the user presses a key on the keyboard, the BWindow calls the focus view's KeyDown() function. If the focus view displays editable data, it's also expected to handle user actions that target the current selection, such as commands to cut, copy, or paste data.

The focus typically doesn't stay on one view all the time; it shifts from view to view. It may change as the user changes the current selection in the window--from text field to text field, for example. Or it changes when the user navigates from one view to another by pressing the Tab key. Only one view in the window can be in focus at a time.

Views put themselves in focus when they're selected by a user action of some kind. For example, when a BView's MouseDown() function is called, notifying it that the user has selected the view, it can grab the focus by calling MakeFocus(). When a BView makes itself the focus view, the previous focus view is notified that it has lost that status.

A view should become the focus view if:

  • It has a KeyDown() function to display typed characters,
  • It has a KeyDown() function so that the user can operate it from the keyboard, or
  • It can show the current selection, whether or not it has a KeyDown() function.

A view should highlight the current selection only while it's in focus.

BViews make themselves the focus view (with the MakeFocus() function), but BWindows report which view is currently in focus (with the CurrentFocus() function).

Kinds of Keyboard Messages

The focus view gets most keyboard messages, but not all. Three kinds of B_KEY_DOWN messages are conscripted for special tasks:

  • If the user holds a Command key down while pressing a character key, the Command-character combination is interpreted as a keyboard shortcut (typically for a menu item, but possibly for some other control device). Instead of assigning the message to a view, the BWindow tries to issue the command associated with the shortcut.

  • If the user holds an Option key down while pressing the Tab key, the Option-Tab combination is interpreted as an instruction to change the focus view. Instead of assigning the message to a view, the BWindow forces the change. This is done to enable keyboard navigation in all circumstances.

  • If the window has a default button and the user presses the Enter key, the window assigns the message to the button, so that it can respond to the key-down event as it would to a click. A "default button" is simply a button that can be operated from the Enter key on the keyboard.

In all other cases, the BWindow assigns the message to the current focus view and its KeyDown() function is called.

B_KEY_UP messages have a simpler distribution: They all are 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.

Moreover, a focus view that gets a B_KEY_DOWN message may not also get the following B_KEY_UP. If the user changes the focus view after pressing the key but before releasing it, the two messages will go to different views. The B_KEY_UP message will go to an entirely different window if the user changes the active window.


Message Protocols

The BMessage objects that convey interface messages typically contain various kinds of data describing the events they report or clarifying the instructions they give. In most cases, the message contains more information than is passed to the function that starts the application's response. For example, a MouseDown() function is passed the point where the cursor was located when the user pressed the mouse button. But a B_MOUSE_DOWN BMessage also includes information about when the event occurred, what modifier keys the user was holding down at the time, which mouse button was pressed, whether the event counts as a solitary mouse-down, the second event of a double-click, or the third of a triple-click, and so on.

A MouseDown() function can get this information by taking it directly from the BMessage. The BMessage that the window thread is currently responding to can be obtained by calling the CurrentMessage() function, which the BWindow inherits from BLooper. For example, a MouseDown() function might check whether the event is a single-click or the second of a double-click as follows:

   void MyView::MouseDown(BPoint point)
   {
       int32 num;
       Window()->CurrentMessage()->FindInt32("clicks", &num);
       if ( num == 1 ) {
           . . .
       }
       else if ( num == 2 ) {
           . . .
       }
       . . .
   }

The Message Protocols appendix lists the contents of all interface messages.


The User Interface

Since they provide the content that's displayed within windows, BViews carry most of the burden of implementing an application's user interface. Often this is simply a matter of how a BView implements a hook function--how Draw() presents the view or how MouseDown() handles a double-click.

However, in some cases the Interface Kit provides a mechanism that derived 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 the space bar and Enter key (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 the B_NAVIGIBLE_JUMP flag to permit larger jumps between navigible views. Pressing the Control-Tab combination 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, the focus lands on the first B_NAVIGIBLE view at or after the B_NAVIGIBLE_JUMP marker. If a B_NAVIGABLE_JUMP view is not also flagged B_NAVIGABLE, the system searches for the next available B_NAVIGABLE view and jumps to it. The search descends the view hierarchy and moves from one sibling view to another as each branch of the view hierarchy is exhausted. For example, if a B_NAVIGABLE_JUMP parent view is not navigible itself but has navigible children, Control-Tab will land on its first B_NAVIGABLE child.

Unlike B_NAVIGABLE, B_NAVIGABLE_JUMP should not be turned on and off.

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. Guidelines are forthcoming on what the indication should be. Currently, 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() (though it doesn't look exactly like the one above), 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)
   {
       rbg_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);
       }
       . . .
   }

This example is diagrammatic; it may not show an appropriate way for the BViews in your application to draw. (Note that when MakeFocus() called IsFocus(), it returned the BView's previous status, but when Draw() called it, it returned the updated status.)

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):

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

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

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 Be Book, in lovely HTML, for BeOS Release 3.

Copyright © 1998 Be, Inc. All rights reserved.

Last modified March 26, 1998.

Typewritten Software • bear@typewritten.org • Edmonds, WA 98026