browser_non_client_frame_view_ash.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Copyright (c) 2012 The Chromium Authors. All rights reserved. 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Use of this source code is governed by a BSD-style license that can be 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// found in the LICENSE file. 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h" 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "ash/shell_delegate.h" 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "ash/wm/frame_painter.h" 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "ash/wm/workspace/frame_maximize_button.h" 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/themes/theme_properties.h" 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/ash/chrome_shell_delegate.h" 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/browser.h" 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/immersive_fullscreen_configuration.h" 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/views/avatar_label.h" 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/views/avatar_menu_button.h" 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/views/frame/browser_frame.h" 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/views/frame/browser_view.h" 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/views/frame/immersive_mode_controller.h" 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "chrome/browser/ui/views/tab_icon_view.h" 20776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski#include "chrome/browser/ui/views/tabs/tab_strip.h" 21776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski#include "content/public/browser/web_contents.h" 22776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski#include "grit/ash_resources.h" 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "grit/generated_resources.h" // Accessibility names 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "grit/theme_resources.h" 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "ui/aura/client/aura_constants.h" 26b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/aura/window.h" 27b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/base/accessibility/accessible_view_state.h" 28b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/base/hit_test.h" 29b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/base/l10n/l10n_util.h" 30b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/base/layout.h" 31b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/base/resource/resource_bundle.h" 32b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/base/theme_provider.h" 33b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/compositor/layer_animator.h" 34b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/compositor/scoped_animation_duration_scale_mode.h" 35b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/gfx/canvas.h" 36b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/gfx/image/image_skia.h" 37b993f41eb2f165425dfdf0f93ea0b1e354eca837Dianne Hackborn#include "ui/views/controls/button/image_button.h" 385771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda#include "ui/views/controls/label.h" 395771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda#include "ui/views/layout/layout_constants.h" 405771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda#include "ui/views/widget/widget.h" 415771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda#include "ui/views/widget/widget_delegate.h" 425771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda 43ebb47950f21d3c41955a591000dfb1f195e746feNewton Allennamespace { 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4535bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov// The avatar ends 2 px above the bottom of the tabstrip (which, given the 4635bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov// way the tabstrip draws its bottom edge, will appear like a 1 px gap to the 4735bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov// user). 4835bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganovconst int kAvatarBottomSpacing = 2; 4935bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov// There are 2 px on each side of the avatar (between the frame border and 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// it on the left, and between it and the tabstrip on the right). 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectconst int kAvatarSideSpacing = 2; 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Space between left edge of window and tabstrip. 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectconst int kTabstripLeftSpacing = 0; 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Space between right edge of tabstrip and maximize button. 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectconst int kTabstripRightSpacing = 10; 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Height of the shadow of the content area, at the top of the toolbar. 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectconst int kContentShadowHeight = 1; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Space between top of window and top of tabstrip for tall headers, such as 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// for restored windows, apps, etc. 60f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccdaDianne Hackbornconst int kTabstripTopSpacingTall = 7; 61f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccdaDianne Hackborn// Space between top of window and top of tabstrip for short headers, such as 62f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccdaDianne Hackborn// for maximized windows, pop-ups, etc. 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectconst int kTabstripTopSpacingShort = 0; 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Height of the shadow in the tab image, used to ensure clicks in the shadow 65f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccdaDianne Hackborn// area still drag restored windows. This keeps the clickable area large enough 66776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski// to hit easily. 67776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiconst int kTabShadowHeight = 4; 68f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccdaDianne Hackborn 69776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski} // namespace 70776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski 71f4bf0ae2a7c2d9d92c5c8abdb82baa53b4c9ccdaDianne Hackborn/////////////////////////////////////////////////////////////////////////////// 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// BrowserNonClientFrameViewAsh, public: 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// static 7535bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganovconst char BrowserNonClientFrameViewAsh::kViewClassName[] = 7635bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov "BrowserNonClientFrameViewAsh"; 7735bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov 7835bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav GanovBrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh( 7935bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov BrowserFrame* frame, BrowserView* browser_view) 8035bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov : BrowserNonClientFrameView(frame, browser_view), 8135bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov size_button_(NULL), 8235bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov close_button_(NULL), 8335bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov window_icon_(NULL), 8435bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov frame_painter_(new ash::FramePainter), 8535bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov size_button_minimizes_(false) { 8635bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov} 8735bfedeaba724aeadc6f6c890269cb6bf7ef42f5Svetoslav Ganov 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectBrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() { 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectvoid BrowserNonClientFrameViewAsh::Init() { 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Panels only minimize. 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ash::FramePainter::SizeButtonBehavior size_button_behavior; 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project size_button_ = new ash::FrameMaximizeButton(this, this); 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project size_button_behavior = ash::FramePainter::SIZE_BUTTON_MAXIMIZES; 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project size_button_->SetAccessibleName( 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE)); 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AddChildView(size_button_); 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project close_button_ = new views::ImageButton(this); 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project close_button_->SetAccessibleName( 1013e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AddChildView(close_button_); 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Initializing the TabIconView is expensive, so only do it if we need to. 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (browser_view()->ShouldShowWindowIcon()) { 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project window_icon_ = new TabIconView(this); 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project window_icon_->set_is_light(true); 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project AddChildView(window_icon_); 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project window_icon_->Update(); 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Create incognito icon if necessary. 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project UpdateAvatarInfo(); 1143e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Frame painter handles layout of these buttons. 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project frame_painter_->Init(frame(), window_icon_, size_button_, close_button_, 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project size_button_behavior); 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/////////////////////////////////////////////////////////////////////////////// 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// BrowserNonClientFrameView overrides: 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectgfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip( 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project views::View* tabstrip) const { 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!tabstrip) 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return gfx::Rect(); 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project TabStripInsets insets(GetTabStripInsets(false)); 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return gfx::Rect(insets.left, insets.top, 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project std::max(0, width() - insets.left - insets.right), 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project tabstrip->GetPreferredSize().height()); 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectBrowserNonClientFrameView::TabStripInsets 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectBrowserNonClientFrameViewAsh::GetTabStripInsets(bool force_restored) const { 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int left = avatar_button() ? kAvatarSideSpacing + 1363e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing : 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project kTabstripLeftSpacing; 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (avatar_label()) { 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project left += avatar_label()->GetPreferredSize().width() + 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project views::kRelatedControlHorizontalSpacing; 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int right = frame_painter_->GetRightInset() + kTabstripRightSpacing; 143776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski return TabStripInsets(NonClientTopBorderHeight(force_restored), left, right); 144776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski} 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectint BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const { 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return frame_painter_->GetThemeBackgroundXInset(); 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectvoid BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) { 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (window_icon_) 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project window_icon_->Update(); 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/////////////////////////////////////////////////////////////////////////////// 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// views::NonClientFrameView overrides: 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectgfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const { 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int top_height = NonClientTopBorderHeight(false); 1605771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda return frame_painter_->GetBoundsForClientView(top_height, bounds()); 1615771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda} 1625771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda 1635771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerdagfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds( 1645771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda const gfx::Rect& client_bounds) const { 1655771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda int top_height = NonClientTopBorderHeight(false); 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return frame_painter_->GetWindowBoundsForClientBounds(top_height, 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project client_bounds); 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1705771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerdaint BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) { 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int hit_test = frame_painter_->NonClientHitTest(this, point); 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // When the window is restored we want a large click target above the tabs 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // to drag the window, so redirect clicks in the tab's shadow to caption. 1745771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda if (hit_test == HTCLIENT && 1755771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda !(frame()->IsMaximized() || frame()->IsFullscreen())) { 1765771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda // Convert point to client coordinates. 1775771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda gfx::Point client_point(point); 1785771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda View::ConvertPointToTarget(this, frame()->client_view(), &client_point); 1795771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda // Report hits in shadow at top of tabstrip as caption. 1805771302ca13c31cb85f17bc58da8f6f8227c9b85Flavio Lerda gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds()); 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight) 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project hit_test = HTCAPTION; 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return hit_test; 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectvoid BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size, 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project gfx::Path* window_mask) { 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Aura does not use window masks. 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1923e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackbornvoid BrowserNonClientFrameViewAsh::ResetWindowControls() { 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ImmersiveFullscreenConfiguration::UseImmersiveFullscreen()) { 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Hide the caption buttons in immersive mode because it's confusing when 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the user hovers or clicks in the top-right of the screen and hits one. 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Only show them during a reveal. 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ImmersiveModeController* controller = 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project browser_view()->immersive_mode_controller(); 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (controller->IsEnabled()) { 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project bool revealed = controller->IsRevealed(); 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project size_button_->SetVisible(revealed); 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project close_button_->SetVisible(revealed); 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project size_button_->SetVisible(true); 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project close_button_->SetVisible(true); 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project size_button_->SetState(views::CustomButton::STATE_NORMAL); 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // The close button isn't affected by this constraint. 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectvoid BrowserNonClientFrameViewAsh::UpdateWindowIcon() { 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (window_icon_) 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project window_icon_->SchedulePaint(); 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectvoid BrowserNonClientFrameViewAsh::UpdateWindowTitle() { 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!frame()->IsFullscreen()) 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project frame_painter_->SchedulePaintForTitle(this, BrowserFrame::GetTitleFont()); 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/////////////////////////////////////////////////////////////////////////////// 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// views::View overrides: 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectvoid BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) { 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!ShouldPaint()) 228776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski return; 229776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski // The primary header image changes based on window activation state and 230776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski // theme, so we look it up for each paint. 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int theme_frame_image_id = GetThemeFrameImageId(); 2323e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn int theme_frame_overlay_image_id = GetThemeFrameOverlayImageId(); 2333e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn 2343e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn ui::ThemeProvider* theme_provider = GetThemeProvider(); 2353e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn ash::FramePainter::Themed header_themed = ash::FramePainter::THEMED_NO; 2363e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn if (theme_provider->HasCustomImage(theme_frame_image_id) || 2373e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn (theme_frame_overlay_image_id != 0 && 2383e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn theme_provider->HasCustomImage(theme_frame_overlay_image_id))) { 2393e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn header_themed = ash::FramePainter::THEMED_YES; 2403e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn } 2413e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn 2423e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn if (frame_painter_->ShouldUseMinimalHeaderStyle(header_themed)) 2433e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn theme_frame_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL; 2443e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn 2453e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn frame_painter_->PaintHeader( 2463e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn this, 2473e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn canvas, 2483e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn ShouldPaintAsActive() ? 2493e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn ash::FramePainter::ACTIVE : ash::FramePainter::INACTIVE, 2503e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn theme_frame_image_id, 2513e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn theme_frame_overlay_image_id); 2523e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn if (browser_view()->ShouldShowWindowTitle()) 2533e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn frame_painter_->PaintTitleBar(this, canvas, BrowserFrame::GetTitleFont()); 2543e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn if (browser_view()->IsToolbarVisible()) 2553e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn PaintToolbarBackground(canvas); 2563e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn else 2573e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn PaintContentEdge(canvas); 2583e82ba1a67b0c756ab6a289985f4cfc53725b311Dianne Hackborn} 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 260void BrowserNonClientFrameViewAsh::Layout() { 261 frame_painter_->LayoutHeader(this, UseShortHeader()); 262 if (avatar_button()) 263 LayoutAvatar(); 264 BrowserNonClientFrameView::Layout(); 265} 266 267const char* BrowserNonClientFrameViewAsh::GetClassName() const { 268 return kViewClassName; 269} 270 271bool BrowserNonClientFrameViewAsh::HitTestRect(const gfx::Rect& rect) const { 272 // If the rect is outside the bounds of the client area, claim it. 273 if (NonClientFrameView::HitTestRect(rect)) 274 return true; 275 276 // Otherwise claim it only if it's in a non-tab portion of the tabstrip. 277 if (!browser_view()->tabstrip()) 278 return false; 279 gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds()); 280 gfx::Point tabstrip_origin(tabstrip_bounds.origin()); 281 View::ConvertPointToTarget(frame()->client_view(), this, &tabstrip_origin); 282 tabstrip_bounds.set_origin(tabstrip_origin); 283 if (rect.bottom() > tabstrip_bounds.bottom()) 284 return false; 285 286 // We convert from our parent's coordinates since we assume we fill its bounds 287 // completely. We need to do this since we're not a parent of the tabstrip, 288 // meaning ConvertPointToTarget would otherwise return something bogus. 289 // TODO(tdanderson): Initialize |browser_view_point| using |rect| instead of 290 // its center point once GetEventHandlerForRect() is implemented. 291 gfx::Point browser_view_point(rect.CenterPoint()); 292 View::ConvertPointToTarget(parent(), browser_view(), &browser_view_point); 293 return browser_view()->IsPositionInWindowCaption(browser_view_point); 294} 295 296void BrowserNonClientFrameViewAsh::GetAccessibleState( 297 ui::AccessibleViewState* state) { 298 state->role = ui::AccessibilityTypes::ROLE_TITLEBAR; 299} 300 301gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() { 302 return frame_painter_->GetMinimumSize(this); 303} 304 305void BrowserNonClientFrameViewAsh::OnThemeChanged() { 306 BrowserNonClientFrameView::OnThemeChanged(); 307 frame_painter_->OnThemeChanged(); 308} 309 310/////////////////////////////////////////////////////////////////////////////// 311// views::ButtonListener overrides: 312 313void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender, 314 const ui::Event& event) { 315 // When shift-clicking slow down animations for visual debugging. 316 // We used to do this via an event filter that looked for the shift key being 317 // pressed but this interfered with several normal keyboard shortcuts. 318 scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode; 319 if (event.IsShiftDown()) { 320 slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode( 321 ui::ScopedAnimationDurationScaleMode::SLOW_DURATION)); 322 } 323 324 ash::UserMetricsAction action = 325 ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; 326 327 if (sender == size_button_) { 328 // The maximize button may move out from under the cursor. 329 ResetWindowControls(); 330 if (size_button_minimizes_) { 331 frame()->Minimize(); 332 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; 333 } else if (frame()->IsFullscreen()) { // Can be clicked in immersive mode. 334 frame()->SetFullscreen(false); 335 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN; 336 } else if (frame()->IsMaximized()) { 337 frame()->Restore(); 338 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; 339 } else { 340 frame()->Maximize(); 341 } 342 // |this| may be deleted - some windows delete their frames on maximize. 343 } else if (sender == close_button_) { 344 frame()->Close(); 345 action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; 346 } else { 347 return; 348 } 349 ChromeShellDelegate::instance()->RecordUserMetricsAction(action); 350} 351 352/////////////////////////////////////////////////////////////////////////////// 353// chrome::TabIconViewModel overrides: 354 355bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const { 356 // This function is queried during the creation of the window as the 357 // TabIconView we host is initialized, so we need to NULL check the selected 358 // WebContents because in this condition there is not yet a selected tab. 359 content::WebContents* current_tab = browser_view()->GetActiveWebContents(); 360 return current_tab ? current_tab->IsLoading() : false; 361} 362 363gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() { 364 views::WidgetDelegate* delegate = frame()->widget_delegate(); 365 if (!delegate) 366 return gfx::ImageSkia(); 367 return delegate->GetWindowIcon(); 368} 369 370/////////////////////////////////////////////////////////////////////////////// 371// BrowserNonClientFrameViewAsh, private: 372 373 374int BrowserNonClientFrameViewAsh::NonClientTopBorderHeight( 375 bool force_restored) const { 376 if (force_restored) 377 return kTabstripTopSpacingTall; 378 if (frame()->IsFullscreen()) 379 return 0; 380 // Windows with tab strips need a smaller non-client area. 381 if (browser_view()->IsTabStripVisible()) { 382 if (UseShortHeader()) 383 return kTabstripTopSpacingShort; 384 return kTabstripTopSpacingTall; 385 } 386 // For windows without a tab strip (popups, etc.) ensure we have enough space 387 // to see the window caption buttons. 388 return close_button_->bounds().bottom() - kContentShadowHeight; 389} 390 391bool BrowserNonClientFrameViewAsh::UseShortHeader() const { 392 // Restored browser -> tall header 393 // Maximized browser -> short header 394 // Fullscreen browser (header shows with immersive reveal) -> short header 395 // Popup&App window -> tall header 396 // Panel -> short header 397 // Dialogs use short header and are handled via CustomFrameViewAsh. 398 Browser* browser = browser_view()->browser(); 399 switch (browser->type()) { 400 case Browser::TYPE_TABBED: 401 return frame()->IsMaximized() || frame()->IsFullscreen(); 402 case Browser::TYPE_POPUP: 403 return false; 404 default: 405 NOTREACHED(); 406 return false; 407 } 408} 409 410void BrowserNonClientFrameViewAsh::LayoutAvatar() { 411 DCHECK(avatar_button()); 412 gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon(); 413 414 if (frame()->IsFullscreen()) { 415 ImmersiveModeController* immersive_controller = 416 browser_view()->immersive_mode_controller(); 417 // Hide the incognito icon when the top-of-window views are closed in 418 // immersive mode as the tab indicators are too short for the incognito 419 // icon to still be recongizable. 420 if (immersive_controller->IsEnabled() && 421 !immersive_controller->IsRevealed()) { 422 avatar_button()->SetBoundsRect(gfx::Rect()); 423 if (avatar_label()) 424 avatar_label()->SetBoundsRect(gfx::Rect()); 425 return; 426 } 427 } 428 429 int avatar_bottom = GetTabStripInsets(false).top + 430 browser_view()->GetTabStripHeight() - kAvatarBottomSpacing; 431 int avatar_restored_y = avatar_bottom - incognito_icon.height(); 432 int avatar_y = (frame()->IsMaximized() || frame()->IsFullscreen()) ? 433 NonClientTopBorderHeight(false) + kContentShadowHeight : 434 avatar_restored_y; 435 gfx::Rect avatar_bounds(kAvatarSideSpacing, 436 avatar_y, 437 incognito_icon.width(), 438 avatar_bottom - avatar_y); 439 avatar_button()->SetBoundsRect(avatar_bounds); 440 if (avatar_label()) { 441 gfx::Size size = avatar_label()->GetPreferredSize(); 442 int label_height = std::min(avatar_bounds.height(), size.height()); 443 gfx::Rect label_bounds( 444 avatar_bounds.right() + views::kRelatedControlHorizontalSpacing, 445 avatar_y + (avatar_bounds.height() - label_height) / 2, 446 size.width(), 447 size.height()); 448 avatar_label()->SetBoundsRect(label_bounds); 449 } 450} 451 452bool BrowserNonClientFrameViewAsh::ShouldPaint() const { 453 // Immersive mode windows are fullscreen, but need to paint during a reveal. 454 return !frame()->IsFullscreen() || 455 browser_view()->immersive_mode_controller()->IsRevealed(); 456} 457 458void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) { 459 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); 460 if (toolbar_bounds.IsEmpty()) 461 return; 462 gfx::Point toolbar_origin(toolbar_bounds.origin()); 463 View::ConvertPointToTarget(browser_view(), this, &toolbar_origin); 464 toolbar_bounds.set_origin(toolbar_origin); 465 466 int x = toolbar_bounds.x(); 467 int w = toolbar_bounds.width(); 468 int y = toolbar_bounds.y(); 469 int h = toolbar_bounds.height(); 470 471 // Gross hack: We split the toolbar images into two pieces, since sometimes 472 // (popup mode) the toolbar isn't tall enough to show the whole image. The 473 // split happens between the top shadow section and the bottom gradient 474 // section so that we never break the gradient. 475 int split_point = kFrameShadowThickness * 2; 476 int bottom_y = y + split_point; 477 ui::ThemeProvider* tp = GetThemeProvider(); 478 int bottom_edge_height = h - split_point; 479 480 canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height), 481 tp->GetColor(ThemeProperties::COLOR_TOOLBAR)); 482 483 // Paint the main toolbar image. Since this image is also used to draw the 484 // tab background, we must use the tab strip offset to compute the image 485 // source y position. If you have to debug this code use an image editor 486 // to paint a diagonal line through the toolbar image and ensure it lines up 487 // across the tab and toolbar. 488 gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR); 489 canvas->TileImageInt( 490 *theme_toolbar, 491 x + GetThemeBackgroundXInset(), 492 bottom_y - GetTabStripInsets(false).top, 493 x, bottom_y, 494 w, theme_toolbar->height()); 495 496 // The content area line has a shadow that extends a couple of pixels above 497 // the toolbar bounds. 498 const int kContentShadowHeight = 2; 499 gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP); 500 canvas->TileImageInt(*toolbar_top, 501 0, 0, 502 x, y - kContentShadowHeight, 503 w, split_point + kContentShadowHeight + 1); 504 505 // Draw the "lightening" shade line around the edges of the toolbar. 506 gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT); 507 canvas->TileImageInt(*toolbar_left, 508 0, 0, 509 x + kClientEdgeThickness, 510 y + kClientEdgeThickness + kContentShadowHeight, 511 toolbar_left->width(), theme_toolbar->height()); 512 gfx::ImageSkia* toolbar_right = 513 tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT); 514 canvas->TileImageInt(*toolbar_right, 515 0, 0, 516 w - toolbar_right->width() - 2 * kClientEdgeThickness, 517 y + kClientEdgeThickness + kContentShadowHeight, 518 toolbar_right->width(), theme_toolbar->height()); 519 520 // Draw the content/toolbar separator. 521 canvas->FillRect( 522 gfx::Rect(x + kClientEdgeThickness, 523 toolbar_bounds.bottom() - kClientEdgeThickness, 524 w - (2 * kClientEdgeThickness), 525 kClientEdgeThickness), 526 ThemeProperties::GetDefaultColor( 527 ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 528} 529 530void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) { 531 canvas->FillRect(gfx::Rect(0, close_button_->bounds().bottom(), 532 width(), kClientEdgeThickness), 533 ThemeProperties::GetDefaultColor( 534 ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 535} 536 537int BrowserNonClientFrameViewAsh::GetThemeFrameImageId() const { 538 bool is_incognito = browser_view()->IsOffTheRecord() && 539 !browser_view()->IsGuestSession(); 540 if (browser_view()->IsBrowserTypeNormal()) { 541 // Use the standard resource ids to allow users to theme the frames. 542 if (ShouldPaintAsActive()) { 543 return is_incognito ? 544 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; 545 } 546 return is_incognito ? 547 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; 548 } 549 // Never theme app and popup windows. 550 if (ShouldPaintAsActive()) { 551 return is_incognito ? 552 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE : 553 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE; 554 } 555 return is_incognito ? 556 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE : 557 IDR_AURA_WINDOW_HEADER_BASE_INACTIVE; 558} 559 560int BrowserNonClientFrameViewAsh::GetThemeFrameOverlayImageId() const { 561 ui::ThemeProvider* tp = GetThemeProvider(); 562 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && 563 browser_view()->IsBrowserTypeNormal() && 564 !browser_view()->IsOffTheRecord()) { 565 return ShouldPaintAsActive() ? 566 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE; 567 } 568 return 0; 569} 570