[sugar] eBook Reader user interface

Don Hopkins dhopkins at DonHopkins.com
Sat May 5 02:08:55 EDT 2007


Goal for improving the eBook reader user interface:

  I've been doing some exploratory programming with GTK and Sugar,
  trying to improve the user interface of the eBook reader, and make
  it useable in book mode with the gamepad.

  + Support the game keypads in eBook mode. 

    + Low level game keypad support

      Need to remap low level keyboard scan codes to Linux keyboard codes. 

      The setolpckeys.c program remaps the keys and gamepad buttons.

        Currently it maps both gamepads to the numeric keypad keys (KEY_KP8, etc),
	which the X server and GDK translates to directional keys (GDK_Up, etc).

	I tried to map them to buttons (BTN_A, etc), but the X server seems 
	to ignore keycodes in that range. 

	The xorg.conf file has a keycode mask that looked like it might help, 
	but I couldn't get it to work. 

	Need to have unique keycodes reported for each of the two gamepads, 
	which are not the same as any keyboard keys, without any predefined meanings
	like arrow keys have. 

	Need to define special purpose keycodes just for the OLPC gamepad,
	instead of trying to reuse existing but not appropriate keycodes. 

	What is the process for defining new keycodes in <linux/input.h>?

	Here's my strawman proposal for some new keycodes. 

	  Use keys ("KEY_*") instead of buttons ("BTN_*"), since they
	  seem to work better.

	  The 0x1b* range seems to be unused in <linux/input.h>, 
	  and it's between other groups of keycodes, so I'll
	  propose using that range for the OLPC. 

	  The UP/DOWN/LEFT/RIGHT keys correspond to the directional
	  keypad.

	  #define KEY_XO_GAMEPAD_UP      0x1b0
	  #define KEY_XO_GAMEPAD_DOWN    0x1b1
	  #define KEY_XO_GAMEPAD_LEFT    0x1b2
	  #define KEY_XO_GAMEPAD_RIGHT   0x1b3

	  The NORTH/SOUTH/EAST/WEST keys correspond to the other
	  buttons. Those names are agnostic to the button labels,
	  which may change from the current Playstation buttons
	  (X/O/Triangle/Square). Can anyone suggest better names for
	  the four buttons on the right?

	  #define KEY_XO_GAMEPAD_NORTH   0x1b4
	  #define KEY_XO_GAMEPAD_SOUTH   0x1b5
	  #define KEY_XO_GAMEPAD_EAST    0x1b6
	  #define KEY_XO_GAMEPAD_WEST    0x1b7

	  While we're at it, we could define keycodes for the other
	  OLPC buttons and switches on the screen. I think there are
	  some other sensor switches that could generate keycodes,
	  like opening the screen, rotating it around, and putting it
	  into book mode, so I will make some guesses at names for
	  them, just to get the discussion rolling. 

	  #define KEY_XO_SCREEN_ROTATE   0x1b8
	  #define KEY_XO_SCREEN_POWER    0x1b9
	  #define KEY_XO_SCREEN_OPEN     0x1ba
	  #define KEY_XO_SCREEN_CLOSE    0x1bb
	  #define KEY_XO_SCREEN_IN       0x1bc
	  #define KEY_XO_SCREEN_OUT      0x1bd

	  Is there an exhaustive list of all buttons and switches and
	  events on the OLPC? Are any more planned? Which ones should
	  be assigned keycodes?

      Rewrote setolpckeys.c code in Python (just uses ioctl, but needs to know keycodes).
        Writing utilities like that in Python instead of C makes it easier to 
	reconfigure the keys on the OLPC without a C compiler. 

    + High level action support.

      GTK uses "Actions" to define the actions available in an
      application, independent of the user interface used to invoke
      them. Actions can be bound to user interface widgets and
      keyboard accelerators, and they can hide, show, enable and
      disable the corresponding parts of the interface. You can
      subclass Action to define custom toolbar buttons and menu items.

      We need to define a generic way of navigating and executing the
      application's actions from the gamepad.

      We can make Sugar specific actions that create the appropriately
      styled and customized Sugar user interface widgets.

      Actions can be used to support navigation and operation of the
      toolbar components from the gamepad:

      Actions have a list of their "proxy" components (toolbar
      buttons, menu items, etc).

      Actions know how to execute a callback function, so the user
      interface components tell the action to activate, instead of
      calling the function themselves.

      The actions also know their labels, icons and tooltips.

      Actions can be shown and hidden, and all their proxies show and
      hide.

      Actions can be made sensitive or not (disabled), and all their
      proxies enable or disable.

      Actions have methods to construct toolbar buttons and menu
      items, which subclasses can override to customize the user
      interface. The higher level GTK UIManager calls these methods in
      actions to create the user interface, although you can still use
      Actions without the UIManager, by creating the components
      yourself.

      There are three standard kinds of actions: Action, which creates
      a normal button or menu item, ToggleAction, which creates a
      toggle button (checkbox), and RadioAction, which creates a radio
      button (multiple choice). 

      The GTK toolbars and menus support keyboard navigation of
      sensitive buttons, as well as showing tooltips on sensitive
      buttons, but it won't show a tooltip on unsensitive (disabled)
      buttons, tabbing skips over disabled buttons, and it kicks you
      out of buttons that get disabled while you're using them, by
      moving the focus into the next button (whatever that happens to
      be).

      All inactive items should show tooltips telling WHY they are
      inactive, and WHAT you have to do before using them. 

      Unfortunately you can't get a tooltip on an inactive item. 
      This needs to be fixed, so we can display helpful tooltips and
      documentation on any item whether active or not. 
 
      Unfortunlatey you can't navigate to an inactive item.  This
      needs to be fixed, so the user can use the gamepad to navigate
      to an inactive item, to find out what it is and why it's
      inactive. (Also, so you can navigate the interface predicably
      with muscle memory, because the number of presses required to
      navigate doesn't change depending on the state of the
      application.)

      A problem with the current GTK keyboard navigation behavior of
      not allowing inactive items to have the input focus, is that it
      violates the principle of least astonishment, makes the
      interface less predictable, and interferes with type-ahead:

        If you're focused on the "back page" button, and select it
        until you get to the first page, then it will become inactive
        (because you can't go back from the first page), which
        results in the input focus being kicked out of the "back
        page" button and throwing you into the "next page" button.

	Imagine how hard this "astonishing" behavior would be to use
	if you were visually impared. It would not be very obvious
	that you had "bounced off" the first page and suddenly were
	moving forward. Other toolbars might arbitrarily relocate the
	input focus to an even more confusing button, when the one
	you're using gets disabled.

        It would be much less astonishing if the input focus simply
        remained in the "back page" button when you hit the first
        page, and a tooltop popped up saying "You can't go to the
        previous page, because you are at the first page."

	I've made a simple API for changing the sensitivity of a
	component, that takes a "reason" string (translated text) to
	show to the user in the tooltip of a disabled control (after
	the normal text). I've changed the code in the eBook reader
	that disables actions to figure out the most important reason
	(there might be several reasons to disable a control, but
	usually one is most important). It passes that reason to the
	API that disables the action, which currently just stores it
	away in a dictionary. Now it needs to be hooked up so it
	actually shows the tooltip with the reason when disabled.

      GTK has an "AcceleratorList" class that keeps a list of keyboard
      accelerators which invoke actions. The UIManager helps manage
      the accelerators for you automatically. 

	I think we should use accelerators for normal keyboard
	acceleration of application actions, but implement the gamepad
	navigation stuff as a higher level framework that uses a more
	semantically meaningful model. (Not just low level tab/backtab
	focus navigation, or simple global keyboard accelerators, but
	an actual framework specifically designed to support browsing
	and executing arbitrary Actions via the game controller, and
	providing feedback about the reasons controls are disabled.)

	The pygtk library has some methods on action_group to add
	actions "action_group.add_actions(action_list)", toggle
	actions "action_group.add_toggle_actions(action_list)", and
	radio actions "action_group.add_radio_actions(action_list,
	cur_value, callback, *args)". These all take an action_list
	that's a list of action specification tuples. That interface
	is more brittle and less extensible than it should be, because
	it takes tuples instead of dictionaries, and it only makes
	stock GTK actions. 

	We need a more flexible API than add_actions and its ilk, which
	supports custom Sugar actions, and uses dictionaries instead
	of tuples, so we can easily pass in additional optional
	parameters without changing the API.

	I have taken a first cut at rewriting pygtk's ActionGroup's
	add_actions, add_toggle_actions and add_radio_actions
	functions in Python, and changing them to use custom
	SugarAction, SugarToggleAction and SugarRadioAction classes.

	But I still don't think the add_actions_esque interface is
	flexible enough. It needs an easy way to add other custom
	widgets (like the search text input field), and it should make
	it easy to customize the user interface classes and configure
	the objects by passing in additional optional parameters and
	configuration dictionaries. (For example, the action
	specification should be a dictionary that has an optional key
	to specify the toolbar button and menu item classes, and it
	should be possible to plug in different kinds of user
	interface controls than toolbars and menus, like pie menus and
	gamepad specific interfaces). 

      GTK has an "atk" module that interfaces to the accessibility toolkit. 

        I'm not clear on just what this does and how it can help us,
        and I would love to hear from somebody who's familiar with it.

        I think we can do what we need to support the gamepad without
        using the atk, but maybe it could be helpful. But my
        impression is that it's more geared towards screen readers, is
        tied closely to the user interface layout (declaring that this
        label is associated with that control, etc), and it looks like
	it takes a lot of lines of code to use (unless you use some
	kind of framework that supports it automatically).

      GTK has a "UIManager" that knows how to build menu bars, menus,
      toolbars and buttons from XML files, and tie together actions,
      accelerators, menus and toolbars. The XML describes the
      structure and layout of the user interface, but refers to the
      actions by name to configure the buttons and menu items with
      labels, icons, tooltips, accelerators, callbacks, and custom
      component classes. There is nothing in the XML about what label,
      icon, tooltip or widget class to use -- just an Action
      name. It's up to the Action to figure out the visual appearance
      and behavior of the gui. That's why it should be easier to use
      custom Action classes.

        I think we need to write our own UIManager and accessibility
        framework that addresses our specific needs (like the
        focus/tooltip/disable reason issues discussed above, custom
        sugar controls, gamepad support, etc), because I don't think
        the UIManager (plus the pygtk utilities that go along with it)
        are flexible enough to support our needs. I think it would be
        easy to implement it in Python, in a way that would be a lot
        more flexible and easier to extend that the current C
        implementation of GTK's GuiManager.

  + Add support for more advanced evince features. 

    Added support for the various properties and functions that evince supports. 

    Need access to the table of contents of the pdf file. 

  + Integrate Poppler renderer for efficiently rendering PDF documents. 

    Make a higher level library independent interface to evince and poppler. 

    Factor out evince and poppler dependencies so you only need to import 
    the library that you need to render the document. 

    The enumerated types that evince uses should be passed as strings instead, 
    so the interface is independent of evince, and you don't have to define all
    the GObject enumerated type classes just to use the interface. 



More information about the Sugar mailing list