1// Copyright (C) 2020 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use size 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 m from 'mithril'; 16import {Icons} from '../base/semantic_icons'; 17import {raf} from '../core/raf_scheduler'; 18import {Flow} from '../core/flow_types'; 19import {TraceImpl} from '../core/trace_impl'; 20 21export const ALL_CATEGORIES = '_all_'; 22 23export function getFlowCategories(flow: Flow): string[] { 24 const categories: string[] = []; 25 // v1 flows have their own categories 26 if (flow.category) { 27 categories.push(...flow.category.split(',')); 28 return categories; 29 } 30 const beginCats = flow.begin.sliceCategory.split(','); 31 const endCats = flow.end.sliceCategory.split(','); 32 categories.push(...new Set([...beginCats, ...endCats])); 33 return categories; 34} 35 36export interface FlowEventsAreaSelectedPanelAttrs { 37 trace: TraceImpl; 38} 39 40export class FlowEventsAreaSelectedPanel 41 implements m.ClassComponent<FlowEventsAreaSelectedPanelAttrs> 42{ 43 view({attrs}: m.CVnode<FlowEventsAreaSelectedPanelAttrs>) { 44 const selection = attrs.trace.selection.selection; 45 if (selection.kind !== 'area') { 46 return; 47 } 48 49 const columns = [ 50 m('th', 'Flow Category'), 51 m('th', 'Number of flows'), 52 m( 53 'th', 54 'Show', 55 m( 56 'a.warning', 57 m('i.material-icons', 'warning'), 58 m( 59 '.tooltip', 60 'Showing a large number of flows may impact performance.', 61 ), 62 ), 63 ), 64 ]; 65 66 const rows = [m('tr', columns)]; 67 68 const categoryToFlowsNum = new Map<string, number>(); 69 70 const flows = attrs.trace.flows; 71 flows.selectedFlows.forEach((flow) => { 72 const categories = getFlowCategories(flow); 73 categories.forEach((cat) => { 74 if (!categoryToFlowsNum.has(cat)) { 75 categoryToFlowsNum.set(cat, 0); 76 } 77 categoryToFlowsNum.set(cat, categoryToFlowsNum.get(cat)! + 1); 78 }); 79 }); 80 81 const allWasChecked = flows.visibleCategories.get(ALL_CATEGORIES); 82 rows.push( 83 m('tr.sum', [ 84 m('td.sum-data', 'All'), 85 m('td.sum-data', flows.selectedFlows.length), 86 m( 87 'td.sum-data', 88 m( 89 'i.material-icons', 90 { 91 onclick: () => { 92 if (allWasChecked) { 93 for (const k of flows.visibleCategories.keys()) { 94 flows.setCategoryVisible(k, false); 95 } 96 } else { 97 categoryToFlowsNum.forEach((_, cat) => { 98 flows.setCategoryVisible(cat, true); 99 }); 100 } 101 flows.setCategoryVisible(ALL_CATEGORIES, !allWasChecked); 102 }, 103 }, 104 allWasChecked ? Icons.Checkbox : Icons.BlankCheckbox, 105 ), 106 ), 107 ]), 108 ); 109 110 categoryToFlowsNum.forEach((num, cat) => { 111 const wasChecked = 112 flows.visibleCategories.get(cat) || 113 flows.visibleCategories.get(ALL_CATEGORIES); 114 const data = [ 115 m('td.flow-info', cat), 116 m('td.flow-info', num), 117 m( 118 'td.flow-info', 119 m( 120 'i.material-icons', 121 { 122 onclick: () => { 123 if (wasChecked) { 124 flows.setCategoryVisible(ALL_CATEGORIES, false); 125 } 126 flows.setCategoryVisible(cat, !wasChecked); 127 raf.scheduleFullRedraw(); 128 }, 129 }, 130 wasChecked ? Icons.Checkbox : Icons.BlankCheckbox, 131 ), 132 ), 133 ]; 134 rows.push(m('tr', data)); 135 }); 136 137 return m('.details-panel', [ 138 m('.details-panel-heading', m('h2', `Selected flow events`)), 139 m('.flow-events-table', m('table', rows)), 140 ]); 141 } 142} 143