perfetto-ui: Make track selection work with pinned tracks You can now select pinned tracks just as you select regular tracks. This CL also changes the logic for drawing the box selection, previously we were figuring out the snapping logic based on where the mouse was, separately from the logic that figures out which tracks are in the region. This was unnecessary extra work since we can instead use the selected tracks that have already been calculated & find the min and max Y values to know where to draw the box. Change-Id: I0509a0f39683e3a4cda506f4e5f2fb550bb433f5
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts index b2b34ba..9ba8637 100644 --- a/ui/src/frontend/panel_container.ts +++ b/ui/src/frontend/panel_container.ts
@@ -45,6 +45,7 @@ } interface PanelPosition { + id: string; height: number; width: number; x: number; @@ -94,7 +95,8 @@ const pos = this.panelPositions[i]; const realPosX = pos.x - TRACK_SHELL_WIDTH; if (realPosX + pos.width >= minX && realPosX <= maxX && - pos.y + pos.height >= minY && pos.y <= maxY) { + pos.y + pos.height >= minY && pos.y <= maxY && + this.attrs.panels[i].attrs.selectable) { panels.push(this.attrs.panels[i]); } } @@ -111,6 +113,20 @@ globals.frontendLocalState.areaY.end === undefined) { return; } + + // Only get panels from the current panel container if the selection began + // in this container. + const panelContainerTop = this.panelPositions[0].y; + const panelContainerBottom = + this.panelPositions[this.panelPositions.length - 1].y + + this.panelPositions[this.panelPositions.length - 1].height; + if (globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT < + panelContainerTop || + globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT > + panelContainerBottom) { + return; + } + // The Y value is given from the top of the pan and zoom region, we want it // from the top of the panel container. The parent offset corrects that. const panels = this.getPanelsInRegion( @@ -287,8 +303,10 @@ assertTrue(panels.length === this.attrs.panels.length); for (let i = 0; i < panels.length; i++) { const rect = panels[i].getBoundingClientRect() as DOMRect; + const id = this.attrs.panels[i].attrs.id || + this.attrs.panels[i].attrs.trackGroupId; this.panelPositions[i] = - {height: rect.height, width: rect.width, x: rect.x, y: rect.y}; + {id, height: rect.height, width: rect.width, x: rect.x, y: rect.y}; this.totalPanelHeight += rect.height; } @@ -306,9 +324,7 @@ const canvasYStart = Math.floor(this.scrollTop - this.getCanvasOverdrawHeightPerSide()); - if (this.attrs.kind === 'TRACKS') { - this.handleAreaSelection(); - } + this.handleAreaSelection(); let panelYStart = 0; const panels = assertExists(this.attrs).panels; @@ -353,7 +369,6 @@ // the whole canvas rather than per panel. private drawTopLayerOnCanvas() { if (!this.ctx) return; - if (this.attrs.kind !== 'TRACKS') return; const selection = globals.frontendLocalState.selectedArea; const area = selection.area; if (area === undefined || @@ -362,65 +377,49 @@ !globals.frontendLocalState.selectingArea) { return; } - if (this.panelPositions.length === 0) return; + if (this.panelPositions.length === 0 || area.tracks.length === 0) return; + + // Find the minY and maxY of the selected tracks in this panel container. + const panelContainerTop = this.panelPositions[0].y; + const panelContainerBottom = + this.panelPositions[this.panelPositions.length - 1].y + + this.panelPositions[this.panelPositions.length - 1].height; + let selectedTracksMinY = panelContainerBottom; + let selectedTracksMaxY = panelContainerTop; + let trackFromCurrentContainerSelected = false; + for (let i = 0; i < this.panelPositions.length; i++) { + if (area.tracks.includes(this.panelPositions[i].id)) { + trackFromCurrentContainerSelected = true; + selectedTracksMinY = + Math.min(selectedTracksMinY, this.panelPositions[i].y); + selectedTracksMaxY = Math.max( + selectedTracksMaxY, + this.panelPositions[i].y + this.panelPositions[i].height); + } + } + + // No box should be drawn if there are no selected tracks in the current + // container. + if (!trackFromCurrentContainerSelected) { + return; + } + + const startX = globals.frontendLocalState.timeScale.timeToPx(area.startSec); + const endX = globals.frontendLocalState.timeScale.timeToPx(area.endSec); + // To align with where to draw on the canvas subtract the first panel Y. + selectedTracksMinY -= panelContainerTop; + selectedTracksMaxY -= panelContainerTop; this.ctx.save(); this.ctx.strokeStyle = 'rgba(52,69,150)'; this.ctx.lineWidth = 1; const canvasYStart = Math.floor(this.scrollTop - this.getCanvasOverdrawHeightPerSide()); this.ctx.translate(TRACK_SHELL_WIDTH, -canvasYStart); - - const startX = globals.frontendLocalState.timeScale.timeToPx(area.startSec); - const endX = globals.frontendLocalState.timeScale.timeToPx(area.endSec); - const firstPanelY = this.panelPositions[0].y; - const boxTop = Math.min( - globals.frontendLocalState.areaY.start, - globals.frontendLocalState.areaY.end) + - TOPBAR_HEIGHT; - const boxBottom = Math.max( - globals.frontendLocalState.areaY.start, - globals.frontendLocalState.areaY.end) + - TOPBAR_HEIGHT; - - let snapStartY = -1; - let snapEndY = -1; - for (let i = 0; i < this.panelPositions.length; i++) { - const y = this.panelPositions[i].y; - // If the box top is within this panel set the snapStart to the top of - // the panel. - if (y <= boxTop && y + this.panelPositions[i].height >= boxTop) { - snapStartY = y; - } - // If the box bottom is within this panel set the snapBottom to the bottom - // of the panel. - if (y <= boxBottom && y + this.panelPositions[i].height >= boxBottom) { - snapEndY = y + this.panelPositions[i].height; - break; - } - } - - // If the y selection is outside all panels, do not draw a box. - if (snapStartY === -1 && snapEndY === -1) { - this.ctx.restore(); - return; - } - - // If the y selection starts above all panels, snap to the first panel. - if (snapStartY === -1) { - snapStartY = firstPanelY; - } - // If the y selection ends after all panels, snap to the bottom of the - // last panel. - if (snapEndY === -1) { - snapEndY = this.totalPanelHeight; - } - - // The areaY values are given from the top of the pan and zoom handler. - // To align with the current panel container subtract the first panel Y. - snapStartY -= firstPanelY; - snapEndY -= firstPanelY; this.ctx.strokeRect( - startX, snapStartY, endX - startX, snapEndY - snapStartY); + startX, + selectedTracksMaxY, + endX - startX, + selectedTracksMinY - selectedTracksMaxY); this.ctx.restore(); }
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts index 816fccf..1b5c6cf 100644 --- a/ui/src/frontend/track_group_panel.ts +++ b/ui/src/frontend/track_group_panel.ts
@@ -37,6 +37,7 @@ interface Attrs { trackGroupId: string; + selectable: boolean; } export class TrackGroupPanel extends Panel<Attrs> {
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts index 6c9d969..65041e3 100644 --- a/ui/src/frontend/track_panel.ts +++ b/ui/src/frontend/track_panel.ts
@@ -239,6 +239,7 @@ interface TrackPanelAttrs { id: string; + selectable: boolean; } export class TrackPanel extends Panel<TrackPanelAttrs> {
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts index c64a852..20a94e5 100644 --- a/ui/src/frontend/viewer_page.ts +++ b/ui/src/frontend/viewer_page.ts
@@ -245,19 +245,21 @@ } view() { - const scrollingPanels: AnyAttrsVnode[] = - globals.state.scrollingTracks.map(id => m(TrackPanel, {key: id, id})); + const scrollingPanels: AnyAttrsVnode[] = globals.state.scrollingTracks.map( + id => m(TrackPanel, {key: id, id, selectable: true})); for (const group of Object.values(globals.state.trackGroups)) { scrollingPanels.push(m(TrackGroupPanel, { trackGroupId: group.id, key: `trackgroup-${group.id}`, + selectable: true, })); if (group.collapsed) continue; for (const trackId of group.tracks) { scrollingPanels.push(m(TrackPanel, { key: `track-${group.id}-${trackId}`, id: trackId, + selectable: true, })); } } @@ -286,7 +288,7 @@ m(NotesPanel, {key: 'notes'}), m(TickmarkPanel, {key: 'searchTickmarks'}), ...globals.state.pinnedTracks.map( - id => m(TrackPanel, {key: id, id})), + id => m(TrackPanel, {key: id, id, selectable: true})), ], kind: 'OVERVIEW', })),