Minor winscope improvements.
Expand/collapse button for traces now centred with icon. Keyboard arrows move cursor in input text fields, rather than the timeline. Error lines now clickable and hit area increased - click to navigate to error timestamp. Timestamp label on minimized timeline and seek time field on expanded timeline are now editable in the same way as the search timestamp function in search bar. Bug: b/199146199 Test: Upload any trace to see the implementations. Change-Id: Iff2c5423933b7f77292f0786ddcf5ffe715c2854
This commit is contained in:
@@ -107,6 +107,7 @@ export default {
|
||||
navigationStyle: NAVIGATION_STYLE.GLOBAL,
|
||||
flickerTraceView: false,
|
||||
showFileTypes: [],
|
||||
isInputMode: false,
|
||||
}),
|
||||
overlayRef: 'overlay',
|
||||
mainContentStyle: {
|
||||
@@ -187,6 +188,7 @@ export default {
|
||||
},
|
||||
onKeyDown(event) {
|
||||
event = event || window.event;
|
||||
if (this.store.isInputMode) return false;
|
||||
if (event.keyCode == 37 /* left */ ) {
|
||||
this.$store.dispatch('advanceTimeline', DIRECTION.BACKWARD);
|
||||
} else if (event.keyCode == 39 /* right */ ) {
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
<div @click="onClick($event)">
|
||||
<flat-card v-if="hasDataView(file)">
|
||||
<md-card-header>
|
||||
<button class="toggle-view-button" @click="toggleView">
|
||||
<i aria-hidden="true" class="md-icon md-theme-default material-icons">
|
||||
{{ isShowFileType(file.type) ? "expand_more" : "chevron_right" }}
|
||||
</i>
|
||||
</button>
|
||||
<md-card-header-text>
|
||||
<div class="md-title">
|
||||
<button class="toggle-view-button" @click="toggleView">
|
||||
<i aria-hidden="true" class="md-icon md-theme-default material-icons">
|
||||
{{ isShowFileType(file.type) ? "expand_more" : "chevron_right" }}
|
||||
</i>
|
||||
</button>
|
||||
<md-icon>{{ TRACE_ICONS[file.type] }}</md-icon>
|
||||
{{ file.type }}
|
||||
</div>
|
||||
|
||||
@@ -65,6 +65,13 @@
|
||||
md-elevation="0"
|
||||
class="md-transparent">
|
||||
|
||||
<md-button
|
||||
@click="toggleSearch()"
|
||||
class="drop-search"
|
||||
>
|
||||
Toggle search bar
|
||||
</md-button>
|
||||
|
||||
<div class="toolbar" :class="{ expanded: expanded }">
|
||||
<div class="resize-bar" v-show="expanded">
|
||||
<div v-if="video" @mousedown="resizeBottomNav">
|
||||
@@ -75,11 +82,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<md-button
|
||||
@click="toggleSearch()"
|
||||
class="drop-search"
|
||||
>Toggle search bar</md-button>
|
||||
|
||||
<div class="active-timeline" v-show="minimized">
|
||||
<div
|
||||
class="active-timeline-icon"
|
||||
@@ -149,9 +151,15 @@
|
||||
v-show="minimized"
|
||||
v-if="hasTimeline"
|
||||
>
|
||||
<label>
|
||||
{{ seekTime }}
|
||||
</label>
|
||||
<input
|
||||
class="timestamp-search-input"
|
||||
v-model="searchInput"
|
||||
spellcheck="false"
|
||||
:placeholder="seekTime"
|
||||
@focus="updateInputMode(true)"
|
||||
@blur="updateInputMode(false)"
|
||||
@keyup.enter="updateSearchForTimestamp"
|
||||
/>
|
||||
<timeline
|
||||
:store="store"
|
||||
:flickerMode="flickerMode"
|
||||
@@ -213,7 +221,17 @@
|
||||
:style="`padding-top: ${resizeOffset}px;`"
|
||||
>
|
||||
<div class="seek-time" v-if="seekTime">
|
||||
<b>Seek time</b>: {{ seekTime }}
|
||||
<b>Seek time: </b>
|
||||
<input
|
||||
class="timestamp-search-input"
|
||||
:class="{ expanded: expanded }"
|
||||
v-model="searchInput"
|
||||
spellcheck="false"
|
||||
:placeholder="seekTime"
|
||||
@focus="updateInputMode(true)"
|
||||
@blur="updateInputMode(false)"
|
||||
@keyup.enter="updateSearchForTimestamp"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<timelines
|
||||
@@ -283,10 +301,10 @@ import MdIconOption from './components/IconSelection/IconSelectOption.vue';
|
||||
import Searchbar from './Searchbar.vue';
|
||||
import FileType from './mixins/FileType.js';
|
||||
import {NAVIGATION_STYLE} from './utils/consts';
|
||||
import {TRACE_ICONS, FILE_TYPES} from '@/decode.js';
|
||||
import {TRACE_ICONS} from '@/decode.js';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import {nanos_to_string} from './transform.js';
|
||||
import {nanos_to_string, getClosestTimestamp} from './transform.js';
|
||||
|
||||
export default {
|
||||
name: 'overlay',
|
||||
@@ -312,6 +330,8 @@ export default {
|
||||
cropIntent: null,
|
||||
TRACE_ICONS,
|
||||
search: false,
|
||||
searchInput: "",
|
||||
isSeekTimeInputMode: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -324,6 +344,7 @@ export default {
|
||||
},
|
||||
destroyed() {
|
||||
this.$store.commit('removeMergedTimeline', this.mergedTimeline);
|
||||
this.updateInputMode(false);
|
||||
},
|
||||
watch: {
|
||||
navigationStyle(style) {
|
||||
@@ -483,6 +504,26 @@ export default {
|
||||
toggleSearch() {
|
||||
this.search = !(this.search);
|
||||
},
|
||||
/**
|
||||
* determines whether left/right arrow keys should move cursor in input field
|
||||
* and upon click of input field, fills with current timestamp
|
||||
*/
|
||||
updateInputMode(isInputMode) {
|
||||
this.isSeekTimeInputMode = isInputMode;
|
||||
this.store.isInputMode = isInputMode;
|
||||
if (!isInputMode) {
|
||||
this.searchInput = "";
|
||||
} else {
|
||||
this.searchInput = this.seekTime;
|
||||
}
|
||||
},
|
||||
/** Navigates to closest timestamp in timeline to search input*/
|
||||
updateSearchForTimestamp() {
|
||||
const closestTimestamp = getClosestTimestamp(this.searchInput, this.mergedTimeline.timeline);
|
||||
this.$store.dispatch("updateTimelineTime", closestTimestamp);
|
||||
this.updateInputMode(false);
|
||||
},
|
||||
|
||||
emitBottomHeightUpdate() {
|
||||
if (this.$refs.bottomNav) {
|
||||
const newHeight = this.$refs.bottomNav.$el.clientHeight;
|
||||
@@ -855,6 +896,7 @@ export default {
|
||||
color: rgba(0,0,0,0.54);
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.minimized-timeline-content .minimized-timeline {
|
||||
@@ -880,6 +922,27 @@ export default {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.timestamp-search-input {
|
||||
outline: none;
|
||||
border-width: 0 0 1px;
|
||||
border-color: gray;
|
||||
font-family: inherit;
|
||||
color: #448aff;
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
letter-spacing: inherit;
|
||||
width: 125px;
|
||||
}
|
||||
|
||||
.timestamp-search-input:focus {
|
||||
border-color: #448aff;
|
||||
}
|
||||
|
||||
.timestamp-search-input.expanded {
|
||||
font-size: 14px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.drop-search:hover {
|
||||
background-color: #9af39f;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
|
||||
<div class="tabs">
|
||||
<div class="search-timestamp" v-if="isTimestampSearch()">
|
||||
<md-field class="search-input">
|
||||
<md-field md-inline class="search-input">
|
||||
<label>Enter timestamp</label>
|
||||
<md-input v-model="searchInput" @keyup.enter.native="updateSearchForTimestamp" />
|
||||
<md-input
|
||||
v-model="searchInput"
|
||||
v-on:focus="updateInputMode(true)"
|
||||
v-on:blur="updateInputMode(false)"
|
||||
@keyup.enter.native="updateSearchForTimestamp"
|
||||
/>
|
||||
</md-field>
|
||||
<md-button
|
||||
class="md-dense md-primary search-timestamp-button"
|
||||
@@ -37,7 +42,7 @@
|
||||
<th style="width: 80%">Description</th>
|
||||
</tr>
|
||||
|
||||
<tr v-for="item in filteredTransitionsAndErrors" :key="item">
|
||||
<tr v-for="item in filteredTransitionsAndErrors" :key="item.id">
|
||||
<td
|
||||
v-if="isTransition(item)"
|
||||
class="inline-time"
|
||||
@@ -58,9 +63,7 @@
|
||||
v-if="isTransition(item)"
|
||||
class="inline-transition"
|
||||
:style="{color: transitionTextColor(item.transition)}"
|
||||
@click="
|
||||
setCurrentTimestamp(transitionStart(transitionTags(item.id)))
|
||||
"
|
||||
@click="setCurrentTimestamp(transitionStart(transitionTags(item.id)))"
|
||||
>
|
||||
{{ transitionDesc(item.transition) }}
|
||||
</td>
|
||||
@@ -82,12 +85,16 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<md-field class="search-input">
|
||||
<md-field md-inline class="search-input">
|
||||
<label
|
||||
>Filter by transition or error message. Click to navigate to closest
|
||||
timestamp in active timeline.</label
|
||||
>
|
||||
<md-input v-model="searchInput"></md-input>
|
||||
<md-input
|
||||
v-model="searchInput"
|
||||
v-on:focus="updateInputMode(true)"
|
||||
v-on:blur="updateInputMode(false)"
|
||||
/>
|
||||
</md-field>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,9 +114,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { transitionMap, SEARCH_TYPE } from "./utils/consts";
|
||||
import { nanos_to_string, string_to_nanos } from "./transform";
|
||||
|
||||
const regExpTimestampSearch = new RegExp(/^\d+$/);
|
||||
import { nanos_to_string, getClosestTimestamp } from "./transform";
|
||||
|
||||
export default {
|
||||
name: "searchbar",
|
||||
@@ -174,8 +179,10 @@ export default {
|
||||
var times = tags.map((tag) => tag.timestamp);
|
||||
return times[times.length - 1];
|
||||
},
|
||||
/** Upon selecting a start/end tag in the dropdown;
|
||||
* navigates to that timestamp in the timeline */
|
||||
/**
|
||||
* Upon selecting a start/end tag in the dropdown;
|
||||
* navigates to that timestamp in the timeline
|
||||
*/
|
||||
setCurrentTimestamp(timestamp) {
|
||||
this.$store.dispatch("updateTimelineTime", timestamp);
|
||||
},
|
||||
@@ -195,17 +202,7 @@ export default {
|
||||
|
||||
/** Navigates to closest timestamp in timeline to search input*/
|
||||
updateSearchForTimestamp() {
|
||||
if (regExpTimestampSearch.test(this.searchInput)) {
|
||||
var roundedTimestamp = parseInt(this.searchInput);
|
||||
} else {
|
||||
var roundedTimestamp = string_to_nanos(this.searchInput);
|
||||
}
|
||||
var closestTimestamp = this.timeline.reduce(function (prev, curr) {
|
||||
return Math.abs(curr - roundedTimestamp) <
|
||||
Math.abs(prev - roundedTimestamp)
|
||||
? curr
|
||||
: prev;
|
||||
});
|
||||
const closestTimestamp = getClosestTimestamp(this.searchInput, this.timeline);
|
||||
this.setCurrentTimestamp(closestTimestamp);
|
||||
},
|
||||
|
||||
@@ -218,6 +215,11 @@ export default {
|
||||
isTransition(item) {
|
||||
return item.stacktrace === undefined;
|
||||
},
|
||||
|
||||
/** determines whether left/right arrow keys should move cursor in input field */
|
||||
updateInputMode(isInputMode) {
|
||||
this.store.isInputMode = isInputMode;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
filteredTransitionsAndErrors() {
|
||||
@@ -231,6 +233,9 @@ export default {
|
||||
});
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.updateInputMode(false);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
@@ -57,12 +57,13 @@
|
||||
/>
|
||||
<line
|
||||
v-for="error in errorPositions"
|
||||
:key="error"
|
||||
:x1="`${error}%`"
|
||||
:x2="`${error}%`"
|
||||
:key="error.pos"
|
||||
:x1="`${error.pos}%`"
|
||||
:x2="`${error.pos}%`"
|
||||
y1="0"
|
||||
y2="18px"
|
||||
class="error"
|
||||
@click="onErrorClick(error.ts)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -139,6 +140,7 @@ export default {
|
||||
}
|
||||
.error {
|
||||
stroke: rgb(255, 0, 0);
|
||||
stroke-width: 2px;
|
||||
stroke-width: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -45,7 +45,11 @@
|
||||
<md-checkbox v-if="hasTagsOrErrors" v-model="store.flickerTraceView">Flicker</md-checkbox>
|
||||
<md-field md-inline class="filter">
|
||||
<label>Filter...</label>
|
||||
<md-input v-model="hierarchyPropertyFilterString"></md-input>
|
||||
<md-input
|
||||
v-model="hierarchyPropertyFilterString"
|
||||
v-on:focus="updateInputMode(true)"
|
||||
v-on:blur="updateInputMode(false)"
|
||||
/>
|
||||
</md-field>
|
||||
</md-content>
|
||||
<div class="tree-view-wrapper">
|
||||
@@ -98,7 +102,11 @@
|
||||
</md-checkbox>
|
||||
<md-field md-inline class="filter">
|
||||
<label>Filter...</label>
|
||||
<md-input v-model="propertyFilterString"></md-input>
|
||||
<md-input
|
||||
v-model="propertyFilterString"
|
||||
v-on:focus="updateInputMode(true)"
|
||||
v-on:blur="updateInputMode(false)"
|
||||
/>
|
||||
</md-field>
|
||||
</md-content>
|
||||
<div class="properties-content">
|
||||
@@ -335,6 +343,11 @@ export default {
|
||||
isEntryTagMatch(entryItem) {
|
||||
return this.matchItems(this.presentTags, entryItem) || this.matchItems(this.presentErrors, entryItem);
|
||||
},
|
||||
|
||||
/** determines whether left/right arrow keys should move cursor in input field */
|
||||
updateInputMode(isInputMode) {
|
||||
this.store.isInputMode = isInputMode;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
||||
|
||||
@@ -169,7 +169,7 @@ export default {
|
||||
}
|
||||
|
||||
//sort transitions in ascending start position in order to handle overlap
|
||||
transitions.sort((a, b) => (a.startPos > b.startPos) ? 1: -1);
|
||||
transitions.sort((a, b) => (a.startPos > b.startPos) ? 1 : -1);
|
||||
|
||||
//compare each transition to the ones that came before
|
||||
for (let curr=0; curr<transitions.length; curr++) {
|
||||
@@ -194,7 +194,9 @@ export default {
|
||||
},
|
||||
errorPositions() {
|
||||
if (!this.flickerMode) return [];
|
||||
const errorPositions = this.errors.map(error => this.position(error.timestamp));
|
||||
const errorPositions = this.errors.map(
|
||||
error => ({ pos: this.position(error.timestamp), ts: error.timestamp })
|
||||
);
|
||||
return Object.freeze(errorPositions);
|
||||
},
|
||||
},
|
||||
@@ -342,6 +344,16 @@ export default {
|
||||
this.$store.dispatch('updateTimelineTime', timestamp);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the error click event.
|
||||
* When an error in the timeline is clicked this function will update the timeline
|
||||
* to match the error timestamp.
|
||||
* @param {number} errorTimestamp
|
||||
*/
|
||||
onErrorClick(errorTimestamp) {
|
||||
this.$store.dispatch('updateTimelineTime', errorTimestamp);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a block object that can be used by the timeline SVG to render
|
||||
* a transformed block that starts at `startTs` and ends at `endTs`.
|
||||
@@ -373,8 +385,11 @@ export default {
|
||||
const transitionColor = transitionMap.get(transitionType).color;
|
||||
var tooltip = `${transitionDesc}. Start: ${nanos_to_string(startTs)}. End: ${nanos_to_string(endTs)}.`;
|
||||
|
||||
if (layerId !== 0 && taskId === 0 && windowToken === "") tooltip += " SF only.";
|
||||
else if ((taskId !== 0 || windowToken !== "") && layerId === 0) tooltip += " WM only.";
|
||||
if (layerId !== 0 && taskId === 0 && windowToken === "") {
|
||||
tooltip += " SF only.";
|
||||
} else if ((taskId !== 0 || windowToken !== "") && layerId === 0) {
|
||||
tooltip += " WM only.";
|
||||
}
|
||||
|
||||
return new Transition(this.position(startTs), startTs, endTs, transitionWidth, transitionColor, overlap, tooltip);
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import {DiffType} from './utils/diff.js';
|
||||
import {regExpTimestampSearch} from './utils/consts';
|
||||
|
||||
// kind - a type used for categorization of different levels
|
||||
// name - name of the node
|
||||
@@ -400,5 +401,18 @@ function get_visible_chip() {
|
||||
return {short: 'V', long: 'visible', class: 'default'};
|
||||
}
|
||||
|
||||
// Returns closest timestamp in timeline based on search input*/
|
||||
function getClosestTimestamp(searchInput, timeline) {
|
||||
if (regExpTimestampSearch.test(searchInput)) {
|
||||
var roundedTimestamp = parseInt(searchInput);
|
||||
} else {
|
||||
var roundedTimestamp = string_to_nanos(searchInput);
|
||||
}
|
||||
const closestTimestamp = timeline.reduce((prev, curr) => {
|
||||
return Math.abs(curr-roundedTimestamp) < Math.abs(prev-roundedTimestamp) ? curr : prev;
|
||||
});
|
||||
return closestTimestamp;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export {transform, ObjectTransformer, nanos_to_string, string_to_nanos, get_visible_chip};
|
||||
export {transform, ObjectTransformer, nanos_to_string, string_to_nanos, get_visible_chip, getClosestTimestamp};
|
||||
|
||||
@@ -60,4 +60,7 @@ const transitionMap = new Map([
|
||||
[TransitionType.APP_PAIRS_EXIT, {desc: 'Exiting app pairs mode', color: 'rgb(45, 110, 32)'}],
|
||||
])
|
||||
|
||||
export { WebContentScriptMessageType, NAVIGATION_STYLE, SEARCH_TYPE, logLevel, transitionMap };
|
||||
//used to split timestamp search input by unit, to convert to nanoseconds
|
||||
const regExpTimestampSearch = new RegExp(/^\d+$/);
|
||||
|
||||
export { WebContentScriptMessageType, NAVIGATION_STYLE, SEARCH_TYPE, logLevel, transitionMap, regExpTimestampSearch };
|
||||
|
||||
Reference in New Issue
Block a user