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 {searchSegment} from '../base/binary_search'; 16import {Actions} from '../common/actions'; 17import {toNs} from '../common/time'; 18 19import {globals} from './globals'; 20import {scrollToTrackAndTs} from './scroll_helper'; 21 22function setToPrevious(current: number) { 23 const index = Math.max(current - 1, 0); 24 globals.dispatch(Actions.setSearchIndex({index})); 25} 26 27function setToNext(current: number) { 28 const index = 29 Math.min(current + 1, globals.currentSearchResults.totalResults - 1); 30 globals.dispatch(Actions.setSearchIndex({index})); 31} 32 33export function executeSearch(reverse = false) { 34 const index = globals.state.searchIndex; 35 const startNs = toNs(globals.frontendLocalState.visibleWindowTime.start); 36 const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end); 37 const currentTs = globals.currentSearchResults.tsStarts[index]; 38 39 // If this is a new search or the currentTs is not in the viewport, 40 // select the first/last item in the viewport. 41 if (index === -1 || currentTs < startNs || currentTs > endNs) { 42 if (reverse) { 43 const [smaller,] = 44 searchSegment(globals.currentSearchResults.tsStarts, endNs); 45 // If there is no item in the viewport just go to the previous. 46 if (smaller === -1) { 47 setToPrevious(index); 48 } else { 49 globals.dispatch(Actions.setSearchIndex({index: smaller})); 50 } 51 } else { 52 const [, larger] = 53 searchSegment(globals.currentSearchResults.tsStarts, startNs); 54 // If there is no item in the viewport just go to the next. 55 if (larger === -1) { 56 setToNext(index); 57 } else { 58 globals.dispatch(Actions.setSearchIndex({index: larger})); 59 } 60 } 61 } else { 62 // If the currentTs is in the viewport, increment the index. 63 if (reverse) { 64 setToPrevious(index); 65 } else { 66 setToNext(index); 67 } 68 } 69 selectCurrentSearchResult(); 70 71 // TODO(hjd): If the user does a search before any other selection, 72 // the details panel will pop up when the search is executed. If the search 73 // result is behind where the details panel appears then it won't get scrolled 74 // to. This time delay is a workaround for this specific situation. 75 // A better solution will be a callback that allows something to happen on the 76 // first redraw after an Action is applied. 77 const delay = index === -1 ? 50 : 0; 78 setTimeout(() => moveViewportToCurrentSearch(), delay); 79} 80 81function moveViewportToCurrentSearch() { 82 const searchIndex = globals.state.searchIndex; 83 if (searchIndex === -1) return; 84 const currentTs = globals.currentSearchResults.tsStarts[searchIndex]; 85 const trackId = globals.currentSearchResults.trackIds[searchIndex]; 86 scrollToTrackAndTs(trackId, currentTs); 87} 88 89function selectCurrentSearchResult() { 90 const searchIndex = globals.state.searchIndex; 91 const source = globals.currentSearchResults.sources[searchIndex]; 92 const currentId = globals.currentSearchResults.sliceIds[searchIndex]; 93 const trackId = globals.currentSearchResults.trackIds[searchIndex]; 94 95 if (currentId === undefined) return; 96 97 if (source === 'cpu') { 98 globals.dispatch(Actions.selectSlice({id: currentId, trackId})); 99 } else { 100 // Search results only include slices from the slice table for now. 101 // When we include annotations we need to pass the correct table. 102 globals.dispatch( 103 Actions.selectChromeSlice({id: currentId, trackId, table: 'slice'})); 104 } 105} 106