Merge "perfetto-ui: Scrollable details panel"
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index dc4e411..85720b8 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -53,7 +53,6 @@
* {
box-sizing: border-box;
- overflow: hidden;
-webkit-tap-highlight-color: none;
touch-action: none;
}
@@ -97,6 +96,7 @@
grid-template-rows: auto auto 1fr;
grid-template-columns: auto 1fr;
color: #121212;
+ overflow: hidden;
}
button {
@@ -132,7 +132,8 @@
grid-area: page;
position: relative;
display: flex;
- flex-direction: column
+ flex-direction: column;
+ overflow: hidden;
}
.split-panel {
@@ -140,6 +141,7 @@
display: flex;
flex-flow: row;
position: relative;
+ overflow: hidden;
}
.video-panel {
@@ -346,6 +348,24 @@
overflow-y: auto;
flex: 1 1 auto;
will-change: transform; // Force layer creation.
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr;
+ grid-template-areas: "space";
+}
+
+.details-panel-container {
+ position: relative;
+ overflow-x: hidden;
+ overflow-y: auto;
+ flex: 1 1 auto;
+ // TODO(taylori): This causes the sticky header to flicker when scrolling.
+ // Is will-change necessary in the details panel?
+ // will-change: transform;
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr;
+ grid-template-areas: "space";
}
.pinned-panel-container {
@@ -355,6 +375,10 @@
overflow: visible;
box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
z-index: 2;
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr;
+ grid-template-areas: "space";
}
// In the scrolling case, since the canvas is overdrawn and continuously
@@ -362,14 +386,17 @@
// height equaling the total height of the content to prevent scrolling
// height from growing.
.scroll-limiter {
- overflow: hidden;
position: relative;
+ grid-area: space;
+ overflow: hidden;
}
canvas.main-canvas {
- top: 0px;
z-index: -1;
- position: absolute;
+}
+
+.panels {
+ grid-area: space;
}
.panel {
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss
index 5a0e44a..d5f4d5c 100644
--- a/ui/src/assets/details.scss
+++ b/ui/src/assets/details.scss
@@ -33,7 +33,7 @@
.tab {
font-family: 'Google Sans';
color: #3c4b5d;
- padding: 3px 10px 10px 10px;
+ padding: 3px 10px 0px 10px;
margin-top: 3px;
font-size: 13px;
border-radius: 5px 5px 0px 0px;
@@ -78,26 +78,25 @@
}
}
- .details-panel-container {
- .scroll-limiter {
- height: 100%;
- display: flex;
- flex-direction: column;
- .panel:last-child {
- flex-grow: 1;
- }
- }
- }
}
.details-panel {
- padding: 10px;
font-family: 'Google Sans';
color: #3c4b5d;
-
.details-panel-heading {
- font-size: 16px;
- padding-bottom: 5px;
+ padding: 10px 0 5px 0;
+ position: sticky;
+ top: 0px;
+ display: flex;
+ background: white;
+ h2 {
+ font-size: 16px;
+ font-family: 'Google Sans';
+ padding: 0 10px;
+ &.split {
+ width: 50%;
+ }
+ }
}
table {
@@ -109,6 +108,7 @@
max-width: 50%;
table-layout: fixed;
word-wrap: break-word;
+ padding: 10px;
tr:hover {
background-color: hsl(214, 22%, 90%);
}
@@ -141,12 +141,14 @@
padding-bottom: .5rem;
border-radius: .25rem;
margin-top: 12px;
+ margin-left: 10px;
}
.explanation {
font-size: 14px;
width: 35%;
margin-top: 10px;
+ padding-left: 10px;
}
.material-icons {
@@ -209,17 +211,16 @@
display: grid;
grid-template-rows: auto 1fr;
+ header {
+ position: sticky;
+ top: 0px;
+ z-index: 1;
+ }
+
header.stale {
color: grey;
}
- .scrolling-container {
- overflow-y: scroll;
- position: relative;
- width: 100%;
- background-color: #fefefe;
- border-bottom: 1px solid hsl(213, 22%, 75%);
-
.rows {
position: relative;
direction: ltr;
@@ -280,6 +281,5 @@
}
}
}
- }
}
}
diff --git a/ui/src/frontend/chrome_slice_panel.ts b/ui/src/frontend/chrome_slice_panel.ts
index 97ce65c..b43286e 100644
--- a/ui/src/frontend/chrome_slice_panel.ts
+++ b/ui/src/frontend/chrome_slice_panel.ts
@@ -25,7 +25,7 @@
if (sliceInfo.ts && sliceInfo.dur && sliceInfo.name) {
return m(
'.details-panel',
- m('.details-panel-heading', `Slice Details:`),
+ m('.details-panel-heading', m('h2', `Slice Details`)),
m(
'.details-table',
[m('table',
@@ -47,10 +47,11 @@
} else {
return m(
'.details-panel',
- m(
- '.details-panel-heading',
- `Slice Details:`,
- ));
+ m('.details-panel-heading',
+ m(
+ 'h2',
+ `Slice Details`,
+ )));
}
}
renderCanvas(_ctx: CanvasRenderingContext2D, _size: PanelSize) {}
diff --git a/ui/src/frontend/counter_panel.ts b/ui/src/frontend/counter_panel.ts
index 458a037..df207ff 100644
--- a/ui/src/frontend/counter_panel.ts
+++ b/ui/src/frontend/counter_panel.ts
@@ -29,7 +29,7 @@
counterInfo.duration !== undefined) {
return m(
'.details-panel',
- m('.details-panel-heading', `Counter Details:`),
+ m('.details-panel-heading', m('h2', `Counter Details`)),
m(
'.details-table',
[m('table',
@@ -50,7 +50,8 @@
));
} else {
return m(
- '.details-panel', m('.details-panel-heading', `Counter Details:`));
+ '.details-panel',
+ m('.details-panel-heading', m('h2', `Counter Details`)));
}
}
diff --git a/ui/src/frontend/heap_profile_panel.ts b/ui/src/frontend/heap_profile_panel.ts
index 938a80b..524bb40 100644
--- a/ui/src/frontend/heap_profile_panel.ts
+++ b/ui/src/frontend/heap_profile_panel.ts
@@ -37,7 +37,7 @@
this.pid = heapDumpInfo.pid;
return m(
'.details-panel',
- m('.details-panel-heading', `Heap Profile Details:`),
+ m('.details-panel-heading', m('h2', `Heap Profile Details`)),
m(
'.details-table',
[m('table',
@@ -76,7 +76,7 @@
} else {
return m(
'.details-panel',
- m('.details-panel-heading', `Heap Snapshot Details:`));
+ m('.details-panel-heading', m('h2', `Heap Profile Details`)));
}
}
diff --git a/ui/src/frontend/logs_panel.ts b/ui/src/frontend/logs_panel.ts
index 1f80869..2f4c485 100644
--- a/ui/src/frontend/logs_panel.ts
+++ b/ui/src/frontend/logs_panel.ts
@@ -59,8 +59,8 @@
}
oncreate({dom}: m.CVnodeDOM) {
- this.scrollContainer =
- assertExists(dom.querySelector('.scrolling-container') as HTMLElement);
+ this.scrollContainer = assertExists(
+ dom.parentElement!.parentElement!.parentElement as HTMLElement);
this.scrollContainer.addEventListener(
'scroll', this.onScroll.bind(this), {passive: true});
this.bounds = globals.trackDataStore.get(LogBoundsKey) as LogBounds;
@@ -143,8 +143,7 @@
'class': isStale ? 'stale' : '',
},
`Logs rows [${offset}, ${offset + count}] / ${total}`),
- m('.scrolling-container',
- m('.rows', {style: {height: `${total * ROW_H}px`}}, rows)));
+ m('.rows', {style: {height: `${total * ROW_H}px`}}, rows));
}
renderCanvas() {}
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 80f99c2..d040eea 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -140,10 +140,13 @@
m('.panel', panel, m('.debug-panel-border')) :
m('.panel', {key: panel.key}, panel);
- return m(
- '.scroll-limiter',
- m('canvas.main-canvas'),
- attrs.panels.map(renderPanel));
+ return [
+ m(
+ '.scroll-limiter',
+ m('canvas.main-canvas'),
+ ),
+ m('.panels', attrs.panels.map(renderPanel))
+ ];
}
onupdate(vnodeDom: m.CVnodeDOM<Attrs>) {
@@ -210,7 +213,7 @@
this.panelHeights = [];
this.totalPanelHeight = 0;
- const panels = dom.querySelectorAll('.panel');
+ const panels = dom.parentElement!.querySelectorAll('.panel');
assertTrue(panels.length === this.attrs.panels.length);
for (let i = 0; i < panels.length; i++) {
const height = panels[i].getBoundingClientRect().height;
diff --git a/ui/src/frontend/slice_panel.ts b/ui/src/frontend/slice_panel.ts
index 1437c37..7a37de7 100644
--- a/ui/src/frontend/slice_panel.ts
+++ b/ui/src/frontend/slice_panel.ts
@@ -19,7 +19,7 @@
import {translateState} from '../common/thread_state';
import {timeToCode, toNs} from '../common/time';
-import {globals} from './globals';
+import {globals, SliceDetails, ThreadDesc} from './globals';
import {Panel, PanelSize} from './panel';
import {scrollToTrackAndTs} from './scroll_helper';
@@ -29,47 +29,47 @@
if (sliceInfo.utid === undefined) return;
const threadInfo = globals.threads.get(sliceInfo.utid);
- if (threadInfo && sliceInfo.ts !== undefined &&
- sliceInfo.dur !== undefined) {
- return m(
- '.details-panel',
- m('.details-panel-heading', `Slice Details:`),
- m(
- '.details-table',
- [m('table',
- [
- m('tr',
- m('th', `Process`),
- m('td', `${threadInfo.procName} [${threadInfo.pid}]`)),
- m('tr',
- m('th', `Thread`),
- m('td',
- `${threadInfo.threadName} [${threadInfo.tid}]`,
- m('i.material-icons',
- {
- onclick: () => this.goToThread(),
- title: 'Go to thread'
- },
- 'call_made'))),
- m('tr',
- m('th', `Start time`),
- m('td', `${timeToCode(sliceInfo.ts)}`)),
- m('tr',
- m('th', `Duration`),
- m('td', `${timeToCode(sliceInfo.dur)}`)),
- m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
- m('tr',
- m('th', `End State`),
- m('td', `${translateState(sliceInfo.endState)}`))
- ])],
- ));
+ return m(
+ '.details-panel',
+ m('.details-panel-heading',
+ m('h2.split', `Slice Details`),
+ (sliceInfo.wakeupTs && sliceInfo.wakerUtid) ?
+ m('h2.split', 'Scheduling Latency') :
+ ''),
+ this.getDetails(sliceInfo, threadInfo));
+ }
+
+ getDetails(sliceInfo: SliceDetails, threadInfo: ThreadDesc|undefined) {
+ if (!threadInfo || sliceInfo.ts === undefined ||
+ sliceInfo.dur === undefined) {
+ return null;
} else {
return m(
- '.details-panel',
- m(
- '.details-panel-heading',
- `Slice Details:`,
- ));
+ '.details-table',
+ m('table',
+ [
+ m('tr',
+ m('th', `Process`),
+ m('td', `${threadInfo.procName} [${threadInfo.pid}]`)),
+ m('tr',
+ m('th', `Thread`),
+ m('td',
+ `${threadInfo.threadName} [${threadInfo.tid}]`,
+ m('i.material-icons',
+ {onclick: () => this.goToThread(), title: 'Go to thread'},
+ 'call_made'))),
+ m('tr',
+ m('th', `Start time`),
+ m('td', `${timeToCode(sliceInfo.ts)}`)),
+ m('tr',
+ m('th', `Duration`),
+ m('td', `${timeToCode(sliceInfo.dur)}`)),
+ m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
+ m('tr',
+ m('th', `End State`),
+ m('td', `${translateState(sliceInfo.endState)}`))
+ ]),
+ );
}
}
@@ -113,13 +113,8 @@
// Show expanded details on the scheduling of the currently selected slice.
if (details.wakeupTs && details.wakerUtid !== undefined) {
const threadInfo = globals.threads.get(details.wakerUtid);
- // Draw separation line.
- ctx.fillStyle = '#3c4b5d';
- ctx.fillRect(size.width / 2, 10, 1, size.height - 10);
- ctx.font = '16px Google Sans';
- ctx.fillText('Scheduling Latency:', size.width / 2 + 30, 30);
// Draw diamond and vertical line.
- const startDraw = {x: size.width / 2 + 30, y: 52};
+ const startDraw = {x: size.width / 2 + 20, y: 52};
ctx.beginPath();
ctx.moveTo(startDraw.x, startDraw.y + 28);
ctx.fillStyle = 'black';
diff --git a/ui/src/frontend/thread_state_panel.ts b/ui/src/frontend/thread_state_panel.ts
index 3df1884..7ad6127 100644
--- a/ui/src/frontend/thread_state_panel.ts
+++ b/ui/src/frontend/thread_state_panel.ts
@@ -36,7 +36,7 @@
if (threadInfo) {
return m(
'.details-panel',
- m('.details-panel-heading', 'Thread State'),
+ m('.details-panel-heading', m('h2', 'Thread State')),
m('.details-table', [m('table', [
m('tr',
m('th', `Start time`),
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 22e5b9a..c5fc8d7 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -153,7 +153,7 @@
view({attrs}: m.CVnode<TrackContentAttrs>) {
return m('.track-content', {
onmousemove: (e: MouseEvent) => {
- attrs.track.onMouseMove({x: e.layerX, y: e.layerY});
+ attrs.track.onMouseMove({x: e.layerX - TRACK_SHELL_WIDTH, y: e.layerY});
globals.rafScheduler.scheduleRedraw();
},
onmouseout: () => {
@@ -165,15 +165,16 @@
// track.
if (e.shiftKey) return;
// If the click is outside of the current time range, clear it.
- const clickTime =
- globals.frontendLocalState.timeScale.pxToTime(e.layerX);
+ const clickTime = globals.frontendLocalState.timeScale.pxToTime(
+ e.layerX - TRACK_SHELL_WIDTH);
const start = globals.frontendLocalState.selectedTimeRange.startSec;
const end = globals.frontendLocalState.selectedTimeRange.endSec;
if (start !== undefined && end !== undefined &&
(clickTime < start || clickTime > end)) {
globals.frontendLocalState.removeTimeRange();
e.stopPropagation();
- } else if (attrs.track.onMouseClick({x: e.layerX, y: e.layerY})) {
+ } else if (attrs.track.onMouseClick(
+ {x: e.layerX - TRACK_SHELL_WIDTH, y: e.layerY})) {
e.stopPropagation();
}
globals.rafScheduler.scheduleRedraw();