#include "ui/views/view.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <iomanip>
#include <iterator>
#include <memory>
#include <optional>
#include <sstream>
#include <string_view>
#include <utility>
#include "base/auto_reset.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/debug/stack_trace.h"
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "base/tracing/protos/chrome_track_event.pbzero.h"
#include "build/build_config.h"
#include "cc/paint/paint_flags.h"
#include "third_party/perfetto/include/perfetto/tracing/event_context.h"
#include "third_party/perfetto/include/perfetto/tracing/track_event.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/actions/actions.h"
#include "ui/base/accelerators/accelerator_manager.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/metadata/base_type_conversion.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/menu_source_type.mojom.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_provider.h"
#include "ui/compositor/clip_recorder.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_type.h"
#include "ui/compositor/paint_context.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/compositor/transform_recorder.h"
#include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_target_iterator.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/accessibility/accessibility_paint_checks.h"
#include "ui/views/accessibility/ax_update_notifier.h"
#include "ui/views/accessibility/ax_virtual_view.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/actions/action_view_interface.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/buildflags.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/drag_controller.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_observer.h"
#include "ui/views/view_tracker.h"
#include "ui/views/view_utils.h"
#include "ui/views/views_features.h"
#include "ui/views/views_switches.h"
#include "ui/views/widget/tooltip_manager.h"
#include "ui/views/widget/widget.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_gdi_object.h"
#include "ui/native_theme/native_theme_win.h"
#endif
namespace ui {
class ClipboardFormatType;
enum class PropertyChangeReason;
}
namespace views {
namespace {
#if BUILDFLAG(IS_WIN)
constexpr bool kContextMenuOnMousePress = false;
#else
constexpr bool kContextMenuOnMousePress = true;
#endif
constexpr int kDefaultHorizontalDragThreshold = 8;
constexpr int kDefaultVerticalDragThreshold = 8;
constexpr int kXChangedKey = sizeof(int) * 0;
constexpr int kYChangedKey = sizeof(int) * 1;
constexpr int kWidthChangedKey = sizeof(int) * 2;
constexpr int kHeightChangedKey = sizeof(int) * 3;
const View* GetHierarchyRoot(const View* view) {
const View* root = view;
while (root && root->parent()) {
root = root->parent();
}
return root;
}
}
namespace internal {
#if DCHECK_IS_ON()
ScopedChildrenLock::ScopedChildrenLock(const View* view)
: reset_(&view->iterating_, true) {}
ScopedChildrenLock::~ScopedChildrenLock() = default;
#endif
}
class VIEWS_EXPORT ViewMaskLayer : public ui::LayerDelegate,
public ViewObserver {
public:
ViewMaskLayer(const SkPath& path, View* observed_view);
ViewMaskLayer(const ViewMaskLayer& mask_layer) = delete;
ViewMaskLayer& operator=(const ViewMaskLayer& mask_layer) = delete;
~ViewMaskLayer() override;
ui::Layer* layer() { return &layer_; }
private:
void OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) override;
void OnPaintLayer(const ui::PaintContext& context) override;
void OnViewBoundsChanged(View* observed_view) override;
base::ScopedObservation<View, ViewObserver> observed_view_{this};
SkPath path_;
ui::Layer layer_;
};
ViewMaskLayer::ViewMaskLayer(const SkPath& path, View* observed_view)
: path_{path} {
layer_.set_delegate(this);
layer_.SetFillsBoundsOpaquely(false);
layer_.SetName("ViewMaskLayer");
observed_view_.Observe(observed_view);
OnViewBoundsChanged(observed_view);
}
ViewMaskLayer::~ViewMaskLayer() {
layer_.set_delegate(nullptr);
}
void ViewMaskLayer::OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) {}
void ViewMaskLayer::OnPaintLayer(const ui::PaintContext& context) {
cc::PaintFlags flags;
flags.setAlphaf(1.0f);
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setAntiAlias(true);
ui::PaintRecorder recorder(context, layer()->size());
recorder.canvas()->DrawPath(path_, flags);
}
void ViewMaskLayer::OnViewBoundsChanged(View* observed_view) {
layer_.SetBounds(observed_view->GetLocalBounds());
}
View::View() {
SetTargetHandler(this);
if (use_default_fill_layout_) {
SetToDefaultFillLayout();
}
static bool capture_stack_trace =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kViewStackTraces);
if (capture_stack_trace) {
SetProperty(kViewStackTraceKey,
std::make_unique<base::debug::StackTrace>());
}
}
View::~View() {
if (layouts_since_last_paint_) {
UMA_HISTOGRAM_COUNTS_100("Views.UnnecessaryLayouts",
layouts_since_last_paint_);
}
observers_.Notify(&ViewObserver::OnViewHierarchyWillBeDeleted, this);
life_cycle_state_ = LifeCycleState::kDestroying;
if (parent_) {
parent_->RemoveChildView(this);
}
DCHECK_EQ(next_focusable_view_, nullptr);
DCHECK_EQ(previous_focusable_view_, nullptr);
layout_manager_.reset();
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->parent_ = nullptr;
if (child->previous_focusable_view_) {
child->previous_focusable_view_->next_focusable_view_ = nullptr;
}
if (child->next_focusable_view_) {
child->next_focusable_view_->previous_focusable_view_ = nullptr;
}
child->next_focusable_view_ = nullptr;
child->previous_focusable_view_ = nullptr;
if (!child->owned_by_client_) {
delete child;
}
}
children_.clear();
}
observers_.Notify(&ViewObserver::OnViewIsDeleting, this);
for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
layer->RemoveObserver(this);
}
ClearProperties();
life_cycle_state_ = LifeCycleState::kDestroyed;
}
const Widget* View::GetWidget() const {
return widget_;
}
Widget* View::GetWidget() {
return const_cast<Widget*>(const_cast<const View*>(this)->GetWidget());
}
void View::ReorderChildView(View* view, size_t index) {
DCHECK_EQ(view->parent_, this);
const auto i = std::ranges::find(children_, view);
DCHECK(i != children_.end());
const auto pos =
std::next(children_.begin(),
static_cast<ptrdiff_t>(std::min(index, children_.size() - 1)));
if (i == pos) {
return;
}
#if DCHECK_IS_ON()
DCHECK(!iterating_);
#endif
if (pos < i) {
std::rotate(pos, i, std::next(i));
} else {
std::rotate(i, std::next(i), std::next(pos));
}
view->RemoveFromFocusList();
SetFocusSiblings(view, pos);
observers_.Notify(&ViewObserver::OnChildViewReordered, this, view);
ReorderLayers();
InvalidateLayout();
}
void View::RemoveChildView(View* view) {
DoRemoveChildView(view, true, false, nullptr);
}
void View::RemoveAllChildViews() {
while (!children_.empty()) {
DoRemoveChildView(children_.front(), false, true, nullptr);
}
UpdateTooltip();
}
void View::RemoveAllChildViewsWithoutDeleting() {
while (!children_.empty()) {
DoRemoveChildView(children_.front(), false, false, nullptr);
}
UpdateTooltip();
}
bool View::Contains(const View* view) const {
for (const View* v = view; v; v = v->parent_) {
if (v == this) {
return true;
}
}
return false;
}
View::Views::const_iterator View::FindChild(const View* view) const {
return std::ranges::find(children_, view);
}
std::optional<size_t> View::GetIndexOf(const View* view) const {
const auto i = FindChild(view);
return i == children_.cend() ? std::nullopt
: std::make_optional(static_cast<size_t>(
std::distance(children_.cbegin(), i)));
}
void View::PropagateWillClearFocusManager() {
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->PropagateWillClearFocusManager();
}
}
WillClearFocusManager();
}
void View::SetBounds(int x, int y, int width, int height) {
SetBoundsRect(gfx::Rect(x, y, std::max(0, width), std::max(0, height)));
}
void View::SetBoundsRect(const gfx::Rect& bounds) {
if (bounds == bounds_) {
if (needs_layout_) {
needs_layout_ = false;
TRACE_EVENT1("views", "View::Layout(set_bounds)", "class",
GetClassName());
LayoutImmediately();
}
return;
}
bool is_size_changed = bounds_.size() != bounds.size();
SchedulePaintBoundsChanged(is_size_changed);
gfx::Rect prev = bounds_;
bounds_ = bounds;
SchedulePaintBoundsChanged(is_size_changed);
if (layer()) {
if (parent_) {
LayerOffsetData offset_data(
parent_->CalculateOffsetToAncestorWithLayer(nullptr));
offset_data += GetMirroredPosition().OffsetFromOrigin();
SetLayerBounds(size(), offset_data);
} else {
SetLayerBounds(bounds_.size(),
LayerOffsetData() + bounds_.OffsetFromOrigin());
}
if (GetMirrored() && bounds_.width() != prev.width()) {
for (View* child : children_) {
child->UpdateChildLayerBounds(
LayerOffsetData(layer()->device_scale_factor(),
child->GetMirroredPosition().OffsetFromOrigin()));
}
}
} else {
UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(nullptr));
}
OnBoundsChanged(prev);
GetViewAccessibility().SetBounds(gfx::RectF(bounds_));
if (needs_layout_ || is_size_changed) {
needs_layout_ = false;
TRACE_EVENT1("views", "View::Layout(bounds_changed)", "class",
GetClassName());
LayoutImmediately();
}
if (GetNeedsNotificationWhenVisibleBoundsChangeImpl()) {
OnVisibleBoundsChangedImpl();
}
if (descendants_to_notify_) {
for (views::View* i : *descendants_to_notify_) {
i->OnVisibleBoundsChangedImpl();
}
}
observers_.Notify(&ViewObserver::OnViewBoundsChanged, this);
if (prev.x() != bounds_.x()) {
OnPropertyChanged(
ui::metadata::MakeUniquePropertyKey(&bounds_, kXChangedKey),
PropertyEffects::kNone);
}
if (prev.y() != bounds_.y()) {
OnPropertyChanged(
ui::metadata::MakeUniquePropertyKey(&bounds_, kYChangedKey),
PropertyEffects::kNone);
}
if (prev.width() != bounds_.width()) {
OnPropertyChanged(
ui::metadata::MakeUniquePropertyKey(&bounds_, kWidthChangedKey),
PropertyEffects::kNone);
}
if (prev.height() != bounds_.height()) {
OnPropertyChanged(
ui::metadata::MakeUniquePropertyKey(&bounds_, kHeightChangedKey),
PropertyEffects::kNone);
}
}
void View::SetSize(const gfx::Size& size) {
SetBounds(x(), y(), size.width(), size.height());
}
void View::SetPosition(const gfx::Point& position) {
SetBounds(position.x(), position.y(), width(), height());
}
void View::SetX(int x) {
SetBounds(x, y(), width(), height());
}
void View::SetY(int y) {
SetBounds(x(), y, width(), height());
}
gfx::Rect View::GetContentsBounds() const {
gfx::Rect contents_bounds(GetLocalBounds());
contents_bounds.Inset(GetInsets());
return contents_bounds;
}
gfx::Rect View::GetLocalBounds() const {
return gfx::Rect(size());
}
gfx::Insets View::GetInsets() const {
return border_ ? border_->GetInsets() : gfx::Insets();
}
gfx::Rect View::GetVisibleBounds() const {
if (!IsDrawn()) {
return gfx::Rect();
}
gfx::Rect vis_bounds(GetLocalBounds());
gfx::Rect ancestor_bounds;
const View* view = this;
gfx::Transform transform;
while (view != nullptr && !vis_bounds.IsEmpty()) {
transform.PostConcat(view->GetTransform());
gfx::Transform translation;
translation.Translate(static_cast<float>(view->GetMirroredX()),
static_cast<float>(view->y()));
transform.PostConcat(translation);
vis_bounds = view->ConvertRectToParent(vis_bounds);
const View* ancestor = view->parent_;
if (ancestor != nullptr) {
ancestor_bounds.SetRect(0, 0, ancestor->width(), ancestor->height());
vis_bounds.Intersect(ancestor_bounds);
} else if (!view->GetWidget()) {
return gfx::Rect();
}
view = ancestor;
}
if (vis_bounds.IsEmpty()) {
return vis_bounds;
}
return transform.InverseMapRect(vis_bounds).value_or(vis_bounds);
}
gfx::Rect View::GetBoundsInScreen() const {
gfx::Point origin;
View::ConvertPointToScreen(this, &origin);
return gfx::Rect(origin, size());
}
gfx::Rect View::GetAnchorBoundsInScreen() const {
return GetBoundsInScreen();
}
gfx::Size View::GetPreferredSize(const SizeBounds& available_size) const {
if (preferred_size_) {
return *preferred_size_;
}
return CalculatePreferredSize(available_size);
}
int View::GetBaseline() const {
return -1;
}
void View::SetPreferredSize(std::optional<gfx::Size> size) {
if (preferred_size_ == size) {
return;
}
preferred_size_ = std::move(size);
PreferredSizeChanged();
}
void View::SizeToPreferredSize() {
SetSize(GetPreferredSize());
}
gfx::Size View::GetMinimumSize() const {
if (HasLayoutManager()) {
return GetLayoutManager()->GetMinimumSize(this);
}
return GetPreferredSize(SizeBounds(0, 0));
}
gfx::Size View::GetMaximumSize() const {
return gfx::Size();
}
int View::GetHeightForWidth(int w) const {
return GetPreferredSize(SizeBounds(w, {})).height();
}
SizeBounds View::GetAvailableSize(const View* child) const {
if (HasLayoutManager()) {
return GetLayoutManager()->GetAvailableSize(this, child);
}
return SizeBounds();
}
bool View::GetVisible() const {
return visible_;
}
void View::SetVisible(bool visible) {
const bool was_visible = visible_;
if (was_visible != visible) {
if (was_visible) {
SchedulePaint();
}
visible_ = visible;
GetViewAccessibility().UpdateInvisibleByInheritanceRecursive(this,
!visible);
AdvanceFocusIfNecessary();
if (parent_) {
parent_->ChildVisibilityChanged(this);
if (!view_accessibility_ || !view_accessibility_->GetIsIgnored()) {
parent_->NotifyAccessibilityEventDeprecated(
ax::mojom::Event::kChildrenChanged, true);
}
}
PropagateVisibilityNotifications(this, visible_);
UpdateLayerVisibility();
OnPropertyChanged(&visible_, PropertyEffects::kPaint);
if (was_visible) {
UpdateTooltip();
}
}
if (parent_) {
LayoutManager* const layout_manager = parent_->GetLayoutManager();
if (layout_manager && layout_manager->view_setting_visibility_on_ != this) {
layout_manager->ViewVisibilitySet(parent_, this, was_visible, visible);
}
}
}
base::CallbackListSubscription View::AddVisibleChangedCallback(
PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&visible_, std::move(callback));
}
bool View::IsDrawn() const {
return visible_ && parent_ ? parent_->IsDrawn() : false;
}
bool View::GetIsDrawn() const {
return IsDrawn();
}
bool View::GetEnabled() const {
return enabled_;
}
void View::SetEnabled(bool enabled) {
if (enabled_ == enabled) {
return;
}
enabled_ = enabled;
UpdateEnabledInViewsSubtreeState();
OnPropertyChanged(&enabled_, PropertyEffects::kPaint);
}
base::CallbackListSubscription View::AddEnabledChangedCallback(
PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&enabled_, std::move(callback));
}
bool View::GetEnabledInViewsSubtree() const {
return enabled_in_views_subtree_;
}
[[nodiscard]] base::CallbackListSubscription
View::AddEnabledInViewsSubtreeChangedCallback(
PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&enabled_in_views_subtree_,
std::move(callback));
}
View::Views View::GetChildrenInZOrder() {
if (HasLayoutManager()) {
const auto result = GetLayoutManager()->GetChildViewsInPaintOrder(this);
DCHECK_EQ(children_.size(), result.size());
return result;
}
return children_;
}
gfx::Transform View::GetTransform() const {
if (!layer()) {
return gfx::Transform();
}
gfx::Transform transform = layer()->transform();
gfx::PointF scroll_offset = layer()->CurrentScrollOffset();
transform.Translate((GetMirrored() ? 1 : -1) * scroll_offset.x(),
-scroll_offset.y());
return transform;
}
void View::SetClipPath(const SkPath& path) {
clip_path_ = path;
if (layer()) {
CreateMaskLayer();
}
}
void View::SetTransform(const gfx::Transform& transform) {
if (transform.IsIdentity()) {
if (layer()) {
layer()->SetTransform(transform);
}
paint_to_layer_for_transform_ = false;
CreateOrDestroyLayer();
} else {
paint_to_layer_for_transform_ = true;
CreateOrDestroyLayer();
DCHECK_NE(layer(), nullptr);
layer()->SetTransform(transform);
layer()->ScheduleDraw();
}
for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
layer->SetTransform(transform);
}
}
void View::SetPaintToLayer(ui::LayerType layer_type) {
if (paint_to_layer_explicitly_set_) {
DCHECK_NE(layer(), nullptr);
if (layer()->type() == layer_type) {
return;
}
}
DestroyLayerImpl(LayerChangeNotifyBehavior::DONT_NOTIFY);
paint_to_layer_explicitly_set_ = true;
CreateLayer(layer_type);
if (!clip_path_.isEmpty() && !mask_layer_) {
CreateMaskLayer();
}
NotifyParentsOfLayerChange();
}
void View::DestroyLayer() {
paint_to_layer_explicitly_set_ = false;
CreateOrDestroyLayer();
}
void View::AddLayerToRegion(ui::Layer* new_layer, LayerRegion region) {
AddLayerToRegionImpl(
new_layer, region == LayerRegion::kAbove ? layers_above_ : layers_below_);
}
void View::RemoveLayerFromRegions(ui::Layer* old_layer) {
RemoveLayerFromRegionsKeepInLayerTree(old_layer);
ui::Layer* parent_layer = layer()->parent();
if (parent_layer && parent_layer == old_layer->parent()) {
parent_layer->Remove(old_layer);
}
CreateOrDestroyLayer();
}
void View::RemoveLayerFromRegionsKeepInLayerTree(ui::Layer* old_layer) {
auto remove_layer =
[old_layer, this](
std::vector<raw_ptr<ui::Layer, VectorExperimental>>& layer_vector) {
auto layer_pos = std::ranges::find(layer_vector, old_layer);
if (layer_pos == layer_vector.end()) {
return false;
}
layer_vector.erase(layer_pos);
old_layer->RemoveObserver(this);
return true;
};
const bool layer_removed =
remove_layer(layers_below_) || remove_layer(layers_above_);
DCHECK(layer_removed) << "Attempted to remove a layer that was never added.";
}
std::vector<ui::Layer*> View::GetLayersInOrder(ViewLayer view_layer) {
if (!layer()) {
DCHECK(layers_above_.empty());
DCHECK(layers_below_.empty());
return {};
}
std::vector<ui::Layer*> result;
for (ui::Layer* layer_below : layers_below_) {
result.push_back(layer_below);
}
if (view_layer == ViewLayer::kInclude) {
result.push_back(layer());
}
for (ui::Layer* layer_above : layers_above_) {
result.push_back(layer_above);
}
return result;
}
void View::LayerDestroyed(ui::Layer* layer) {
RemoveLayerFromRegions(layer);
}
std::unique_ptr<ui::Layer> View::RecreateLayer() {
std::unique_ptr<ui::Layer> old_layer = LayerOwner::RecreateLayer();
Widget* widget = GetWidget();
if (widget) {
widget->LayerTreeChanged();
}
return old_layer;
}
bool View::GetClipLayerToVisibleBounds() const {
return clip_layer_to_visible_bounds_;
}
void View::SetClipLayerToVisibleBounds(bool clip_layer) {
if (clip_layer_to_visible_bounds_ == clip_layer) {
return;
}
bool remove_layer_clip = clip_layer_to_visible_bounds_;
auto* widget = GetWidget();
if (!clip_layer && widget) {
UnregisterForVisibleBoundsNotification();
}
clip_layer_to_visible_bounds_ = clip_layer;
if (clip_layer_to_visible_bounds_ && widget) {
RegisterForVisibleBoundsNotification();
}
UpdateLayerClipForVisibleBounds(remove_layer_clip);
OnPropertyChanged(&clip_layer_to_visible_bounds_, PropertyEffects::kNone);
}
gfx::Rect View::GetMirroredBounds() const {
gfx::Rect bounds(bounds_);
bounds.set_x(GetMirroredX());
return bounds;
}
gfx::Rect View::GetMirroredContentsBounds() const {
gfx::Rect bounds(bounds_);
bounds.Inset(GetInsets());
bounds.set_x(GetMirroredX());
return bounds;
}
gfx::Point View::GetMirroredPosition() const {
return gfx::Point(GetMirroredX(), y());
}
int View::GetMirroredX() const {
return parent_ ? parent_->GetMirroredXForRect(bounds_) : x();
}
int View::GetMirroredXForRect(const gfx::Rect& rect) const {
return GetMirrored() ? (width() - rect.x() - rect.width()) : rect.x();
}
gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const {
gfx::Rect mirrored_rect = rect;
mirrored_rect.set_x(GetMirroredXForRect(rect));
return mirrored_rect;
}
int View::GetMirroredXInView(int x) const {
return GetMirrored() ? width() - x : x;
}
int View::GetMirroredXWithWidthInView(int x, int w) const {
return GetMirrored() ? width() - x - w : x;
}
void View::DeprecatedLayoutImmediately() {
LayoutImmediately();
}
void View::Layout(PassKey) {
needs_layout_ = false;
if (HasLayoutManager()) {
GetLayoutManager()->Layout(this);
}
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
if (child->needs_layout_ || !HasLayoutManager() ||
child->GetProperty(kViewIgnoredByLayoutKey)) {
TRACE_EVENT1("views", "View::LayoutChildren", "class",
child->GetClassName());
child->needs_layout_ = false;
child->LayoutImmediately();
}
}
}
void View::SetLayoutManagerUseConstrainedSpace(
bool layout_manager_use_constrained_space) {
if (layout_manager_use_constrained_space ==
layout_manager_use_constrained_space_) {
return;
}
layout_manager_use_constrained_space_ = layout_manager_use_constrained_space;
InvalidateLayout();
}
void View::InvalidateLayout(bool avoid_propagate_during_layout) {
if (invalidating_) {
return;
}
if (Widget* widget = GetWidget(); widget && widget->IsClosed()) {
return;
}
base::AutoReset<bool> invalidating(&invalidating_, true);
++invalidates_during_layout_;
needs_layout_ = true;
observers_.Notify(&ViewObserver::OnViewLayoutInvalidated, this);
if (HasLayoutManager()) {
GetLayoutManager()->InvalidateLayout();
}
if (parent_) {
if (!avoid_propagate_during_layout || !parent_->performing_layout_) {
parent_->InvalidateLayout();
}
} else {
Widget* widget = GetWidget();
if (widget) {
widget->OnRootViewLayoutInvalidated();
}
}
}
LayoutManager* View::GetLayoutManager() const {
return layout_manager_.get();
}
void View::SetLayoutManager(std::nullptr_t) {
SetLayoutManagerImpl(nullptr);
}
bool View::GetUseDefaultFillLayout() const {
return use_default_fill_layout_;
}
void View::SetUseDefaultFillLayout(bool value) {
if (value == use_default_fill_layout_) {
return;
}
use_default_fill_layout_ = value;
if (use_default_fill_layout_) {
SetToDefaultFillLayout();
} else {
SetLayoutManager(nullptr);
}
OnPropertyChanged(&use_default_fill_layout_, PropertyEffects::kLayout);
}
const View* View::GetViewByID(int id) const {
if (id == id_) {
return const_cast<View*>(this);
}
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
if (const View* view = child->GetViewByID(id)) {
return view;
}
}
return nullptr;
}
View* View::GetViewByID(int id) {
return const_cast<View*>(const_cast<const View*>(this)->GetViewByID(id));
}
void View::SetID(int id) {
if (id == id_) {
return;
}
id_ = id;
OnPropertyChanged(&id_, PropertyEffects::kNone);
}
const View* View::GetViewByElementId(ui::ElementIdentifier element_id) const {
if (element_id == GetProperty(kElementIdentifierKey)) {
return const_cast<View*>(this);
}
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
if (const View* view = child->GetViewByElementId(element_id)) {
return view;
}
}
return nullptr;
}
View* View::GetViewByElementId(ui::ElementIdentifier element_id) {
return const_cast<View*>(
const_cast<const View*>(this)->GetViewByElementId(element_id));
}
base::CallbackListSubscription View::AddIDChangedCallback(
PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&id_, callback);
}
void View::SetGroup(int gid) {
DCHECK(group_ == -1 || group_ == gid);
if (group_ != gid) {
group_ = gid;
OnPropertyChanged(&group_, PropertyEffects::kNone);
}
}
void View::SetOwnedGroup(int group_id) {
DCHECK(owned_group_ == -1 || owned_group_ == group_id);
if (owned_group_ != group_id) {
owned_group_ = group_id;
OnPropertyChanged(&owned_group_, PropertyEffects::kNone);
}
}
int View::GetGroup() const {
return group_;
}
int View::GetOwnedGroup() const {
return owned_group_;
}
base::CallbackListSubscription View::AddGroupChangedCallback(
PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&group_, callback);
}
bool View::IsGroupFocusTraversable() const {
return true;
}
void View::GetViewsInGroup(int group, Views* views) {
if (group_ == group) {
views->push_back(this);
}
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->GetViewsInGroup(group, views);
}
}
View* View::GetSelectedViewForGroup(int group) {
Views views;
GetWidget()->GetRootView()->GetViewsInGroup(group, &views);
return views.empty() ? nullptr : views[0];
}
std::string View::GetObjectName() const {
return std::string(GetClassName());
}
void View::ConvertPointToTarget(const View* source,
const View* target,
gfx::Point* point) {
DCHECK(source);
DCHECK(target);
if (source == target) {
return;
}
const View* root = GetHierarchyRoot(target);
#if BUILDFLAG(IS_MAC)
if (GetHierarchyRoot(source) != root) {
const Widget* source_top_level_widget =
source->GetWidget()->GetTopLevelWidget();
const Widget* target_top_level_widget =
target->GetWidget()->GetTopLevelWidget();
CHECK_EQ(source_top_level_widget, target_top_level_widget);
}
#else
CHECK_EQ(GetHierarchyRoot(source), root);
#endif
if (source != root) {
source->ConvertPointForAncestor(root, point);
}
if (target != root) {
target->ConvertPointFromAncestor(root, point);
}
}
gfx::Point View::ConvertPointToTarget(const View* source,
const View* target,
const gfx::Point& point) {
gfx::Point local_point = point;
ConvertPointToTarget(source, target, &local_point);
return local_point;
}
void View::ConvertRectToTarget(const View* source,
const View* target,
gfx::RectF* rect) {
DCHECK(source);
DCHECK(target);
if (source == target) {
return;
}
const View* root = GetHierarchyRoot(target);
CHECK_EQ(GetHierarchyRoot(source), root);
if (source != root) {
source->ConvertRectForAncestor(root, rect);
}
if (target != root) {
target->ConvertRectFromAncestor(root, rect);
}
}
gfx::RectF View::ConvertRectToTarget(const View* source,
const View* target,
const gfx::RectF& rect) {
gfx::RectF local_rect = rect;
ConvertRectToTarget(source, target, &local_rect);
return local_rect;
}
gfx::Rect View::ConvertRectToTarget(const View* source,
const View* target,
const gfx::Rect& rect) {
constexpr float kDefaultAllowedConversionError = 0.00001f;
return gfx::ToEnclosedRectIgnoringError(
ConvertRectToTarget(source, target, gfx::RectF(rect)),
kDefaultAllowedConversionError);
}
void View::ConvertPointToWidget(const View* src, gfx::Point* p) {
DCHECK(src);
DCHECK(p);
src->ConvertPointForAncestor(nullptr, p);
}
void View::ConvertPointFromWidget(const View* dest, gfx::Point* p) {
DCHECK(dest);
DCHECK(p);
dest->ConvertPointFromAncestor(nullptr, p);
}
void View::ConvertPointToScreen(const View* src, gfx::Point* p) {
DCHECK(src);
DCHECK(p);
const Widget* widget = src->GetWidget();
if (widget) {
ConvertPointToWidget(src, p);
*p += widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
}
}
gfx::Point View::ConvertPointToScreen(const View* src, const gfx::Point& p) {
gfx::Point screen_pt = p;
ConvertPointToScreen(src, &screen_pt);
return screen_pt;
}
void View::ConvertPointFromScreen(const View* dst, gfx::Point* p) {
DCHECK(dst);
DCHECK(p);
const views::Widget* widget = dst->GetWidget();
if (!widget) {
return;
}
*p -= widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
ConvertPointFromWidget(dst, p);
}
gfx::Point View::ConvertPointFromScreen(const View* src, const gfx::Point& p) {
gfx::Point local_pt = p;
ConvertPointFromScreen(src, &local_pt);
return local_pt;
}
void View::ConvertRectToScreen(const View* src, gfx::Rect* rect) {
DCHECK(src);
DCHECK(rect);
gfx::Point new_origin = rect->origin();
views::View::ConvertPointToScreen(src, &new_origin);
rect->set_origin(new_origin);
}
gfx::Rect View::ConvertRectToParent(const gfx::Rect& rect) const {
gfx::Rect x_rect = GetTransform().MapRect(rect);
x_rect.Offset(GetMirroredPosition().OffsetFromOrigin());
return x_rect;
}
gfx::Rect View::ConvertRectToWidget(const gfx::Rect& rect) const {
gfx::Rect x_rect = rect;
for (const View* v = this; v; v = v->parent_) {
x_rect = v->ConvertRectToParent(x_rect);
}
return x_rect;
}
void View::SchedulePaint() {
SchedulePaintInRect(GetLocalBounds());
}
void View::SchedulePaintInRect(const gfx::Rect& rect) {
if (Widget* widget = GetWidget(); widget && widget->IsClosed()) {
return;
}
needs_paint_ = true;
SchedulePaintInRectImpl(rect);
}
std::string IntToHex(uint32_t value) {
std::stringstream stream;
stream << std::hex << std::setw(8) << std::setfill('0') << value;
return stream.str();
}
void View::Paint(const PaintInfo& parent_paint_info) {
CHECK_EQ(life_cycle_state_, LifeCycleState::kAlive)
<< "life_cycle_state_: 0x"
<< IntToHex(static_cast<uint32_t>(life_cycle_state_))
<< " Classname: " << GetClassName();
if (!ShouldPaint()) {
return;
}
if (!has_run_accessibility_paint_checks_) {
RunAccessibilityPaintChecks(this);
has_run_accessibility_paint_checks_ = true;
}
const gfx::Rect& parent_bounds =
!parent() ? GetMirroredBounds() : parent()->GetMirroredBounds();
PaintInfo paint_info = PaintInfo::CreateChildPaintInfo(
parent_paint_info, GetMirroredBounds(), parent_bounds.size(),
GetPaintScaleType(), !!layer());
needs_paint_ = false;
const ui::PaintContext& context = paint_info.context();
bool is_invalidated = true;
if (paint_info.context().CanCheckInvalid()) {
#if DCHECK_IS_ON()
if (!context.is_pixel_canvas()) {
gfx::Vector2d offset;
context.Visited(this);
View* view = this;
while (view->parent() && !view->layer()) {
DCHECK(view->GetTransform().IsIdentity());
offset += view->GetMirroredPosition().OffsetFromOrigin();
view = view->parent();
}
DCHECK_EQ(context.PaintOffset().x(), offset.x());
DCHECK_EQ(context.PaintOffset().y(), offset.y());
DCHECK_EQ(context.RootVisited(), view);
}
#endif
is_invalidated = paint_info.ShouldPaint();
}
TRACE_EVENT1("views", "View::Paint", "class", GetClassName());
if (layouts_since_last_paint_) {
UMA_HISTOGRAM_COUNTS_100("Views.UnnecessaryLayouts",
layouts_since_last_paint_ - 1);
layouts_since_last_paint_ = 0;
}
ui::ClipRecorder clip_recorder(parent_paint_info.context());
if (!layer()) {
if (clip_path_.isEmpty()) {
clip_recorder.ClipRect(gfx::Rect(paint_info.paint_recording_size()) +
paint_info.offset_from_parent());
} else {
gfx::Transform to_parent_recording_space;
to_parent_recording_space.Translate(paint_info.offset_from_parent());
to_parent_recording_space.Scale(
SkFloatToScalar(paint_info.paint_recording_scale_x()),
SkFloatToScalar(paint_info.paint_recording_scale_y()));
const SkPath clip_path_in_parent = clip_path_.makeTransform(
gfx::TransformToFlattenedSkMatrix(to_parent_recording_space));
clip_recorder.ClipPathWithAntiAliasing(clip_path_in_parent);
}
}
ui::TransformRecorder transform_recorder(context);
SetUpTransformRecorderForPainting(paint_info.offset_from_parent(),
&transform_recorder);
if (is_invalidated ||
!paint_cache_.UseCache(context, paint_info.paint_recording_size())) {
ui::PaintRecorder recorder(context, paint_info.paint_recording_size(),
paint_info.paint_recording_scale_x(),
paint_info.paint_recording_scale_y(),
&paint_cache_);
gfx::Canvas* canvas = recorder.canvas();
gfx::ScopedCanvas scoped_canvas(canvas);
if (flip_canvas_on_paint_for_rtl_ui_) {
scoped_canvas.FlipIfRTL(width());
}
OnPaint(canvas);
}
CHECK_EQ(life_cycle_state_, LifeCycleState::kAlive);
PaintChildren(paint_info);
}
void View::SetBackground(std::unique_ptr<Background> b) {
background_ = std::move(b);
if (background_ && GetWidget()) {
background_->OnViewThemeChanged(this);
}
SchedulePaint();
}
Background* View::GetBackground() const {
return background_.get();
}
void View::SetBorder(std::unique_ptr<Border> b) {
const gfx::Rect old_contents_bounds = GetContentsBounds();
border_ = std::move(b);
if (border_ && GetWidget()) {
border_->OnViewThemeChanged(this);
}
if (old_contents_bounds != GetContentsBounds()) {
InvalidateLayout();
}
SchedulePaint();
}
Border* View::GetBorder() const {
return border_.get();
}
const ui::ThemeProvider* View::GetThemeProvider() const {
const auto* widget = GetWidget();
return widget ? widget->GetThemeProvider() : nullptr;
}
const LayoutProvider* View::GetLayoutProvider() const {
if (!GetWidget()) {
return nullptr;
}
return LayoutProvider::Get();
}
const ui::ColorProvider* View::GetColorProvider() const {
const auto* widget = GetWidget();
return widget ? widget->GetColorProvider() : nullptr;
}
const ui::NativeTheme* View::GetNativeTheme() const {
if (parent()) {
return parent()->GetNativeTheme();
}
const Widget* widget = GetWidget();
if (widget) {
return widget->GetNativeTheme();
}
static bool has_crashed_reported = false;
if (!has_crashed_reported) {
DCHECK(false);
base::debug::DumpWithoutCrashing();
has_crashed_reported = true;
}
return ui::NativeTheme::GetInstanceForNativeUi();
}
bool View::GetFlipCanvasOnPaintForRTLUI() const {
return flip_canvas_on_paint_for_rtl_ui_;
}
void View::SetFlipCanvasOnPaintForRTLUI(bool enable) {
if (enable == flip_canvas_on_paint_for_rtl_ui_) {
return;
}
flip_canvas_on_paint_for_rtl_ui_ = enable;
OnPropertyChanged(&flip_canvas_on_paint_for_rtl_ui_, PropertyEffects::kPaint);
}
base::CallbackListSubscription
View::AddFlipCanvasOnPaintForRTLUIChangedCallback(
PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&flip_canvas_on_paint_for_rtl_ui_,
std::move(callback));
}
void View::SetMirrored(bool is_mirrored) {
if (is_mirrored_ && is_mirrored_.value() == is_mirrored) {
return;
}
is_mirrored_ = is_mirrored;
OnPropertyChanged(&is_mirrored_, PropertyEffects::kPaint);
}
bool View::GetMirrored() const {
return is_mirrored_.value_or(base::i18n::IsRTL());
}
View* View::GetEventHandlerForPoint(const gfx::Point& point) {
return GetEventHandlerForRect(gfx::Rect(point, gfx::Size(1, 1)));
}
View* View::GetEventHandlerForRect(const gfx::Rect& rect) {
return GetEffectiveViewTargeter()->TargetForRect(this, rect);
}
bool View::GetCanProcessEventsWithinSubtree() const {
return can_process_events_within_subtree_;
}
void View::SetCanProcessEventsWithinSubtree(bool can_process) {
if (can_process_events_within_subtree_ == can_process) {
return;
}
can_process_events_within_subtree_ = can_process;
OnPropertyChanged(&can_process_events_within_subtree_,
PropertyEffects::kNone);
}
View* View::GetTooltipHandlerForPoint(const gfx::Point& point) {
if (!HitTestPoint(point) || !GetCanProcessEventsWithinSubtree()) {
return nullptr;
}
View::Views children = GetChildrenInZOrder();
DCHECK_EQ(children_.size(), children.size());
for (views::View* child : base::Reversed(children)) {
if (!child->GetVisible()) {
continue;
}
gfx::Point point_in_child_coords(point);
ConvertPointToTarget(this, child, &point_in_child_coords);
View* handler = child->GetTooltipHandlerForPoint(point_in_child_coords);
if (handler) {
return handler;
}
}
return this;
}
ui::Cursor View::GetCursor(const ui::MouseEvent& event) {
return ui::Cursor();
}
bool View::HitTestPoint(const gfx::Point& point) const {
return HitTestRect(gfx::Rect(point, gfx::Size(1, 1)));
}
bool View::HitTestRect(const gfx::Rect& rect) const {
return GetEffectiveViewTargeter()->DoesIntersectRect(this, rect);
}
bool View::IsMouseHovered() const {
if (!GetWidget()) {
return false;
}
if (!GetWidget()->IsMouseEventsEnabled()) {
return false;
}
gfx::Point cursor_pos(display::Screen::Get()->GetCursorScreenPoint());
ConvertPointFromScreen(this, &cursor_pos);
return HitTestPoint(cursor_pos);
}
bool View::OnMousePressed(const ui::MouseEvent& event) {
return false;
}
bool View::OnMouseDragged(const ui::MouseEvent& event) {
return false;
}
void View::OnMouseReleased(const ui::MouseEvent& event) {}
void View::OnMouseCaptureLost() {}
void View::OnMouseMoved(const ui::MouseEvent& event) {}
void View::OnMouseEntered(const ui::MouseEvent& event) {}
void View::OnMouseExited(const ui::MouseEvent& event) {}
void View::SetMouseAndGestureHandler(View* new_handler) {
if (parent_) {
parent_->SetMouseAndGestureHandler(new_handler);
}
}
void View::SetMouseHandler(View* new_handler) {
if (parent_) {
parent_->SetMouseHandler(new_handler);
}
}
bool View::OnKeyPressed(const ui::KeyEvent& event) {
return false;
}
bool View::OnKeyReleased(const ui::KeyEvent& event) {
return false;
}
bool View::OnMouseWheel(const ui::MouseWheelEvent& event) {
return false;
}
void View::OnEvent(ui::Event* event) {
if (!GetEnabledInViewsSubtree()) {
return;
}
ui::EventHandler::OnEvent(event);
}
void View::OnKeyEvent(ui::KeyEvent* event) {
bool consumed = (event->type() == ui::EventType::kKeyPressed)
? OnKeyPressed(*event)
: OnKeyReleased(*event);
if (consumed) {
event->StopPropagation();
}
}
void View::OnMouseEvent(ui::MouseEvent* event) {
switch (event->type()) {
case ui::EventType::kMousePressed:
if (ProcessMousePressed(*event)) {
event->SetHandled();
}
return;
case ui::EventType::kMouseMoved:
if ((event->flags() &
(ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON |
ui::EF_MIDDLE_MOUSE_BUTTON)) == 0) {
OnMouseMoved(*event);
return;
}
[[fallthrough]];
case ui::EventType::kMouseDragged:
ProcessMouseDragged(event);
return;
case ui::EventType::kMouseReleased:
ProcessMouseReleased(*event);
return;
case ui::EventType::kMousewheel:
if (OnMouseWheel(*event->AsMouseWheelEvent())) {
event->SetHandled();
}
break;
case ui::EventType::kMouseEntered:
if (event->flags() & ui::EF_TOUCH_ACCESSIBILITY) {
NotifyAccessibilityEventDeprecated(ax::mojom::Event::kHover, true);
}
OnMouseEntered(*event);
break;
case ui::EventType::kMouseExited:
OnMouseExited(*event);
break;
default:
return;
}
}
void View::OnScrollEvent(ui::ScrollEvent* event) {}
void View::OnTouchEvent(ui::TouchEvent* event) {
NOTREACHED() << "Views should not receive touch events.";
}
void View::OnGestureEvent(ui::GestureEvent* event) {}
std::string_view View::GetLogContext() const {
return GetClassName();
}
void View::SetNotifyEnterExitOnChild(bool notify) {
notify_enter_exit_on_child_ = notify;
}
bool View::GetNotifyEnterExitOnChild() const {
return notify_enter_exit_on_child_;
}
const ui::InputMethod* View::GetInputMethod() const {
Widget* widget = const_cast<Widget*>(GetWidget());
return widget ? const_cast<const ui::InputMethod*>(widget->GetInputMethod())
: nullptr;
}
std::unique_ptr<ViewTargeter> View::SetEventTargeter(
std::unique_ptr<ViewTargeter> targeter) {
std::unique_ptr<ViewTargeter> old_targeter = std::move(targeter_);
targeter_ = std::move(targeter);
return old_targeter;
}
ViewTargeter* View::GetEffectiveViewTargeter() const {
DCHECK(GetWidget());
ViewTargeter* view_targeter = targeter();
if (!view_targeter) {
view_targeter = GetWidget()->GetRootView()->targeter();
}
CHECK(view_targeter);
return view_targeter;
}
WordLookupClient* View::GetWordLookupClient() {
return nullptr;
}
bool View::CanAcceptEvent(const ui::Event& event) {
return IsDrawn();
}
ui::EventTarget* View::GetParentTarget() {
return parent_;
}
std::unique_ptr<ui::EventTargetIterator> View::GetChildIterator() const {
return std::make_unique<ui::EventTargetIteratorPtrImpl<View>>(children_);
}
ui::EventTargeter* View::GetEventTargeter() {
return targeter_.get();
}
void View::ConvertEventToTarget(const ui::EventTarget* target,
ui::LocatedEvent* event) const {
event->ConvertLocationToTarget(this, static_cast<const View*>(target));
}
gfx::PointF View::GetScreenLocationF(const ui::LocatedEvent& event) const {
DCHECK_EQ(this, event.target());
gfx::Point screen_location(event.location());
ConvertPointToScreen(this, &screen_location);
return gfx::PointF(screen_location);
}
void View::AddAccelerator(const ui::Accelerator& accelerator) {
if (!accelerators_) {
accelerators_ = std::make_unique<std::vector<ui::Accelerator>>();
}
if (!base::Contains(*accelerators_, accelerator)) {
accelerators_->push_back(accelerator);
}
RegisterPendingAccelerators();
}
void View::RemoveAccelerator(const ui::Accelerator& accelerator) {
CHECK(accelerators_) << "Removing non-existent accelerator "
<< accelerator.GetShortcutText();
const auto found_iter = std::ranges::find(*accelerators_, accelerator);
CHECK(found_iter != accelerators_->end())
<< "Removing non-existent accelerator " << accelerator.GetShortcutText();
const auto index = static_cast<size_t>(found_iter - accelerators_->begin());
accelerators_->erase(found_iter);
if (index >= registered_accelerator_count_) {
return;
}
--registered_accelerator_count_;
if (auto* focus_manager = GetFocusManager()) {
focus_manager->UnregisterAccelerator(accelerator, this);
}
}
void View::ResetAccelerators() {
UnregisterAccelerators(false);
}
bool View::AcceleratorPressed(const ui::Accelerator& accelerator) {
return false;
}
bool View::CanHandleAccelerators() const {
const Widget* widget = GetWidget();
if (!GetEnabledInViewsSubtree() || !IsDrawn() || !widget ||
!widget->IsVisible()) {
return false;
}
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
bool child = widget && widget->GetTopLevelWidget() != widget;
bool focus_in_child = widget && widget->GetRootView()->Contains(
GetFocusManager()->GetFocusedView());
if ((child && !focus_in_child) || (!child && !widget->IsActive())) {
return false;
}
#endif
return true;
}
base::span<const ui::Accelerator> View::GetAccelerators() const {
return accelerators_ ? *accelerators_ : base::span<const ui::Accelerator>();
}
bool View::HasFocus() const {
const FocusManager* focus_manager = GetFocusManager();
return focus_manager && (focus_manager->GetFocusedView() == this);
}
View* View::GetNextFocusableView() {
return next_focusable_view_;
}
const View* View::GetNextFocusableView() const {
return next_focusable_view_;
}
View* View::GetPreviousFocusableView() {
return previous_focusable_view_;
}
void View::RemoveFromFocusList() {
View* const old_prev = previous_focusable_view_;
View* const old_next = next_focusable_view_;
previous_focusable_view_ = nullptr;
next_focusable_view_ = nullptr;
if (old_prev) {
old_prev->next_focusable_view_ = old_next;
}
if (old_next) {
old_next->previous_focusable_view_ = old_prev;
}
}
void View::InsertBeforeInFocusList(View* view) {
DCHECK(view);
DCHECK_EQ(parent_, view->parent_);
if (view == next_focusable_view_) {
return;
}
RemoveFromFocusList();
next_focusable_view_ = view;
previous_focusable_view_ = view->previous_focusable_view_;
if (previous_focusable_view_) {
previous_focusable_view_->next_focusable_view_ = this;
}
next_focusable_view_->previous_focusable_view_ = this;
}
void View::InsertAfterInFocusList(View* view) {
DCHECK(view);
DCHECK_EQ(parent_, view->parent_);
if (view == previous_focusable_view_) {
return;
}
RemoveFromFocusList();
if (view->next_focusable_view_) {
InsertBeforeInFocusList(view->next_focusable_view_);
return;
}
view->next_focusable_view_ = this;
previous_focusable_view_ = view;
}
View::Views View::GetChildrenFocusList() {
View* starting_focus_view = nullptr;
Views children_views = children();
for (View* child : children_views) {
if (child->GetPreviousFocusableView() == nullptr) {
starting_focus_view = child;
break;
}
}
if (starting_focus_view == nullptr) {
return {};
}
Views result;
base::flat_set<View*> seen_views;
View* cur = starting_focus_view;
while (cur != nullptr) {
if (seen_views.contains(cur)) {
LOG(ERROR) << "View focus cycle detected.";
return {};
}
seen_views.insert(cur);
result.push_back(cur);
cur = cur->GetNextFocusableView();
}
return result;
}
View::FocusBehavior View::GetFocusBehavior() const {
return focus_behavior_;
}
void View::SetFocusBehavior(FocusBehavior focus_behavior) {
if (GetFocusBehavior() == focus_behavior) {
return;
}
focus_behavior_ = focus_behavior;
GetViewAccessibility().UpdateFocusableState();
GetViewAccessibility().SetHasFocusableAncestorRecursive(focus_behavior_ !=
FocusBehavior::NEVER);
AdvanceFocusIfNecessary();
OnPropertyChanged(&focus_behavior_, PropertyEffects::kNone);
}
bool View::IsFocusable() const {
return GetFocusBehavior() == FocusBehavior::ALWAYS &&
GetEnabledInViewsSubtree() && IsDrawn();
}
FocusManager* View::GetFocusManager() {
Widget* widget = GetWidget();
FocusManager* focus_manager = GetProperty(kDetachedViewFocusManagerKey);
if (focus_manager) {
CHECK(!widget);
return focus_manager;
}
return widget ? widget->GetFocusManager() : nullptr;
}
const FocusManager* View::GetFocusManager() const {
return const_cast<View*>(this)->GetFocusManager();
}
void View::RequestFocus() {
FocusManager* focus_manager = GetFocusManager();
if (focus_manager) {
bool focusable = focus_manager->keyboard_accessible()
? GetViewAccessibility().IsAccessibilityFocusable()
: IsFocusable();
if (focusable) {
focus_manager->SetFocusedView(this);
}
}
}
bool View::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
return false;
}
FocusTraversable* View::GetFocusTraversable() {
return nullptr;
}
FocusTraversable* View::GetPaneFocusTraversable() {
return nullptr;
}
void View::SetTooltipText(const std::u16string& text) {
if (cached_tooltip_text_ == text) {
return;
}
std::u16string previous_tooltip_text = std::move(cached_tooltip_text_);
cached_tooltip_text_ = text;
OnTooltipTextChanged(previous_tooltip_text);
}
void View::OnTooltipTextChanged(const std::u16string& old_tooltip_text) {
TooltipTextChanged();
GetViewAccessibility().OnTooltipTextChanged(old_tooltip_text);
}
base::CallbackListSubscription View::AddTooltipTextChangedCallback(
PropertyChangedCallback callback) {
return AddPropertyChangedCallback(&cached_tooltip_text_, std::move(callback));
}
std::u16string View::GetRenderedTooltipText(const gfx::Point& p) const {
return GetTooltipText();
}
const std::u16string& View::GetTooltipText() const {
return cached_tooltip_text_;
}
void View::set_context_menu_controller(ContextMenuController* menu_controller) {
context_menu_controller_ = menu_controller;
GetViewAccessibility().SetShowContextMenu(menu_controller != nullptr);
}
void View::ShowContextMenu(const gfx::Point& p,
ui::mojom::MenuSourceType source_type) {
if (!context_menu_controller_) {
return;
}
context_menu_controller_->ShowContextMenuForView(this, p, source_type);
}
gfx::Point View::GetKeyboardContextMenuLocation() {
gfx::Rect vis_bounds = GetVisibleBounds();
gfx::Point screen_point(vis_bounds.x() + vis_bounds.width() / 2,
vis_bounds.y() + vis_bounds.height() / 2);
ConvertPointToScreen(this, &screen_point);
return screen_point;
}
bool View::GetDropFormats(int* formats,
std::set<ui::ClipboardFormatType>* format_types) {
return false;
}
bool View::AreDropTypesRequired() {
return false;
}
bool View::CanDrop(const OSExchangeData& data) {
return false;
}
void View::OnDragEntered(const ui::DropTargetEvent& event) {}
int View::OnDragUpdated(const ui::DropTargetEvent& event) {
return ui::DragDropTypes::DRAG_NONE;
}
void View::OnDragExited() {}
void View::OnDragDone() {}
View::DropCallback View::GetDropCallback(const ui::DropTargetEvent& event) {
return base::NullCallback();
}
bool View::ExceededDragThreshold(const gfx::Vector2d& delta) {
return (abs(delta.x()) > GetHorizontalDragThreshold() ||
abs(delta.y()) > GetVerticalDragThreshold());
}
ViewAccessibility& View::GetViewAccessibility() const {
if (!view_accessibility_) {
view_accessibility_ = ViewAccessibility::Create(const_cast<View*>(this));
}
return *view_accessibility_;
}
void View::SetAccessibleName(const std::u16string& name) {
SetAccessibleName(name, GetViewAccessibility().GetCachedNameFrom());
}
void View::SetAccessibleName(std::u16string name,
ax::mojom::NameFrom name_from) {
GetViewAccessibility().SetName(name, name_from);
}
void View::SetAccessibleName(View* naming_view) {
DCHECK(naming_view);
GetViewAccessibility().SetName(*naming_view);
}
std::u16string View::GetAccessibleName() const {
return GetViewAccessibility().GetCachedName();
}
void View::SetAccessibleRole(const ax::mojom::Role role) {
GetViewAccessibility().SetRole(role);
}
void View::SetAccessibleRole(const ax::mojom::Role role,
const std::u16string& role_description) {
GetViewAccessibility().SetRole(role, role_description);
}
ax::mojom::Role View::GetAccessibleRole() const {
return GetViewAccessibility().GetCachedRole();
}
void View::SetAccessibleDescription(const std::u16string& description) {
GetViewAccessibility().SetDescription(description);
}
void View::SetAccessibleDescription(
const std::u16string& description,
ax::mojom::DescriptionFrom description_from) {
GetViewAccessibility().SetDescription(description, description_from);
}
void View::SetAccessibleDescription(View* describing_view) {
DCHECK(describing_view);
GetViewAccessibility().SetDescription(*describing_view);
}
std::u16string View::GetAccessibleDescription() const {
return GetViewAccessibility().GetCachedDescription();
}
bool View::HandleAccessibleAction(const ui::AXActionData& action_data) {
switch (action_data.action) {
case ax::mojom::Action::kBlur:
if (HasFocus()) {
GetFocusManager()->ClearFocus();
return true;
}
break;
case ax::mojom::Action::kDoDefault: {
const gfx::Point center = GetLocalBounds().CenterPoint();
ui::MouseEvent press(ui::EventType::kMousePressed, center, center,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
OnEvent(&press);
ui::MouseEvent release(ui::EventType::kMouseReleased, center, center,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
OnEvent(&release);
return true;
}
case ax::mojom::Action::kFocus:
if (GetViewAccessibility().IsAccessibilityFocusable()) {
RequestFocus();
return true;
}
break;
case ax::mojom::Action::kScrollToMakeVisible:
ScrollRectToVisible(GetLocalBounds());
return true;
case ax::mojom::Action::kShowContextMenu:
ShowContextMenu(GetBoundsInScreen().CenterPoint(),
ui::mojom::MenuSourceType::kKeyboard);
return true;
default:
break;
}
return false;
}
gfx::NativeViewAccessible View::GetNativeViewAccessible() {
return GetViewAccessibility().GetNativeObject();
}
void View::NotifyAccessibilityEventDeprecated(ax::mojom::Event event_type,
bool send_native_event) {
GetViewAccessibility().NotifyEvent(event_type, send_native_event);
}
void View::OnAccessibilityEvent(ax::mojom::Event event_type) {}
void View::ScrollRectToVisible(const gfx::Rect& rect) {
if (parent_) {
parent_->ScrollRectToVisible(rect + bounds().OffsetFromOrigin());
}
}
void View::ScrollViewToVisible() {
ScrollRectToVisible(GetLocalBounds());
}
void View::AddObserver(ViewObserver* observer) {
CHECK(observer);
observers_.AddObserver(observer);
}
void View::RemoveObserver(ViewObserver* observer) {
observers_.RemoveObserver(observer);
}
bool View::HasObserver(const ViewObserver* observer) const {
return observers_.HasObserver(observer);
}
gfx::Size View::CalculatePreferredSize(const SizeBounds& available_size) const {
if (HasLayoutManager()) {
return GetLayoutManager()->GetPreferredSize(
this,
layout_manager_use_constrained_space_ ? available_size : SizeBounds());
}
return gfx::Size();
}
void View::PreferredSizeChanged() {
if (parent_) {
parent_->ChildPreferredSizeChanged(this);
}
InvalidateLayout();
observers_.Notify(&ViewObserver::OnViewPreferredSizeChanged, this);
}
bool View::GetNeedsNotificationWhenVisibleBoundsChange() const {
return false;
}
void View::OnVisibleBoundsChanged() {}
void View::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) {}
void View::VisibilityChanged(View* starting_from, bool is_visible) {}
void View::WillClearFocusManager() {
UnregisterAccelerators(true);
}
void View::NativeViewHierarchyChanged() {
RegisterPendingAccelerators();
}
void View::AddedToWidget() {}
void View::RemovedFromWidget() {}
void View::OnDidSchedulePaint(const gfx::Rect& rect) {}
void View::PaintChildren(const PaintInfo& paint_info) {
TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName());
RecursivePaintHelper(&View::Paint, paint_info);
}
void View::OnPaint(gfx::Canvas* canvas) {
TRACE_EVENT1("views", "View::OnPaint", "class", GetClassName());
OnPaintBackground(canvas);
OnPaintBorder(canvas);
}
void View::OnPaintBackground(gfx::Canvas* canvas) {
if (background_) {
TRACE_EVENT0("views", "View::OnPaintBackground");
background_->Paint(canvas, this);
}
}
void View::OnPaintBorder(gfx::Canvas* canvas) {
if (border_) {
TRACE_EVENT0("views", "View::OnPaintBorder");
border_->Paint(*this, canvas);
}
}
View::LayerOffsetData View::CalculateOffsetToAncestorWithLayer(
ui::Layer** layer_parent) {
if (layer()) {
if (layer_parent) {
*layer_parent = layer();
}
return LayerOffsetData(layer()->device_scale_factor());
}
if (!parent_) {
return LayerOffsetData();
}
LayerOffsetData offset_data =
parent_->CalculateOffsetToAncestorWithLayer(layer_parent);
return offset_data + GetMirroredPosition().OffsetFromOrigin();
}
void View::UpdateParentLayer() {
if (!layer()) {
return;
}
ui::Layer* parent_layer = nullptr;
if (parent_) {
parent_->CalculateOffsetToAncestorWithLayer(&parent_layer);
}
ReparentLayer(parent_layer);
}
void View::MoveLayerToParent(ui::Layer* parent_layer,
const LayerOffsetData& offset_data) {
LayerOffsetData local_offset_data(offset_data);
if (parent_layer != layer()) {
local_offset_data += GetMirroredPosition().OffsetFromOrigin();
}
if (layer() && parent_layer != layer()) {
SetLayerParent(parent_layer);
SetLayerBounds(size(), local_offset_data);
} else {
internal::ScopedChildrenLock lock(this);
for (views::View* child : GetChildrenInZOrder()) {
child->MoveLayerToParent(parent_layer, local_offset_data);
}
}
}
void View::UpdateLayerVisibility() {
bool visible = visible_;
for (const View* v = parent_; visible && v && !v->layer(); v = v->parent_) {
visible = v->GetVisible();
}
UpdateChildLayerVisibility(visible);
}
void View::UpdateChildLayerVisibility(bool ancestor_visible) {
const bool layers_visible = ancestor_visible && visible_;
if (layer()) {
for (ui::Layer* layer : GetLayersInOrder()) {
layer->SetVisible(layers_visible);
}
}
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->UpdateChildLayerVisibility(layers_visible);
}
}
}
void View::DestroyLayerImpl(LayerChangeNotifyBehavior notify_parents) {
DCHECK(layers_below_.empty());
DCHECK(layers_above_.empty());
if (!layer()) {
return;
}
std::vector<raw_ptr<ui::Layer, VectorExperimental>> children =
layer()->children();
ui::Layer* new_parent = layer()->parent();
for (ui::Layer* child : children) {
layer()->Remove(child);
if (new_parent) {
new_parent->Add(child);
}
}
mask_layer_.reset();
LayerOwner::DestroyLayer();
if (new_parent) {
ReorderLayers();
}
UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(nullptr));
SchedulePaint();
if (notify_parents == LayerChangeNotifyBehavior::NOTIFY) {
NotifyParentsOfLayerChange();
}
Widget* widget = GetWidget();
if (widget) {
widget->LayerTreeChanged();
}
}
void View::NotifyParentsOfLayerChange() {
View* view_parent = parent();
while (view_parent) {
view_parent->OnChildLayerChanged(this);
view_parent = view_parent->parent();
}
}
void View::UpdateChildLayerBounds(const LayerOffsetData& offset_data) {
if (layer()) {
SetLayerBounds(size(), offset_data);
} else {
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->UpdateChildLayerBounds(
offset_data + child->GetMirroredPosition().OffsetFromOrigin());
}
}
}
void View::OnPaintLayer(const ui::PaintContext& context) {
PaintFromPaintRoot(context);
}
void View::OnLayerTransformed(const gfx::Transform& old_transform,
ui::PropertyChangeReason reason) {
NotifyAccessibilityEventDeprecated(ax::mojom::Event::kLocationChanged, false);
observers_.Notify(&ViewObserver::OnViewLayerTransformed, this);
}
void View::OnLayerClipRectChanged(const gfx::Rect& old_rect,
ui::PropertyChangeReason reason) {
observers_.Notify(&ViewObserver::OnViewLayerClipRectChanged, this);
}
void View::OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) {
snap_layer_to_pixel_boundary_ =
(new_device_scale_factor - std::floor(new_device_scale_factor)) != 0.0f;
if (!layer()) {
return;
}
if (!parent() || !layer()->parent()) {
return;
}
if (layer()->parent() && layer()->GetCompositor() &&
layer()->GetCompositor()->is_pixel_canvas()) {
LayerOffsetData offset_data(
parent()->CalculateOffsetToAncestorWithLayer(nullptr));
offset_data += GetMirroredPosition().OffsetFromOrigin();
SnapLayerToPixelBoundary(offset_data);
} else {
SnapLayerToPixelBoundary(LayerOffsetData());
}
GetViewAccessibility().SetChildTreeScaleFactor(new_device_scale_factor);
}
void View::CreateOrDestroyLayer() {
if (paint_to_layer_explicitly_set_ || paint_to_layer_for_transform_ ||
!layers_below_.empty() || !layers_above_.empty()) {
if (!layer()) {
CreateLayer(ui::LAYER_TEXTURED);
}
} else if (layer()) {
DestroyLayerImpl(LayerChangeNotifyBehavior::NOTIFY);
}
}
void View::ReorderLayers() {
View* v = this;
while (v && !v->layer()) {
v = v->parent();
}
Widget* widget = GetWidget();
if (!v) {
if (widget) {
ui::Layer* layer = widget->GetLayer();
if (layer) {
widget->GetRootView()->ReorderChildLayers(layer);
}
}
} else {
v->ReorderChildLayers(v->layer());
}
if (widget) {
widget->ReorderNativeViews();
}
}
void View::AddLayerToRegionImpl(
ui::Layer* new_layer,
std::vector<raw_ptr<ui::Layer, VectorExperimental>>& layer_vector) {
DCHECK(new_layer);
DCHECK(!base::Contains(layer_vector, new_layer)) << "Layer already added.";
new_layer->AddObserver(this);
new_layer->SetVisible(GetVisible());
layer_vector.push_back(new_layer);
if (layer()) {
ui::Layer* const parent_layer = layer()->parent();
if (parent_layer && parent_layer != new_layer->parent()) {
parent_layer->Add(new_layer);
}
new_layer->SetBounds(gfx::Rect(new_layer->size()) +
layer()->bounds().OffsetFromOrigin());
if (parent()) {
parent()->ReorderLayers();
}
}
CreateOrDestroyLayer();
if (layer()->type() != ui::LAYER_SOLID_COLOR) {
layer()->SetFillsBoundsOpaquely(false);
}
}
void View::SetLayerParent(ui::Layer* parent_layer) {
for (ui::Layer* extra_layer : GetLayersInOrder(ViewLayer::kExclude)) {
parent_layer->Add(extra_layer);
}
parent_layer->Add(layer());
if (!layers_below_.empty()) {
parent_layer->StackAbove(layer(), layers_below_.back());
} else if (!layers_above_.empty()) {
parent_layer->StackBelow(layer(), layers_above_.front());
}
}
bool View::GetNeedsNotificationWhenVisibleBoundsChangeImpl() const {
return clip_layer_to_visible_bounds_ ||
GetNeedsNotificationWhenVisibleBoundsChange();
}
void View::OnVisibleBoundsChangedImpl() {
OnVisibleBoundsChanged();
if (!clip_layer_to_visible_bounds_) {
return;
}
UpdateLayerClipForVisibleBounds(false);
}
void View::UpdateLayerClipForVisibleBounds(bool remove_layer_clip) {
if (!layer()) {
return;
}
std::optional<gfx::Rect> clip_bounds = GetVisibleBounds();
if (remove_layer_clip || clip_bounds == GetLocalBounds()) {
clip_bounds.reset();
}
auto apply_clip_bounds = [](ui::Layer* layer,
std::optional<gfx::Rect>& clip_bounds) {
if (!clip_bounds) {
layer->SetClipRect({});
} else if (clip_bounds->IsEmpty()) {
constexpr gfx::Rect kOutOfBounds(-2, -2, 1, 1);
layer->SetClipRect(kOutOfBounds);
} else {
layer->SetClipRect(*clip_bounds);
}
};
apply_clip_bounds(layer(), clip_bounds);
for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
apply_clip_bounds(layer, clip_bounds);
}
}
void View::ReorderChildLayers(ui::Layer* parent_layer) {
if (layer() && layer() != parent_layer) {
DCHECK_EQ(parent_layer, layer()->parent());
for (ui::Layer* layer_above : layers_above_) {
parent_layer->StackAtBottom(layer_above);
}
parent_layer->StackAtBottom(layer());
for (ui::Layer* layer_below : layers_below_) {
parent_layer->StackAtBottom(layer_below);
}
} else {
View::Views children = GetChildrenInZOrder();
DCHECK_EQ(children_.size(), children.size());
for (views::View* child : base::Reversed(children)) {
child->ReorderChildLayers(parent_layer);
}
}
}
void View::OnChildLayerChanged(View* child) {}
View::DragInfo* View::GetDragInfo() {
return parent_ ? parent_->GetDragInfo() : nullptr;
}
void View::OnFocus() {}
void View::OnBlur() {}
void View::Focus() {
OnFocus();
if (!suppress_default_focus_handling_) {
FocusManager* focus_manager = GetFocusManager();
if (focus_manager) {
focus_manager->ClearNativeFocus();
}
AXVirtualView* const focused_virtual_child =
view_accessibility_ ? view_accessibility_->FocusedVirtualChild()
: nullptr;
if (!ui::AXPlatformNode::GetPopupFocusOverride() ||
ui::AXPlatformNode::GetPopupFocusOverride() ==
GetNativeViewAccessible()) {
if (focused_virtual_child) {
focused_virtual_child->NotifyEvent(ax::mojom::Event::kFocus, true);
} else {
NotifyAccessibilityEventDeprecated(ax::mojom::Event::kFocus, true);
}
}
}
ScrollView* const scroll_view = ScrollView::GetScrollViewForContents(this);
if (scroll_view) {
scroll_view->ScrollViewToVisible();
} else {
ScrollViewToVisible();
}
UpdateTooltipForFocus();
observers_.Notify(&ViewObserver::OnViewFocused, this);
}
void View::Blur() {
ViewTracker tracker(this);
OnBlur();
if (tracker.view()) {
observers_.Notify(&ViewObserver::OnViewBlurred, this);
}
}
void View::OnThemeChanged() {
#if DCHECK_IS_ON()
on_theme_changed_called_ = true;
#endif
}
void View::TooltipTextChanged() {
Widget* widget = GetWidget();
if (widget && !widget->IsClosed() && widget->GetTooltipManager()) {
widget->GetTooltipManager()->TooltipTextChanged(this);
}
OnPropertyChanged(&cached_tooltip_text_, PropertyEffects::kNone);
}
void View::UpdateTooltipForFocus() {
if (base::FeatureList::IsEnabled(
::views::features::kKeyboardAccessibleTooltipInViews) &&
!kShouldDisableKeyboardTooltipsForTesting) {
Widget* widget = GetWidget();
if (widget && widget->GetTooltipManager()) {
widget->GetTooltipManager()->UpdateTooltipForFocus(this);
}
}
}
int View::GetDragOperations(const gfx::Point& press_pt) {
return drag_controller_
? drag_controller_->GetDragOperationsForView(this, press_pt)
: ui::DragDropTypes::DRAG_NONE;
}
void View::WriteDragData(const gfx::Point& press_pt, OSExchangeData* data) {
DCHECK(drag_controller_);
drag_controller_->WriteDragDataForView(this, press_pt, data);
}
bool View::InDrag() const {
const Widget* widget = GetWidget();
return widget ? widget->dragged_view() == this : false;
}
int View::GetHorizontalDragThreshold() {
return kDefaultHorizontalDragThreshold;
}
int View::GetVerticalDragThreshold() {
return kDefaultVerticalDragThreshold;
}
PaintInfo::ScaleType View::GetPaintScaleType() const {
if (::features::IsPixelCanvasRecordingEnabled()) {
return PaintInfo::ScaleType::kScaleWithEdgeSnapping;
}
return PaintInfo::ScaleType::kUniformScaling;
}
void View::HandlePropertyChangeEffects(PropertyEffects effects) {
switch (effects) {
case PropertyEffects::kPreferredSizeChanged:
PreferredSizeChanged();
return;
case PropertyEffects::kLayout:
InvalidateLayout();
return;
case PropertyEffects::kPaint:
SchedulePaint();
return;
case PropertyEffects::kNone:
return;
}
NOTREACHED();
}
void View::AfterPropertyChange(const void* key, int64_t old_value) {
if (key == kElementIdentifierKey) {
const ui::ElementIdentifier old_element_id =
ui::ElementIdentifier::FromRawValue(
base::checked_cast<intptr_t>(old_value));
if (old_element_id) {
views::ElementTrackerViews::GetInstance()->UnregisterView(old_element_id,
this);
}
const ui::ElementIdentifier new_element_id =
GetProperty(kElementIdentifierKey);
if (new_element_id) {
views::ElementTrackerViews::GetInstance()->RegisterView(new_element_id,
this);
}
}
observers_.Notify(&ViewObserver::OnViewPropertyChanged, this, key, old_value);
}
void View::OnPropertyChanged(ui::metadata::PropertyKey property,
PropertyEffects property_effects) {
if (property_effects != PropertyEffects::kNone) {
HandlePropertyChangeEffects(property_effects);
}
TriggerChangedCallback(property);
}
int View::GetX() const {
return x();
}
int View::GetY() const {
return y();
}
int View::GetWidth() const {
return width();
}
int View::GetHeight() const {
return height();
}
void View::SetWidth(int width) {
SetBounds(x(), y(), width, height());
}
void View::SetHeight(int height) {
SetBounds(x(), y(), width(), height);
}
std::u16string View::GetTooltip() const {
return GetTooltipText();
}
void View::DragInfo::Reset() {
possible_drag = false;
start_pt = gfx::Point();
}
void View::DragInfo::PossibleDrag(const gfx::Point& p) {
possible_drag = true;
start_pt = p;
}
void View::SchedulePaintInRectImpl(const gfx::Rect& rect) {
OnDidSchedulePaint(rect);
if (!visible_) {
return;
}
if (layer()) {
layer()->SchedulePaint(rect);
} else if (parent_) {
parent_->SchedulePaintInRectImpl(ConvertRectToParent(rect));
}
}
void View::SchedulePaintBoundsChanged(bool size_changed) {
if (!visible_) {
return;
}
if (!layer() || size_changed) {
SchedulePaint();
} else {
layer()->ScheduleDraw();
}
}
void View::SchedulePaintOnParent() {
if (parent_) {
parent_->SchedulePaintInRect(ConvertRectToParent(GetLocalBounds()));
}
}
bool View::ShouldPaint() const {
return visible_ && !size().IsEmpty();
}
void View::SetUpTransformRecorderForPainting(
const gfx::Vector2d& offset_from_parent,
ui::TransformRecorder* recorder) const {
if (layer()) {
return;
}
gfx::Transform transform_from_parent;
transform_from_parent.Translate(offset_from_parent.x(),
offset_from_parent.y());
recorder->Transform(transform_from_parent);
}
void View::RecursivePaintHelper(void (View::*func)(const PaintInfo&),
const PaintInfo& info) {
View::Views children = GetChildrenInZOrder();
DCHECK_EQ(children_.size(), children.size());
for (views::View* child : children) {
if (!child->layer()) {
(child->*func)(info);
}
}
}
void View::PaintFromPaintRoot(const ui::PaintContext& parent_context) {
PaintInfo paint_info = PaintInfo::CreateRootPaintInfo(
parent_context, layer() ? layer()->size() : size());
Paint(paint_info);
static bool draw_view_bounds_rects =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDrawViewBoundsRects);
if (draw_view_bounds_rects) {
PaintDebugRects(paint_info);
}
}
void View::PaintDebugRects(const PaintInfo& parent_paint_info) {
if (!ShouldPaint()) {
return;
}
const gfx::Rect& parent_bounds = (layer() || !parent())
? GetMirroredBounds()
: parent()->GetMirroredBounds();
PaintInfo paint_info = PaintInfo::CreateChildPaintInfo(
parent_paint_info, GetMirroredBounds(), parent_bounds.size(),
GetPaintScaleType(), !!layer());
const ui::PaintContext& context = paint_info.context();
ui::TransformRecorder transform_recorder(context);
SetUpTransformRecorderForPainting(paint_info.offset_from_parent(),
&transform_recorder);
RecursivePaintHelper(&View::PaintDebugRects, paint_info);
ui::PaintRecorder recorder(context, paint_info.paint_recording_size(),
paint_info.paint_recording_scale_x(),
paint_info.paint_recording_scale_y(),
nullptr);
gfx::Canvas* canvas = recorder.canvas();
const float scale = canvas->UndoDeviceScaleFactor();
gfx::RectF outline_rect(ScaleToEnclosedRect(GetLocalBounds(), scale));
gfx::RectF content_outline_rect(
ScaleToEnclosedRect(GetContentsBounds(), scale));
const auto* color_provider = GetColorProvider();
if (content_outline_rect != outline_rect) {
content_outline_rect.Inset(0.5f);
canvas->DrawRect(content_outline_rect,
color_provider->GetColor(ui::kColorDebugContentOutline));
}
outline_rect.Inset(0.5f);
canvas->DrawRect(outline_rect,
color_provider->GetColor(ui::kColorDebugBoundsOutline));
}
void View::AddChildViewAtImpl(View* view, size_t index) {
CHECK_NE(view, this) << "You cannot add a view as its own child";
DCHECK_LE(index, children_.size());
View* parent = view->parent_;
if (parent == this) {
ReorderChildView(view, index);
return;
}
Widget* old_widget = view->GetWidget();
ui::NativeTheme* old_theme = old_widget ? view->GetNativeTheme() : nullptr;
if (parent) {
parent->DoRemoveChildView(view, true, false, this);
}
view->parent_ = this;
#if DCHECK_IS_ON()
DCHECK(!iterating_);
#endif
const auto pos = children_.insert(
std::next(children_.cbegin(), static_cast<ptrdiff_t>(index)), view);
Widget* widget = GetWidget();
if (widget) {
view->SetWidget(widget);
}
view->RemoveFromFocusList();
SetFocusSiblings(view, pos);
const bool did_reparent_any_layers = view->UpdateParentLayers();
if (did_reparent_any_layers && widget) {
widget->LayerTreeChanged();
}
ReorderLayers();
view->UpdateLayerVisibility();
if (GetViewAccessibility().IsAccessibilityEnabled()) {
view->GetViewAccessibility().CompleteCacheInitialization();
}
view->GetViewAccessibility().OnViewHasNewAncestor(this);
if (widget) {
view->GetViewAccessibility().OnWidgetUpdated(widget, old_widget);
}
if (HasLayoutManager()) {
GetLayoutManager()->ViewAdded(this, view);
}
if (widget && (view->GetNativeTheme() != old_theme)) {
view->PropagateThemeChanged();
}
ViewHierarchyChangedDetails details(true, this, view, parent);
for (View* v = this; v; v = v->parent_) {
v->ViewHierarchyChangedImpl(details);
}
view->PropagateAddNotifications(details, widget && widget != old_widget);
view->UpdateEnabledInViewsSubtreeState();
UpdateTooltip();
if (widget) {
RegisterChildrenForVisibleBoundsNotification(view);
if (view->GetVisible()) {
view->SchedulePaint();
}
}
observers_.Notify(&ViewObserver::OnChildViewAdded, this, view);
}
void View::DoRemoveChildView(View* view,
bool update_tool_tip,
bool delete_removed_view,
View* new_parent) {
DCHECK(view);
const auto i = FindChild(view);
if (i == children_.cend()) {
return;
}
std::unique_ptr<View> view_to_be_deleted;
view->RemoveFromFocusList();
Widget* widget = GetWidget();
bool is_removed_from_widget = false;
if (widget) {
UnregisterChildrenForVisibleBoundsNotification(view);
if (view->GetVisible()) {
view->SchedulePaint();
}
is_removed_from_widget = !new_parent || new_parent->GetWidget() != widget;
if (is_removed_from_widget) {
widget->NotifyWillRemoveView(view);
}
}
if (HasLayoutManager()) {
GetLayoutManager()->ViewRemoved(this, view);
}
view->PropagateRemoveNotifications(this, new_parent, is_removed_from_widget);
view->OrphanLayers();
if (widget) {
widget->LayerTreeChanged();
}
if (view->parent_) {
view->parent_->GetViewAccessibility().NotifyEvent(
ax::mojom::Event::kChildrenChanged, true);
}
view->parent_ = nullptr;
if (is_removed_from_widget) {
view->SetWidget(nullptr);
}
if (delete_removed_view && !view->owned_by_client_) {
view_to_be_deleted.reset(view);
}
#if DCHECK_IS_ON()
DCHECK(!iterating_);
#endif
children_.erase(i);
if (update_tool_tip) {
UpdateTooltip();
}
observers_.Notify(&ViewObserver::OnChildViewRemoved, this, view);
}
void View::PropagateRemoveNotifications(View* old_parent,
View* new_parent,
bool is_removed_from_widget) {
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->PropagateRemoveNotifications(old_parent, new_parent,
is_removed_from_widget);
}
}
UnregisterAccelerators(true);
ViewHierarchyChangedDetails details(false, old_parent, this, new_parent);
for (View* v = this; v; v = v->parent_) {
v->ViewHierarchyChangedImpl(details);
}
if (is_removed_from_widget) {
RemovedFromWidget();
GetViewAccessibility().OnViewRemovedFromWidget();
observers_.Notify(&ViewObserver::OnViewRemovedFromWidget, this);
}
}
void View::PropagateAddNotifications(const ViewHierarchyChangedDetails& details,
bool is_added_to_widget) {
RegisterPendingAccelerators();
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->PropagateAddNotifications(details, is_added_to_widget);
}
}
ViewHierarchyChangedImpl(details);
if (is_added_to_widget) {
AddedToWidget();
GetViewAccessibility().OnViewAddedToWidget();
observers_.Notify(&ViewObserver::OnViewAddedToWidget, this);
}
}
void View::PropagateNativeViewHierarchyChanged() {
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->PropagateNativeViewHierarchyChanged();
}
}
NativeViewHierarchyChanged();
}
void View::ViewHierarchyChangedImpl(
const ViewHierarchyChangedDetails& details) {
ViewHierarchyChanged(details);
observers_.Notify(&ViewObserver::OnViewHierarchyChanged, this, details);
details.parent->needs_layout_ = true;
}
void View::SetWidget(Widget* widget) {
widget_ = widget;
internal::ScopedChildrenLock lock(this);
for (View* child : children_) {
child->SetWidget(widget);
}
}
void View::PropagateVisibilityNotifications(View* start, bool is_visible) {
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->PropagateVisibilityNotifications(start, is_visible);
}
}
VisibilityChangedImpl(start, is_visible);
}
void View::VisibilityChangedImpl(View* starting_from, bool is_visible) {
VisibilityChanged(starting_from, is_visible);
observers_.Notify(&ViewObserver::OnViewVisibilityChanged, this, starting_from,
is_visible);
}
void View::SnapLayerToPixelBoundary(const LayerOffsetData& offset_data) {
if (!layer()) {
return;
}
#if DCHECK_IS_ON()
for (ui::Layer* layer_above_below : GetLayersInOrder(ViewLayer::kExclude)) {
DCHECK_EQ(layer()->parent(), layer_above_below->parent());
}
#endif
if (layer()->GetCompositor() && layer()->GetCompositor()->is_pixel_canvas()) {
gfx::Vector2dF offset = snap_layer_to_pixel_boundary_ && layer()->parent()
? offset_data.GetSubpixelOffset()
: gfx::Vector2dF();
layer()->SetSubpixelPositionOffset(offset);
for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
layer->SetSubpixelPositionOffset(offset);
}
}
}
void View::RegisterChildrenForVisibleBoundsNotification(View* view) {
if (view->GetNeedsNotificationWhenVisibleBoundsChangeImpl()) {
view->RegisterForVisibleBoundsNotification();
}
for (View* child : view->children_) {
RegisterChildrenForVisibleBoundsNotification(child);
}
}
void View::UnregisterChildrenForVisibleBoundsNotification(View* view) {
if (view->GetNeedsNotificationWhenVisibleBoundsChangeImpl()) {
view->UnregisterForVisibleBoundsNotification();
}
for (View* child : view->children_) {
UnregisterChildrenForVisibleBoundsNotification(child);
}
}
void View::RegisterForVisibleBoundsNotification() {
if (registered_for_visible_bounds_notification_) {
return;
}
registered_for_visible_bounds_notification_ = true;
for (View* ancestor = parent_; ancestor; ancestor = ancestor->parent_) {
ancestor->AddDescendantToNotify(this);
}
}
void View::UnregisterForVisibleBoundsNotification() {
if (!registered_for_visible_bounds_notification_) {
return;
}
registered_for_visible_bounds_notification_ = false;
for (View* ancestor = parent_; ancestor; ancestor = ancestor->parent_) {
ancestor->RemoveDescendantToNotify(this);
}
}
void View::AddDescendantToNotify(View* view) {
DCHECK(view);
if (!descendants_to_notify_) {
descendants_to_notify_ = std::make_unique<Views>();
}
descendants_to_notify_->push_back(view);
}
void View::RemoveDescendantToNotify(View* view) {
DCHECK(view);
DCHECK(descendants_to_notify_);
auto i = std::ranges::find(*descendants_to_notify_, view);
CHECK(i != descendants_to_notify_->end());
descendants_to_notify_->erase(i);
if (descendants_to_notify_->empty()) {
descendants_to_notify_.reset();
}
}
void View::SetLayoutManagerImpl(std::unique_ptr<LayoutManager> layout_manager) {
CHECK(!layout_manager || layout_manager_ != layout_manager);
layout_manager_ = std::move(layout_manager);
if (layout_manager_) {
layout_manager_->Installed(this);
}
if (!layout_manager_ && use_default_fill_layout_) {
SetToDefaultFillLayout();
return;
}
has_default_fill_layout_ = false;
}
void View::SetToDefaultFillLayout() {
SetLayoutManager(std::make_unique<FillLayout>())->SetIncludeInsets(false);
has_default_fill_layout_ = true;
}
void View::UpdateEnabledInViewsSubtreeState() {
bool new_state = GetEnabled();
if (parent() && !parent()->GetEnabledInViewsSubtree()) {
new_state = false;
}
if (enabled_in_views_subtree_ == new_state) {
return;
}
enabled_in_views_subtree_ = new_state;
GetViewAccessibility().SetIsEnabled(enabled_in_views_subtree_);
AdvanceFocusIfNecessary();
internal::ScopedChildrenLock lock(this);
for (views::View* child : base::Reversed(children_)) {
child->UpdateEnabledInViewsSubtreeState();
}
OnPropertyChanged(&enabled_in_views_subtree_, PropertyEffects::kPaint);
}
void View::SetLayerBounds(const gfx::Size& size,
const LayerOffsetData& offset_data) {
const gfx::Rect bounds = gfx::Rect(size) + offset_data.offset();
layer()->SetBounds(bounds);
for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
layer->SetBounds(gfx::Rect(layer->size()) + bounds.OffsetFromOrigin());
}
SnapLayerToPixelBoundary(offset_data);
observers_.Notify(&ViewObserver::OnViewLayerBoundsSet, this);
}
bool View::GetTransformRelativeTo(const View* ancestor,
gfx::Transform* transform) const {
const View* p = this;
while (p && p != ancestor) {
transform->PostConcat(p->GetTransform());
gfx::Transform translation;
translation.Translate(static_cast<float>(p->GetMirroredX()),
static_cast<float>(p->y()));
transform->PostConcat(translation);
p = p->parent_;
}
return p == ancestor;
}
bool View::ConvertPointForAncestor(const View* ancestor,
gfx::Point* point) const {
gfx::Transform trans;
bool result = GetTransformRelativeTo(ancestor, &trans);
*point = gfx::ToFlooredPoint(trans.MapPoint(gfx::PointF(*point)));
return result;
}
bool View::ConvertPointFromAncestor(const View* ancestor,
gfx::Point* point) const {
gfx::Transform trans;
bool result = GetTransformRelativeTo(ancestor, &trans);
if (const std::optional<gfx::PointF> transformed_point =
trans.InverseMapPoint(gfx::PointF(*point))) {
*point = gfx::ToFlooredPoint(transformed_point.value());
}
return result;
}
bool View::ConvertRectForAncestor(const View* ancestor,
gfx::RectF* rect) const {
gfx::Transform trans;
bool result = GetTransformRelativeTo(ancestor, &trans);
*rect = trans.MapRect(*rect);
return result;
}
bool View::ConvertRectFromAncestor(const View* ancestor,
gfx::RectF* rect) const {
gfx::Transform trans;
bool result = GetTransformRelativeTo(ancestor, &trans);
*rect = trans.InverseMapRect(*rect).value_or(*rect);
return result;
}
void View::CreateLayer(ui::LayerType layer_type) {
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->UpdateChildLayerVisibility(true);
}
}
SetLayer(std::make_unique<ui::Layer>(layer_type));
layer()->set_delegate(this);
layer()->SetName(std::string(GetClassName()));
UpdateParentLayers();
UpdateLayerVisibility();
if (parent()) {
parent()->ReorderLayers();
}
Widget* widget = GetWidget();
if (widget) {
widget->LayerTreeChanged();
}
SchedulePaintOnParent();
}
bool View::UpdateParentLayers() {
TRACE_EVENT1("views", "View::UpdateParentLayers", "class", GetClassName());
if (layer()) {
if (!layer()->parent()) {
UpdateParentLayer();
return true;
}
return false;
}
bool result = false;
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
if (child->UpdateParentLayers()) {
result = true;
}
}
return result;
}
void View::OrphanLayers() {
if (layer()) {
if (ui::Layer* parent = layer()->parent()) {
for (ui::Layer* layer : GetLayersInOrder()) {
SCOPED_CRASH_KEY_BOOL("OrphanLayers", "layer_valid", !!layer);
SCOPED_CRASH_KEY_BOOL("OrphanLayers", "layer_is_sibling",
layer->parent() == parent);
SCOPED_CRASH_KEY_STRING256("OrphanLayers", "this_layer_name",
this->layer()->name());
SCOPED_CRASH_KEY_STRING256("OrphanLayers", "parent_layer_name",
parent->name());
SCOPED_CRASH_KEY_STRING256("OrphanLayers", "sibling_layer_name",
layer->name());
SCOPED_CRASH_KEY_STRING256("OrphanLayers", "widget_name",
GetWidget() ? GetWidget()->GetName() : "");
SCOPED_CRASH_KEY_STRING256("OrphanLayers", "view_class_name",
GetClassName());
parent->Remove(layer);
}
}
return;
}
internal::ScopedChildrenLock lock(this);
for (views::View* child : children_) {
child->OrphanLayers();
}
}
void View::ReparentLayer(ui::Layer* parent_layer) {
DCHECK_NE(layer(), parent_layer);
if (parent_layer) {
SetLayerParent(parent_layer);
}
LayerOffsetData offset =
parent_ ? parent_->CalculateOffsetToAncestorWithLayer(nullptr)
: LayerOffsetData(layer()->device_scale_factor());
SetLayerBounds(size(), offset + GetMirroredBounds().OffsetFromOrigin());
layer()->SchedulePaint(GetLocalBounds());
MoveLayerToParent(layer(), LayerOffsetData(layer()->device_scale_factor()));
}
void View::CreateMaskLayer() {
DCHECK(layer());
mask_layer_ = std::make_unique<views::ViewMaskLayer>(clip_path_, this);
layer()->SetMaskLayer(mask_layer_->layer());
}
bool View::HasLayoutManager() const {
if (has_default_fill_layout_) {
return !children_.empty();
}
return !!layout_manager_;
}
void View::LayoutImmediately() {
TRACE_EVENT("ui", "View::LayoutImmediately", [&](perfetto::EventContext ctx) {
auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
auto* data = event->set_view_class_name();
data->set_name(std::string(GetClassName()));
});
invalidates_during_layout_ = 0;
++layouts_since_last_paint_;
base::AutoReset performing_layout(&performing_layout_, true);
++current_layout_call_depth_;
++max_layout_call_depth_;
Layout(PassKey());
UMA_HISTOGRAM_COUNTS_100("Views.InvalidatesDuringLayout",
invalidates_during_layout_);
--current_layout_call_depth_;
if (current_layout_call_depth_ == 0) {
UMA_HISTOGRAM_EXACT_LINEAR("Views.LayoutCallDepth", max_layout_call_depth_,
6);
max_layout_call_depth_ = 0;
}
}
bool View::ProcessMousePressed(const ui::MouseEvent& event) {
int drag_operations = (enabled_ && event.IsOnlyLeftMouseButton() &&
HitTestPoint(event.location()))
? GetDragOperations(event.location())
: 0;
ContextMenuController* context_menu_controller =
event.IsRightMouseButton() ? context_menu_controller_.get() : nullptr;
View::DragInfo* drag_info = GetDragInfo();
const bool was_enabled = GetEnabled();
const bool result = OnMousePressed(event);
if (!was_enabled) {
return result;
}
if (event.IsOnlyRightMouseButton() && context_menu_controller &&
kContextMenuOnMousePress) {
if (HitTestPoint(event.location())) {
gfx::Point location(event.location());
ConvertPointToScreen(this, &location);
ShowContextMenu(location, ui::mojom::MenuSourceType::kMouse);
return true;
}
}
if (drag_operations != ui::DragDropTypes::DRAG_NONE) {
drag_info->PossibleDrag(event.location());
return true;
}
return !!context_menu_controller || result;
}
void View::ProcessMouseDragged(ui::MouseEvent* event) {
ContextMenuController* context_menu_controller = context_menu_controller_;
const bool possible_drag = GetDragInfo()->possible_drag;
if (possible_drag &&
ExceededDragThreshold(GetDragInfo()->start_pt - event->location()) &&
(!drag_controller_ ||
drag_controller_->CanStartDragForView(this, GetDragInfo()->start_pt,
event->location()))) {
if (DoDrag(*event, GetDragInfo()->start_pt,
ui::mojom::DragEventSource::kMouse)) {
event->StopPropagation();
return;
}
} else {
if (OnMouseDragged(*event)) {
event->SetHandled();
return;
}
}
if ((context_menu_controller != nullptr) || possible_drag) {
event->SetHandled();
}
}
void View::ProcessMouseReleased(const ui::MouseEvent& event) {
if (!kContextMenuOnMousePress && context_menu_controller_ &&
event.IsOnlyRightMouseButton()) {
gfx::Point location(event.location());
OnMouseReleased(event);
if (HitTestPoint(location)) {
ConvertPointToScreen(this, &location);
ShowContextMenu(location, ui::mojom::MenuSourceType::kMouse);
}
} else {
OnMouseReleased(event);
}
}
void View::RegisterPendingAccelerators() {
if (!accelerators_ ||
registered_accelerator_count_ == accelerators_->size()) {
return;
}
if (!GetWidget()) {
return;
}
auto* focus_manager = GetFocusManager();
if (!focus_manager) {
return;
}
for (std::vector<ui::Accelerator>::const_iterator i =
accelerators_->begin() +
static_cast<ptrdiff_t>(registered_accelerator_count_);
i != accelerators_->end(); ++i) {
focus_manager->RegisterAccelerator(
*i, ui::AcceleratorManager::kNormalPriority, this);
}
registered_accelerator_count_ = accelerators_->size();
}
void View::UnregisterAccelerators(bool leave_data_intact) {
if (!accelerators_) {
return;
}
if (GetWidget()) {
if (auto* focus_manager = GetFocusManager()) {
focus_manager->UnregisterAccelerators(this);
}
if (!leave_data_intact) {
accelerators_->clear();
accelerators_.reset();
}
registered_accelerator_count_ = 0;
}
}
void View::SetFocusSiblings(View* view, Views::const_iterator pos) {
DCHECK(!children_.empty());
DCHECK(pos != children_.cend());
DCHECK_EQ(view, *pos);
if (children_.size() > 1) {
if (std::next(pos) == children_.cend()) {
View* const old_last = *std::ranges::find_if_not(
children_.cbegin(), pos, &View::next_focusable_view_);
DCHECK_NE(old_last, view);
view->InsertAfterInFocusList(old_last);
} else {
view->InsertBeforeInFocusList(*std::next(pos));
}
}
}
void View::AdvanceFocusIfNecessary() {
if (GetViewAccessibility().ViewAccessibility::IsAccessibilityFocusable() ||
!HasFocus()) {
return;
}
FocusManager* focus_manager = GetFocusManager();
if (focus_manager) {
focus_manager->AdvanceFocusIfNecessary();
}
}
void View::PropagateThemeChanged() {
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : base::Reversed(children_)) {
child->PropagateThemeChanged();
}
}
OnThemeChanged();
if (border_) {
border_->OnViewThemeChanged(this);
}
if (background_) {
background_->OnViewThemeChanged(this);
}
#if DCHECK_IS_ON()
DCHECK(on_theme_changed_called_)
<< "views::View::OnThemeChanged() has not been called. This means that "
"some class in the hierarchy is not calling their direct parent's "
"OnThemeChanged(). Please fix this by adding the missing call. Do not "
"call views::View::OnThemeChanged() directly unless views::View is "
"the direct parent class.";
on_theme_changed_called_ = false;
#endif
observers_.Notify(&ViewObserver::OnViewThemeChanged, this);
}
void View::PropagateDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) {
{
internal::ScopedChildrenLock lock(this);
for (views::View* child : base::Reversed(children_)) {
child->PropagateDeviceScaleFactorChanged(old_device_scale_factor,
new_device_scale_factor);
}
}
if (!layer()) {
OnDeviceScaleFactorChanged(old_device_scale_factor,
new_device_scale_factor);
return;
}
GetViewAccessibility().SetChildTreeScaleFactor(new_device_scale_factor);
}
void View::UpdateTooltip() {
Widget* widget = GetWidget();
if (widget && !widget->IsClosed() && widget->GetTooltipManager()) {
widget->GetTooltipManager()->UpdateTooltip();
}
}
bool View::kShouldDisableKeyboardTooltipsForTesting = false;
void View::DisableKeyboardTooltipsForTesting() {
View::kShouldDisableKeyboardTooltipsForTesting = true;
}
void View::EnableKeyboardTooltipsForTesting() {
View::kShouldDisableKeyboardTooltipsForTesting = false;
}
bool View::DoDrag(const ui::LocatedEvent& event,
const gfx::Point& press_pt,
ui::mojom::DragEventSource source) {
int drag_operations = GetDragOperations(press_pt);
if (drag_operations == ui::DragDropTypes::DRAG_NONE) {
return false;
}
Widget* widget = GetWidget();
DCHECK(widget);
if (widget->dragged_view()) {
return false;
}
std::unique_ptr<OSExchangeData> data(std::make_unique<OSExchangeData>());
WriteDragData(press_pt, data.get());
gfx::Point widget_location(event.location());
ConvertPointToWidget(this, &widget_location);
widget->RunShellDrag(this, std::move(data), widget_location, drag_operations,
source);
return true;
}
std::unique_ptr<ActionViewInterface> View::GetActionViewInterface() {
return std::make_unique<BaseActionViewInterface>(this);
}
base::CallbackListSubscription View::RegisterNotifyViewControllerCallback(
base::RepeatingClosureList::CallbackType callback) {
return notify_view_controller_callback_list_.Add(std::move(callback));
}
void View::NotifyViewControllerCallback() {
notify_view_controller_callback_list_.Notify();
}
BaseActionViewInterface::BaseActionViewInterface(View* action_view)
: action_view_(action_view) {}
void BaseActionViewInterface::ActionItemChangedImpl(
actions::ActionItem* action_item) {
action_view_->SetEnabled(action_item->GetEnabled());
action_view_->SetVisible(action_item->GetVisible());
}
BEGIN_METADATA_BASE(View)
ADD_PROPERTY_METADATA(std::unique_ptr<Background>, Background)
ADD_PROPERTY_METADATA(std::unique_ptr<Border>, Border)
ADD_READONLY_PROPERTY_METADATA(std::string_view, ClassName)
ADD_PROPERTY_METADATA(bool, Enabled)
ADD_READONLY_PROPERTY_METADATA(bool, EnabledInViewsSubtree)
ADD_PROPERTY_METADATA(View::FocusBehavior, FocusBehavior)
ADD_PROPERTY_METADATA(bool, FlipCanvasOnPaintForRTLUI)
ADD_PROPERTY_METADATA(int, Group)
ADD_PROPERTY_METADATA(int, OwnedGroup)
ADD_PROPERTY_METADATA(int, Height)
ADD_PROPERTY_METADATA(int, ID)
ADD_READONLY_PROPERTY_METADATA(bool, IsDrawn);
ADD_READONLY_PROPERTY_METADATA(gfx::Size, MaximumSize)
ADD_READONLY_PROPERTY_METADATA(gfx::Size, MinimumSize)
ADD_PROPERTY_METADATA(bool, Mirrored)
ADD_PROPERTY_METADATA(bool, NotifyEnterExitOnChild)
ADD_READONLY_PROPERTY_METADATA(std::string, ObjectName)
ADD_READONLY_PROPERTY_METADATA(std::u16string, Tooltip)
ADD_PROPERTY_METADATA(bool, Visible)
ADD_PROPERTY_METADATA(bool, CanProcessEventsWithinSubtree)
ADD_PROPERTY_METADATA(bool, UseDefaultFillLayout)
ADD_PROPERTY_METADATA(int, Width)
ADD_PROPERTY_METADATA(int, X)
ADD_PROPERTY_METADATA(int, Y)
ADD_PROPERTY_METADATA(std::u16string, TooltipText)
ADD_PROPERTY_METADATA(bool, ClipLayerToVisibleBounds)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kMarginsKey)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kInternalPaddingKey)
ADD_CLASS_PROPERTY_METADATA(LayoutAlignment, kCrossAxisAlignmentKey)
ADD_CLASS_PROPERTY_METADATA(bool, kViewIgnoredByLayoutKey)
END_METADATA
}
DEFINE_ENUM_CONVERTERS(views::View::FocusBehavior,
{views::View::FocusBehavior::ACCESSIBLE_ONLY,
u"ACCESSIBLE_ONLY"},
{views::View::FocusBehavior::ALWAYS, u"ALWAYS"},
{views::View::FocusBehavior::NEVER, u"NEVER"})