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.