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