@@ -11,9 +11,10 @@ import type {NativeEvent, ReactProfilerData} from '../types';
1111import type { Interaction , MouseMoveInteraction , Rect , Size } from '../view-base' ;
1212
1313import {
14+ durationToWidth ,
1415 positioningScaleFactor ,
15- timestampToPosition ,
1616 positionToTimestamp ,
17+ timestampToPosition ,
1718} from './utils/positioning' ;
1819import {
1920 View ,
@@ -24,28 +25,77 @@ import {
2425} from '../view-base' ;
2526import {
2627 COLORS ,
27- EVENT_ROW_PADDING ,
28- EVENT_DIAMETER ,
28+ TEXT_PADDING ,
29+ NATIVE_EVENT_HEIGHT ,
30+ FONT_SIZE ,
2931 BORDER_SIZE ,
3032} from './constants' ;
3133
32- const EVENT_ROW_HEIGHT_FIXED =
33- EVENT_ROW_PADDING + EVENT_DIAMETER + EVENT_ROW_PADDING ;
34+ const ROW_WITH_BORDER_HEIGHT = NATIVE_EVENT_HEIGHT + BORDER_SIZE ;
35+
36+ // TODO (scheduling profiler) Make this a reusable util
37+ const cachedFlamechartTextWidths = new Map ( ) ;
38+ const trimFlamechartText = (
39+ context : CanvasRenderingContext2D ,
40+ text : string ,
41+ width : number ,
42+ ) => {
43+ for ( let i = text . length - 1 ; i >= 0 ; i -- ) {
44+ const trimmedText = i === text . length - 1 ? text : text . substr ( 0 , i ) + '…' ;
45+
46+ let measuredWidth = cachedFlamechartTextWidths . get ( trimmedText ) ;
47+ if ( measuredWidth == null ) {
48+ measuredWidth = context . measureText ( trimmedText ) . width ;
49+ cachedFlamechartTextWidths . set ( trimmedText , measuredWidth ) ;
50+ }
51+
52+ if ( measuredWidth <= width ) {
53+ return trimmedText ;
54+ }
55+ }
56+
57+ return null ;
58+ } ;
3459
3560export class NativeEventsView extends View {
36- _profilerData : ReactProfilerData ;
61+ _depthToNativeEvent : Map < number , NativeEvent [ ] > ;
62+ _hoveredEvent: NativeEvent | null = null ;
3763 _intrinsicSize: Size ;
64+ _maxDepth: number = 0 ;
65+ _profilerData: ReactProfilerData ;
3866
39- _hoveredEvent : NativeEvent | null = null ;
4067 onHover: ( ( event : NativeEvent | null ) => void ) | null = null ;
4168
4269 constructor ( surface : Surface , frame : Rect , profilerData : ReactProfilerData ) {
4370 super ( surface , frame ) ;
71+
4472 this . _profilerData = profilerData ;
4573
74+ this . _performPreflightComputations ( ) ;
75+ console . log ( this . _depthToNativeEvent ) ;
76+ }
77+
78+ _performPreflightComputations ( ) {
79+ this . _depthToNativeEvent = new Map ( ) ;
80+
81+ const { duration, nativeEvents} = this . _profilerData ;
82+
83+ nativeEvents . forEach ( event => {
84+ const depth = event . depth ;
85+
86+ this . _maxDepth = Math . max ( this . _maxDepth , depth ) ;
87+
88+ if ( ! this . _depthToNativeEvent . has ( depth ) ) {
89+ this . _depthToNativeEvent . set ( depth , [ event ] ) ;
90+ } else {
91+ // $FlowFixMe This is unnecessary.
92+ this . _depthToNativeEvent . get ( depth ) . push ( event ) ;
93+ }
94+ } ) ;
95+
4696 this . _intrinsicSize = {
47- width : this . _profilerData . duration ,
48- height : EVENT_ROW_HEIGHT_FIXED ,
97+ width : duration ,
98+ height : ( this . _maxDepth + 1 ) * ROW_WITH_BORDER_HEIGHT ,
4999 } ;
50100 }
51101
@@ -73,7 +123,9 @@ export class NativeEventsView extends View {
73123 showHoverHighlight : boolean ,
74124 ) {
75125 const { frame} = this ;
76- const { duration, timestamp} = event ;
126+ const { depth, duration, highlight, timestamp, type} = event ;
127+
128+ baseY += depth * ROW_WITH_BORDER_HEIGHT ;
77129
78130 const xStart = timestampToPosition ( timestamp , scaleFactor , frame ) ;
79131 const xStop = timestampToPosition ( timestamp + duration , scaleFactor , frame ) ;
@@ -82,25 +134,60 @@ export class NativeEventsView extends View {
82134 x : xStart ,
83135 y : baseY ,
84136 } ,
85- size : { width : xStop - xStart , height : EVENT_DIAMETER } ,
137+ size : { width : xStop - xStart , height : NATIVE_EVENT_HEIGHT } ,
86138 } ;
87139 if ( ! rectIntersectsRect ( eventRect , rect ) ) {
88140 return ; // Not in view
89141 }
90142
91- const fillStyle = showHoverHighlight
92- ? COLORS . NATIVE_EVENT_HOVER
93- : COLORS . NATIVE_EVENT ;
143+ const width = durationToWidth ( duration , scaleFactor ) ;
144+ if ( width < 1 ) {
145+ return ; // Too small to render at this zoom level
146+ }
94147
95148 const drawableRect = intersectionOfRects ( eventRect , rect ) ;
96149 context . beginPath ( ) ;
97- context . fillStyle = fillStyle ;
150+ if ( highlight ) {
151+ context . fillStyle = showHoverHighlight
152+ ? COLORS . NATIVE_EVENT_WARNING_HOVER
153+ : COLORS . NATIVE_EVENT_WARNING ;
154+ } else {
155+ context . fillStyle = showHoverHighlight
156+ ? COLORS . NATIVE_EVENT_HOVER
157+ : COLORS . NATIVE_EVENT ;
158+ }
98159 context . fillRect (
99160 drawableRect . origin . x ,
100161 drawableRect . origin . y ,
101162 drawableRect . size . width ,
102163 drawableRect . size . height ,
103164 ) ;
165+
166+ // Render event type label
167+ context . textAlign = 'left' ;
168+ context . textBaseline = 'middle' ;
169+ context . font = `${ FONT_SIZE } px sans-serif` ;
170+
171+ if ( width > TEXT_PADDING * 2 ) {
172+ const x = Math . floor ( timestampToPosition ( timestamp , scaleFactor , frame ) ) ;
173+ const trimmedName = trimFlamechartText (
174+ context ,
175+ type ,
176+ width - TEXT_PADDING * 2 + ( x < 0 ? x : 0 ) ,
177+ ) ;
178+
179+ if ( trimmedName !== null ) {
180+ context . fillStyle = highlight
181+ ? COLORS . NATIVE_EVENT_WARNING_TEXT
182+ : COLORS . TEXT_COLOR ;
183+
184+ context . fillText (
185+ trimmedName ,
186+ eventRect . origin . x + TEXT_PADDING - ( x < 0 ? x : 0 ) ,
187+ eventRect . origin . y + NATIVE_EVENT_HEIGHT / 2 ,
188+ ) ;
189+ }
190+ }
104191 }
105192
106193 draw ( context : CanvasRenderingContext2D ) {
@@ -111,7 +198,7 @@ export class NativeEventsView extends View {
111198 visibleArea,
112199 } = this ;
113200
114- context . fillStyle = COLORS . BACKGROUND ;
201+ context . fillStyle = COLORS . PRIORITY_BACKGROUND ;
115202 context . fillRect (
116203 visibleArea . origin . x ,
117204 visibleArea . origin . y ,
@@ -120,57 +207,43 @@ export class NativeEventsView extends View {
120207 ) ;
121208
122209 // Draw events
123- const baseY = frame . origin . y + EVENT_ROW_PADDING ;
124210 const scaleFactor = positioningScaleFactor (
125211 this . _intrinsicSize . width ,
126212 frame ,
127213 ) ;
128214
129215 nativeEvents . forEach ( event => {
130- if ( event === _hoveredEvent ) {
131- // Draw the highlighted items on top so they stand out.
132- // This is helpful if there are multiple (overlapping) items close to each other.
133- this . _drawSingleNativeEvent (
134- context ,
135- visibleArea ,
136- event ,
137- baseY ,
138- scaleFactor ,
139- true ,
140- ) ;
141- } else {
142- this . _drawSingleNativeEvent (
143- context ,
144- visibleArea ,
145- event ,
146- baseY ,
147- scaleFactor ,
148- false ,
149- ) ;
150- }
216+ this . _drawSingleNativeEvent (
217+ context ,
218+ visibleArea ,
219+ event ,
220+ frame . origin . y ,
221+ scaleFactor ,
222+ event === _hoveredEvent ,
223+ ) ;
151224 } ) ;
152225
153- // Render bottom border .
154- // Propose border rect, check if intersects with `rect`, draw intersection.
155- const borderFrame : Rect = {
156- origin : {
157- x : frame . origin . x ,
158- y : frame . origin . y + EVENT_ROW_HEIGHT_FIXED - BORDER_SIZE ,
159- } ,
160- size : {
161- width : frame . size . width ,
162- height : BORDER_SIZE ,
163- } ,
164- } ;
165- if ( rectIntersectsRect ( borderFrame , visibleArea ) ) {
166- const borderDrawableRect = intersectionOfRects ( borderFrame , visibleArea ) ;
167- context . fillStyle = COLORS . PRIORITY_BORDER ;
168- context . fillRect (
169- borderDrawableRect . origin . x ,
170- borderDrawableRect . origin . y ,
171- borderDrawableRect . size . width ,
172- borderDrawableRect . size . height ,
173- ) ;
226+ // Render bottom borders .
227+ for ( let i = 0 ; i <= this . _maxDepth ; i ++ ) {
228+ const borderFrame : Rect = {
229+ origin : {
230+ x : frame . origin . x ,
231+ y : frame . origin . y + NATIVE_EVENT_HEIGHT ,
232+ } ,
233+ size : {
234+ width : frame . size . width ,
235+ height : BORDER_SIZE ,
236+ } ,
237+ } ;
238+ if ( rectIntersectsRect ( borderFrame , visibleArea ) ) {
239+ context . fillStyle = COLORS . PRIORITY_BORDER ;
240+ context . fillRect (
241+ visibleArea . origin . x ,
242+ frame . origin . y + ( i + 1 ) * ROW_WITH_BORDER_HEIGHT - BORDER_SIZE ,
243+ visibleArea . size . width ,
244+ BORDER_SIZE ,
245+ ) ;
246+ }
174247 }
175248 }
176249
@@ -189,25 +262,26 @@ export class NativeEventsView extends View {
189262 return ;
190263 }
191264
192- const { nativeEvents} = this . _profilerData ;
193-
194265 const scaleFactor = positioningScaleFactor ( _intrinsicSize . width , frame ) ;
195266 const hoverTimestamp = positionToTimestamp ( location . x , scaleFactor , frame ) ;
196267
197- // Find the event being hovered over.
198- //
199- // Because data ranges may overlap, we want to find the last intersecting item.
200- // This will always be the one on "top" (the one the user is hovering over).
201- for ( let index = nativeEvents . length - 1 ; index >= 0 ; index -- ) {
202- const nativeEvent = nativeEvents [ index ] ;
203- const { duration, timestamp} = nativeEvent ;
204-
205- if (
206- hoverTimestamp >= timestamp &&
207- hoverTimestamp <= timestamp + duration
208- ) {
209- onHover ( nativeEvent ) ;
210- return ;
268+ const adjustedCanvasMouseY = location . y - frame . origin . y ;
269+ const depth = Math . floor ( adjustedCanvasMouseY / ROW_WITH_BORDER_HEIGHT ) ;
270+ const nativeEventsAtDepth = this . _depthToNativeEvent . get ( depth ) ;
271+
272+ if ( nativeEventsAtDepth ) {
273+ // Find the event being hovered over.
274+ for ( let index = nativeEventsAtDepth . length - 1 ; index >= 0 ; index -- ) {
275+ const nativeEvent = nativeEventsAtDepth [ index ] ;
276+ const { duration, timestamp} = nativeEvent ;
277+
278+ if (
279+ hoverTimestamp >= timestamp &&
280+ hoverTimestamp <= timestamp + duration
281+ ) {
282+ onHover ( nativeEvent ) ;
283+ return ;
284+ }
211285 }
212286 }
213287
0 commit comments