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