Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 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 | |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 15 | import {Actions} from '../common/actions'; |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 16 | import {getContainingTrackId} from '../common/state'; |
| 17 | import {fromNs, TimeSpan, toNs} from '../common/time'; |
| 18 | |
| 19 | import {globals} from './globals'; |
| 20 | |
| 21 | /** |
| 22 | * Given a timestamp, if |ts| is not currently in view move the view to |
| 23 | * center |ts|, keeping the same zoom level. |
| 24 | */ |
| 25 | export function horizontalScrollToTs(ts: number) { |
| 26 | const startNs = toNs(globals.frontendLocalState.visibleWindowTime.start); |
| 27 | const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end); |
| 28 | const currentViewNs = endNs - startNs; |
| 29 | if (ts < startNs || ts > endNs) { |
| 30 | // TODO(taylori): This is an ugly jump, we should do a smooth pan instead. |
| 31 | globals.frontendLocalState.updateVisibleTime(new TimeSpan( |
| 32 | fromNs(ts - currentViewNs / 2), fromNs(ts + currentViewNs / 2))); |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | /** |
| 37 | * Given a start and end timestamp (in ns), move the view to center this range |
| 38 | * and zoom to a level where the range is 1/5 of the viewport. |
| 39 | */ |
| 40 | export function horizontalScrollAndZoomToRange(startTs: number, endTs: number) { |
| 41 | const visibleDur = globals.frontendLocalState.visibleWindowTime.end - |
| 42 | globals.frontendLocalState.visibleWindowTime.start; |
| 43 | const selectDur = endTs - startTs; |
| 44 | const viewStartNs = toNs(globals.frontendLocalState.visibleWindowTime.start); |
| 45 | const viewEndNs = toNs(globals.frontendLocalState.visibleWindowTime.end); |
| 46 | if (selectDur / visibleDur < 0.05 || startTs < viewStartNs || |
| 47 | endTs > viewEndNs) { |
| 48 | globals.frontendLocalState.updateVisibleTime( |
| 49 | new TimeSpan(startTs - (selectDur * 2), endTs + (selectDur * 2))); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | * Given a track id, find a track with that id and scroll it into view. If the |
| 55 | * track is nested inside a track group, scroll to that track group instead. |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 56 | * If |openGroup| then open the track group and scroll to the track. |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 57 | */ |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 58 | export function verticalScrollToTrack( |
| 59 | trackId: string|number, openGroup = false) { |
Primiano Tucci | c350da5 | 2019-11-01 12:13:01 +0100 | [diff] [blame] | 60 | const trackIdString = `${trackId}`; |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 61 | const track = document.querySelector('#track_' + trackIdString); |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 62 | |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 63 | if (track) { |
| 64 | // block: 'nearest' means that it will only scroll if the track is not |
| 65 | // currently in view. |
| 66 | track.scrollIntoView({behavior: 'smooth', block: 'nearest'}); |
| 67 | return; |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 68 | } |
| 69 | |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 70 | let trackGroup = null; |
| 71 | const trackGroupId = getContainingTrackId(globals.state, trackIdString); |
| 72 | if (trackGroupId) { |
| 73 | trackGroup = document.querySelector('#track_' + trackGroupId); |
| 74 | } |
| 75 | |
| 76 | if (!trackGroupId || !trackGroup) { |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 77 | console.error(`Can't scroll, track (${trackIdString}) not found.`); |
| 78 | return; |
| 79 | } |
| 80 | |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 81 | // The requested track is inside a closed track group, either open the track |
| 82 | // group and scroll to the track or just scroll to the track group. |
| 83 | if (openGroup) { |
| 84 | // After the track exists in the dom, it will be scrolled to. |
| 85 | globals.frontendLocalState.scrollToTrackId = trackId; |
| 86 | globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId})); |
| 87 | return; |
| 88 | } else { |
| 89 | trackGroup.scrollIntoView({behavior: 'smooth', block: 'nearest'}); |
| 90 | } |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 91 | } |
| 92 | |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 93 | |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 94 | /** |
| 95 | * Scroll vertically and horizontally to reach track (|trackId|) at |ts|. |
| 96 | */ |
Isabelle Taylor | 613d38c | 2019-10-24 12:35:31 +0100 | [diff] [blame] | 97 | export function scrollToTrackAndTs( |
Primiano Tucci | c350da5 | 2019-11-01 12:13:01 +0100 | [diff] [blame] | 98 | trackId: string|number|undefined, ts: number, openGroup = false) { |
| 99 | if (trackId !== undefined) { |
| 100 | verticalScrollToTrack(trackId, openGroup); |
| 101 | } |
Isabelle Taylor | ec09515 | 2019-09-26 17:45:03 +0100 | [diff] [blame] | 102 | horizontalScrollToTs(ts); |
| 103 | } |