perfetto-ui: Add panning hint

Since we are changing the controls to make selection the default
action we need to help users who might not know how to pan.

This hint appears in the top right hand corner when the first
area selection is made and then disappears when they click dismiss
or pan.

Change-Id: I8f9fb761972965f21cd86e95167d18e308c8b5e0
diff --git a/ui/src/assets/topbar.scss b/ui/src/assets/topbar.scss
index f5ce2f5..03a907f 100644
--- a/ui/src/assets/topbar.scss
+++ b/ui/src/assets/topbar.scss
@@ -222,3 +222,29 @@
         }
     }
 }
+
+.helpful-hint {
+  position: absolute;
+  right: 5px;
+  top: 5px;
+  width: 300px;
+  background-color: white;
+  font-size: 12px;
+  color: #3f4040;
+  display: grid;
+  border-radius: 5px;
+  padding: 8px;
+  box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
+}
+
+.hint-text {
+  padding-bottom: 5px
+}
+
+.hint-dismiss-button {
+  color: #f4fafb;
+  background-color: #19212b;
+  width: fit-content;
+  padding: 3px;
+  border-radius: 3px;
+}
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index 0f4a605..97acfd3 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -99,6 +99,7 @@
   showNotePreview = false;
   localOnlyMode = false;
   sidebarVisible = true;
+  showPanningHint = false;
   // This is used to calculate the tracks within a Y range for area selection.
   areaY: Range = {};
   visibleTracks = new Set<string>();
@@ -225,6 +226,7 @@
   selectArea(
       startSec: number, endSec: number,
       tracks = this._selectedArea.area ? this._selectedArea.area.tracks : []) {
+    this.showPanningHint = true;
     this._selectedArea = {
       area: {startSec, endSec, tracks},
       lastUpdate: Date.now() / 1000
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 4fd3c66..a7bd25f 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -32,6 +32,8 @@
   [COMMAND]: 'e.g. select * from sched left join thread using(utid) limit 10'
 };
 
+export const DISMISSED_PANNING_HINT_KEY = 'dismissedPanningHint';
+
 let selResult = 0;
 let numResults = 0;
 let mode: Mode = SEARCH;
@@ -255,6 +257,31 @@
   }
 }
 
+
+class HelpPanningNotification implements m.ClassComponent {
+  view() {
+    const dismissed = localStorage.getItem(DISMISSED_PANNING_HINT_KEY);
+    if (dismissed === 'true' || !globals.frontendLocalState.showPanningHint) {
+      return;
+    }
+    return m(
+        '.helpful-hint',
+        m('.hint-text',
+          'Are you trying to pan? Use the WASD keys or hold shift to click ' +
+              'and drag. Press \'?\' for more help.'),
+        m('button.hint-dismiss-button',
+          {
+            onclick: () => {
+              globals.frontendLocalState.showPanningHint = false;
+              localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
+              globals.rafScheduler.scheduleFullRedraw();
+            }
+          },
+          'Dismiss'),
+    );
+  }
+}
+
 export class Topbar implements m.ClassComponent {
   view() {
     return m(
@@ -262,6 +289,7 @@
         globals.frontendLocalState.newVersionAvailable ?
             m(NewVersionNotification) :
             m(Omnibox),
-        m(Progress));
+        m(Progress),
+        m(HelpPanningNotification));
   }
 }
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 6dc5475..5dd811e 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -32,6 +32,7 @@
 import {TimeAxisPanel} from './time_axis_panel';
 import {computeZoom} from './time_scale';
 import {TimeSelectionPanel} from './time_selection_panel';
+import {DISMISSED_PANNING_HINT_KEY} from './topbar';
 import {TrackGroupPanel} from './track_group_panel';
 import {TrackPanel} from './track_panel';
 import {VideoPanel} from './video_panel';
@@ -167,6 +168,8 @@
           tStart = tEnd - origDelta;
         }
         frontendLocalState.updateVisibleTime(new TimeSpan(tStart, tEnd));
+        // If the user has panned they no longer need the hint.
+        localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
         globals.rafScheduler.scheduleRedraw();
       },
       onZoomed: (zoomedPositionPx: number, zoomRatio: number) => {