summaryrefslogtreecommitdiffstats
path: root/ui/views/view.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ui/views/view.cc')
-rw-r--r--ui/views/view.cc2061
1 files changed, 2061 insertions, 0 deletions
diff --git a/ui/views/view.cc b/ui/views/view.cc
new file mode 100644
index 0000000..09d3cbe
--- /dev/null
+++ b/ui/views/view.cc
@@ -0,0 +1,2061 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/view.h"
+
+#include <algorithm>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/base/accessibility/accessibility_types.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/compositor/compositor.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/gfx/compositor/layer_animator.h"
+#include "ui/gfx/interpolated_transform.h"
+#include "ui/gfx/path.h"
+#include "ui/gfx/point3.h"
+#include "ui/gfx/transform.h"
+#include "ui/views/context_menu_controller.h"
+#include "ui/views/drag_controller.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/widget/native_widget_private.h"
+#include "ui/views/widget/root_view.h"
+#include "ui/views/widget/tooltip_manager.h"
+#include "ui/views/widget/widget.h"
+#include "views/background.h"
+#include "views/views_delegate.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_gdi_object.h"
+#include "ui/views/accessibility/native_view_accessibility_win.h"
+#endif
+#if defined(TOOLKIT_USES_GTK)
+#include "ui/base/gtk/scoped_handle_gtk.h"
+#endif
+
+namespace {
+
+// Whether to use accelerated compositing when necessary (e.g. when a view has a
+// transformation).
+#if defined(VIEWS_COMPOSITOR)
+bool use_acceleration_when_possible = true;
+#else
+bool use_acceleration_when_possible = false;
+#endif
+
+// Saves the drawing state, and restores the state when going out of scope.
+class ScopedCanvas {
+ public:
+ explicit ScopedCanvas(gfx::Canvas* canvas) : canvas_(canvas) {
+ if (canvas_)
+ canvas_->Save();
+ }
+ ~ScopedCanvas() {
+ if (canvas_)
+ canvas_->Restore();
+ }
+ void SetCanvas(gfx::Canvas* canvas) {
+ if (canvas_)
+ canvas_->Restore();
+ canvas_ = canvas;
+ canvas_->Save();
+ }
+
+ private:
+ gfx::Canvas* canvas_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCanvas);
+};
+
+// Returns the top view in |view|'s hierarchy.
+const views::View* GetHierarchyRoot(const views::View* view) {
+ const views::View* root = view;
+ while (root && root->parent())
+ root = root->parent();
+ return root;
+}
+
+} // namespace
+
+namespace views {
+
+// static
+ViewsDelegate* ViewsDelegate::views_delegate = NULL;
+
+// static
+const char View::kViewClassName[] = "views/View";
+
+////////////////////////////////////////////////////////////////////////////////
+// View, public:
+
+// TO BE MOVED -----------------------------------------------------------------
+
+void View::SetHotTracked(bool flag) {
+}
+
+bool View::IsHotTracked() const {
+ return false;
+}
+
+// Creation and lifetime -------------------------------------------------------
+
+View::View()
+ : parent_owned_(true),
+ id_(0),
+ group_(-1),
+ parent_(NULL),
+ visible_(true),
+ enabled_(true),
+ painting_enabled_(true),
+ registered_for_visible_bounds_notification_(false),
+ clip_x_(0.0),
+ clip_y_(0.0),
+ needs_layout_(true),
+ flip_canvas_on_paint_for_rtl_ui_(false),
+ paint_to_layer_(false),
+ accelerator_registration_delayed_(false),
+ accelerator_focus_manager_(NULL),
+ registered_accelerator_count_(0),
+ next_focusable_view_(NULL),
+ previous_focusable_view_(NULL),
+ focusable_(false),
+ accessibility_focusable_(false),
+ context_menu_controller_(NULL),
+ drag_controller_(NULL) {
+}
+
+View::~View() {
+ if (parent_)
+ parent_->RemoveChildView(this);
+
+ for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) {
+ (*i)->parent_ = NULL;
+ if ((*i)->parent_owned())
+ delete *i;
+ }
+
+#if defined(OS_WIN)
+ if (native_view_accessibility_win_.get())
+ native_view_accessibility_win_->set_view(NULL);
+#endif
+}
+
+// Tree operations -------------------------------------------------------------
+
+const Widget* View::GetWidget() const {
+ // The root view holds a reference to this view hierarchy's Widget.
+ return parent_ ? parent_->GetWidget() : NULL;
+}
+
+Widget* View::GetWidget() {
+ return const_cast<Widget*>(const_cast<const View*>(this)->GetWidget());
+}
+
+void View::AddChildView(View* view) {
+ AddChildViewAt(view, child_count());
+}
+
+void View::AddChildViewAt(View* view, int index) {
+ CHECK_NE(view, this) << "You cannot add a view as its own child";
+
+ // If |view| has a parent, remove it from its parent.
+ View* parent = view->parent_;
+ if (parent)
+ parent->RemoveChildView(view);
+
+ // Sets the prev/next focus views.
+ InitFocusSiblings(view, index);
+
+ // Let's insert the view.
+ view->parent_ = this;
+ children_.insert(children_.begin() + index, view);
+
+ for (View* v = this; v; v = v->parent_)
+ v->ViewHierarchyChangedImpl(false, true, this, view);
+
+ view->PropagateAddNotifications(this, view);
+ UpdateTooltip();
+ if (GetWidget())
+ RegisterChildrenForVisibleBoundsNotification(view);
+
+ if (layout_manager_.get())
+ layout_manager_->ViewAdded(this, view);
+
+ if (use_acceleration_when_possible)
+ ReorderLayers();
+
+ // Make sure the visibility of the child layers are correct.
+ // If any of the parent View is hidden, then the layers of the subtree
+ // rooted at |this| should be hidden. Otherwise, all the child layers should
+ // inherit the visibility of the owner View.
+ UpdateLayerVisibility();
+}
+
+void View::ReorderChildView(View* view, int index) {
+ DCHECK_EQ(view->parent_, this);
+ if (index < 0)
+ index = child_count() - 1;
+ else if (index >= child_count())
+ return;
+ if (children_[index] == view)
+ return;
+
+ const Views::iterator i(std::find(children_.begin(), children_.end(), view));
+ DCHECK(i != children_.end());
+ children_.erase(i);
+
+ // Unlink the view first
+ View* next_focusable = view->next_focusable_view_;
+ View* prev_focusable = view->previous_focusable_view_;
+ if (prev_focusable)
+ prev_focusable->next_focusable_view_ = next_focusable;
+ if (next_focusable)
+ next_focusable->previous_focusable_view_ = prev_focusable;
+
+ // Add it in the specified index now.
+ InitFocusSiblings(view, index);
+ children_.insert(children_.begin() + index, view);
+
+ if (use_acceleration_when_possible)
+ ReorderLayers();
+}
+
+void View::RemoveChildView(View* view) {
+ DoRemoveChildView(view, true, true, false);
+}
+
+void View::RemoveAllChildViews(bool delete_children) {
+ while (!children_.empty())
+ DoRemoveChildView(children_.front(), false, false, delete_children);
+ UpdateTooltip();
+}
+
+bool View::Contains(const View* view) const {
+ for (const View* v = view; v; v = v->parent_) {
+ if (v == this)
+ return true;
+ }
+ return false;
+}
+
+int View::GetIndexOf(const View* view) const {
+ Views::const_iterator i(std::find(children_.begin(), children_.end(), view));
+ return i != children_.end() ? static_cast<int>(i - children_.begin()) : -1;
+}
+
+// Size and disposition --------------------------------------------------------
+
+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;
+ Layout();
+ SchedulePaint();
+ }
+ return;
+ }
+
+ if (IsVisible()) {
+ // Paint where the view is currently.
+ SchedulePaintBoundsChanged(
+ bounds_.size() == bounds.size() ? SCHEDULE_PAINT_SIZE_SAME :
+ SCHEDULE_PAINT_SIZE_CHANGED);
+ }
+
+ gfx::Rect prev = bounds_;
+ bounds_ = bounds;
+ BoundsChanged(prev);
+}
+
+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());
+ if (border_.get()) {
+ gfx::Insets insets;
+ border_->GetInsets(&insets);
+ contents_bounds.Inset(insets);
+ }
+ return contents_bounds;
+}
+
+gfx::Rect View::GetLocalBounds() const {
+ return gfx::Rect(gfx::Point(), size());
+}
+
+gfx::Insets View::GetInsets() const {
+ gfx::Insets insets;
+ if (border_.get())
+ border_->GetInsets(&insets);
+ return insets;
+}
+
+gfx::Rect View::GetVisibleBounds() const {
+ if (!IsVisibleInRootView())
+ return gfx::Rect();
+ gfx::Rect vis_bounds(0, 0, width(), height());
+ gfx::Rect ancestor_bounds;
+ const View* view = this;
+ ui::Transform transform;
+
+ while (view != NULL && !vis_bounds.IsEmpty()) {
+ transform.ConcatTransform(view->GetTransform());
+ transform.ConcatTranslate(static_cast<float>(view->GetMirroredX()),
+ static_cast<float>(view->y()));
+
+ vis_bounds = view->ConvertRectToParent(vis_bounds);
+ const View* ancestor = view->parent_;
+ if (ancestor != NULL) {
+ ancestor_bounds.SetRect(0, 0, ancestor->width(), ancestor->height());
+ vis_bounds = vis_bounds.Intersect(ancestor_bounds);
+ } else if (!view->GetWidget()) {
+ // If the view has no Widget, we're not visible. Return an empty rect.
+ return gfx::Rect();
+ }
+ view = ancestor;
+ }
+ if (vis_bounds.IsEmpty())
+ return vis_bounds;
+ // Convert back to this views coordinate system.
+ transform.TransformRectReverse(&vis_bounds);
+ return vis_bounds;
+}
+
+gfx::Rect View::GetScreenBounds() const {
+ gfx::Point origin;
+ View::ConvertPointToScreen(this, &origin);
+ return gfx::Rect(origin, size());
+}
+
+gfx::Size View::GetPreferredSize() {
+ if (layout_manager_.get())
+ return layout_manager_->GetPreferredSize(this);
+ return gfx::Size();
+}
+
+int View::GetBaseline() const {
+ return -1;
+}
+
+void View::SizeToPreferredSize() {
+ gfx::Size prefsize = GetPreferredSize();
+ if ((prefsize.width() != width()) || (prefsize.height() != height()))
+ SetBounds(x(), y(), prefsize.width(), prefsize.height());
+}
+
+gfx::Size View::GetMinimumSize() {
+ return GetPreferredSize();
+}
+
+int View::GetHeightForWidth(int w) {
+ if (layout_manager_.get())
+ return layout_manager_->GetPreferredHeightForWidth(this, w);
+ return GetPreferredSize().height();
+}
+
+void View::SetVisible(bool visible) {
+ if (visible != visible_) {
+ // If the View is currently visible, schedule paint to refresh parent.
+ // TODO(beng): not sure we should be doing this if we have a layer.
+ if (visible_)
+ SchedulePaint();
+
+ visible_ = visible;
+
+ // This notifies all sub-views recursively.
+ PropagateVisibilityNotifications(this, visible_);
+ UpdateLayerVisibility();
+
+ // If we are newly visible, schedule paint.
+ if (visible_)
+ SchedulePaint();
+ }
+}
+
+bool View::IsVisible() const {
+ return visible_;
+}
+
+bool View::IsVisibleInRootView() const {
+ return IsVisible() && parent_ ? parent_->IsVisibleInRootView() : false;
+}
+
+void View::SetEnabled(bool enabled) {
+ if (enabled != enabled_) {
+ enabled_ = enabled;
+ OnEnabledChanged();
+ }
+}
+
+bool View::IsEnabled() const {
+ return enabled_;
+}
+
+void View::OnEnabledChanged() {
+ SchedulePaint();
+}
+
+// Transformations -------------------------------------------------------------
+
+const ui::Transform& View::GetTransform() const {
+ static const ui::Transform* no_op = new ui::Transform;
+ return layer() ? layer()->transform() : *no_op;
+}
+
+void View::SetTransform(const ui::Transform& transform) {
+ if (!transform.HasChange()) {
+ if (layer()) {
+ layer()->SetTransform(transform);
+ if (!paint_to_layer_)
+ DestroyLayer();
+ } else {
+ // Nothing.
+ }
+ } else {
+ if (!layer())
+ CreateLayer();
+ layer()->SetTransform(transform);
+ layer()->ScheduleDraw();
+ }
+}
+
+void View::SetPaintToLayer(bool paint_to_layer) {
+ paint_to_layer_ = paint_to_layer;
+ if (paint_to_layer_ && !layer()) {
+ CreateLayer();
+ } else if (!paint_to_layer_ && layer()) {
+ DestroyLayer();
+ }
+}
+
+// RTL positioning -------------------------------------------------------------
+
+gfx::Rect View::GetMirroredBounds() const {
+ gfx::Rect bounds(bounds_);
+ 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& bounds) const {
+ return base::i18n::IsRTL() ?
+ (width() - bounds.x() - bounds.width()) : bounds.x();
+}
+
+int View::GetMirroredXInView(int x) const {
+ return base::i18n::IsRTL() ? width() - x : x;
+}
+
+int View::GetMirroredXWithWidthInView(int x, int w) const {
+ return base::i18n::IsRTL() ? width() - x - w : x;
+}
+
+// Layout ----------------------------------------------------------------------
+
+void View::Layout() {
+ needs_layout_ = false;
+
+ // If we have a layout manager, let it handle the layout for us.
+ if (layout_manager_.get())
+ layout_manager_->Layout(this);
+
+ // Make sure to propagate the Layout() call to any children that haven't
+ // received it yet through the layout manager and need to be laid out. This
+ // is needed for the case when the child requires a layout but its bounds
+ // weren't changed by the layout manager. If there is no layout manager, we
+ // just propagate the Layout() call down the hierarchy, so whoever receives
+ // the call can take appropriate action.
+ for (int i = 0, count = child_count(); i < count; ++i) {
+ View* child = child_at(i);
+ if (child->needs_layout_ || !layout_manager_.get()) {
+ child->needs_layout_ = false;
+ child->Layout();
+ }
+ }
+}
+
+void View::InvalidateLayout() {
+ // Always invalidate up. This is needed to handle the case of us already being
+ // valid, but not our parent.
+ needs_layout_ = true;
+ if (parent_)
+ parent_->InvalidateLayout();
+}
+
+LayoutManager* View::GetLayoutManager() const {
+ return layout_manager_.get();
+}
+
+void View::SetLayoutManager(LayoutManager* layout_manager) {
+ if (layout_manager_.get())
+ layout_manager_->Uninstalled(this);
+
+ layout_manager_.reset(layout_manager);
+ if (layout_manager_.get())
+ layout_manager_->Installed(this);
+}
+
+// Attributes ------------------------------------------------------------------
+
+std::string View::GetClassName() const {
+ return kViewClassName;
+}
+
+View* View::GetAncestorWithClassName(const std::string& name) {
+ for (View* view = this; view; view = view->parent_) {
+ if (view->GetClassName() == name)
+ return view;
+ }
+ return NULL;
+}
+
+const View* View::GetViewByID(int id) const {
+ if (id == id_)
+ return const_cast<View*>(this);
+
+ for (int i = 0, count = child_count(); i < count; ++i) {
+ const View* view = child_at(i)->GetViewByID(id);
+ if (view)
+ return view;
+ }
+ return NULL;
+}
+
+View* View::GetViewByID(int id) {
+ return const_cast<View*>(const_cast<const View*>(this)->GetViewByID(id));
+}
+
+void View::SetGroup(int gid) {
+ // Don't change the group id once it's set.
+ DCHECK(group_ == -1 || group_ == gid);
+ group_ = gid;
+}
+
+int View::GetGroup() const {
+ return group_;
+}
+
+bool View::IsGroupFocusTraversable() const {
+ return true;
+}
+
+void View::GetViewsInGroup(int group, Views* views) {
+ if (group_ == group)
+ views->push_back(this);
+
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->GetViewsInGroup(group, views);
+}
+
+View* View::GetSelectedViewForGroup(int group) {
+ Views views;
+ GetWidget()->GetRootView()->GetViewsInGroup(group, &views);
+ return views.empty() ? NULL : views[0];
+}
+
+// Coordinate conversion -------------------------------------------------------
+
+// static
+void View::ConvertPointToView(const View* source,
+ const View* target,
+ gfx::Point* point) {
+ if (source == target)
+ return;
+
+ // |source| can be NULL.
+ const View* root = GetHierarchyRoot(target);
+ if (source) {
+ CHECK_EQ(GetHierarchyRoot(source), root);
+
+ if (source != root)
+ source->ConvertPointForAncestor(root, point);
+ }
+
+ if (target != root)
+ target->ConvertPointFromAncestor(root, point);
+
+ // API defines NULL |source| as returning the point in screen coordinates.
+ if (!source) {
+ *point = point->Subtract(
+ root->GetWidget()->GetClientAreaScreenBounds().origin());
+ }
+}
+
+// static
+void View::ConvertPointToWidget(const View* src, gfx::Point* p) {
+ DCHECK(src);
+ DCHECK(p);
+
+ src->ConvertPointForAncestor(NULL, p);
+}
+
+// static
+void View::ConvertPointFromWidget(const View* dest, gfx::Point* p) {
+ DCHECK(dest);
+ DCHECK(p);
+
+ dest->ConvertPointFromAncestor(NULL, p);
+}
+
+// static
+void View::ConvertPointToScreen(const View* src, gfx::Point* p) {
+ DCHECK(src);
+ DCHECK(p);
+
+ // If the view is not connected to a tree, there's nothing we can do.
+ const Widget* widget = src->GetWidget();
+ if (widget) {
+ ConvertPointToWidget(src, p);
+ gfx::Rect r = widget->GetClientAreaScreenBounds();
+ p->SetPoint(p->x() + r.x(), p->y() + r.y());
+ }
+}
+
+gfx::Rect View::ConvertRectToParent(const gfx::Rect& rect) const {
+ gfx::Rect x_rect = rect;
+ GetTransform().TransformRect(&x_rect);
+ x_rect.Offset(GetMirroredPosition());
+ 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;
+}
+
+// Painting --------------------------------------------------------------------
+
+void View::SchedulePaint() {
+ SchedulePaintInRect(GetLocalBounds());
+}
+
+void View::SchedulePaintInRect(const gfx::Rect& rect) {
+ if (!IsVisible() || !painting_enabled_)
+ return;
+
+ if (layer()) {
+ layer()->SchedulePaint(rect);
+ } else if (parent_) {
+ // Translate the requested paint rect to the parent's coordinate system
+ // then pass this notification up to the parent.
+ parent_->SchedulePaintInRect(ConvertRectToParent(rect));
+ }
+}
+
+void View::Paint(gfx::Canvas* canvas) {
+ TRACE_EVENT0("views", "View::Paint");
+
+ ScopedCanvas scoped_canvas(canvas);
+
+ // Paint this View and its children, setting the clip rect to the bounds
+ // of this View and translating the origin to the local bounds' top left
+ // point.
+ //
+ // Note that the X (or left) position we pass to ClipRectInt takes into
+ // consideration whether or not the view uses a right-to-left layout so that
+ // we paint our view in its mirrored position if need be.
+ if (!canvas->ClipRect(gfx::Rect(GetMirroredX(), y(),
+ width() - static_cast<int>(clip_x_),
+ height() - static_cast<int>(clip_y_)))) {
+ return;
+ }
+ // Non-empty clip, translate the graphics such that 0,0 corresponds to
+ // where this view is located (related to its parent).
+ canvas->Translate(GetMirroredPosition());
+ canvas->Transform(GetTransform());
+
+ PaintCommon(canvas);
+}
+
+ThemeProvider* View::GetThemeProvider() const {
+ const Widget* widget = GetWidget();
+ return widget ? widget->GetThemeProvider() : NULL;
+}
+
+// Accelerated Painting --------------------------------------------------------
+
+// static
+void View::set_use_acceleration_when_possible(bool use) {
+ use_acceleration_when_possible = use;
+}
+
+// static
+bool View::get_use_acceleration_when_possible() {
+ return use_acceleration_when_possible;
+}
+
+// Input -----------------------------------------------------------------------
+
+View* View::GetEventHandlerForPoint(const gfx::Point& point) {
+ // Walk the child Views recursively looking for the View that most
+ // tightly encloses the specified point.
+ for (int i = child_count() - 1; i >= 0; --i) {
+ View* child = child_at(i);
+ if (!child->IsVisible())
+ continue;
+
+ gfx::Point point_in_child_coords(point);
+ View::ConvertPointToView(this, child, &point_in_child_coords);
+ if (child->HitTest(point_in_child_coords))
+ return child->GetEventHandlerForPoint(point_in_child_coords);
+ }
+ return this;
+}
+
+gfx::NativeCursor View::GetCursor(const MouseEvent& event) {
+#if defined(OS_WIN) && !defined(USE_AURA)
+ static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW);
+ return arrow;
+#else
+ return gfx::kNullCursor;
+#endif
+}
+
+bool View::HitTest(const gfx::Point& l) const {
+ if (GetLocalBounds().Contains(l)) {
+ if (HasHitTestMask()) {
+ gfx::Path mask;
+ GetHitTestMask(&mask);
+#if defined(USE_AURA)
+ // TODO: should we use this every where?
+ SkRegion clip_region;
+ clip_region.setRect(0, 0, width(), height());
+ SkRegion mask_region;
+ return mask_region.setPath(mask, clip_region) &&
+ mask_region.contains(l.x(), l.y());
+#elif defined(OS_WIN)
+ base::win::ScopedRegion rgn(mask.CreateNativeRegion());
+ return !!PtInRegion(rgn, l.x(), l.y());
+#elif defined(TOOLKIT_USES_GTK)
+ ui::ScopedRegion rgn(mask.CreateNativeRegion());
+ return gdk_region_point_in(rgn.Get(), l.x(), l.y());
+#endif
+ }
+ // No mask, but inside our bounds.
+ return true;
+ }
+ // Outside our bounds.
+ return false;
+}
+
+bool View::OnMousePressed(const MouseEvent& event) {
+ return false;
+}
+
+bool View::OnMouseDragged(const MouseEvent& event) {
+ return false;
+}
+
+void View::OnMouseReleased(const MouseEvent& event) {
+}
+
+void View::OnMouseCaptureLost() {
+}
+
+void View::OnMouseMoved(const MouseEvent& event) {
+}
+
+void View::OnMouseEntered(const MouseEvent& event) {
+}
+
+void View::OnMouseExited(const MouseEvent& event) {
+}
+
+ui::TouchStatus View::OnTouchEvent(const TouchEvent& event) {
+ DVLOG(1) << "visited the OnTouchEvent";
+ return ui::TOUCH_STATUS_UNKNOWN;
+}
+
+void View::SetMouseHandler(View *new_mouse_handler) {
+ // It is valid for new_mouse_handler to be NULL
+ if (parent_)
+ parent_->SetMouseHandler(new_mouse_handler);
+}
+
+bool View::OnKeyPressed(const KeyEvent& event) {
+ return false;
+}
+
+bool View::OnKeyReleased(const KeyEvent& event) {
+ return false;
+}
+
+bool View::OnMouseWheel(const MouseWheelEvent& event) {
+ return false;
+}
+
+ui::TextInputClient* View::GetTextInputClient() {
+ return NULL;
+}
+
+InputMethod* View::GetInputMethod() {
+ Widget* widget = GetWidget();
+ return widget ? widget->GetInputMethod() : NULL;
+}
+
+// Accelerators ----------------------------------------------------------------
+
+void View::AddAccelerator(const ui::Accelerator& accelerator) {
+ if (!accelerators_.get())
+ accelerators_.reset(new std::vector<ui::Accelerator>());
+
+ DCHECK(std::find(accelerators_->begin(), accelerators_->end(), accelerator) ==
+ accelerators_->end())
+ << "Registering the same accelerator multiple times";
+
+ accelerators_->push_back(accelerator);
+ RegisterPendingAccelerators();
+}
+
+void View::RemoveAccelerator(const ui::Accelerator& accelerator) {
+ if (!accelerators_.get()) {
+ NOTREACHED() << "Removing non-existing accelerator";
+ return;
+ }
+
+ std::vector<ui::Accelerator>::iterator i(
+ std::find(accelerators_->begin(), accelerators_->end(), accelerator));
+ if (i == accelerators_->end()) {
+ NOTREACHED() << "Removing non-existing accelerator";
+ return;
+ }
+
+ size_t index = i - accelerators_->begin();
+ accelerators_->erase(i);
+ if (index >= registered_accelerator_count_) {
+ // The accelerator is not registered to FocusManager.
+ return;
+ }
+ --registered_accelerator_count_;
+
+ // Providing we are attached to a Widget and registered with a focus manager,
+ // we should de-register from that focus manager now.
+ if (GetWidget() && accelerator_focus_manager_)
+ accelerator_focus_manager_->UnregisterAccelerator(accelerator, this);
+}
+
+void View::ResetAccelerators() {
+ if (accelerators_.get())
+ UnregisterAccelerators(false);
+}
+
+bool View::AcceleratorPressed(const ui::Accelerator& accelerator) {
+ return false;
+}
+
+// Focus -----------------------------------------------------------------------
+
+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::SetNextFocusableView(View* view) {
+ view->previous_focusable_view_ = this;
+ next_focusable_view_ = view;
+}
+
+bool View::IsFocusableInRootView() const {
+ return IsFocusable() && IsVisibleInRootView();
+}
+
+bool View::IsAccessibilityFocusableInRootView() const {
+ return (focusable_ || accessibility_focusable_) && IsEnabled() &&
+ IsVisibleInRootView();
+}
+
+FocusManager* View::GetFocusManager() {
+ Widget* widget = GetWidget();
+ return widget ? widget->GetFocusManager() : NULL;
+}
+
+const FocusManager* View::GetFocusManager() const {
+ const Widget* widget = GetWidget();
+ return widget ? widget->GetFocusManager() : NULL;
+}
+
+void View::RequestFocus() {
+ FocusManager* focus_manager = GetFocusManager();
+ if (focus_manager && IsFocusableInRootView())
+ focus_manager->SetFocusedView(this);
+}
+
+bool View::SkipDefaultKeyEventProcessing(const KeyEvent& event) {
+ return false;
+}
+
+FocusTraversable* View::GetFocusTraversable() {
+ return NULL;
+}
+
+FocusTraversable* View::GetPaneFocusTraversable() {
+ return NULL;
+}
+
+// Tooltips --------------------------------------------------------------------
+
+bool View::GetTooltipText(const gfx::Point& p, string16* tooltip) const {
+ return false;
+}
+
+bool View::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* loc) const {
+ return false;
+}
+
+// Context menus ---------------------------------------------------------------
+
+void View::ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture) {
+ if (!context_menu_controller_)
+ return;
+
+ context_menu_controller_->ShowContextMenuForView(this, p, is_mouse_gesture);
+}
+
+// Drag and drop ---------------------------------------------------------------
+
+bool View::GetDropFormats(
+ int* formats,
+ std::set<OSExchangeData::CustomFormat>* custom_formats) {
+ return false;
+}
+
+bool View::AreDropTypesRequired() {
+ return false;
+}
+
+bool View::CanDrop(const OSExchangeData& data) {
+ // TODO(sky): when I finish up migration, this should default to true.
+ return false;
+}
+
+void View::OnDragEntered(const DropTargetEvent& event) {
+}
+
+int View::OnDragUpdated(const DropTargetEvent& event) {
+ return ui::DragDropTypes::DRAG_NONE;
+}
+
+void View::OnDragExited() {
+}
+
+int View::OnPerformDrop(const DropTargetEvent& event) {
+ return ui::DragDropTypes::DRAG_NONE;
+}
+
+void View::OnDragDone() {
+}
+
+// static
+bool View::ExceededDragThreshold(int delta_x, int delta_y) {
+ return (abs(delta_x) > GetHorizontalDragThreshold() ||
+ abs(delta_y) > GetVerticalDragThreshold());
+}
+
+// Scrolling -------------------------------------------------------------------
+
+void View::ScrollRectToVisible(const gfx::Rect& rect) {
+ // We must take RTL UI mirroring into account when adjusting the position of
+ // the region.
+ if (parent_) {
+ gfx::Rect scroll_rect(rect);
+ scroll_rect.Offset(GetMirroredX(), y());
+ parent_->ScrollRectToVisible(scroll_rect);
+ }
+}
+
+int View::GetPageScrollIncrement(ScrollView* scroll_view,
+ bool is_horizontal, bool is_positive) {
+ return 0;
+}
+
+int View::GetLineScrollIncrement(ScrollView* scroll_view,
+ bool is_horizontal, bool is_positive) {
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// View, protected:
+
+// Size and disposition --------------------------------------------------------
+
+void View::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+}
+
+void View::PreferredSizeChanged() {
+ InvalidateLayout();
+ if (parent_)
+ parent_->ChildPreferredSizeChanged(this);
+}
+
+bool View::NeedsNotificationWhenVisibleBoundsChange() const {
+ return false;
+}
+
+void View::OnVisibleBoundsChanged() {
+}
+
+// Tree operations -------------------------------------------------------------
+
+void View::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+}
+
+void View::VisibilityChanged(View* starting_from, bool is_visible) {
+}
+
+void View::NativeViewHierarchyChanged(bool attached,
+ gfx::NativeView native_view,
+ internal::RootView* root_view) {
+ FocusManager* focus_manager = GetFocusManager();
+ if (!accelerator_registration_delayed_ &&
+ accelerator_focus_manager_ &&
+ accelerator_focus_manager_ != focus_manager) {
+ UnregisterAccelerators(true);
+ accelerator_registration_delayed_ = true;
+ }
+ if (accelerator_registration_delayed_ && attached) {
+ if (focus_manager) {
+ RegisterPendingAccelerators();
+ accelerator_registration_delayed_ = false;
+ }
+ }
+}
+
+// Painting --------------------------------------------------------------------
+
+void View::PaintChildren(gfx::Canvas* canvas) {
+ TRACE_EVENT0("views", "View::PaintChildren");
+ for (int i = 0, count = child_count(); i < count; ++i)
+ if (!child_at(i)->layer())
+ child_at(i)->Paint(canvas);
+}
+
+void View::OnPaint(gfx::Canvas* canvas) {
+ TRACE_EVENT0("views", "View::OnPaint");
+ OnPaintBackground(canvas);
+ OnPaintFocusBorder(canvas);
+ OnPaintBorder(canvas);
+}
+
+void View::OnPaintBackground(gfx::Canvas* canvas) {
+ if (background_.get()) {
+ TRACE_EVENT2("views", "View::OnPaintBackground",
+ "width", canvas->GetSkCanvas()->getDevice()->width(),
+ "height", canvas->GetSkCanvas()->getDevice()->height());
+ background_->Paint(canvas, this);
+ }
+}
+
+void View::OnPaintBorder(gfx::Canvas* canvas) {
+ if (border_.get()) {
+ TRACE_EVENT2("views", "View::OnPaintBorder",
+ "width", canvas->GetSkCanvas()->getDevice()->width(),
+ "height", canvas->GetSkCanvas()->getDevice()->height());
+ border_->Paint(*this, canvas);
+ }
+}
+
+void View::OnPaintFocusBorder(gfx::Canvas* canvas) {
+ if ((IsFocusable() || IsAccessibilityFocusableInRootView()) && HasFocus()) {
+ TRACE_EVENT2("views", "views::OnPaintFocusBorder",
+ "width", canvas->GetSkCanvas()->getDevice()->width(),
+ "height", canvas->GetSkCanvas()->getDevice()->height());
+ canvas->DrawFocusRect(GetLocalBounds());
+ }
+}
+
+// Accelerated Painting --------------------------------------------------------
+
+void View::SetFillsBoundsOpaquely(bool fills_bounds_opaquely) {
+ // This method should not have the side-effect of creating the layer.
+ if (layer())
+ layer()->SetFillsBoundsOpaquely(fills_bounds_opaquely);
+}
+
+bool View::SetExternalTexture(ui::Texture* texture) {
+ DCHECK(texture);
+ SetPaintToLayer(true);
+
+ layer()->SetExternalTexture(texture);
+
+ // Child views must not paint into the external texture. So make sure each
+ // child view has its own layer to paint into.
+ for (Views::iterator i = children_.begin(); i != children_.end(); ++i)
+ (*i)->SetPaintToLayer(true);
+
+ SchedulePaintInRect(GetLocalBounds());
+ return true;
+}
+
+void View::CalculateOffsetToAncestorWithLayer(gfx::Point* offset,
+ ui::Layer** layer_parent) {
+ if (layer()) {
+ if (layer_parent)
+ *layer_parent = layer();
+ return;
+ }
+ if (!parent_)
+ return;
+
+ offset->Offset(x(), y());
+ parent_->CalculateOffsetToAncestorWithLayer(offset, layer_parent);
+}
+
+void View::MoveLayerToParent(ui::Layer* parent_layer,
+ const gfx::Point& point) {
+ gfx::Point local_point(point);
+ if (parent_layer != layer())
+ local_point.Offset(x(), y());
+ if (layer() && parent_layer != layer()) {
+ parent_layer->Add(layer());
+ layer()->SetBounds(gfx::Rect(local_point.x(), local_point.y(),
+ width(), height()));
+ } else {
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->MoveLayerToParent(parent_layer, local_point);
+ }
+}
+
+void View::UpdateLayerVisibility() {
+ if (!use_acceleration_when_possible)
+ return;
+ bool visible = IsVisible();
+ for (const View* v = parent_; visible && v && !v->layer(); v = v->parent_)
+ visible = v->IsVisible();
+
+ UpdateChildLayerVisibility(visible);
+}
+
+void View::UpdateChildLayerVisibility(bool ancestor_visible) {
+ if (layer()) {
+ layer()->SetVisible(ancestor_visible && IsVisible());
+ } else {
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->UpdateChildLayerVisibility(ancestor_visible && IsVisible());
+ }
+}
+
+void View::UpdateChildLayerBounds(const gfx::Point& offset) {
+ if (layer()) {
+ layer()->SetBounds(gfx::Rect(offset.x(), offset.y(), width(), height()));
+ } else {
+ for (int i = 0, count = child_count(); i < count; ++i) {
+ gfx::Point new_offset(offset.x() + child_at(i)->x(),
+ offset.y() + child_at(i)->y());
+ child_at(i)->UpdateChildLayerBounds(new_offset);
+ }
+ }
+}
+
+void View::OnPaintLayer(gfx::Canvas* canvas) {
+ if (!layer() || !layer()->fills_bounds_opaquely())
+ canvas->GetSkCanvas()->drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
+ PaintCommon(canvas);
+}
+
+void View::ReorderLayers() {
+ View* v = this;
+ while (v && !v->layer())
+ v = v->parent();
+
+ // Forward to widget in case we're in a NativeWidgetView.
+ if (!v) {
+ if (GetWidget())
+ GetWidget()->ReorderLayers();
+ } else {
+ for (Views::const_iterator i(v->children_.begin());
+ i != v->children_.end();
+ ++i)
+ (*i)->ReorderChildLayers(v->layer());
+ }
+}
+
+void View::ReorderChildLayers(ui::Layer* parent_layer) {
+ if (layer()) {
+ DCHECK_EQ(parent_layer, layer()->parent());
+ parent_layer->StackAtTop(layer());
+ } else {
+ for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i)
+ (*i)->ReorderChildLayers(parent_layer);
+ }
+}
+
+// Input -----------------------------------------------------------------------
+
+bool View::HasHitTestMask() const {
+ return false;
+}
+
+void View::GetHitTestMask(gfx::Path* mask) const {
+ DCHECK(mask);
+}
+
+// Focus -----------------------------------------------------------------------
+
+bool View::IsFocusable() const {
+ return focusable_ && IsEnabled() && IsVisible();
+}
+
+void View::OnFocus() {
+ // TODO(beng): Investigate whether it's possible for us to move this to
+ // Focus().
+ // By default, we clear the native focus. This ensures that no visible native
+ // view as the focus and that we still receive keyboard inputs.
+ FocusManager* focus_manager = GetFocusManager();
+ if (focus_manager)
+ focus_manager->ClearNativeFocus();
+
+ // TODO(beng): Investigate whether it's possible for us to move this to
+ // Focus().
+ // Notify assistive technologies of the focus change.
+ GetWidget()->NotifyAccessibilityEvent(
+ this, ui::AccessibilityTypes::EVENT_FOCUS, true);
+}
+
+void View::OnBlur() {
+}
+
+void View::Focus() {
+ SchedulePaint();
+ OnFocus();
+}
+
+void View::Blur() {
+ SchedulePaint();
+ OnBlur();
+}
+
+// Tooltips --------------------------------------------------------------------
+
+void View::TooltipTextChanged() {
+ Widget* widget = GetWidget();
+ // TooltipManager may be null if there is a problem creating it.
+ if (widget && widget->native_widget_private()->GetTooltipManager()) {
+ widget->native_widget_private()->GetTooltipManager()->
+ TooltipTextChanged(this);
+ }
+}
+
+// Context menus ---------------------------------------------------------------
+
+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;
+}
+
+// Drag and drop ---------------------------------------------------------------
+
+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() {
+ Widget* widget = GetWidget();
+ return widget ? widget->dragged_view() == this : false;
+}
+
+// Debugging -------------------------------------------------------------------
+
+#if !defined(NDEBUG)
+
+std::string View::PrintViewGraph(bool first) {
+ return DoPrintViewGraph(first, this);
+}
+
+std::string View::DoPrintViewGraph(bool first, View* view_with_children) {
+ // 64-bit pointer = 16 bytes of hex + "0x" + '\0' = 19.
+ const size_t kMaxPointerStringLength = 19;
+
+ std::string result;
+
+ if (first)
+ result.append("digraph {\n");
+
+ // Node characteristics.
+ char p[kMaxPointerStringLength];
+
+ size_t baseNameIndex = GetClassName().find_last_of('/');
+ if (baseNameIndex == std::string::npos)
+ baseNameIndex = 0;
+ else
+ baseNameIndex++;
+
+ char bounds_buffer[512];
+
+ // Information about current node.
+ base::snprintf(p, arraysize(bounds_buffer), "%p", view_with_children);
+ result.append(" N");
+ result.append(p+2);
+ result.append(" [label=\"");
+
+ result.append(GetClassName().substr(baseNameIndex).c_str());
+
+ base::snprintf(bounds_buffer,
+ arraysize(bounds_buffer),
+ "\\n bounds: (%d, %d), (%dx%d)",
+ this->bounds().x(),
+ this->bounds().y(),
+ this->bounds().width(),
+ this->bounds().height());
+ result.append(bounds_buffer);
+
+ if (layer() && !layer()->hole_rect().IsEmpty()) {
+ base::snprintf(bounds_buffer,
+ arraysize(bounds_buffer),
+ "\\n hole bounds: (%d, %d), (%dx%d)",
+ layer()->hole_rect().x(),
+ layer()->hole_rect().y(),
+ layer()->hole_rect().width(),
+ layer()->hole_rect().height());
+ result.append(bounds_buffer);
+ }
+
+ if (GetTransform().HasChange()) {
+ gfx::Point translation;
+ float rotation;
+ gfx::Point3f scale;
+ if (ui::InterpolatedTransform::FactorTRS(GetTransform(),
+ &translation,
+ &rotation,
+ &scale)) {
+ if (translation != gfx::Point(0, 0)) {
+ base::snprintf(bounds_buffer,
+ arraysize(bounds_buffer),
+ "\\n translation: (%d, %d)",
+ translation.x(),
+ translation.y());
+ result.append(bounds_buffer);
+ }
+
+ if (fabs(rotation) > 1e-5) {
+ base::snprintf(bounds_buffer,
+ arraysize(bounds_buffer),
+ "\\n rotation: %3.2f", rotation);
+ result.append(bounds_buffer);
+ }
+
+ if (scale.AsPoint() != gfx::Point(0, 0)) {
+ base::snprintf(bounds_buffer,
+ arraysize(bounds_buffer),
+ "\\n scale: (%2.4f, %2.4f)",
+ scale.x(),
+ scale.y());
+ result.append(bounds_buffer);
+ }
+ }
+ }
+
+ result.append("\"");
+ if (!parent_)
+ result.append(", shape=box");
+ if (layer()) {
+ if (layer()->texture())
+ result.append(", color=green");
+ else
+ result.append(", color=red");
+
+ if (layer()->fills_bounds_opaquely())
+ result.append(", style=filled");
+ }
+ result.append("]\n");
+
+ // Link to parent.
+ if (parent_) {
+ char pp[kMaxPointerStringLength];
+
+ base::snprintf(pp, kMaxPointerStringLength, "%p", parent_);
+ result.append(" N");
+ result.append(pp+2);
+ result.append(" -> N");
+ result.append(p+2);
+ result.append("\n");
+ }
+
+ // Children.
+ for (int i = 0, count = view_with_children->child_count(); i < count; ++i)
+ result.append(view_with_children->child_at(i)->PrintViewGraph(false));
+
+ if (first)
+ result.append("}\n");
+
+ return result;
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// View, private:
+
+// DropInfo --------------------------------------------------------------------
+
+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;
+}
+
+// Painting --------------------------------------------------------------------
+
+void View::SchedulePaintBoundsChanged(SchedulePaintType type) {
+ // If we have a layer and the View's size did not change, we do not need to
+ // schedule any paints since the layer will be redrawn at its new location
+ // during the next Draw() cycle in the compositor.
+ if (!layer() || type == SCHEDULE_PAINT_SIZE_CHANGED) {
+ // Otherwise, if the size changes or we don't have a layer then we need to
+ // use SchedulePaint to invalidate the area occupied by the View.
+ SchedulePaint();
+ } else if (parent_ && type == SCHEDULE_PAINT_SIZE_SAME) {
+ // The compositor doesn't Draw() until something on screen changes, so
+ // if our position changes but nothing is being animated on screen, then
+ // tell the compositor to redraw the scene. We know layer() exists due to
+ // the above if clause.
+ layer()->ScheduleDraw();
+ }
+}
+
+void View::PaintCommon(gfx::Canvas* canvas) {
+ if (!IsVisible() || !painting_enabled_)
+ return;
+
+ {
+ // If the View we are about to paint requested the canvas to be flipped, we
+ // should change the transform appropriately.
+ // The canvas mirroring is undone once the View is done painting so that we
+ // don't pass the canvas with the mirrored transform to Views that didn't
+ // request the canvas to be flipped.
+ ScopedCanvas scoped(canvas);
+ if (FlipCanvasOnPaintForRTLUI()) {
+ canvas->Translate(gfx::Point(width(), 0));
+ canvas->Scale(-1, 1);
+ }
+
+ OnPaint(canvas);
+ }
+
+ PaintChildren(canvas);
+}
+
+// Tree operations -------------------------------------------------------------
+
+void View::DoRemoveChildView(View* view,
+ bool update_focus_cycle,
+ bool update_tool_tip,
+ bool delete_removed_view) {
+ DCHECK(view);
+ const Views::iterator i(std::find(children_.begin(), children_.end(), view));
+ scoped_ptr<View> view_to_be_deleted;
+ if (i != children_.end()) {
+ if (update_focus_cycle) {
+ // Let's remove the view from the focus traversal.
+ View* next_focusable = view->next_focusable_view_;
+ View* prev_focusable = view->previous_focusable_view_;
+ if (prev_focusable)
+ prev_focusable->next_focusable_view_ = next_focusable;
+ if (next_focusable)
+ next_focusable->previous_focusable_view_ = prev_focusable;
+ }
+
+ if (GetWidget())
+ UnregisterChildrenForVisibleBoundsNotification(view);
+ view->PropagateRemoveNotifications(this);
+ view->parent_ = NULL;
+ view->UpdateLayerVisibility();
+
+ if (delete_removed_view && view->parent_owned())
+ view_to_be_deleted.reset(view);
+
+ children_.erase(i);
+ }
+
+ if (update_tool_tip)
+ UpdateTooltip();
+
+ if (layout_manager_.get())
+ layout_manager_->ViewRemoved(this, view);
+}
+
+void View::PropagateRemoveNotifications(View* parent) {
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->PropagateRemoveNotifications(parent);
+
+ for (View* v = this; v; v = v->parent_)
+ v->ViewHierarchyChangedImpl(true, false, parent, this);
+}
+
+void View::PropagateAddNotifications(View* parent, View* child) {
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->PropagateAddNotifications(parent, child);
+ ViewHierarchyChangedImpl(true, true, parent, child);
+}
+
+void View::PropagateNativeViewHierarchyChanged(bool attached,
+ gfx::NativeView native_view,
+ internal::RootView* root_view) {
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->PropagateNativeViewHierarchyChanged(attached,
+ native_view,
+ root_view);
+ NativeViewHierarchyChanged(attached, native_view, root_view);
+}
+
+void View::ViewHierarchyChangedImpl(bool register_accelerators,
+ bool is_add,
+ View* parent,
+ View* child) {
+ if (register_accelerators) {
+ if (is_add) {
+ // If you get this registration, you are part of a subtree that has been
+ // added to the view hierarchy.
+ if (GetFocusManager()) {
+ RegisterPendingAccelerators();
+ } else {
+ // Delay accelerator registration until visible as we do not have
+ // focus manager until then.
+ accelerator_registration_delayed_ = true;
+ }
+ } else {
+ if (child == this)
+ UnregisterAccelerators(true);
+ }
+ }
+
+ if (is_add && layer() && !layer()->parent()) {
+ UpdateParentLayer();
+ } else if (!is_add && child == this) {
+ // Make sure the layers beloning to the subtree rooted at |child| get
+ // removed from layers that do not belong in the same subtree.
+ OrphanLayers();
+ }
+
+ ViewHierarchyChanged(is_add, parent, child);
+ parent->needs_layout_ = true;
+}
+
+// Size and disposition --------------------------------------------------------
+
+void View::PropagateVisibilityNotifications(View* start, bool is_visible) {
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->PropagateVisibilityNotifications(start, is_visible);
+ VisibilityChangedImpl(start, is_visible);
+}
+
+void View::VisibilityChangedImpl(View* starting_from, bool is_visible) {
+ if (is_visible)
+ RegisterPendingAccelerators();
+ else
+ UnregisterAccelerators(true);
+ VisibilityChanged(starting_from, is_visible);
+}
+
+void View::BoundsChanged(const gfx::Rect& previous_bounds) {
+ if (IsVisible()) {
+ // Paint the new bounds.
+ SchedulePaintBoundsChanged(
+ bounds_.size() == previous_bounds.size() ? SCHEDULE_PAINT_SIZE_SAME :
+ SCHEDULE_PAINT_SIZE_CHANGED);
+ }
+
+ if (use_acceleration_when_possible) {
+ if (layer()) {
+ if (parent_) {
+ gfx::Point offset;
+ parent_->CalculateOffsetToAncestorWithLayer(&offset, NULL);
+ offset.Offset(x(), y());
+ layer()->SetBounds(gfx::Rect(offset, size()));
+ } else {
+ layer()->SetBounds(bounds_);
+ }
+ // TODO(beng): this seems redundant with the SchedulePaint at the top of
+ // this function. explore collapsing.
+ if (previous_bounds.size() != bounds_.size() &&
+ !layer()->layer_updated_externally()) {
+ // If our bounds have changed then we need to update the complete
+ // texture.
+ layer()->SchedulePaint(GetLocalBounds());
+ }
+ } else {
+ // If our bounds have changed, then any descendant layer bounds may
+ // have changed. Update them accordingly.
+ gfx::Point offset;
+ CalculateOffsetToAncestorWithLayer(&offset, NULL);
+ UpdateChildLayerBounds(offset);
+ }
+ }
+
+ OnBoundsChanged(previous_bounds);
+
+ if (previous_bounds.size() != size()) {
+ needs_layout_ = false;
+ Layout();
+ }
+
+ if (NeedsNotificationWhenVisibleBoundsChange())
+ OnVisibleBoundsChanged();
+
+ // Notify interested Views that visible bounds within the root view may have
+ // changed.
+ if (descendants_to_notify_.get()) {
+ for (Views::iterator i(descendants_to_notify_->begin());
+ i != descendants_to_notify_->end(); ++i) {
+ (*i)->OnVisibleBoundsChanged();
+ }
+ }
+}
+
+// static
+void View::RegisterChildrenForVisibleBoundsNotification(View* view) {
+ if (view->NeedsNotificationWhenVisibleBoundsChange())
+ view->RegisterForVisibleBoundsNotification();
+ for (int i = 0; i < view->child_count(); ++i)
+ RegisterChildrenForVisibleBoundsNotification(view->child_at(i));
+}
+
+// static
+void View::UnregisterChildrenForVisibleBoundsNotification(View* view) {
+ if (view->NeedsNotificationWhenVisibleBoundsChange())
+ view->UnregisterForVisibleBoundsNotification();
+ for (int i = 0; i < view->child_count(); ++i)
+ UnregisterChildrenForVisibleBoundsNotification(view->child_at(i));
+}
+
+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_.get())
+ descendants_to_notify_.reset(new Views);
+ descendants_to_notify_->push_back(view);
+}
+
+void View::RemoveDescendantToNotify(View* view) {
+ DCHECK(view && descendants_to_notify_.get());
+ Views::iterator i(std::find(
+ descendants_to_notify_->begin(), descendants_to_notify_->end(), view));
+ DCHECK(i != descendants_to_notify_->end());
+ descendants_to_notify_->erase(i);
+ if (descendants_to_notify_->empty())
+ descendants_to_notify_.reset();
+}
+
+// Transformations -------------------------------------------------------------
+
+bool View::GetTransformRelativeTo(const View* ancestor,
+ ui::Transform* transform) const {
+ const View* p = this;
+
+ while (p && p != ancestor) {
+ transform->ConcatTransform(p->GetTransform());
+ transform->ConcatTranslate(static_cast<float>(p->GetMirroredX()),
+ static_cast<float>(p->y()));
+
+ p = p->parent_;
+ }
+
+ return p == ancestor;
+}
+
+// Coordinate conversion -------------------------------------------------------
+
+bool View::ConvertPointForAncestor(const View* ancestor,
+ gfx::Point* point) const {
+ ui::Transform trans;
+ // TODO(sad): Have some way of caching the transformation results.
+ bool result = GetTransformRelativeTo(ancestor, &trans);
+ gfx::Point3f p(*point);
+ trans.TransformPoint(p);
+ *point = p.AsPoint();
+ return result;
+}
+
+bool View::ConvertPointFromAncestor(const View* ancestor,
+ gfx::Point* point) const {
+ ui::Transform trans;
+ bool result = GetTransformRelativeTo(ancestor, &trans);
+ gfx::Point3f p(*point);
+ trans.TransformPointReverse(p);
+ *point = p.AsPoint();
+ return result;
+}
+
+// Accelerated painting --------------------------------------------------------
+
+void View::CreateLayer() {
+ // A new layer is being created for the view. So all the layers of the
+ // sub-tree can inherit the visibility of the corresponding view.
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->UpdateChildLayerVisibility(true);
+
+ layer_.reset(new ui::Layer());
+ layer_->set_delegate(this);
+#if !defined(NDEBUG)
+ layer_->set_name(GetClassName());
+#endif
+
+ UpdateParentLayers();
+ UpdateLayerVisibility();
+
+ // The new layer needs to be ordered in the layer tree according
+ // to the view tree. Children of this layer were added in order
+ // in UpdateParentLayers().
+ if (parent())
+ parent()->ReorderLayers();
+}
+
+void View::UpdateParentLayers() {
+ // Attach all top-level un-parented layers.
+ if (layer() && !layer()->parent()) {
+ UpdateParentLayer();
+ } else {
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->UpdateParentLayers();
+ }
+}
+
+void View::UpdateParentLayer() {
+ if (!layer())
+ return;
+
+ ui::Layer* parent_layer = NULL;
+ gfx::Point offset(x(), y());
+
+ // TODO(sad): The NULL check here for parent_ essentially is to check if this
+ // is the RootView. Instead of doing this, this function should be made
+ // virtual and overridden from the RootView.
+ if (parent_)
+ parent_->CalculateOffsetToAncestorWithLayer(&offset, &parent_layer);
+ else if (!parent_ && GetWidget())
+ GetWidget()->CalculateOffsetToAncestorWithLayer(&offset, &parent_layer);
+
+ ReparentLayer(offset, parent_layer);
+}
+
+void View::OrphanLayers() {
+ if (layer()) {
+ if (layer()->parent())
+ layer()->parent()->Remove(layer());
+
+ // The layer belonging to this View has already been orphaned. It is not
+ // necessary to orphan the child layers.
+ return;
+ }
+ for (int i = 0, count = child_count(); i < count; ++i)
+ child_at(i)->OrphanLayers();
+}
+
+void View::ReparentLayer(const gfx::Point& offset, ui::Layer* parent_layer) {
+ layer_->SetBounds(gfx::Rect(offset.x(), offset.y(), width(), height()));
+ DCHECK_NE(layer(), parent_layer);
+ if (parent_layer)
+ parent_layer->Add(layer());
+ layer_->SchedulePaint(GetLocalBounds());
+ MoveLayerToParent(layer(), gfx::Point());
+}
+
+void View::DestroyLayer() {
+ ui::Layer* new_parent = layer()->parent();
+ std::vector<ui::Layer*> children = layer()->children();
+ for (size_t i = 0; i < children.size(); ++i) {
+ layer()->Remove(children[i]);
+ if (new_parent)
+ new_parent->Add(children[i]);
+ }
+
+ layer_.reset();
+
+ if (new_parent)
+ ReorderLayers();
+
+ gfx::Point offset;
+ CalculateOffsetToAncestorWithLayer(&offset, NULL);
+ UpdateChildLayerBounds(offset);
+
+ SchedulePaint();
+}
+
+// Input -----------------------------------------------------------------------
+
+bool View::ProcessMousePressed(const MouseEvent& event, DragInfo* drag_info) {
+ const bool enabled = IsEnabled();
+ int drag_operations =
+ (enabled && event.IsOnlyLeftMouseButton() && HitTest(event.location())) ?
+ GetDragOperations(event.location()) : 0;
+ ContextMenuController* context_menu_controller = event.IsRightMouseButton() ?
+ context_menu_controller_ : 0;
+
+ const bool result = OnMousePressed(event);
+ // WARNING: we may have been deleted, don't use any View variables.
+
+ if (!enabled)
+ return result;
+
+ if (drag_operations != ui::DragDropTypes::DRAG_NONE) {
+ drag_info->PossibleDrag(event.location());
+ return true;
+ }
+ return !!context_menu_controller || result;
+}
+
+bool View::ProcessMouseDragged(const MouseEvent& event, DragInfo* drag_info) {
+ // Copy the field, that way if we're deleted after drag and drop no harm is
+ // done.
+ ContextMenuController* context_menu_controller = context_menu_controller_;
+ const bool possible_drag = drag_info->possible_drag;
+ if (possible_drag && ExceededDragThreshold(
+ drag_info->start_pt.x() - event.x(),
+ drag_info->start_pt.y() - event.y())) {
+ if (!drag_controller_ ||
+ drag_controller_->CanStartDragForView(
+ this, drag_info->start_pt, event.location()))
+ DoDrag(event, drag_info->start_pt);
+ } else {
+ if (OnMouseDragged(event))
+ return true;
+ // Fall through to return value based on context menu controller.
+ }
+ // WARNING: we may have been deleted.
+ return (context_menu_controller != NULL) || possible_drag;
+}
+
+void View::ProcessMouseReleased(const MouseEvent& event) {
+ if (context_menu_controller_ && event.IsOnlyRightMouseButton()) {
+ // Assume that if there is a context menu controller we won't be deleted
+ // from mouse released.
+ gfx::Point location(event.location());
+ OnMouseReleased(event);
+ if (HitTest(location)) {
+ ConvertPointToScreen(this, &location);
+ ShowContextMenu(location, true);
+ }
+ } else {
+ OnMouseReleased(event);
+ }
+ // WARNING: we may have been deleted.
+}
+
+ui::TouchStatus View::ProcessTouchEvent(const TouchEvent& event) {
+ // TODO(rjkroege): Implement a grab scheme similar to as as is found in
+ // MousePressed.
+ return OnTouchEvent(event);
+}
+
+// Accelerators ----------------------------------------------------------------
+
+void View::RegisterPendingAccelerators() {
+ if (!accelerators_.get() ||
+ registered_accelerator_count_ == accelerators_->size()) {
+ // No accelerators are waiting for registration.
+ return;
+ }
+
+ if (!GetWidget()) {
+ // The view is not yet attached to a widget, defer registration until then.
+ return;
+ }
+
+ accelerator_focus_manager_ = GetFocusManager();
+ if (!accelerator_focus_manager_) {
+ // Some crash reports seem to show that we may get cases where we have no
+ // focus manager (see bug #1291225). This should never be the case, just
+ // making sure we don't crash.
+
+ // TODO(jcampan): This fails for a view under NativeWidgetGtk with
+ // TYPE_CHILD. (see http://crbug.com/21335) reenable
+ // NOTREACHED assertion and verify accelerators works as
+ // expected.
+#if defined(OS_WIN)
+ NOTREACHED();
+#endif
+ return;
+ }
+ // Only register accelerators if we are visible.
+ if (!IsVisibleInRootView() || !GetWidget()->IsVisible())
+ return;
+ for (std::vector<ui::Accelerator>::const_iterator i(
+ accelerators_->begin() + registered_accelerator_count_);
+ i != accelerators_->end(); ++i) {
+ accelerator_focus_manager_->RegisterAccelerator(*i, this);
+ }
+ registered_accelerator_count_ = accelerators_->size();
+}
+
+void View::UnregisterAccelerators(bool leave_data_intact) {
+ if (!accelerators_.get())
+ return;
+
+ if (GetWidget()) {
+ if (accelerator_focus_manager_) {
+ // We may not have a FocusManager if the window containing us is being
+ // closed, in which case the FocusManager is being deleted so there is
+ // nothing to unregister.
+ accelerator_focus_manager_->UnregisterAccelerators(this);
+ accelerator_focus_manager_ = NULL;
+ }
+ if (!leave_data_intact) {
+ accelerators_->clear();
+ accelerators_.reset();
+ }
+ registered_accelerator_count_ = 0;
+ }
+}
+
+// Focus -----------------------------------------------------------------------
+
+void View::InitFocusSiblings(View* v, int index) {
+ int count = child_count();
+
+ if (count == 0) {
+ v->next_focusable_view_ = NULL;
+ v->previous_focusable_view_ = NULL;
+ } else {
+ if (index == count) {
+ // We are inserting at the end, but the end of the child list may not be
+ // the last focusable element. Let's try to find an element with no next
+ // focusable element to link to.
+ View* last_focusable_view = NULL;
+ for (Views::iterator i(children_.begin()); i != children_.end(); ++i) {
+ if (!(*i)->next_focusable_view_) {
+ last_focusable_view = *i;
+ break;
+ }
+ }
+ if (last_focusable_view == NULL) {
+ // Hum... there is a cycle in the focus list. Let's just insert ourself
+ // after the last child.
+ View* prev = children_[index - 1];
+ v->previous_focusable_view_ = prev;
+ v->next_focusable_view_ = prev->next_focusable_view_;
+ prev->next_focusable_view_->previous_focusable_view_ = v;
+ prev->next_focusable_view_ = v;
+ } else {
+ last_focusable_view->next_focusable_view_ = v;
+ v->next_focusable_view_ = NULL;
+ v->previous_focusable_view_ = last_focusable_view;
+ }
+ } else {
+ View* prev = children_[index]->GetPreviousFocusableView();
+ v->previous_focusable_view_ = prev;
+ v->next_focusable_view_ = children_[index];
+ if (prev)
+ prev->next_focusable_view_ = v;
+ children_[index]->previous_focusable_view_ = v;
+ }
+ }
+}
+
+// System events ---------------------------------------------------------------
+
+void View::PropagateThemeChanged() {
+ for (int i = child_count() - 1; i >= 0; --i)
+ child_at(i)->PropagateThemeChanged();
+ OnThemeChanged();
+}
+
+void View::PropagateLocaleChanged() {
+ for (int i = child_count() - 1; i >= 0; --i)
+ child_at(i)->PropagateLocaleChanged();
+ OnLocaleChanged();
+}
+
+// Tooltips --------------------------------------------------------------------
+
+void View::UpdateTooltip() {
+ Widget* widget = GetWidget();
+ // TODO(beng): The TooltipManager NULL check can be removed when we
+ // consolidate Init() methods and make views_unittests Init() all
+ // Widgets that it uses.
+ if (widget && widget->native_widget_private()->GetTooltipManager())
+ widget->native_widget_private()->GetTooltipManager()->UpdateTooltip();
+}
+
+// Drag and drop ---------------------------------------------------------------
+
+void View::DoDrag(const MouseEvent& event, const gfx::Point& press_pt) {
+ int drag_operations = GetDragOperations(press_pt);
+ if (drag_operations == ui::DragDropTypes::DRAG_NONE)
+ return;
+
+ OSExchangeData data;
+ WriteDragData(press_pt, &data);
+
+ // Message the RootView to do the drag and drop. That way if we're removed
+ // the RootView can detect it and avoid calling us back.
+ GetWidget()->RunShellDrag(this, data, drag_operations);
+}
+
+} // namespace views