1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_
#define UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <utility>
#include <vector>
#include "base/dcheck_is_on.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "base/types/pass_key.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/layout/layout_manager.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/layout/proposed_layout.h"
#include "ui/views/view_observer.h"
#include "ui/views/views_export.h"
namespace views {
class View;
// Base class for layout managers that can do layout calculation separately
// from layout application. Derived classes must implement
// CalculateProposedLayout(). Used in interpolating and animating layouts.
class VIEWS_EXPORT LayoutManagerBase : public LayoutManager,
public ViewObserver {
public:
using PassKeyType = base::NonCopyablePassKey<LayoutManagerBase>;
LayoutManagerBase(const LayoutManagerBase&) = delete;
LayoutManagerBase& operator=(const LayoutManagerBase&) = delete;
~LayoutManagerBase() override;
View* host_view() { return host_view_; }
const View* host_view() const { return host_view_; }
// Fetches a proposed layout for a host view with size |host_size|. If the
// result had already been calculated, a cached value may be returned.
ProposedLayout GetProposedLayout(const gfx::Size& host_size) const;
// Fetches a proposed layout for a host view with `size_bounds`. This function
// does not require caching because it is generally used in combination with
// other LayoutManager.
ProposedLayout GetProposedLayout(const SizeBounds& size_bounds,
PassKeyType) const;
// LayoutManager:
gfx::Size GetPreferredSize(const View* host) const override;
gfx::Size GetPreferredSize(const View* host,
const SizeBounds& available_size) const override;
gfx::Size GetMinimumSize(const View* host) const override;
int GetPreferredHeightForWidth(const View* host, int width) const override;
SizeBounds GetAvailableSize(const View* host,
const View* view) const override;
void Layout(View* host) final;
// ViewObserver:
void OnViewPropertyChanged(View* observed_view,
const void* key,
int64_t old_value) final;
// Returns whether the specified child view can be visible. To be able to be
// visible, |child| must be a child of the host view, and must have been
// visible when it was added or most recently had SetVisible(true) called on
// it by non-layout code.
bool CanBeVisible(const View* child) const;
protected:
LayoutManagerBase();
PassKeyType PassKey() const { return PassKeyType(); }
// LayoutManager:
std::vector<raw_ptr<View, VectorExperimental>> GetChildViewsInPaintOrder(
const View* host) const override;
// Direct cache control for subclasses that want to override default caching
// behavior. Use at your own risk.
std::optional<gfx::Size> cached_minimum_size() const {
return cached_minimum_size_;
}
void set_cached_minimum_size(
const std::optional<gfx::Size>& minimum_size) const {
cached_minimum_size_ = minimum_size;
}
const std::optional<gfx::Size>& cached_preferred_size() const {
return cached_preferred_size_;
}
void set_cached_preferred_size(
const std::optional<gfx::Size>& preferred_size) const {
cached_preferred_size_ = preferred_size;
}
const std::optional<gfx::Size>& cached_height_for_width() const {
return cached_height_for_width_;
}
void set_cached_height_for_width(
const std::optional<gfx::Size>& height_for_width) const {
cached_height_for_width_ = height_for_width;
}
const std::optional<gfx::Size>& cached_layout_size() const {
return cached_layout_size_;
}
void set_cached_layout_size(
const std::optional<gfx::Size>& layout_size) const {
cached_layout_size_ = layout_size;
}
const ProposedLayout& cached_layout() const { return cached_layout_; }
void set_cached_layout(const ProposedLayout& layout) const {
cached_layout_ = layout;
}
// Returns the size available to the host view from its parent.
SizeBounds GetAvailableHostSize() const;
// Returns true if the specified view is a child of the host view and is not
// ignored. Views hidden by external code are only included if
// |include_hidden| is set.
bool IsChildIncludedInLayout(const View* child,
bool include_hidden = false) const;
// Creates a proposed layout for the host view, including bounds and
// visibility for all children currently included in the layout.
virtual ProposedLayout CalculateProposedLayout(
const SizeBounds& size_bounds) const = 0;
// Does the actual work of laying out the host view and its children.
// Default implementation is just getting the proposed layout for the host
// size and then applying it.
virtual void LayoutImpl();
// Applies |layout| to the children of the host view.
void ApplyLayout(const ProposedLayout& layout);
// Invalidates the host view (if present).
//
// If |mark_layouts_changed| is true, OnLayoutChanged() will also be called
// for each layout associated with the host, as if the host were invalidated
// by external code. If there is no host (yet), the behavior is simulated by
// invalidating the root layout manager - see GetRootLayoutManager() below.
void InvalidateHost(bool mark_layouts_changed);
// The following methods are called on this layout and any owned layouts when
// e.g. InvalidateLayout(), Installed(), etc. are called, in order to do any
// additional layout-specific work required. Returns whether the host view
// must be invalidated as a result of the update. All of these call
// OnLayoutChanged() by default (see below).
virtual bool OnChildViewIncludedInLayoutSet(View* child_view, bool included);
virtual bool OnViewAdded(View* host, View* view);
virtual bool OnViewRemoved(View* host, View* view);
virtual bool OnViewVisibilitySet(View* host, View* view, bool visible);
// Called when the layout is installed in a host view. Default is a no-op.
virtual void OnInstalled(View* host);
// Called whenever the layout manager is invalidated, or when the layout may
// have changed as the result of an operation. Default behavior is to clear
// all cached data.
virtual void OnLayoutChanged();
// Adds an owned layout. The primary layout propagates events (installation,
// view addition, etc.) to all owned layouts. Subclasses of LayoutManagerBase
// that need to compose or transform the output of one or more embedded
// layouts should use the |owned_layouts| system.
template <class T>
T* AddOwnedLayout(std::unique_ptr<T> owned_layout) {
T* layout = owned_layout.get();
AddOwnedLayoutInternal(std::move(owned_layout));
return layout;
}
size_t num_owned_layouts() const { return owned_layouts_.size(); }
LayoutManagerBase* owned_layout(size_t index) {
return owned_layouts_[index].get();
}
const LayoutManagerBase* owned_layout(size_t index) const {
return owned_layouts_[index].get();
}
private:
friend class ManualLayoutUtil;
friend class LayoutManagerBaseAvailableSizeTest;
// Holds bookkeeping data used to determine inclusion of children in the
// layout.
struct ChildInfo {
bool can_be_visible = true;
bool included_in_layout = true;
};
// LayoutManager:
void InvalidateLayout() final;
void Installed(View* host) final;
void ViewAdded(View* host, View* view) final;
void ViewRemoved(View* host, View* view) final;
void ViewVisibilitySet(View* host,
View* view,
bool old_visibility,
bool new_visibility) final;
void AddOwnedLayoutInternal(std::unique_ptr<LayoutManagerBase> owned_layout);
// Gets the top layout in the ownership chain that includes this layout.
LayoutManagerBase* GetRootLayoutManager();
// Do the work of propagating events to owned layouts. Returns true if the
// host view must be invalidated.
bool PropagateChildViewIncludedInLayout(View* child_view, bool included);
bool PropagateViewAdded(View* host, View* view);
bool PropagateViewRemoved(View* host, View* view);
bool PropagateViewVisibilitySet(View* host, View* view, bool visible);
void PropagateInstalled(View* host);
void PropagateInvalidateLayout();
raw_ptr<View> host_view_ = nullptr;
// Monitors child views so we will be notified if their "ignored by layout"
// state changes. This should only ever be observing anything for the root
// layout manager, which in turn will propagate changes to owned layout
// managers as needed.
base::ScopedMultiSourceObservation<View, ViewObserver> view_observations_{
this};
std::map<const View*, ChildInfo> child_infos_;
std::vector<std::unique_ptr<LayoutManagerBase>> owned_layouts_;
raw_ptr<LayoutManagerBase> parent_layout_ = nullptr;
// Used to suspend invalidation while processing signals from the host view,
// or while invalidating the host view without invalidating the layout.
bool suppress_invalidate_ = false;
// Used during layout to determine if available size has changed for children;
// when it changes, children are always laid out regardless of visibility or
// whether their bounds have changed.
SizeBounds cached_available_size_;
#if (DCHECK_IS_ON())
// Used to prevent GetProposedLayout() from being re-entrant.
mutable bool calculating_layout_ = false;
#endif
// Do some really simple caching because layout generation can cost as much
// as 1ms or more for complex views.
mutable std::optional<gfx::Size> cached_minimum_size_;
mutable std::optional<gfx::Size> cached_preferred_size_;
mutable std::optional<gfx::Size> cached_height_for_width_;
mutable std::optional<gfx::Size> cached_layout_size_;
mutable ProposedLayout cached_layout_;
};
// Provides methods for doing additional, manual manipulation of a
// `LayoutManagerBase` and its managed Views inside its host View's
// layout implementation, ideally before `LayoutManager::Layout()` is invoked.
//
// In most cases, the layout manager should do all of the layout. However, in
// some cases, specific children of the host may be explicitly manipulated; for
// example, to conditionally show a button which (if visible) should be included
// in the layout.
//
// All of the direct manipulation functions on `LayoutManagerBase` and `View`,
// such as `View::SetVisible()` and
// `LayoutManagerBase::SetChildIncludedInLayout()`, cause cascades of layout
// invalidation up the Views tree, so are not appropriate to be used inside of a
// `Layout()` override. In the case that manual layout manipulation is required
// alongside the use of a layout manager, a `ManualLayoutUtil` should be used
// instead of callin those other methods directly.
//
// This class should only be instantiated and used inside the `Layout()` method
// of a `View` or derived class, before `LayoutManager::Layout()` is invoked.
class VIEWS_EXPORT ManualLayoutUtil {
public:
explicit ManualLayoutUtil(LayoutManagerBase* layout_manager);
~ManualLayoutUtil();
ManualLayoutUtil(const ManualLayoutUtil&) = delete;
void operator=(const ManualLayoutUtil&) = delete;
// Includes, or excludes and hides, `child_view`.
//
// Example:
// ```
// MyView::Layout(PassKey) {
// // Only include `foo_button_` in the layout if the feature is enabled;
// // otherwise hide it.
// ManualLayoutUtil layout_util(flex_layout_.get());
// layout_util.SetViewHidden(foo_button_, !foo_enabled);
//
// // Do the standard Views layout, which invokes the layout manager.
// LayoutSuperclass<View>(this);
// }
// ```
//
// Note that if instead the code had read
// `foo_button_.SetVisible(foo_enabled)`, the current view and every view up
// the hierarchy would be invalidated, which could result in a layout loop.
void SetViewHidden(View* child_view, bool hidden);
// This is implementation for the method below; the exclusion is ended when
// the TemporaryExclusion goes out of scope.
using TemporaryExclusionData =
std::pair<const raw_ptr<ManualLayoutUtil>, const raw_ptr<View>>;
struct VIEWS_EXPORT TemporaryExclusionDeleter {
void operator()(TemporaryExclusionData*) const;
};
using TemporaryExclusion =
std::unique_ptr<TemporaryExclusionData,
ManualLayoutUtil::TemporaryExclusionDeleter>;
// Temporarily removes `child_view` from the layout. Use during manual layout
// to see how the layout would look without the child view. When the return
// value goes out of scope, the exclusion is undone.
[[nodiscard]] TemporaryExclusion TemporarilyExcludeFromLayout(
View* child_view);
private:
void EndTemporaryExclusion(View* child_view);
const raw_ptr<LayoutManagerBase> layout_manager_;
};
} // namespace views
#endif // UI_VIEWS_LAYOUT_LAYOUT_MANAGER_BASE_H_
|