Skip to content

Commit 3eb2f86

Browse files
AlinaVarkkiDevtools-frontend LUCI CQ
authored andcommitted
[RPP] Fix the Entry coordinate missing when the trace is loaded
The arrow was broken when the connected even was off screen on load. Example in the bug. While I was at it, I also refactored the function that rerenders the Overlay. Bug: 395567067 Change-Id: I851978cde04658269edd60c9bf0661c8af123e36 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6429296 Reviewed-by: Jack Franklin <[email protected]> Auto-Submit: Alina Varkki <[email protected]> Commit-Queue: Jack Franklin <[email protected]>
1 parent 59e1ee7 commit 3eb2f86

File tree

2 files changed

+119
-93
lines changed

2 files changed

+119
-93
lines changed

front_end/panels/timeline/overlays/OverlaysImpl.ts

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,30 +1148,32 @@ export class Overlays extends EventTarget {
11481148
const entryToWrapper = component.entryToWrapper();
11491149

11501150
if (entryTo && entryToWrapper) {
1151-
let toEntryX = 0;
1151+
let toEntryX = this.xPixelForEventStartOnChart(entryTo) ?? 0;
11521152
// If the 'to' entry is visible, set the entry Y as an arrow coordinate to point to. If not, get the canvas edge coordate to point the arrow to.
11531153
let toEntryY = this.#yCoordinateForNotVisibleEntry(entryTo);
1154-
1155-
if (entryToVisibility) {
1156-
const toEntryParams = this.#positionEntryBorderOutlineType(entryTo, entryToWrapper);
1157-
1158-
if (toEntryParams) {
1159-
const toEntryHeight = toEntryParams?.entryHeight;
1160-
const toEntryWidth = toEntryParams?.entryWidth;
1161-
const toCutOffHeight = toEntryParams?.cutOffHeight;
1162-
toEntryX = toEntryParams?.x;
1163-
toEntryY = toEntryParams?.y;
1164-
1165-
component.toEntryCoordinateAndDimensions = {
1166-
x: toEntryX,
1167-
y: toEntryY,
1168-
length: toEntryWidth,
1169-
height: toEntryHeight - toCutOffHeight,
1170-
};
1171-
} else {
1172-
// Something went if the entry is visible and we cannot get its' parameters.
1173-
return;
1174-
}
1154+
const toEntryParams = this.#positionEntryBorderOutlineType(entryTo, entryToWrapper);
1155+
1156+
if (toEntryParams) {
1157+
const toEntryHeight = toEntryParams?.entryHeight;
1158+
const toEntryWidth = toEntryParams?.entryWidth;
1159+
const toCutOffHeight = toEntryParams?.cutOffHeight;
1160+
toEntryX = toEntryParams?.x;
1161+
toEntryY = toEntryParams?.y;
1162+
1163+
component.toEntryCoordinateAndDimensions = {
1164+
x: toEntryX,
1165+
y: toEntryY,
1166+
length: toEntryWidth,
1167+
height: toEntryHeight - toCutOffHeight,
1168+
};
1169+
} else {
1170+
// if the entry exists and we cannot get its' parameters, it is probably loaded and is off screen.
1171+
// In this case, assign the coordinates so we can draw the arrow in the right direction.
1172+
component.toEntryCoordinateAndDimensions = {
1173+
x: toEntryX,
1174+
y: toEntryY,
1175+
};
1176+
return;
11751177
}
11761178

11771179
} else {

front_end/panels/timeline/overlays/components/EntriesLinkOverlay.ts

Lines changed: 95 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ export class EntriesLinkOverlay extends HTMLElement {
4343
#connector: SVGLineElement|null = null;
4444
#entryFromWrapper: HTMLElement|null = null;
4545
#entryToWrapper: HTMLElement|null = null;
46-
#entryFromConnector: SVGCircleElement|null = null;
47-
#entryToConnector: SVGCircleElement|null = null;
46+
#entryFromCirleConnector: SVGCircleElement|null = null;
47+
#entryToCircleConnector: SVGCircleElement|null = null;
4848
#entryFromVisible = true;
4949
#entryToVisible = true;
5050
#canvasRect: DOMRect|null = null;
@@ -73,8 +73,8 @@ export class EntriesLinkOverlay extends HTMLElement {
7373
this.#connector = this.#connectorLineContainer?.querySelector('line') ?? null;
7474
this.#entryFromWrapper = this.#shadow.querySelector('.from-highlight-wrapper') ?? null;
7575
this.#entryToWrapper = this.#shadow.querySelector('.to-highlight-wrapper') ?? null;
76-
this.#entryFromConnector = this.#connectorLineContainer?.querySelector('.entryFromConnector') ?? null;
77-
this.#entryToConnector = this.#connectorLineContainer?.querySelector('.entryToConnector') ?? null;
76+
this.#entryFromCirleConnector = this.#connectorLineContainer?.querySelector('.entryFromConnector') ?? null;
77+
this.#entryToCircleConnector = this.#connectorLineContainer?.querySelector('.entryToConnector') ?? null;
7878
this.#linkState = linkCreationNotStartedState;
7979
this.#render();
8080
}
@@ -117,13 +117,13 @@ export class EntriesLinkOverlay extends HTMLElement {
117117
this.#coordinateFrom = {x: fromEntryParams.x, y: fromEntryParams.y};
118118
this.#fromEntryDimensions = {width: fromEntryParams.length, height: fromEntryParams.height};
119119
this.#updateCreateLinkBox();
120-
this.#redrawConnectionArrow();
120+
this.#redrawAllEntriesLinkParts();
121121
}
122122

123123
set entriesVisibility(entriesVisibility: {fromEntryVisibility: boolean, toEntryVisibility: boolean}) {
124124
this.#entryFromVisible = entriesVisibility.fromEntryVisibility;
125125
this.#entryToVisible = entriesVisibility.toEntryVisibility;
126-
this.#redrawConnectionArrow();
126+
this.#redrawAllEntriesLinkParts();
127127
}
128128

129129
// The arrow might be pointing either to an entry or an empty space.
@@ -137,7 +137,7 @@ export class EntriesLinkOverlay extends HTMLElement {
137137
}
138138

139139
this.#updateCreateLinkBox();
140-
this.#redrawConnectionArrow();
140+
this.#redrawAllEntriesLinkParts();
141141
}
142142

143143
set fromEntryIsSource(x: boolean) {
@@ -156,97 +156,121 @@ export class EntriesLinkOverlay extends HTMLElement {
156156
this.#render();
157157
}
158158

159-
#redrawConnectionArrow(): void {
160-
if (!this.#connector || !this.#entryFromWrapper || !this.#entryToWrapper || !this.#entryFromConnector ||
161-
!this.#entryToConnector) {
162-
console.error('`connector` element is missing.');
159+
/*
160+
Redraw all parts of the EntriesLink overlay
161+
_________
162+
|__entry__|o\ <-- 'from 'entry wrapper and the circle connector next to it
163+
\
164+
\ <-- Arrow Connector
165+
\ ________________
166+
➘ o|_____entry______| <-- 'to' entry wrapper and the circle connector next to it
167+
*/
168+
#redrawAllEntriesLinkParts(): void {
169+
if (!this.#connector || !this.#entryFromWrapper || !this.#entryToWrapper || !this.#entryFromCirleConnector ||
170+
!this.#entryToCircleConnector) {
171+
console.error('one of the required Entries Link elements is missing.');
163172
return;
164173
}
165174

166175
if (this.#linkState === Trace.Types.File.EntriesLinkState.CREATION_NOT_STARTED) {
167-
this.#entryFromConnector.setAttribute('visibility', 'hidden');
168-
this.#entryToConnector.setAttribute('visibility', 'hidden');
176+
this.#entryFromCirleConnector.setAttribute('visibility', 'hidden');
177+
this.#entryToCircleConnector.setAttribute('visibility', 'hidden');
178+
this.#connector.style.display = 'none';
169179
return;
170180
}
171181

182+
this.#setEntriesWrappersVisibility();
183+
this.#setConnectorCirclesVisibility();
184+
this.#setArrowConnectorStyle();
185+
this.#positionConnectorLineAndCircles();
186+
187+
this.#render();
188+
}
189+
190+
// Only draw the entry wrapper if that entry is visible
191+
#setEntriesWrappersVisibility(): void {
192+
if (!this.#entryFromWrapper || !this.#entryToWrapper) {
193+
return;
194+
}
195+
this.#entryFromWrapper.style.visibility = this.#entryFromVisible ? 'visible' : 'hidden';
196+
this.#entryToWrapper.style.visibility = this.#entryToVisible ? 'visible' : 'hidden';
197+
}
198+
199+
// Draw the entry connector circles:
200+
// - The entry the arrow is connecting to is the connection source
201+
// - That entry currently is visible
202+
// - There is enough space for the connector circle
203+
#setConnectorCirclesVisibility(): void {
204+
if (!this.#toEntryDimensions || !this.#entryFromCirleConnector || !this.#entryToCircleConnector) {
205+
return;
206+
}
172207
// If the user is zoomed out, the connector circles can be as large as the
173208
// event itself. So if the rectangle for this entry is too small, we
174209
// don't draw the circles.
175210
const minWidthToDrawConnectorCircles = 8;
176-
177-
// We do not draw the connectors if the entry is not visible, or if the
178-
// entry we are connecting to isn't the actual source entry.
179-
// We also don't draw them if an entry is completely hidden, in which case
180-
// we aren't drawing the arrows, so it doesn't make sense to draw the
181-
// connectors.
182211
const drawFromEntryConnectorCircle = this.#entryFromVisible && !this.#arrowHidden && this.#fromEntryIsSource &&
183212
this.#fromEntryDimensions.width >= minWidthToDrawConnectorCircles;
184-
185-
const widthOfToEntry = this.#toEntryDimensions?.width ?? 0;
186213
const drawToEntryConnectorCircle = !this.#arrowHidden && this.#entryToVisible && this.#toEntryIsSource &&
187-
widthOfToEntry >= minWidthToDrawConnectorCircles && !this.#arrowHidden;
214+
this.#toEntryDimensions?.width >= minWidthToDrawConnectorCircles && !this.#arrowHidden;
215+
216+
this.#entryFromCirleConnector.setAttribute('visibility', drawFromEntryConnectorCircle ? 'visible' : 'hidden');
217+
this.#entryToCircleConnector.setAttribute('visibility', drawToEntryConnectorCircle ? 'visible' : 'hidden');
218+
}
219+
220+
#setArrowConnectorStyle(): void {
221+
if (!this.#connector) {
222+
return;
223+
}
224+
225+
// If neither entry is visible, do not display the connector
226+
this.#connector.style.display = (this.#entryFromVisible || this.#entryToVisible) ? 'block' : 'none';
227+
this.#connector.setAttribute('stroke-width', '2');
228+
229+
const arrowColor = ThemeSupport.ThemeSupport.instance().getComputedValue('--color-text-primary');
230+
231+
// Use a solid stroke if the 'to' entry's dimensions are unknown (during link creation) or if both entries are visible.
232+
if (!this.#toEntryDimensions || (this.#entryFromVisible && this.#entryToVisible)) {
233+
this.#connector.setAttribute('stroke', arrowColor);
234+
return;
235+
}
188236

189-
this.#entryFromConnector.setAttribute('visibility', drawFromEntryConnectorCircle ? 'visible' : 'hidden');
190-
this.#entryToConnector.setAttribute('visibility', drawToEntryConnectorCircle ? 'visible' : 'hidden');
237+
// If one entry is not visible and one is, fade the arrow.
238+
if (this.#entryFromVisible && !this.#entryToVisible) {
239+
this.#connector.setAttribute('stroke', 'url(#fromVisibleLineGradient)');
240+
} else if (this.#entryToVisible && !this.#entryFromVisible) {
241+
this.#connector.setAttribute('stroke', 'url(#toVisibleLineGradient)');
242+
}
243+
}
244+
245+
#positionConnectorLineAndCircles(): void {
246+
if (!this.#connector || !this.#entryFromCirleConnector || !this.#entryToCircleConnector) {
247+
return;
248+
}
191249

192250
// If the entry is visible, the entry arrow starts from the middle of the right edge of the entry (end on the X axis and middle of the Y axis).
193251
// If not, draw it to the y coordinate of the entry and the edge of the timeline so it is pointing in the direction of the entry.
194252
const halfFromEntryHeight = this.#fromEntryDimensions.height / 2;
195-
if (this.#entryFromVisible) {
196-
const endConnectionPointX = String(this.#coordinateFrom.x + this.#fromEntryDimensions.width);
197-
const endConnectionPointY = String(this.#coordinateFrom.y + halfFromEntryHeight);
253+
const fromX = this.#coordinateFrom.x + this.#fromEntryDimensions.width;
254+
const fromY = this.#coordinateFrom.y + halfFromEntryHeight;
198255

199-
this.#connector.setAttribute('x1', endConnectionPointX);
200-
this.#connector.setAttribute('y1', endConnectionPointY);
256+
this.#connector.setAttribute('x1', fromX.toString());
257+
this.#connector.setAttribute('y1', fromY.toString());
201258

202-
this.#entryFromConnector.setAttribute('cx', endConnectionPointX);
203-
this.#entryFromConnector.setAttribute('cy', endConnectionPointY);
204-
this.#entryFromWrapper.style.visibility = 'visible';
205-
} else {
206-
this.#connector.setAttribute('x1', (this.#coordinateFrom.x + this.#fromEntryDimensions.width).toString());
207-
this.#connector.setAttribute('y1', String(this.#coordinateFrom.y + halfFromEntryHeight));
208-
this.#entryFromWrapper.style.visibility = 'hidden';
209-
}
259+
this.#entryFromCirleConnector.setAttribute('cx', fromX.toString());
260+
this.#entryFromCirleConnector.setAttribute('cy', fromY.toString());
210261

211262
// If the arrow is pointing to the entry and that entry is visible, point it to the middle of the entry.
212263
// If the entry is not visible, point the arrow to the edge of the screen towards the entry.
213264
// Otherwise, the arrow is following the mouse so we assign it to the provided coordinates.
214-
if (this.#toEntryDimensions && this.#entryToVisible) {
215-
const connectionPointX = String(this.#coordinateTo.x);
216-
const connectionPointY = String(this.#coordinateTo.y + this.#toEntryDimensions.height / 2);
217-
218-
this.#connector.setAttribute('x2', connectionPointX);
219-
this.#connector.setAttribute('y2', connectionPointY);
265+
const toX = this.#coordinateTo.x;
266+
const toY = this.#toEntryDimensions ? this.#coordinateTo.y + (this.#toEntryDimensions?.height ?? 0) / 2 :
267+
this.#coordinateTo.y;
220268

221-
this.#entryToConnector.setAttribute('cx', connectionPointX);
222-
this.#entryToConnector.setAttribute('cy', connectionPointY);
269+
this.#connector.setAttribute('x2', toX.toString());
270+
this.#connector.setAttribute('y2', toY.toString());
223271

224-
this.#entryToWrapper.style.visibility = 'visible';
225-
} else {
226-
this.#entryToWrapper.style.visibility = 'hidden';
227-
this.#connector.setAttribute('x2', this.#coordinateTo.x.toString());
228-
// If `toEntryDimensions` exist, the arrow points to the entry and we need to take its height into account.
229-
// Otherwise, it is following the mouse.
230-
if (this.#toEntryDimensions) {
231-
const halfToEntryHeight = this.#toEntryDimensions.height / 2;
232-
this.#connector.setAttribute('y2', String(this.#coordinateTo.y + halfToEntryHeight));
233-
} else {
234-
this.#connector.setAttribute('y2', (this.#coordinateTo.y).toString());
235-
}
236-
}
237-
238-
this.#connector.setAttribute('stroke-width', '2');
239-
240-
if (this.#toEntryDimensions && this.#entryFromVisible && !this.#entryToVisible) {
241-
this.#connector.setAttribute('stroke', 'url(#fromVisibleLineGradient)');
242-
} else if (this.#toEntryDimensions && this.#entryToVisible && !this.#entryFromVisible) {
243-
this.#connector.setAttribute('stroke', 'url(#toVisibleLineGradient)');
244-
} else {
245-
const arrowColor = ThemeSupport.ThemeSupport.instance().getComputedValue('--color-text-primary');
246-
this.#connector.setAttribute('stroke', arrowColor);
247-
}
248-
249-
this.#render();
272+
this.#entryToCircleConnector.setAttribute('cx', toX.toString());
273+
this.#entryToCircleConnector.setAttribute('cy', toY.toString());
250274
}
251275

252276
/*

0 commit comments

Comments
 (0)