• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {Actions} from '../common/actions';
16import {
17  HighPrecisionTime,
18  HighPrecisionTimeSpan,
19} from '../common/high_precision_time';
20import {getContainingTrackId} from '../common/state';
21import {TPTime} from '../common/time';
22
23import {globals} from './globals';
24
25
26// Given a timestamp, if |ts| is not currently in view move the view to
27// center |ts|, keeping the same zoom level.
28// TODO(stevegolton): Remove me!
29export function horizontalScrollToTs(ts: TPTime) {
30  console.log('horizontalScrollToTs', ts);
31  const time = HighPrecisionTime.fromTPTime(ts);
32  const {start, end, duration} = globals.frontendLocalState.visibleWindowTime;
33  const halfDuration = duration.nanos / 2;
34  if (time.isLessThan(start) || time.isGreaterThan(end)) {
35    // TODO(hjd): This is an ugly jump, we should do a smooth pan instead.
36    globals.frontendLocalState.updateVisibleTime(new HighPrecisionTimeSpan(
37        time.subtractNanos(halfDuration), time.addNanos(halfDuration)));
38  }
39}
40
41// Given a start and end timestamp (in ns), move the viewport to center this
42// range and zoom if necessary:
43// - If [viewPercentage] is specified, the viewport will be zoomed so that
44//   the given time range takes up this percentage of the viewport.
45// The following scenarios assume [viewPercentage] is undefined.
46// - If the new range is more than 50% of the viewport, zoom out to a level
47// where
48//   the range is 1/5 of the viewport.
49// - If the new range is already centered, update the zoom level for the
50// viewport
51//   to cover 1/5 of the viewport.
52// - Otherwise, preserve the zoom range.
53export function focusHorizontalRange(
54    start: TPTime, end: TPTime, viewPercentage?: number) {
55  console.log('focusHorizontalRange', start, end);
56  const visible = globals.frontendLocalState.visibleWindowTime;
57  const trace = globals.stateTraceTime();
58  const select = HighPrecisionTimeSpan.fromTpTime(start, end);
59
60  if (viewPercentage !== undefined) {
61    if (viewPercentage <= 0.0 || viewPercentage > 1.0) {
62      console.warn(
63          'Invalid value for [viewPercentage]. ' +
64              'Value must be between 0.0 (exclusive) and 1.0 (inclusive).',
65      );
66      // Default to 50%.
67      viewPercentage = 0.5;
68    }
69    const paddingPercentage = 1.0 - viewPercentage;
70    const paddingTime = select.duration.multiply(paddingPercentage);
71    const halfPaddingTime = paddingTime.divide(2);
72    globals.frontendLocalState.updateVisibleTime(select.pad(halfPaddingTime));
73    return;
74  }
75  // If the range is too large to fit on the current zoom level, resize.
76  if (select.duration.isGreaterThan(visible.duration.multiply(0.5))) {
77    const paddedRange = select.pad(select.duration.multiply(2));
78    globals.frontendLocalState.updateVisibleTime(paddedRange);
79    return;
80  }
81  // Calculate the new visible window preserving the zoom level.
82  let newStart = select.midpoint.subtract(visible.duration.divide(2));
83  let newEnd = select.midpoint.add(visible.duration.divide(2));
84
85  // Adjust the new visible window if it intersects with the trace boundaries.
86  // It's needed to make the "update the zoom level if visible window doesn't
87  // change" logic reliable.
88  if (newEnd.isGreaterThan(trace.end)) {
89    newStart = trace.end.subtract(visible.duration);
90    newEnd = trace.end;
91  }
92  if (newStart.isLessThan(trace.start)) {
93    newStart = trace.start;
94    newEnd = trace.start.add(visible.duration);
95  }
96
97  const view = new HighPrecisionTimeSpan(newStart, newEnd);
98
99  // If preserving the zoom doesn't change the visible window, update the zoom
100  // level.
101  if (view.start.equals(visible.start) && view.end.equals(visible.end)) {
102    const padded = select.pad(select.duration.multiply(2));
103    globals.frontendLocalState.updateVisibleTime(padded);
104  } else {
105    globals.frontendLocalState.updateVisibleTime(view);
106  }
107}
108
109// Given a track id, find a track with that id and scroll it into view. If the
110// track is nested inside a track group, scroll to that track group instead.
111// If |openGroup| then open the track group and scroll to the track.
112export function verticalScrollToTrack(
113    trackId: string|number, openGroup = false) {
114  const trackIdString = `${trackId}`;
115  const track = document.querySelector('#track_' + trackIdString);
116
117  if (track) {
118    // block: 'nearest' means that it will only scroll if the track is not
119    // currently in view.
120    track.scrollIntoView({behavior: 'smooth', block: 'nearest'});
121    return;
122  }
123
124  let trackGroup = null;
125  const trackGroupId = getContainingTrackId(globals.state, trackIdString);
126  if (trackGroupId) {
127    trackGroup = document.querySelector('#track_' + trackGroupId);
128  }
129
130  if (!trackGroupId || !trackGroup) {
131    console.error(`Can't scroll, track (${trackIdString}) not found.`);
132    return;
133  }
134
135  // The requested track is inside a closed track group, either open the track
136  // group and scroll to the track or just scroll to the track group.
137  if (openGroup) {
138    // After the track exists in the dom, it will be scrolled to.
139    globals.frontendLocalState.scrollToTrackId = trackId;
140    globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId}));
141    return;
142  } else {
143    trackGroup.scrollIntoView({behavior: 'smooth', block: 'nearest'});
144  }
145}
146
147
148// Scroll vertically and horizontally to reach track (|trackId|) at |ts|.
149export function scrollToTrackAndTs(
150    trackId: string|number|undefined, ts: TPTime, openGroup = false) {
151  if (trackId !== undefined) {
152    verticalScrollToTrack(trackId, openGroup);
153  }
154  horizontalScrollToTs(ts);
155}
156