1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "ui/views/controls/native/native_view_host.h" 6 7#include "base/logging.h" 8#include "ui/base/cursor/cursor.h" 9#include "ui/gfx/canvas.h" 10#include "ui/views/accessibility/native_view_accessibility.h" 11#include "ui/views/controls/native/native_view_host_wrapper.h" 12#include "ui/views/widget/widget.h" 13 14namespace views { 15 16// static 17const char NativeViewHost::kViewClassName[] = "NativeViewHost"; 18const char kWidgetNativeViewHostKey[] = "WidgetNativeViewHost"; 19 20//////////////////////////////////////////////////////////////////////////////// 21// NativeViewHost, public: 22 23NativeViewHost::NativeViewHost() 24 : native_view_(NULL), 25 fast_resize_(false), 26 fast_resize_at_last_layout_(false), 27 focus_view_(NULL) { 28} 29 30NativeViewHost::~NativeViewHost() { 31} 32 33void NativeViewHost::Attach(gfx::NativeView native_view) { 34 DCHECK(native_view); 35 DCHECK(!native_view_); 36 native_view_ = native_view; 37 // If set_focus_view() has not been invoked, this view is the one that should 38 // be seen as focused when the native view receives focus. 39 if (!focus_view_) 40 focus_view_ = this; 41 native_wrapper_->AttachNativeView(); 42 Layout(); 43 44 Widget* widget = Widget::GetWidgetForNativeView(native_view); 45 if (widget) 46 widget->SetNativeWindowProperty(kWidgetNativeViewHostKey, this); 47} 48 49void NativeViewHost::Detach() { 50 Detach(false); 51} 52 53void NativeViewHost::SetPreferredSize(const gfx::Size& size) { 54 preferred_size_ = size; 55 PreferredSizeChanged(); 56} 57 58void NativeViewHost::NativeViewDestroyed() { 59 // Detach so we can clear our state and notify the native_wrapper_ to release 60 // ref on the native view. 61 Detach(true); 62} 63 64//////////////////////////////////////////////////////////////////////////////// 65// NativeViewHost, View overrides: 66 67gfx::Size NativeViewHost::GetPreferredSize() const { 68 return preferred_size_; 69} 70 71void NativeViewHost::Layout() { 72 if (!native_view_ || !native_wrapper_.get()) 73 return; 74 75 gfx::Rect vis_bounds = GetVisibleBounds(); 76 bool visible = !vis_bounds.IsEmpty(); 77 78 if (visible && !fast_resize_) { 79 if (vis_bounds.size() != size()) { 80 // Only a portion of the Widget is really visible. 81 int x = vis_bounds.x(); 82 int y = vis_bounds.y(); 83 native_wrapper_->InstallClip(x, y, vis_bounds.width(), 84 vis_bounds.height()); 85 } else if (native_wrapper_->HasInstalledClip()) { 86 // The whole widget is visible but we installed a clip on the widget, 87 // uninstall it. 88 native_wrapper_->UninstallClip(); 89 } 90 } 91 92 if (visible) { 93 // Since widgets know nothing about the View hierarchy (they are direct 94 // children of the Widget that hosts our View hierarchy) they need to be 95 // positioned in the coordinate system of the Widget, not the current 96 // view. Also, they should be positioned respecting the border insets 97 // of the native view. 98 gfx::Rect local_bounds = ConvertRectToWidget(GetContentsBounds()); 99 native_wrapper_->ShowWidget(local_bounds.x(), local_bounds.y(), 100 local_bounds.width(), 101 local_bounds.height()); 102 } else { 103 native_wrapper_->HideWidget(); 104 } 105 fast_resize_at_last_layout_ = visible && fast_resize_; 106} 107 108void NativeViewHost::OnPaint(gfx::Canvas* canvas) { 109 // Paint background if there is one. NativeViewHost needs to paint 110 // a background when it is hosted in a TabbedPane. For Gtk implementation, 111 // NativeTabbedPaneGtk uses a NativeWidgetGtk as page container and because 112 // NativeWidgetGtk hook "expose" with its root view's paint, we need to 113 // fill the content. Otherwise, the tab page's background is not properly 114 // cleared. For Windows case, it appears okay to not paint background because 115 // we don't have a container window in-between. However if you want to use 116 // customized background, then this becomes necessary. 117 OnPaintBackground(canvas); 118 119 // The area behind our window is black, so during a fast resize (where our 120 // content doesn't draw over the full size of our native view, and the native 121 // view background color doesn't show up), we need to cover that blackness 122 // with something so that fast resizes don't result in black flash. 123 // 124 // It would be nice if this used some approximation of the page's 125 // current background color. 126 if (native_wrapper_->HasInstalledClip()) 127 canvas->FillRect(GetLocalBounds(), SK_ColorWHITE); 128} 129 130void NativeViewHost::VisibilityChanged(View* starting_from, bool is_visible) { 131 Layout(); 132} 133 134bool NativeViewHost::GetNeedsNotificationWhenVisibleBoundsChange() const { 135 // The native widget is placed relative to the root. As such, we need to 136 // know when the position of any ancestor changes, or our visibility relative 137 // to other views changed as it'll effect our position relative to the root. 138 return true; 139} 140 141void NativeViewHost::OnVisibleBoundsChanged() { 142 Layout(); 143} 144 145void NativeViewHost::ViewHierarchyChanged( 146 const ViewHierarchyChangedDetails& details) { 147 views::Widget* this_widget = GetWidget(); 148 149 // A non-NULL |details.move_view| indicates a move operation i.e. |this| is 150 // is being reparented. If the previous and new parents belong to the same 151 // widget, don't remove |this| from the widget. This saves resources from 152 // removing from widget and immediately followed by adding to widget; in 153 // particular, there wouldn't be spurious visibilitychange events for web 154 // contents of |WebView|. 155 if (details.move_view && this_widget && 156 details.move_view->GetWidget() == this_widget) { 157 return; 158 } 159 160 if (details.is_add && this_widget) { 161 if (!native_wrapper_.get()) 162 native_wrapper_.reset(NativeViewHostWrapper::CreateWrapper(this)); 163 native_wrapper_->AddedToWidget(); 164 } else if (!details.is_add) { 165 native_wrapper_->RemovedFromWidget(); 166 } 167} 168 169const char* NativeViewHost::GetClassName() const { 170 return kViewClassName; 171} 172 173void NativeViewHost::OnFocus() { 174 native_wrapper_->SetFocus(); 175 NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); 176} 177 178gfx::NativeViewAccessible NativeViewHost::GetNativeViewAccessible() { 179 if (native_wrapper_.get()) { 180 gfx::NativeViewAccessible accessible_view = 181 native_wrapper_->GetNativeViewAccessible(); 182 if (accessible_view) 183 return accessible_view; 184 } 185 186 return View::GetNativeViewAccessible(); 187} 188 189gfx::NativeCursor NativeViewHost::GetCursor(const ui::MouseEvent& event) { 190 return native_wrapper_->GetCursor(event.x(), event.y()); 191} 192 193//////////////////////////////////////////////////////////////////////////////// 194// NativeViewHost, private: 195 196void NativeViewHost::Detach(bool destroyed) { 197 if (native_view_) { 198 if (!destroyed) { 199 Widget* widget = Widget::GetWidgetForNativeView(native_view_); 200 if (widget) 201 widget->SetNativeWindowProperty(kWidgetNativeViewHostKey, NULL); 202 ClearFocus(); 203 } 204 native_wrapper_->NativeViewDetaching(destroyed); 205 native_view_ = NULL; 206 } 207} 208 209void NativeViewHost::ClearFocus() { 210 FocusManager* focus_manager = GetFocusManager(); 211 if (!focus_manager || !focus_manager->GetFocusedView()) 212 return; 213 214 Widget::Widgets widgets; 215 Widget::GetAllChildWidgets(native_view(), &widgets); 216 for (Widget::Widgets::iterator i = widgets.begin(); i != widgets.end(); ++i) { 217 focus_manager->ViewRemoved((*i)->GetRootView()); 218 if (!focus_manager->GetFocusedView()) 219 return; 220 } 221} 222 223} // namespace views 224