Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render annotation flags over track group headers #97

Open
wants to merge 3 commits into
base: reuse_timeline
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 44 additions & 11 deletions ui/src/frontend/panel_container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,12 @@ export class PanelContainer implements m.ClassComponent<Attrs> {
let totalOnCanvas = 0;
const flowEventsRendererArgs =
new FlowEventsRendererArgs(this.parentWidth, this.canvasHeight);

const stickyPanels: {
panel: AnyAttrsVnode & m.Vnode<{ trackGroupId: string }>;
yOffset: number;
}[] = [];

for (let i = 0; i < this.panelInfos.length; i++) {
const panel = this.panelInfos[i].vnode;
const panelHeight = this.panelInfos[i].height;
Expand All @@ -405,23 +411,50 @@ export class PanelContainer implements m.ClassComponent<Attrs> {

if (!this.overlapsCanvas(yStartOnCanvas, yStartOnCanvas + panelHeight)) {
panelYStart += panelHeight;

// If it's a sticky header for a track-group that is expanded, it
// will be stuck showing as long as any of its member tracks is
// in view, so keep track of it to redraw it later when we find
// overlapping panels
if (panel.attrs.trackGroupId !== undefined) {
const trackGroup =
globals.state.trackGroups[panel.attrs.trackGroupId];
if (!trackGroup.collapsed) {
stickyPanels.push({panel, yOffset: yStartOnCanvas});
}
}

continue;
}

totalOnCanvas++;

this.ctx.save();
this.ctx.translate(0, yStartOnCanvas);
const clipRect = new Path2D();
const size = {width: this.parentWidth, height: panelHeight};
clipRect.rect(0, 0, size.width, size.height);
this.ctx.clip(clipRect);
const beforeRender = debugNow();
panel.state.renderCanvas(this.ctx, size, panel);
this.updatePanelStats(
i, panel.state, debugNow() - beforeRender, this.ctx, size);
this.ctx.restore();
const draw = (
ctx: CanvasRenderingContext2D,
panel: AnyAttrsVnode,
yOffset: number): void => {
ctx.save();
ctx.translate(0, yOffset);
const clipRect = new Path2D();
const size = {width: this.parentWidth, height: panelHeight};
clipRect.rect(0, 0, size.width, size.height);
ctx.clip(clipRect);
const beforeRender = debugNow();
panel.state.renderCanvas(this.ctx, size, panel);
this.updatePanelStats(
i, panel.state, debugNow() - beforeRender, ctx, size);
ctx.restore();
};

panelYStart += panelHeight;

// Draw all of the sticky panels encountered so far
for (const sticky of stickyPanels) {
totalOnCanvas++;
draw(this.ctx, sticky.panel, sticky.yOffset);
}

draw(this.ctx, panel, yStartOnCanvas);
}

this.drawTopLayerOnCanvas();
Expand Down
88 changes: 65 additions & 23 deletions ui/src/frontend/track_group_panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
} from './icons';
import {Panel, PanelSize} from './panel';
import {Track} from './track';
import {TrackButton, TrackContent} from './track_panel';
import {TrackButton, TrackContent, TrackContentAttrs} from './track_panel';
import {trackRegistry} from './track_registry';
import {
drawVerticalLineAtTime,
Expand All @@ -55,6 +55,7 @@ export class TrackGroupPanel extends Panel<Attrs> {
private summaryTrack: Track|undefined;
private dragging = false;
private dropping: 'before'|'after'|undefined = undefined;
private privateCtx: CanvasRenderingContext2D | null = null;

// Caches the last state.trackGroups[this.trackGroupId].
// This is to deal with track group deletion. See comments
Expand Down Expand Up @@ -172,6 +173,21 @@ export class TrackGroupPanel extends Panel<Attrs> {
const titleStyling = indent(depth(trackGroup));
const dragClass = this.dragging ? `drag` : '';
const dropClass = this.dropping ? `drop-${this.dropping}` : '';

const trackContentAttrs: TrackContentAttrs | undefined = this.summaryTrack ?
{track: this.summaryTrack} :
undefined;

// Need a canvas to draw on because our positioning is relative,
// not static like the shared canvas, and we can stick to the top
// of the track scroll area
if (!collapsed && trackContentAttrs) {
trackContentAttrs.tagname = 'canvas';
trackContentAttrs.attrs = {
height: this.summaryTrack!.getHeight(),
};
}

return m(
`.track-group-panel[collapsed=${collapsed}]`,
{
Expand Down Expand Up @@ -232,9 +248,7 @@ export class TrackGroupPanel extends Panel<Attrs> {

this.summaryTrack ?
m(TrackContent,
{
track: this.summaryTrack,
},
trackContentAttrs!,
(!this.trackGroupState.collapsed && child !== null) ?
m('span', child) :
null) :
Expand Down Expand Up @@ -358,6 +372,17 @@ export class TrackGroupPanel extends Panel<Attrs> {
// TODO(andrewbb): move this to css_constants
this.backgroundColor =
getComputedStyle(dom).getPropertyValue('--collapsed-background');
const canvas = dom.querySelector<HTMLCanvasElement>('canvas.track-content');
if (!canvas) {
this.privateCtx = null;
} else {
this.privateCtx = canvas.getContext('2d');
const canvasWidth = dom.clientWidth - this.shellWidth;
if (canvas.width !== canvasWidth) {
canvas.width = canvasWidth;
}
}

if (this.summaryTrack !== undefined) {
this.summaryTrack.onFullRedraw();
}
Expand Down Expand Up @@ -449,35 +474,51 @@ export class TrackGroupPanel extends Panel<Attrs> {
}

renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
if (this.privateCtx) {
// Render on our own canvas. It's already offset by the
// track shell width, so offset the rendering's translation
this.privateCtx.save();
this.privateCtx.translate(-this.shellWidth, 0);
this.doRenderOn(this.privateCtx, size);
this.privateCtx.restore();
} else {
// Shared canvas
this.doRenderOn(this.privateCtx ?? ctx, size);
}
}

protected doRenderOn(ctx: CanvasRenderingContext2D, size: PanelSize) {
const collapsed = this.trackGroupState.collapsed;

ctx.fillStyle = this.backgroundColor;
ctx.fillRect(0, 0, size.width, size.height);

if (!collapsed) return;
// Only draw the grid lines, vsync highlights, and summary
// (like a track) when collapsed.
if (collapsed) {
// If we have vsync data, render columns under the track group
const vsync = getActiveVsyncData();
if (vsync) {
ctx.save();
ctx.translate(this.shellWidth, 0);
renderVsyncColumns(ctx, size.height, vsync);
ctx.restore();
}

drawGridLines(
ctx,
size.width,
size.height);

// If we have vsync data, render columns under the track group
const vsync = getActiveVsyncData();
if (vsync) {
ctx.save();
ctx.translate(this.shellWidth, 0);
renderVsyncColumns(ctx, size.height, vsync);
if (this.summaryTrack) {
this.summaryTrack.render(ctx);
}
ctx.restore();
}

drawGridLines(
ctx,
size.width,
size.height);

ctx.save();
ctx.translate(this.shellWidth, 0);
if (this.summaryTrack) {
this.summaryTrack.render(ctx);
this.highlightIfTrackSelected(ctx, size);
}
ctx.restore();

this.highlightIfTrackSelected(ctx, size);

const {visibleTimeScale} = globals.frontendLocalState;
// Draw vertical line when hovering on the notes panel.
Expand All @@ -498,7 +539,7 @@ export class TrackGroupPanel extends Panel<Attrs> {
`#344596`);
}

if (globals.state.currentSelection !== null) {
if (collapsed && globals.state.currentSelection !== null) {
if (globals.state.currentSelection.kind === 'SLICE' &&
globals.sliceDetails.wakeupTs !== undefined) {
drawVerticalLineAtTime(
Expand All @@ -509,6 +550,7 @@ export class TrackGroupPanel extends Panel<Attrs> {
getCssStr('--main-foreground-color'));
}
}

// All marked areas should have semi-transparent vertical lines
// marking the start and end.
for (const note of Object.values(globals.state.notes)) {
Expand Down
14 changes: 12 additions & 2 deletions ui/src/frontend/track_panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,16 @@ class TrackShell implements m.ClassComponent<TrackShellAttrs> {
}
}

export interface TrackContentAttrs { track: Track; }
export interface TrackContentAttrs {
track: Track;
/** Optional tag name to instantiate instead of the default `<div>`. */
tagname?: keyof HTMLElementTagNameMap;
/**
* Optional attributes to add to the track content element, especially
* when using the custom `tagname`.
*/
attrs?: m.Attributes;
}
export class TrackContent implements m.ClassComponent<TrackContentAttrs> {
private mouseDownX?: number;
private mouseDownY?: number;
Expand All @@ -317,8 +326,9 @@ export class TrackContent implements m.ClassComponent<TrackContentAttrs> {
view(node: m.CVnode<TrackContentAttrs>) {
const attrs = node.attrs;
return m(
'.track-content',
`${attrs.tagname ?? ''}.track-content`,
{
...(attrs.attrs ?? {}),
onmousemove: (e: PerfettoMouseEvent) => {
attrs.track.onMouseMove(
{x: e.layerX - TRACK_SHELL_WIDTH, y: e.layerY});
Expand Down