• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2// Copyright (C) 2019 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16import m from 'mithril';
17
18import {globals} from './globals';
19import {
20  KeyboardLayoutMap,
21  nativeKeyboardLayoutMap,
22  NotSupportedError,
23} from './keyboard_layout_map';
24import {showModal} from './modal';
25import {KeyMapping} from './pan_and_zoom_handler';
26import {Spinner} from './widgets/spinner';
27
28export function toggleHelp() {
29  globals.logging.logEvent('User Actions', 'Show help');
30  showHelp();
31}
32
33function keycap(glyph: m.Children): m.Children {
34  return m('.keycap', glyph);
35}
36
37// A fallback keyboard map based on the QWERTY keymap. Converts keyboard event
38// codes to their associated glyphs on an English QWERTY keyboard.
39class EnglishQwertyKeyboardLayoutMap implements KeyboardLayoutMap {
40  get(code: string): string {
41    // Converts 'KeyX' -> 'x'
42    return code.replace(/^Key([A-Z])$/, '$1').toLowerCase();
43  }
44}
45
46class KeyMappingsHelp implements m.ClassComponent {
47  private keyMap?: KeyboardLayoutMap;
48
49  oninit() {
50    nativeKeyboardLayoutMap()
51        .then((keyMap: KeyboardLayoutMap) => {
52          this.keyMap = keyMap;
53          globals.rafScheduler.scheduleFullRedraw();
54        })
55        .catch((e) => {
56          if (e instanceof NotSupportedError ||
57              e.toString().includes('SecurityError')) {
58            // Keyboard layout is unavailable. Since showing the keyboard
59            // mappings correct for the user's keyboard layout is a nice-to-
60            // have, and users with non-QWERTY layouts are usually aware of the
61            // fact that the are using non-QWERTY layouts, we resort to showing
62            // English QWERTY mappings as a best-effort approach.
63            // The alternative would be to show key mappings for all keyboard
64            // layouts which is not feasible.
65            this.keyMap = new EnglishQwertyKeyboardLayoutMap();
66            globals.rafScheduler.scheduleFullRedraw();
67          } else {
68            // Something unexpected happened. Either the browser doesn't conform
69            // to the keyboard API spec, or the keyboard API spec has changed!
70            throw e;
71          }
72        });
73  }
74
75  view(_: m.Vnode): m.Children {
76    const ctrlOrCmd =
77        window.navigator.platform.indexOf('Mac') !== -1 ? 'Cmd' : 'Ctrl';
78
79    const queryPageInstructions = globals.hideSidebar ? [] : [
80      m('h2', 'Making SQL queries from the query page'),
81      m('table',
82        m('tr',
83          m('td', keycap('Ctrl'), ' + ', keycap('Enter')),
84          m('td', 'Execute query')),
85        m('tr',
86          m('td', keycap('Ctrl'), ' + ', keycap('Enter'), ' (with selection)'),
87          m('td', 'Execute selection'))),
88    ];
89
90    const sidebarInstructions = globals.hideSidebar ?
91        [] :
92        [m('tr',
93           m('td', keycap(ctrlOrCmd), ' + ', keycap('b')),
94           m('td', 'Toggle display of sidebar'))];
95
96    return m(
97        '.help',
98        m('h2', 'Navigation'),
99        m(
100            'table',
101            m(
102                'tr',
103                m('td',
104                  this.codeToKeycap(KeyMapping.KEY_ZOOM_IN),
105                  '/',
106                  this.codeToKeycap(KeyMapping.KEY_ZOOM_OUT)),
107                m('td', 'Zoom in/out'),
108                ),
109            m(
110                'tr',
111                m('td',
112                  this.codeToKeycap(KeyMapping.KEY_PAN_LEFT),
113                  '/',
114                  this.codeToKeycap(KeyMapping.KEY_PAN_RIGHT)),
115                m('td', 'Pan left/right'),
116                ),
117            ),
118        m('h2', 'Mouse Controls'),
119        m('table',
120          m('tr', m('td', 'Click'), m('td', 'Select event')),
121          m('tr', m('td', 'Ctrl + Scroll wheel'), m('td', 'Zoom in/out')),
122          m('tr', m('td', 'Click + Drag'), m('td', 'Select area')),
123          m('tr', m('td', 'Shift + Click + Drag'), m('td', 'Pan left/right'))),
124        m('h2', 'Making SQL queries from the viewer page'),
125        m('table',
126          m('tr',
127            m('td', keycap(':'), ' in the (empty) search box'),
128            m('td', 'Switch to query input')),
129          m('tr', m('td', keycap('Enter')), m('td', 'Execute query')),
130          m('tr',
131            m('td', keycap('Ctrl'), ' + ', keycap('Enter')),
132            m('td',
133              'Execute query and pin output ' +
134                  '(output will not be replaced by regular query input)'))),
135        ...queryPageInstructions,
136        m('h2', 'Other'),
137        m(
138            'table',
139            m('tr',
140              m('td', keycap('f'), ' (with event selected)'),
141              m('td', 'Scroll + zoom to current selection')),
142            m('tr',
143              m('td', keycap('['), '/', keycap(']'), ' (with event selected)'),
144              m('td',
145                'Select next/previous slice that is connected by a flow.',
146                m('br'),
147                'If there are multiple flows,' +
148                    'the one that is in focus (bold) is selected')),
149            m('tr',
150              m('td',
151                keycap(ctrlOrCmd),
152                ' + ',
153                keycap('['),
154                '/',
155                keycap(']'),
156                ' (with event selected)'),
157              m('td', 'Switch focus to another flow')),
158            m('tr',
159              m('td', keycap('m'), ' (with event or area selected)'),
160              m('td', 'Mark the area (temporarily)')),
161            m('tr',
162              m('td',
163                keycap('Shift'),
164                ' + ',
165                keycap('m'),
166                ' (with event or area selected)'),
167              m('td', 'Mark the area (persistently)')),
168            m('tr',
169              m('td', keycap(ctrlOrCmd), ' + ', keycap('a')),
170              m('td', 'Select all')),
171            ...sidebarInstructions,
172            m('tr', m('td', keycap('?')), m('td', 'Show help')),
173            ));
174  }
175
176  private codeToKeycap(code: string): m.Children {
177    if (this.keyMap) {
178      return keycap(this.keyMap.get(code));
179    } else {
180      return keycap(m(Spinner));
181    }
182  }
183}
184
185function showHelp() {
186  showModal({
187    title: 'Perfetto Help',
188    content: () => m(KeyMappingsHelp),
189    buttons: [],
190  });
191}
192