1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151

ash / shelf / README.md [blame]

# Shelf

This is the ash shelf, the system interface surface that allows users to launch
application shortcuts or go to the home screen, among other things.

## Components

The shelf contains the following components, each of which lives in its own
widget:

* The **shelf widget** contains no actionable UI but contains the semi-opaque
background shown behind the whole shelf as well as the drag handle (in certain
circumstances) to give users a hint that gestures can be performed. In that
sense, even though the shelf widget does not actually contain other components,
it usually serves as a backdrop for them.

* The **navigation widget** contains the home and back buttons. It is usually
shown in clamshell mode (but only with the home button) and hidden in tablet
mode, unless the activation of select accessibility features forces it to be
shown. When the navigation widget is not shown, the user can achieve the same
actions by performing gestures.

* The **hotseat widget** contains icons for application shortcuts and running
applications. In clamshell mode, it is always visually contained within the
shelf widget; in tablet mode, it can appear and move independently.

* The **status area widget** (whose code lives in `ash/system`) shows
information such as the clock or current battery level, and can toggle the
system tray.

## Alignment

The shelf is aligned to the bottom of the screen by default, but the user can 
choose (only in clamshell mode) to align it to the left or right of the screen. 
It always occupies the entirety of the corresponding dimension (width for a 
horizontal shelf, height otherwise), with the navigation widget shown at the 
start (top or left in left-to-right interfaces, bottom or right in 
right-to-left) and the status area at the other end.

## Auto-hiding

The system allows the user to set a boolean preference, on a per-display basis, 
specifying whether the shelf should "auto-hide". In that case, the shelf and its 
components will be hidden from the screen most of the time, unless there are no 
un-minimized windows or unless the user actively brings up the shelf with the 
mouse or with a swipe.

## Centering

The hotseat widget is centered on the screen according to the following
principle:

* All icons are placed at the center of the whole display if they can fit
without overlapping with any other shelf component.

* Otherwise, they are centered within the space available to the hotseat.

* If there are too many icons to fit in that space, the hotseat becomes
scrollable.

## Responsive layout

The shelf and its components need to adjust to a certain number of changes that
may or may not be user-triggered:

* Switching between clamshell and tablet mode.

* Changing the display size (for smaller displays, the shelf becomes more
compact) or orientation.

* Changing the shelf alignment.

* User events (clicks, taps, swipes).

### Coordination

All shelf components need to react to these changes in a coordinated manner to
maintain the smoothness of animations.

Components should not register themselves as observers of these changes and
react to them on their own, because an adequate reaction may involve other
components as well. For instance, whether the navigation widget is shown (or is
scheduled to be shown at the end of the animation) will influence the amount of
space the hotseat widget can occupy.

Instead, listening to those changes are handled at the `ShelfLayoutManager`
level, which is then responsible for making the changes trickling down to each
component as necessary.

### Aim first, move second

In reaction to any of these global changes, each component must first determine
where it wants to be at the end of the animation ("aim"). That calculation may
depend on the other shelf components. Then, and only then, should the change of
bounds be actually committed to each widget and the animations triggered
("move"). Failing to respect this "two-phase" approach may lead to janky
animations as each component may realize, only after it has started moving, that
another component's movement forces it to alter its final destination.

### `ShelfComponent` interface

Each of the shelf components exposes an API to other classes in order to ease
the process of responding to layout changes:

* `CalculateTargetBounds` is the "aim" phase, where each component figures out
where it wants to be given the new conditions. This method must be called on
each component by order of dependency (a component B "depends" on another
component A if B needs to know A's target bounds before calculating its own).

* `GetTargetBounds` allows for components depending on this one to calculate
their own target bounds accordingly.

* `UpdateLayout` is the "move" phase, where each component actually changes it
bounds according to its target.

* `UpdateTargetBoundsForGesture` allows each component to respond to a gesture
in progress by determining how (and whether) it should follow other components
along in the gesture.

### Layout inputs

Each shelf component is aware of the set of inputs that can cause its layout to
change. Each time the `UpdateLayout` method is called on it, it determines
whether any of its inputs has changed. If not, the method returns early and
avoids any actual re-layout for itself as well as other components that depend
solely on it.

## Keyboard navigation

In order for keyboard users to navigate smoothly between the various parts of
the shelf as they would expect, the `ShelfFocusCycler` class passes the focus to
each shelf component as appropriate, depending on which component has just
reliquished focus and on which direction the focus is going. The `ShelfWidget`
class is the only shelf component that doesn't receive keyboard focus since it
does not have any activatable elements.

## Buttons

The base class for all buttons on shelf components is `ShelfButton`, which
handles basic logic for keyboard navigation and ink drops. This class is then
derived into `ShelfControlButton` for things like the home or back button, and
`ShelfAppButton` for application shortcuts.


## Tooltips

Tooltips for elements on the shelf require some specific logic on top of the
common tooltips because as a user hovers over each app shortcut, trying to
figure out what each one does, we do not want to adopt the default tooltip
behavior which would be to dismiss the previous tooltip and make the user wait
for the common timeout before showing the next one.