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--> 15<template> 16 <md-card-content class="container"> 17 <md-card class="rects" v-if="hasScreenView"> 18 <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense"> 19 <h2 class="md-title">Screen</h2> 20 </md-whiteframe> 21 <md-whiteframe md-elevation="8"> 22 <rects :bounds="bounds" :rects="rects" :highlight="highlight" @rect-click="onRectClick" /> 23 </md-whiteframe> 24 </md-card> 25 <md-card class="hierarchy"> 26 <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense"> 27 <h2 class="md-title" style="flex: 1;">Hierarchy</h2> 28 <md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox> 29 <md-checkbox v-model="store.flattened">Flat</md-checkbox> 30 <input id="filter" type="search" placeholder="Filter..." v-model="hierarchyPropertyFilterString" /> 31 </md-whiteframe> 32 <tree-view class="data-card" :item="tree" @item-selected="itemSelected" :selected="hierarchySelected" :filter="hierarchyFilter" :flattened="store.flattened" ref="hierarchy" /> 33 </md-card> 34 <md-card class="properties"> 35 <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense"> 36 <h2 class="md-title" style="flex: 1">Properties</h2> 37 <div class="filter"> 38 <input id="filter" type="search" placeholder="Filter..." v-model="propertyFilterString" /> 39 </div> 40 </md-whiteframe> 41 <tree-view class="pre-line-data-card" :item="selectedTree" :filter="propertyFilter" /> 42 </md-card> 43 </md-card-content> 44</template> 45<script> 46import TreeView from './TreeView.vue' 47import Timeline from './Timeline.vue' 48import Rects from './Rects.vue' 49 50import { transform_json } from './transform.js' 51import { format_transform_type, is_simple_transform } from './matrix_utils.js' 52import { DATA_TYPES } from './decode.js' 53 54function formatColorTransform(vals) { 55 const fixedVals = vals.map(v => v.toFixed(1)); 56 var formatted = ``; 57 for (var i = 0; i < fixedVals.length; i += 4) { 58 formatted += `[`; 59 formatted += fixedVals.slice(i, i + 4).join(", "); 60 formatted += `] `; 61 } 62 return formatted; 63} 64 65 66function formatProto(obj) { 67 if (!obj || !obj.$type) { 68 return; 69 } 70 if (obj.$type.name === 'RectProto') { 71 return `(${obj.left}, ${obj.top}) - (${obj.right}, ${obj.bottom})`; 72 } else if (obj.$type.name === 'FloatRectProto') { 73 return `(${obj.left.toFixed(3)}, ${obj.top.toFixed(3)}) - (${obj.right.toFixed(3)}, ${obj.bottom.toFixed(3)})`; 74 } else if (obj.$type.name === 'PositionProto') { 75 return `(${obj.x.toFixed(3)}, ${obj.y.toFixed(3)})`; 76 } else if (obj.$type.name === 'SizeProto') { 77 return `${obj.w} x ${obj.h}`; 78 } else if (obj.$type.name === 'ColorProto') { 79 return `r:${obj.r} g:${obj.g} \n b:${obj.b} a:${obj.a}`; 80 } else if (obj.$type.name === 'TransformProto') { 81 var transform_type = format_transform_type(obj); 82 if (is_simple_transform(obj)) { 83 return `${transform_type}`; 84 } 85 return `${transform_type} dsdx:${obj.dsdx.toFixed(3)} dtdx:${obj.dtdx.toFixed(3)} dsdy:${obj.dsdy.toFixed(3)} dtdy:${obj.dtdy.toFixed(3)}`; 86 } else if (obj.$type.name === 'ColorTransformProto') { 87 var formated = formatColorTransform(obj.val); 88 return `${formated}`; 89 } 90} 91 92export default { 93 name: 'traceview', 94 data() { 95 return { 96 propertyFilterString: "", 97 hierarchyPropertyFilterString:"", 98 selectedTree: {}, 99 hierarchySelected: null, 100 lastSelectedStableId: null, 101 bounds: {}, 102 rects: [], 103 tree: null, 104 highlight: null, 105 } 106 }, 107 methods: { 108 itemSelected(item) { 109 this.hierarchySelected = item; 110 this.selectedTree = transform_json(item.obj, item.name, { 111 skip: item.skip, 112 formatter: formatProto 113 }); 114 this.highlight = item.highlight; 115 this.lastSelectedStableId = item.stableId; 116 this.$emit('focus'); 117 }, 118 onRectClick(item) { 119 if (item) { 120 this.itemSelected(item); 121 } 122 }, 123 setData(item) { 124 this.tree = item; 125 this.rects = [...item.rects].reverse(); 126 this.bounds = item.bounds; 127 128 this.hierarchySelected = null; 129 this.selectedTree = {}; 130 this.highlight = null; 131 132 function find_item(item, stableId) { 133 if (item.stableId === stableId) { 134 return item; 135 } 136 if (Array.isArray(item.children)) { 137 for (var child of item.children) { 138 var found = find_item(child, stableId); 139 if (found) { 140 return found; 141 } 142 } 143 } 144 return null; 145 } 146 147 if (this.lastSelectedStableId) { 148 var found = find_item(item, this.lastSelectedStableId); 149 if (found) { 150 this.itemSelected(found); 151 } 152 } 153 }, 154 arrowUp() { 155 return this.$refs.hierarchy.selectPrev(); 156 }, 157 arrowDown() { 158 return this.$refs.hierarchy.selectNext(); 159 }, 160 }, 161 created() { 162 this.setData(this.file.data[this.file.selectedIndex]); 163 }, 164 watch: { 165 selectedIndex() { 166 this.setData(this.file.data[this.file.selectedIndex]); 167 } 168 }, 169 props: ['store', 'file'], 170 computed: { 171 selectedIndex() { 172 return this.file.selectedIndex; 173 }, 174 hierarchyFilter() { 175 var hierarchyPropertyFilter = getFilter(this.hierarchyPropertyFilterString); 176 return this.store.onlyVisible ? (c) => { 177 return c.visible && hierarchyPropertyFilter(c);} : hierarchyPropertyFilter; 178 }, 179 propertyFilter() { 180 return getFilter(this.propertyFilterString); 181 }, 182 hasScreenView() { 183 return this.file.type !== DATA_TYPES.TRANSACTION; 184 }, 185 }, 186 components: { 187 'tree-view': TreeView, 188 'rects': Rects, 189 } 190} 191 192function getFilter(filterString) { 193 var filterStrings = filterString.split(","); 194 var positive = []; 195 var negative = []; 196 filterStrings.forEach((f) => { 197 if (f.startsWith("!")) { 198 var str = f.substring(1); 199 negative.push((s) => s.indexOf(str) === -1); 200 } else { 201 var str = f; 202 positive.push((s) => s.indexOf(str) !== -1); 203 } 204 }); 205 var filter = (item) => { 206 var apply = (f) => f(String(item.name)); 207 return (positive.length === 0 || positive.some(apply)) && 208 (negative.length === 0 || negative.every(apply)); 209 }; 210 return filter; 211} 212 213</script> 214<style> 215.rects { 216 flex: none; 217 margin: 8px; 218} 219 220.hierarchy, 221.properties { 222 flex: 1; 223 margin: 8px; 224 min-width: 400px; 225} 226 227.hierarchy>.tree-view, 228.properties>.tree-view { 229 margin: 16px; 230} 231 232.data-card { 233 overflow: auto; 234 max-height: 48em; 235} 236 237.pre-line-data-card { 238 overflow: auto; 239 max-height: 48em; 240 white-space: pre-line; 241} 242 243</style> 244