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