Bidirectional Layouts in GTK+


Behdad Esfahbod
behdad behdad org
Sharif FarsiWeb, Inc.
http://www.farsiweb.info/


Desktop Developers' Conference
Ottawa, 19 July 2004


Valid HTML 4.0! Valid CSS!
Slides at http://behdad.org/download/Presentations/bidi-layouts/
Sources at http://behdad.org/download/Presentations/bidi-layouts/bidi-layouts.tar.gz




Overview





Bidirectional Scripts





Unicode Bidirectional Algorithm

Unicode Standard Annex #9
http://www.unicode.org/reports/tr9/

Phase 1:
Input:
  • Optional paragraph direction
  • One paragraph of text
Output:
  • Resolved paragraph direction
  • Resolved embedding levels
  Phase 2:
Input:
  • One line of text
  • Resolved embedding levels
Output:
  • Visual order of characters in the line





Example:
CAPITAL letters denote Right-to-Left characters

Phase 1:
Input:
  • Paragraph:
    >->
    NEW CAR IS new car IN HEBREW.
Output:
  • Resolved paragraph direction:
    RTL (Right-to-Left)
  • Resolved embedding levels:
    <-<
    11111111111
    >->
    2222222
    <-<
    11111111111
Line break:
Lines of text:
>->
NEW CAR IS new
>->
car IN HEBREW.
  Phase 2:
Input:
  • Lines of text:
    >->
    NEW CAR IS new
    >->
    car IN HEBREW.
  • Resolved embedding levels:
    <-<
    11111111111
    >->
    222
    >->
    222
    <-<
    11111111111
Output:
  • Visual order:
    >->
    new
    <-<
     SI RAC WEN
    <-<
    .WERBEH NI 
    >->
    car





Wrong: If we do both phases on lines:
Line break:
Lines of text:
>->
NEW CAR IS new
>->
car IN HEBREW.
Phase 1:
Input:
  • Paragraphs:
    >->
    NEW CAR IS new
    >->
    car IN HEBREW.
Output:
  • Resolved paragraph direction: RTL, LTR
  • Resolved embedding levels:
    <-<
    11111111111
    >->
    222
    >->
    0000
    <-<
    111111111
    >->
    0
  Phase 2:
Input:
  • Lines of text:
    >->
    NEW CAR IS new
    >->
    car IN HEBREW.
  • Resolved embedding levels:
    <-<
    11111111111
    >->
    222
    >->
    0000
    <-<
    111111111
    >->
    0
Output:
  • Visual order:
    >->
    new
    <-<
     SI RAC WEN
    >->
    car 
    <-<
    WERBEH NI
    >->
    .




Wrong: If we do both phases on paragraphs:
Phase 1:
Input:
  • Paragraph:
    >->
    NEW CAR IS new car IN HEBREW.
Output:
  • Resolved paragraph direction:
    RTL (Right-to-Left)
  • Resolved embedding levels:
    <-<
    11111111111
    >->
    2222222
    <-<
    11111111111
  Phase 2:
Input:
  • Line of text:
    >->
    NEW CAR IS new car IN HEBREW.
  • Resolved embedding levels:
    <-<
    11111111111
    >->
    2222222
    <-<
    11111111111
Output:
  • Visual order:
    <-<
    .WERBEH NI 
    >->
    new car
    <-<
     SI RAC WEN
Line break:
Lines of text:
<-<
.WERBEH NI 
>->
new
>->
car
<-<
 SI RAC WEN




Bidirectional Text Layout

Unicode Bidi Algorithm leaves it to higher level protocols to: What we expect from this higher level protocol is:




Paragraph Direction Propagation Algorithm

For each paragraph:
  • If not neutral, resolve to paragraph direction
  • Otherwise, if previous paragraph has a resolved direction, resolve to that direction
  • Otherwise, if next paragraph has a resolved direction, resolve to that direction
  • Otherwise, resolve to document direction




Paragraph Direction Propagation Algorithm continued

Pros: Implementations:




Paragraph Direction Propagation Algorithm continued

Cons: Solutions:




Big problem: GTK+ already implements this, but several applications that do not work with plain text need to implement it again, eg. GAIM, AbiWord, ...
Bad (Current):
An screen-shot of GAIM, showing some of current problems
   Good (Ideal):
Another screen-shot of GAIM, showing the ideal layout
Solution:




Sources of Document Direction

  • Resolved Widget Direction
    • Robust
    • My favorite, unless a better solution is found
  • Keyboard-Layout Direction
    • Not available all the time
    • Currently is used in GTK+ to override the last item when available
    • Feels better if you are entering text in the other direction
    • Neutral paragraph will jump to the other side when you leave the field...
      • ...unless you put markup to remember the direction...
        • ...which makes it quite complicated
        • and hard to predict
  • GNOME Bugs #116626 and #136529




Direction, Alignment, Justification

Right direction (RTL)
نمی‌دانم. ولی به نظر تنها کاری می‌رسید که در هفته‌ی اولِ ژانویه‌ی ۱۹۶۴ می‌شد انجام داد، و دو نفر را هم پیدا کردم که همراهی‌ام کنند. یکی از آن‌ها خواسته که نامش فاش نشود، که عیبی ندارد...
   
نمی‌دانم. ولی به نظر تنها کاری می‌رسید که در هفته‌ی اولِ ژانویه‌ی ۱۹۶۴ می‌شد انجام داد، و دو نفر را هم پیدا کردم که همراهی‌ام کنند. یکی از آن‌ها خواسته که نامش فاش نشود، که عیبی ندارد...
   
نمی‌دانم. ولی به نظر تنها کاری می‌رسید که در هفته‌ی اولِ ژانویه‌ی ۱۹۶۴ می‌شد انجام داد، و دو نفر را هم پیدا کردم که همراهی‌ام کنند. یکی از آن‌ها خواسته که نامش فاش نشود، که عیبی ندارد...
 
نمی‌دانم. ولی به نظر تنها کاری می‌رسید که در هفته‌ی اولِ ژانویه‌ی ۱۹۶۴ می‌شد انجام داد، و دو نفر را هم پیدا کردم که همراهی‌ام کنند. یکی از آن‌ها خواسته که نامش فاش نشود، که عیبی ندارد...
   
نمی‌دانم. ولی به نظر تنها کاری می‌رسید که در هفته‌ی اولِ ژانویه‌ی ۱۹۶۴ می‌شد انجام داد، و دو نفر را هم پیدا کردم که همراهی‌ام کنند. یکی از آن‌ها خواسته که نامش فاش نشود، که عیبی ندارد...
   
نمی‌دانم. ولی به نظر تنها کاری می‌رسید که در هفته‌ی اولِ ژانویه‌ی ۱۹۶۴ می‌شد انجام داد، و دو نفر را هم پیدا کردم که همراهی‌ام کنند. یکی از آن‌ها خواسته که نامش فاش نشود، که عیبی ندارد...
Wrong direction (LTR)





Bidirectional Widget Layout

Classic from Pango Gallery
Screen-shot from old GTK+ color-selection dialog under fa_IR locale





Bidirectional Widget Layouts
in GTK+ Today

  • Translator decides that a locale is Right-to-Left
    ...good way, but eventually the Glibc locale should provide this information
  • All in a sudden, Left and Right are swapped!
  • Did you have this in mind?
  • Widget directions default to Right-to-Left
  • Horizontal packing is done from Right to Left
  • X Align is done from Right to Left




Examples - gedit

gedit under en_US (English) locale: An annotated screen-shot of gedit, under en_US locale

gedit under fa_IR (Persian) locale: Another annotated screen-shot of gedit, under fa_IR locale





Examples - AbiWord

AbiWord under en_US (English) locale: An annotated screen-shot of AbiWord, under en_US locale

AbiWord under fa_IR (Persian) locale: Another annotated screen-shot of AbiWord, under fa_IR locale





GtkJustification

/* justification for label and maybe other widgets (text?) */
typedef enum
{
  GTK_JUSTIFY_LEFT,
  GTK_JUSTIFY_RIGHT,
  GTK_JUSTIFY_CENTER,
  GTK_JUSTIFY_FILL
} GtkJustification;

void             gtk_label_set_justify (GtkLabel         *label,
                                        GtkJustification jtype);
GtkJustification gtk_label_get_justify (GtkLabel         *label);

(GTK_JUSTIFY_FILL is just a place-holder right now)

LEFT
A label with GtkJustification set to LEFT
RIGHT
A label with GtkJustification set to RIGHT
CENTER
A label with GtkJustification set to CENTER
FILL
A label with GtkJustification set to FILL

Wrong!





GtkJustification - Better?

  • Should change direction under Right-to-Left locale? (widget direction)
  • Should change direction under Right-to-Left paragraph? (paragraph direction)
  • Do you mean the other side?

23=8 different alignment functions





8!

en_US
Am
من
fa_IR
Am
من
    en_US
Am
من
fa_IR
Am
من
    en_US
Am
من
fa_IR
Am
من
    en_US
Am
من
fa_IR
Am
من
   
 
en_US
Am
من
fa_IR
Am
من
    en_US
Am
من
fa_IR
Am
من
    en_US
Am
من
fa_IR
Am
من
    en_US
Am
من
fa_IR
Am
من
   
 




Do We Need them All?

Seems so

User:
Password:
Language:
         
کاربر:
گذرواژه:
زبان:





So?

  • Sounds good, we have two of these, add the other six: New values, backward compatible!

But Wait!

  • Add up to 10 counting for Center and Fill too, can you name them?
  • Current GTK_JUSTIFY_LEFT and GTK_JUSTIFY_RIGHT are wrong
    • Fix the code?
    • Then applications break :-(
    • What a Western developer means by LEFT, most probably is: LEFT for Latin and RIGHT for Arabic, which is the current behavior
Solution?
  • Define bit masks for new alignments in GtkJustification, deprecate old values and keep for compatibility




GtkTextDirection

/* Reading directions for text */
typedef enum
{
  GTK_TEXT_DIR_NONE,
  GTK_TEXT_DIR_LTR,
  GTK_TEXT_DIR_RTL
} GtkTextDirection;

/* Functions for setting directionality for widgets*/
void             gtk_widget_set_direction         (GtkWidget        *widget,
                                                   GtkTextDirection  dir);
GtkTextDirection gtk_widget_get_direction         (GtkWidget        *widget);
void             gtk_widget_set_default_direction (GtkTextDirection  dir);
GtkTextDirection gtk_widget_get_default_direction (void);
  • Unlike what comment and names suggest, this is used for all packing and other widget directions too (comments in source explain that)
  • The default direction is initialized by the locale direction
  • Since our paragraph direction propagation algorithm hardly relies on the widget direction, user should not need too much control over this! Good
  • GTK_TEXT_DIR_NONE means default direction, what about reverse of the default direction? Add value to enum for completeness?




A Hierarchical Model?

How about inheriting direction from parent?
  • HTML/CSS model does this
  • Allows RTL and LTR layouts simultaneously
But:
  • Current locale model in Glibc does not allow two locales simultaneously anyway
  • Current model in GTK+ does not allow that
  • Childs are binded to parents after initialization
  • Is complicated and hard to predict




What Else?

  • It's just the beginning
  • All those LEFTs and RIGHTs in gtkenums.h are scary
  • A framework for icon translation? Sure? What about themes then?
  • Lots of work needs to be done in individual applications...
  • ...not a big problem, those who care about Bidirectional layouts go and fix them
  • Didn't covered in this talk: Cursor movement and mouse selection
Solution:
  • Start a working group on freedesktop.org and document these all
  • Write HOWTOs for different toolboxes on how to design bidi-friendly layouts
  • Work with GNOME Usability people to update the HIG if necessary, and add needed checks to their checklists




Questions?

~FIN~