chrome/browser/ui/views/frame/layout/README.md
This is an overview of the BrowserViewLayout system, and a how-to for
adding/modifying layout logic.
Every BrowserView has a BrowserViewLayout, which is a LayoutManager and
which uses the standard Views layout logic. Which implementation is used depends
on the type of browser, and is created by BrowserViewLayout::CreateLayout():
BrowserViewLayout differs from most LayoutManager implementations in that it
lays out not just children of the BrowserView, but also descendants and
elements on overlay widgets (e.g. the toolbar and tab strip in immersive
fullscreen mode).
Each layout implementation has access to the following:
BrowserViewLayoutDelegate which provides access to layout-specific
browser and window properties. The layout should never read browser-,
browser view-, or browser window-specific properties directly from the
Browser or BrowserView, so that the behaviors can be mocked for testing.BrowserViewLayoutViews detailing all of the View objects the layout is
responsible for. When possible, these should be stored as views::Views,
not as specific derived classes, unless some specific layout- or visuals-
related API needs to be directly accessed by the layout.All of the layout work is done in BrowserViewLayoutImpl::Layout().
The steps are as follows:
BrowserLayoutParams from the frame by way of the delegate. This
defines the geometry of the region in which the browser view's contents can
lay out.DoPreLayoutComputations() - computes and stores any values that are used
across multiple methods in the layout process. Do not compute anything here
that is only used in one place.CalculateTopContainerLayout().
Note that this method is also used when the top container is in the
browser view and laid out in step (4) below.CalculateProposedLayout() is where all of the "normal" layout
calculation happens. If the top container is in the browser view, this will
also call CalculateTopContainerLayout() at the appropriate time.ConfigureTopContainerBackground() - sets appropriate background properties
and colors for the top container.DoPostLayoutVisualAdjustments() - sets any visual properties, clip paths,
fades, corners, etc. that aren't strictly part of layout but which require
each view to be laid out in its correct bounds.UpdateBubbles().DoPostLayoutCleanup() - clears out any data generated in
DoPreLayoutComputations() or during layout. This is done so that data from
one layout pass doesn't "leak" into the next, and that transient layout data
isn't accessed outside of layout.The goal of the BrowserViewLayout system is to keep the layout code as simple,
straightforward, and readable as possible. This is a challenge because of the
complexity of BrowserView, so great care should be taken in modifying the
code.
To add a new view to the layout, add a field to BrowserViewLayoutViews and
ensure it is set in BrowserView::AddedToWidget(). Views added in this way
should typically be owned by the BrowserView or part of the top container.
Again, prefer to add a field of type raw_ptr<views::View> rather than some
derived class.
In CalculateProposedLayout() or CalculateTopContainerLayout() (depending on
where your View lives), add a block at the appropriate point in the layout:
// In CalculateProposedLayout():
if (IsParentedTo(views().my_view, views().browser_view)) {
const bool my_view_should_be_visible = ... // Calculate visibility here.
gfx::Rect my_view_bounds;
if (my_view_should_be_visible) {
my_view_bounds = ... // Calculate view bounds from `params`.
// Maybe inset `params` based on the space my view will take up.
}
// This sets both the bounds and target visibility.
layout.AddChild(views().my_view, my_view_bounds, my_view_should_be_visible);
}
Note that visibility calculation (if any) and bounds calculation (if any) happen as part of this block. If you need to "save" the view's visibility or bounds for some later computation, you can declare them outside the block:
bool my_view_visible = false;
gfx::Rect my_view_bounds;
if (IsParentedTo(views().my_view, view().browser_view)) {
my_view_visible = ...
my_view_bounds = ...
layout.AddChild(...);
}
You can also save the layout itself for later use:
ProposedLayout* my_view_layout = nullptr;
if (IsParentedTo(views().my_view, view().browser_view)) {
const bool my_view_visible = ...
gfx::Rect my_view_bounds = ...
my_view_layout = &layout.AddChild(...);
}
Note that bounds and visibility will be set on the view by the time that
DoPostLayoutVisualAdjustments() is called, so you do not need to preserve them
outside of the layout calculation.
If you do need some common value to calculate both your view's bounds and/or
visibility and some visual property that can't be determined directly from
your view's bounds and visibility, the value can be calculated and saved in
DoPreLayoutComputations() and then used wherever it is needed.
Prefer to set the visibility of a view as part of CalculateProposedLayout()
(for a full example see Laying a View Out above).
In other words, prefer something like this:
const bool my_view_should_be_visible = ... // Calculate visibility here.
// ...
layout.AddChild(views().my_view, my_view_bounds, my_view_should_be_visible);
In rare cases, a view's visibility will be set by some external system, in which
case you can do the child layout calculation inside an
if (IsParentedToAndVisible(...)) { ... } block.
All of this work should be done in DoPostLayoutVisualAdjustments() if
possible.
For views which use CustomCornersBackground, prefer to configure it by
dynamically casting the background rather than adding a custom accessor to the
View class (this helps us avoid using specific View classes in
BrowserViewLayoutViews):
auto* const my_view_background =
views().my_view->background()->AsA<CustomCornersBackground>();
// Set background properties
See the BrowserAnimationController documentation for information on how to create and extend animations.
Some general guidelines:
View, you can
read it in that View's CalculatePreferredSize() method and not reference it
directly in the layout at all.DoPostLayoutVisualAdjustments().CalculateProposedLayout().DoPreLayoutComputations() so it can be used throughout the layout.You may need to have your view listen for animation updates from the
BrowserAnimationController just to call InvalidateLayout() or
PreferredSizeChanged().
BrowserViewLayout outside of
BrowserViewLayout::CalculateProposedLayout().BrowserViewLayout
outside of BrowserViewLayout::CalculateProposedLayout().BrowserViewLayout outside of
BrowserViewLayout::DoPostLayoutVisualAdjustments().Initial states may, however, be set during View creation.