Conversation
|
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/mlh-fellowship/scheduling-profiler-prototype/gs678bh9d |
|
@jevakallio this is ready for an approach/architecture review :) I've described the high level goals, broken down the classes and described the key behaviors. At this point, This PR is roughly half complete, where completion = feature/functional parity with the current profiler + #71. I'd love to get your thoughts on this, especially regarding these questions:
|
|
Thanks @jevakallio for the call! Key points brought up during the call:
|
|
Hey @bvaughn, I'd like to get your thoughts on the approach of this PR. Essentially, I'm thinking of replacing the current
My goals are:
My concerns:
Although I think the benefits of this view approach outweigh these cons, I'd like to get your feedback on this before I proceed further in this direction :) We can talk about this more during our call on Monday and I can walk you through this then, but for now the PR description will hopefully describe this in greater detail. Specifically, I'd like to know if you think this view-based system is a good approach, and whether you have any requirements/suggestions. |
|
Hi @taneliang, I'm likely not going to have a chance to review this code today because my schedule is pretty full. Happy to talk through it with you on Monday morning though. At a high level, the approach you're describing makes sense- memoizing/limiting the re-rendering work- although I have some concern when reading through the classes section of the PR. Using inheritance for different behaviors feels like it might be problematic. The classes designed seemed overly narrow. For instance, I imagine that I'm also not sure what an example of We can talk more on Monday 😄 Thanks for the ping and the detailed description! |
|
Thanks @bvaughn!
Ah, I think I should've clarified this better. The behaviors are currently composed by nesting views, and so our content views (e.g. The Vercel deploy preview currently has our content views in a vertical stack This is definitely not as clean a design as I'd hoped; I'd have preferred to inject behaviors into a I'm reasonably confident that this design works and may be good enough™️ since behaviors are also composed in a similar way in UIKit.
Nope, this architecture will be able to do everything that we can do now. On top of that, this design is also partially intended to also make it easier to support more complex UIs and interactions, including having the lanes area scroll separately from the flamechart area. There are some implementation details to be sorted out to ensure that the horizontal pan and zooms remain synced between the two views (even though they're in the same canvas, the views maintain their own pan and zoom state), but that should be relatively easy to solve. Let's talk more about this on Monday; it'll probably be easier to talk through these ideas then. Thanks for looking at this despite your packed schedule! Have a great weekend 😄 |
setNeedsDisplay used to propagate upwards, marking every recursive superview as needing display. This was necessary to allow every render to efficiently find views that need rendering. However, this prevent setNeedsDisplay from being used to propagate view invalidation. This commit changes setNeedsDisplay to propagate downwards in the view heirarchy, following UIView's setNeedsDisplay. A new setSubviewNeedsDisplay method is added to fulfill the need for efficiently locating views that need redrawing. Fixes the bug where canvas resizing leaves black areas where views did not redraw.
jevakallio
left a comment
There was a problem hiding this comment.
Rubberstamp approved, as per discussion on Discord
|
Woo hoo! 🥳 |
Summary This PR begins a stack of PRs that improves the base `View` class implemented in #80. This PR adds subview management to `View`, as there was a lot of duplicated subview handling code present in almost all our `View` subclasses. This also brings us closer to UIKit's `UIView`, which also handles its own subviews. Implements #95. Test Plan * `yarn start`: nothing broken * `yarn lint` * `yarn flow`: no errors in affected code * `yarn test`
Summary This PR begins a stack of PRs that improves the base `View` class implemented in #80. This PR adds subview management to `View`, as there was a lot of duplicated subview handling code present in almost all our `View` subclasses. This also brings us closer to UIKit's `UIView`, which also handles its own subviews. Implements #95. Test Plan * `yarn start`: nothing broken * `yarn lint` * `yarn flow`: no errors in affected code * `yarn test`
Summary This PR begins a stack of PRs that improves the base `View` class implemented in #80. This PR adds subview management to `View`, as there was a lot of duplicated subview handling code present in almost all our `View` subclasses. This also brings us closer to UIKit's `UIView`, which also handles its own subviews. Implements #95. Test Plan * `yarn start`: nothing broken * `yarn lint` * `yarn flow`: no errors in affected code * `yarn test`
Summary This PR begins a stack of PRs that improves the base `View` class implemented in #80. This PR adds subview management to `View`, as there was a lot of duplicated subview handling code present in almost all our `View` subclasses. This also brings us closer to UIKit's `UIView`, which also handles its own subviews. Implements #95. Test Plan * `yarn start`: nothing broken * `yarn lint` * `yarn flow`: no errors in affected code * `yarn test`
Summary
TL;DR
Replaces Brian's original procedural code with view classes. The views are rudimentary implementations of iOS UIKit-style
UIViews.Originally discussed at #50 (comment).
Related to #50. Resolves #71, resolves #39, resolves #83. Unblocks others (#70, #72)
This PR is enormous but I can't really split it, as I'm fixing conceptual issues as I go.
Problems this solves
renderCanvasandgetHoveredEvent) as we aren't able to share layout code between them.usePanAndZoom. This is a problem as Add vertical resizer between canvas #19 requires resizing and independent scrolling of 2 areas of the same canvas (or 2 separate canvasses). This view architecture will allow us to implement independent scrolling within 1 canvas by storing state in individual classes.CanvasPageReact component, which in practice meant that moving the cursor across the canvas triggered a full-canvas render. This view architecture solves this with:CanvasPagedoes not need will be stored in mutable refs or as variables in the view instances.setNeedsDisplayorsetSubviewsNeedDisplayhas been called on them. If a view needs display, it'll lay out its subviews and draw them. This allows us to easily redraw parts of the canvas that have changed, improving hovering performance. Note that this does not improve scroll/zoom operations that require a near-full-canvas redraw.CanvasPagestate needs to be modified.Pros
Cons
Classes
All views inherit from the
Viewclass. The main classes and key purposes are listed below:Surface: Represents the canvas surface. Also the main point of entry for all incoming interactions.View: Base class that can be subclassed to implement custom content views (e.g. our flamegraph, React data, etc.). This is the equivalent of UIKit'sUIView.StaticLayoutView: A simpleViewsubclass that can display a list of subviews laid out by an injectedlayouterfunction. This is used for horizontal and vertical stacking as well as layering.HorizontalPanAndZoomView: AViewsubclass that handles horizontal pan, scroll and zoom interactions. This contains a singlecontentViewsubview. When panning and zooming, thecontentView'sframeis mutated. Known issue: thecontentView'sframeis not clamped and you can scroll offscreen.More envisioned layout views:
VerticalScrollView: AViewsubclass that handles vertical pan and scroll interactions. This will be similar toHorizontalPanAndZoomViewexcept that it does not zoom.ResizableSplitView: AViewsubclass that has 2 subviews divided by a resize bar. For (Add vertical resizer between canvas #19).This design is not the cleanest as we complect a bunch of behaviors into the same class, and it's strange that only one class has an injected layouter. This can be cleaned up in the future if needed.
Content views:
TimeAxisMarkersViewReactEventsViewReactMeasuresView(includes modifications to address Hide empty lanes #71)FlamegraphViewLaneLabelView(envisioned, for Show React version #73)UserTimingMarksView(envisioned, for Show remaining User Timing marks in the UI #72)These are instantiated in
CanvasPage. Their code is a light-ish adaptation of the existing code inrenderCanvas, but in the future we may want to turnReactMeasuresViewandFlamegraphViewinto a vertically stackedStaticLayoutViewto reuse moreViewlogic.Key flows
Rendering
displayIfNeededmethod is invoked, it calls its root view'sdisplayIfNeededmethod.needsDisplayorsubviewsNeedDisplayflags are true and the view is visible onscreen, it will layout its subviews and draw.displayIfNeededmethods.Event handling
useCanvasInteraction.useCanvasInteractionis given a callback that invokes aSurface'shandleInteractionmethod.useCanvasInteractiontranslates browser events into higher-level interactions (e.g. mouse wheel event -> zoom or scroll interactions). Note: The current implementation is a WIP and is neither complete nor correct.useCanvasInteractioncalls itsinteractorcallback. Our interactor is set inCanvasPage, which calls the surface'shandleInteractionmethod.handleInteractionAndPropagateToSubviewsmethod. A view may handle the interaction, but it must propagate the interaction to its subviews.State updates
setNeedsDisplaymethod should be called. This is often done in superviews'layoutSubviewsor in event handlers.setNeedsDisplaysets the view'sneedsDisplayto true, and recursively invokes all its superclasses'setSubviewsNeedDisplay. This ensures that the next render will be able to efficiently find all views that need to be redrawn.Test Plan
1.4k lines of new code and no tests 🙃
yarn lintyarn flow(has errors in untouched code, but none in the new code)yarn test(tests untouched, but still passing)Manually tested on:
60 FPS on the Facebook.com profile 😍
TODO
To be addressed in this PR
HorizontalPanAndZoom:frame/visibleAreadon't get painted and leave a black area (canvas resizes should forceneedsDisplayon all views)To be fixed in future PRs (will be convert most to issues)
View(Views: Support subviews in View class #95)ResizableSplitView(Add vertical resizer between canvas #19)