opaque_browser_frame_view_layout.cc revision 58537e28ecd584eab876aee8be7156509866d23a
1// Copyright 2013 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 "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h" 6 7#include "ui/gfx/font.h" 8#include "ui/views/controls/button/image_button.h" 9#include "ui/views/controls/label.h" 10 11#if defined(OS_WIN) 12#include "win8/util/win8_util.h" 13#endif // OS_WIN 14 15namespace { 16 17// Besides the frame border, there's another 9 px of empty space atop the 18// window in restored mode, to use to drag the window around. 19const int kNonClientRestoredExtraThickness = 9; 20 21// The titlebar never shrinks too short to show the caption button plus some 22// padding below it. 23const int kCaptionButtonHeightWithPadding = 19; 24 25// There is a 5 px gap between the title text and the caption buttons. 26const int kTitleLogoSpacing = 5; 27 28// The frame border is only visible in restored mode and is hardcoded to 4 px on 29// each side regardless of the system window border size. 30const int kFrameBorderThickness = 4; 31 32// The titlebar has a 2 px 3D edge along the top and bottom. 33const int kTitlebarTopAndBottomEdgeThickness = 2; 34 35// The icon is inset 2 px from the left frame border. 36const int kIconLeftSpacing = 2; 37 38// There is a 4 px gap between the icon and the title text. 39const int kIconTitleSpacing = 4; 40 41// The avatar ends 2 px above the bottom of the tabstrip (which, given the 42// way the tabstrip draws its bottom edge, will appear like a 1 px gap to the 43// user). 44const int kAvatarBottomSpacing = 2; 45 46// Space between the frame border and the left edge of the avatar. 47const int kAvatarLeftSpacing = 2; 48 49// Space between the right edge of the avatar and the tabstrip. 50const int kAvatarRightSpacing = -4; 51 52// In restored mode, the New Tab button isn't at the same height as the caption 53// buttons, but the space will look cluttered if it actually slides under them, 54// so we stop it when the gap between the two is down to 5 px. 55const int kNewTabCaptionRestoredSpacing = 5; 56 57// In maximized mode, where the New Tab button and the caption buttons are at 58// similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid 59// looking too cluttered. 60const int kNewTabCaptionMaximizedSpacing = 16; 61 62// The top 3 px of the tabstrip is shadow; in maximized mode we push this off 63// the top of the screen so the tabs appear flush against the screen edge. 64const int kTabstripTopShadowThickness = 3; 65 66// How far to indent the tabstrip from the left side of the screen when there 67// is no avatar icon. 68const int kTabStripIndent = -6; 69 70#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 71// Default extra space between the top of the frame and the top of the window 72// caption buttons. 73const int kExtraCaption = 2; 74 75// Default extra spacing between individual window caption buttons. 76const int kCaptionButtonSpacing = 2; 77#else 78const int kExtraCaption = 0; 79const int kCaptionButtonSpacing = 0; 80#endif 81 82} // namespace 83 84/////////////////////////////////////////////////////////////////////////////// 85// OpaqueBrowserFrameView, public: 86 87OpaqueBrowserFrameViewLayout::OpaqueBrowserFrameViewLayout( 88 OpaqueBrowserFrameViewLayoutDelegate* delegate) 89 : delegate_(delegate), 90 leading_button_start_(0), 91 trailing_button_start_(0), 92 minimum_size_for_buttons_(0), 93 has_leading_buttons_(false), 94 has_trailing_buttons_(false), 95 extra_caption_y_(kExtraCaption), 96 window_caption_spacing_(kCaptionButtonSpacing), 97 minimize_button_(NULL), 98 maximize_button_(NULL), 99 restore_button_(NULL), 100 close_button_(NULL), 101 window_icon_(NULL), 102 window_title_(NULL), 103 avatar_label_(NULL), 104 avatar_button_(NULL) { 105 trailing_buttons_.push_back(BUTTON_MINIMIZE); 106 trailing_buttons_.push_back(BUTTON_MAXIMIZE); 107 trailing_buttons_.push_back(BUTTON_CLOSE); 108} 109 110OpaqueBrowserFrameViewLayout::~OpaqueBrowserFrameViewLayout() {} 111 112// static 113bool OpaqueBrowserFrameViewLayout::ShouldAddDefaultCaptionButtons() { 114#if defined(OS_WIN) 115 return !win8::IsSingleWindowMetroMode(); 116#endif // OS_WIN 117 return true; 118} 119 120gfx::Rect OpaqueBrowserFrameViewLayout::GetBoundsForTabStrip( 121 const gfx::Size& tabstrip_preferred_size, 122 int available_width) const { 123 available_width -= trailing_button_start_; 124 available_width -= leading_button_start_; 125 126 if (delegate_->GetAdditionalReservedSpaceInTabStrip()) 127 available_width -= delegate_->GetAdditionalReservedSpaceInTabStrip(); 128 129 const int caption_spacing = delegate_->IsMaximized() ? 130 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing; 131 const int tabstrip_width = available_width - caption_spacing; 132 gfx::Rect bounds(leading_button_start_, GetTabStripInsetsTop(false), 133 std::max(0, tabstrip_width), 134 tabstrip_preferred_size.height()); 135 136 int leading_tabstrip_indent = kTabStripIndent; 137 if (delegate_->ShouldShowAvatar()) { 138 if (avatar_label_ && avatar_label_->bounds().width()) { 139 // Space between the trailing edge of the avatar label and the tabstrip. 140 const int kAvatarLabelRightSpacing = -10; 141 leading_tabstrip_indent -= kAvatarLabelRightSpacing; 142 } else { 143 leading_tabstrip_indent -= kAvatarRightSpacing; 144 } 145 } 146 bounds.Inset(leading_tabstrip_indent, 0, 0, 0); 147 return bounds; 148} 149 150gfx::Size OpaqueBrowserFrameViewLayout::GetMinimumSize( 151 int available_width) const { 152 gfx::Size min_size = delegate_->GetBrowserViewMinimumSize(); 153 int border_thickness = NonClientBorderThickness(); 154 min_size.Enlarge(2 * border_thickness, 155 NonClientTopBorderHeight(false) + border_thickness); 156 157 // Ensure that we can, at minimum, hold our window controls and avatar icon. 158 min_size.set_width(std::max(min_size.width(), minimum_size_for_buttons_)); 159 160 // Ensure that the minimum width is enough to hold a minimum width tab strip 161 // at its usual insets. 162 if (delegate_->IsTabStripVisible()) { 163 gfx::Size preferred_size = delegate_->GetTabstripPreferredSize(); 164 const int min_tabstrip_width = preferred_size.width(); 165 const int caption_spacing = delegate_->IsMaximized() ? 166 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing; 167 min_size.Enlarge(min_tabstrip_width + caption_spacing, 0); 168 } 169 170 return min_size; 171} 172 173gfx::Rect OpaqueBrowserFrameViewLayout::GetWindowBoundsForClientBounds( 174 const gfx::Rect& client_bounds) const { 175 int top_height = NonClientTopBorderHeight(false); 176 int border_thickness = NonClientBorderThickness(); 177 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), 178 std::max(0, client_bounds.y() - top_height), 179 client_bounds.width() + (2 * border_thickness), 180 client_bounds.height() + top_height + border_thickness); 181} 182 183int OpaqueBrowserFrameViewLayout::FrameBorderThickness(bool restored) const { 184 return (!restored && (delegate_->IsMaximized() || 185 delegate_->IsFullscreen())) ? 186 0 : kFrameBorderThickness; 187} 188 189int OpaqueBrowserFrameViewLayout::NonClientBorderThickness() const { 190 // When we fill the screen, we don't show a client edge. 191 return FrameBorderThickness(false) + 192 ((delegate_->IsMaximized() || delegate_->IsFullscreen()) ? 193 0 : views::NonClientFrameView::kClientEdgeThickness); 194} 195 196int OpaqueBrowserFrameViewLayout::NonClientTopBorderHeight( 197 bool restored) const { 198 if (delegate_->ShouldShowWindowTitle()) { 199 return std::max(FrameBorderThickness(restored) + delegate_->GetIconSize(), 200 CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) + 201 TitlebarBottomThickness(restored); 202 } 203 204 return FrameBorderThickness(restored) - 205 ((delegate_->IsTabStripVisible() && 206 !restored && !delegate_->ShouldLeaveOffsetNearTopBorder()) 207 ? kTabstripTopShadowThickness : 0); 208} 209 210int OpaqueBrowserFrameViewLayout::GetTabStripInsetsTop(bool restored) const { 211 return NonClientTopBorderHeight(restored) + ((!restored && 212 (!delegate_->ShouldLeaveOffsetNearTopBorder() || 213 delegate_->IsFullscreen())) ? 214 0 : kNonClientRestoredExtraThickness); 215} 216 217int OpaqueBrowserFrameViewLayout::TitlebarBottomThickness(bool restored) const { 218 return kTitlebarTopAndBottomEdgeThickness + 219 ((!restored && delegate_->IsMaximized()) ? 0 : 220 views::NonClientFrameView::kClientEdgeThickness); 221} 222 223int OpaqueBrowserFrameViewLayout::CaptionButtonY(bool restored) const { 224 // Maximized buttons start at window top so that even if their images aren't 225 // drawn flush with the screen edge, they still obey Fitts' Law. 226 return ((!restored && delegate_->IsMaximized()) ? 227 FrameBorderThickness(false) : 228 views::NonClientFrameView::kFrameShadowThickness) + extra_caption_y_; 229} 230 231gfx::Rect OpaqueBrowserFrameViewLayout::IconBounds() const { 232 return window_icon_bounds_; 233} 234 235gfx::Rect OpaqueBrowserFrameViewLayout::CalculateClientAreaBounds( 236 int width, 237 int height) const { 238 int top_height = NonClientTopBorderHeight(false); 239 int border_thickness = NonClientBorderThickness(); 240 return gfx::Rect(border_thickness, top_height, 241 std::max(0, width - (2 * border_thickness)), 242 std::max(0, height - top_height - border_thickness)); 243} 244 245/////////////////////////////////////////////////////////////////////////////// 246// OpaqueBrowserFrameView, private: 247 248void OpaqueBrowserFrameViewLayout::LayoutWindowControls(views::View* host) { 249 if (!ShouldAddDefaultCaptionButtons()) 250 return; 251 252 int caption_y = CaptionButtonY(false); 253 254 // Keep a list of all buttons that we don't show. 255 std::vector<ButtonID> buttons_not_shown; 256 buttons_not_shown.push_back(BUTTON_MAXIMIZE); 257 buttons_not_shown.push_back(BUTTON_MINIMIZE); 258 buttons_not_shown.push_back(BUTTON_CLOSE); 259 260 for (std::vector<ButtonID>::const_iterator it = leading_buttons_.begin(); 261 it != leading_buttons_.end(); ++it) { 262 ConfigureButton(host, *it, ALIGN_LEADING, caption_y); 263 buttons_not_shown.erase( 264 std::remove(buttons_not_shown.begin(), buttons_not_shown.end(), *it), 265 buttons_not_shown.end()); 266 } 267 268 for (std::vector<ButtonID>::const_reverse_iterator it = 269 trailing_buttons_.rbegin(); it != trailing_buttons_.rend(); ++it) { 270 ConfigureButton(host, *it, ALIGN_TRAILING, caption_y); 271 buttons_not_shown.erase( 272 std::remove(buttons_not_shown.begin(), buttons_not_shown.end(), *it), 273 buttons_not_shown.end()); 274 } 275 276 for (std::vector<ButtonID>::const_iterator it = buttons_not_shown.begin(); 277 it != buttons_not_shown.end(); ++it) { 278 HideButton(*it); 279 } 280} 281 282void OpaqueBrowserFrameViewLayout::LayoutTitleBar(views::View* host) { 283 bool use_hidden_icon_location = true; 284 285 int size = delegate_->GetIconSize(); 286 int frame_thickness = FrameBorderThickness(false); 287 bool should_show_icon = delegate_->ShouldShowWindowIcon(); 288 bool should_show_title = delegate_->ShouldShowWindowTitle(); 289 290 if (should_show_icon || should_show_title) { 291 use_hidden_icon_location = false; 292 293 // Our frame border has a different "3D look" than Windows'. Theirs has 294 // a more complex gradient on the top that they push their icon/title 295 // below; then the maximized window cuts this off and the icon/title are 296 // centered in the remaining space. Because the apparent shape of our 297 // border is simpler, using the same positioning makes things look 298 // slightly uncentered with restored windows, so when the window is 299 // restored, instead of calculating the remaining space from below the 300 // frame border, we calculate from below the 3D edge. 301 int unavailable_px_at_top = delegate_->IsMaximized() ? 302 frame_thickness : kTitlebarTopAndBottomEdgeThickness; 303 // When the icon is shorter than the minimum space we reserve for the 304 // caption button, we vertically center it. We want to bias rounding to 305 // put extra space above the icon, since the 3D edge (+ client edge, for 306 // restored windows) below looks (to the eye) more like additional space 307 // than does the 3D edge (or nothing at all, for maximized windows) 308 // above; hence the +1. 309 int y = unavailable_px_at_top + (NonClientTopBorderHeight(false) - 310 unavailable_px_at_top - size - 311 TitlebarBottomThickness(false) + 1) / 2; 312 313 window_icon_bounds_ = gfx::Rect(leading_button_start_ + kIconLeftSpacing, y, 314 size, size); 315 leading_button_start_ += size + kIconLeftSpacing; 316 minimum_size_for_buttons_ += size + kIconLeftSpacing; 317 } 318 319 if (should_show_icon) 320 window_icon_->SetBoundsRect(window_icon_bounds_); 321 322 if (window_title_) { 323 window_title_->SetVisible(should_show_title); 324 if (should_show_title) { 325 window_title_->SetText(delegate_->GetWindowTitle()); 326 327 int text_width = std::max( 328 0, host->width() - trailing_button_start_ - kTitleLogoSpacing - 329 leading_button_start_ - kIconTitleSpacing); 330 window_title_->SetBounds(leading_button_start_ + kIconTitleSpacing, 331 window_icon_bounds_.y(), 332 text_width, window_icon_bounds_.height()); 333 leading_button_start_ += text_width + kIconTitleSpacing; 334 } 335 } 336 337 if (use_hidden_icon_location) { 338 if (has_leading_buttons_) { 339 // There are window button icons on the left. Don't size the hidden window 340 // icon that people can double click on to close the window. 341 window_icon_bounds_ = gfx::Rect(); 342 } else { 343 // We set the icon bounds to a small rectangle in the top leading corner 344 // if there are no icons on the leading side. 345 window_icon_bounds_ = gfx::Rect( 346 frame_thickness + kIconLeftSpacing, frame_thickness, size, size); 347 } 348 } 349} 350 351void OpaqueBrowserFrameViewLayout::LayoutAvatar() { 352 // Even though the avatar is used for both incognito and profiles we always 353 // use the incognito icon to layout the avatar button. The profile icon 354 // can be customized so we can't depend on its size to perform layout. 355 gfx::ImageSkia incognito_icon = delegate_->GetOTRAvatarIcon(); 356 357 int avatar_bottom = GetTabStripInsetsTop(false) + 358 delegate_->GetTabStripHeight() - kAvatarBottomSpacing; 359 int avatar_restored_y = avatar_bottom - incognito_icon.height(); 360 int avatar_y = delegate_->IsMaximized() ? 361 (NonClientTopBorderHeight(false) + kTabstripTopShadowThickness) : 362 avatar_restored_y; 363 avatar_bounds_.SetRect(leading_button_start_ + kAvatarLeftSpacing, 364 avatar_y, incognito_icon.width(), 365 delegate_->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0); 366 if (avatar_button_) { 367 avatar_button_->SetBoundsRect(avatar_bounds_); 368 leading_button_start_ += kAvatarLeftSpacing + incognito_icon.width(); 369 minimum_size_for_buttons_ += kAvatarLeftSpacing + incognito_icon.width(); 370 } 371 372 if (avatar_label_) { 373 // Space between the bottom of the avatar and the bottom of the avatar 374 // label. 375 const int kAvatarLabelBottomSpacing = 3; 376 // Space between the frame border and the left edge of the avatar label. 377 const int kAvatarLabelLeftSpacing = -1; 378 gfx::Size label_size = avatar_label_->GetPreferredSize(); 379 gfx::Rect label_bounds( 380 leading_button_start_ + kAvatarLabelLeftSpacing, 381 avatar_bottom - kAvatarLabelBottomSpacing - label_size.height(), 382 label_size.width(), 383 delegate_->ShouldShowAvatar() ? label_size.height() : 0); 384 avatar_label_->SetBoundsRect(label_bounds); 385 leading_button_start_ += kAvatarLabelLeftSpacing + label_size.width(); 386 } 387} 388 389void OpaqueBrowserFrameViewLayout::ConfigureButton( 390 views::View* host, 391 ButtonID button_id, 392 ButtonAlignment alignment, 393 int caption_y) { 394 switch (button_id) { 395 case BUTTON_MINIMIZE: { 396 minimize_button_->SetVisible(true); 397 SetBoundsForButton(host, minimize_button_, alignment, caption_y); 398 break; 399 } 400 case BUTTON_MAXIMIZE: { 401 // When the window is restored, we show a maximized button; otherwise, we 402 // show a restore button. 403 bool is_restored = !delegate_->IsMaximized() && !delegate_->IsMinimized(); 404 views::ImageButton* invisible_button = is_restored ? 405 restore_button_ : maximize_button_; 406 invisible_button->SetVisible(false); 407 408 views::ImageButton* visible_button = is_restored ? 409 maximize_button_ : restore_button_; 410 visible_button->SetVisible(true); 411 SetBoundsForButton(host, visible_button, alignment, caption_y); 412 break; 413 } 414 case BUTTON_CLOSE: { 415 close_button_->SetVisible(true); 416 SetBoundsForButton(host, close_button_, alignment, caption_y); 417 break; 418 } 419 } 420} 421 422void OpaqueBrowserFrameViewLayout::HideButton(ButtonID button_id) { 423 switch (button_id) { 424 case BUTTON_MINIMIZE: 425 minimize_button_->SetVisible(false); 426 break; 427 case BUTTON_MAXIMIZE: 428 restore_button_->SetVisible(false); 429 maximize_button_->SetVisible(false); 430 break; 431 case BUTTON_CLOSE: 432 close_button_->SetVisible(false); 433 break; 434 } 435} 436 437void OpaqueBrowserFrameViewLayout::SetBoundsForButton( 438 views::View* host, 439 views::ImageButton* button, 440 ButtonAlignment alignment, 441 int caption_y) { 442 gfx::Size button_size = button->GetPreferredSize(); 443 444 button->SetImageAlignment( 445 (alignment == ALIGN_LEADING) ? 446 views::ImageButton::ALIGN_RIGHT : views::ImageButton::ALIGN_LEFT, 447 views::ImageButton::ALIGN_BOTTOM); 448 449 // There should always be the same number of non-shadow pixels visible to the 450 // side of the caption buttons. In maximized mode we extend the rightmost 451 // button to the screen corner to obey Fitts' Law. 452 bool is_maximized = delegate_->IsMaximized(); 453 454 switch (alignment) { 455 case ALIGN_LEADING: { 456 if (has_leading_buttons_) 457 leading_button_start_ += window_caption_spacing_; 458 459 // If we're the first button on the left and maximized, add with to the 460 // right hand side of the screen. 461 int extra_width = (is_maximized && !has_leading_buttons_) ? 462 (kFrameBorderThickness - 463 views::NonClientFrameView::kFrameShadowThickness) : 0; 464 465 button->SetBounds( 466 leading_button_start_ - extra_width, 467 caption_y, 468 button_size.width() + extra_width, 469 button_size.height()); 470 471 leading_button_start_ += extra_width + button_size.width(); 472 minimum_size_for_buttons_ += extra_width + button_size.width(); 473 has_leading_buttons_ = true; 474 break; 475 } 476 case ALIGN_TRAILING: { 477 if (has_trailing_buttons_) 478 trailing_button_start_ += window_caption_spacing_; 479 480 // If we're the first button on the right and maximized, add with to the 481 // right hand side of the screen. 482 int extra_width = (is_maximized && !has_trailing_buttons_) ? 483 (kFrameBorderThickness - 484 views::NonClientFrameView::kFrameShadowThickness) : 0; 485 486 button->SetBounds( 487 host->width() - trailing_button_start_ - extra_width - 488 button_size.width(), 489 caption_y, 490 button_size.width() + extra_width, 491 button_size.height()); 492 493 trailing_button_start_ += extra_width + button_size.width(); 494 minimum_size_for_buttons_ += extra_width + button_size.width(); 495 has_trailing_buttons_ = true; 496 break; 497 } 498 } 499} 500 501void OpaqueBrowserFrameViewLayout::SetView(int id, views::View* view) { 502 // Why do things this way instead of having an Init() method, where we're 503 // passed the views we'll handle? Because OpaqueBrowserFrameView doesn't own 504 // all the views which are part of it. The avatar stuff, for example, will be 505 // added and removed by the base class of OpaqueBrowserFrameView. 506 switch (id) { 507 case VIEW_ID_MINIMIZE_BUTTON: 508 if (view) { 509 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 510 view->GetClassName()); 511 } 512 minimize_button_ = static_cast<views::ImageButton*>(view); 513 break; 514 case VIEW_ID_MAXIMIZE_BUTTON: 515 if (view) { 516 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 517 view->GetClassName()); 518 } 519 maximize_button_ = static_cast<views::ImageButton*>(view); 520 break; 521 case VIEW_ID_RESTORE_BUTTON: 522 if (view) { 523 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 524 view->GetClassName()); 525 } 526 restore_button_ = static_cast<views::ImageButton*>(view); 527 break; 528 case VIEW_ID_CLOSE_BUTTON: 529 if (view) { 530 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 531 view->GetClassName()); 532 } 533 close_button_ = static_cast<views::ImageButton*>(view); 534 break; 535 case VIEW_ID_WINDOW_ICON: 536 window_icon_ = view; 537 break; 538 case VIEW_ID_WINDOW_TITLE: 539 if (view) { 540 DCHECK_EQ(std::string(views::Label::kViewClassName), 541 view->GetClassName()); 542 } 543 window_title_ = static_cast<views::Label*>(view); 544 break; 545 case VIEW_ID_AVATAR_LABEL: 546 avatar_label_ = view; 547 break; 548 case VIEW_ID_AVATAR_BUTTON: 549 avatar_button_ = view; 550 break; 551 default: 552 NOTIMPLEMENTED() << "Unknown view id " << id; 553 break; 554 } 555} 556 557/////////////////////////////////////////////////////////////////////////////// 558// OpaqueBrowserFrameView, views::LayoutManager: 559 560void OpaqueBrowserFrameViewLayout::Layout(views::View* host) { 561 // Reset all our data so that everything is invisible. 562 int thickness = FrameBorderThickness(false); 563 leading_button_start_ = thickness; 564 trailing_button_start_ = thickness; 565 minimum_size_for_buttons_ = leading_button_start_ + trailing_button_start_; 566 has_leading_buttons_ = false; 567 has_trailing_buttons_ = false; 568 569 LayoutWindowControls(host); 570 LayoutTitleBar(host); 571 572 // We now add a single pixel to the leading spacing. We do this because the 573 // avatar and tab strip start one pixel inward compared to where things start 574 // on the trailing side. 575 leading_button_start_++; 576 577 LayoutAvatar(); 578 579 client_view_bounds_ = CalculateClientAreaBounds( 580 host->width(), host->height()); 581} 582 583gfx::Size OpaqueBrowserFrameViewLayout::GetPreferredSize(views::View* host) { 584 // This is never used; NonClientView::GetPreferredSize() will be called 585 // instead. 586 NOTREACHED(); 587 return gfx::Size(); 588} 589 590void OpaqueBrowserFrameViewLayout::ViewAdded(views::View* host, 591 views::View* view) { 592 SetView(view->id(), view); 593} 594 595void OpaqueBrowserFrameViewLayout::ViewRemoved(views::View* host, 596 views::View* view) { 597 SetView(view->id(), NULL); 598} 599