blob: cc6a6bc770c824df73ad32c01816275e2f6ba68f [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
#include <algorithm>
#include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
#include "ash/frame/default_header_painter.h"
#include "ash/frame/frame_border_hit_test_controller.h"
#include "ash/frame/header_painter_util.h"
#include "ash/shell.h"
#include "base/profiler/scoped_tracker.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/views/frame/browser_frame.h"
#include "chrome/browser/ui/views/frame/browser_header_painter_ash.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
#include "chrome/browser/ui/views/frame/web_app_left_header_view_ash.h"
#include "chrome/browser/ui/views/layout_constants.h"
#include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
#include "chrome/browser/ui/views/tab_icon_view.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/web_applications/web_app.h"
#include "components/signin/core/common/profile_management_switches.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "grit/theme_resources.h"
#include "ui/accessibility/ax_view_state.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/base/hit_test.h"
#include "ui/base/layout.h"
#include "ui/base/resource/material_design/material_design_controller.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"
#include "ui/compositor/layer_animator.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/layout_constants.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#if defined(ENABLE_SUPERVISED_USERS)
#include "chrome/browser/ui/views/profiles/supervised_user_avatar_label.h"
#endif
namespace {
#if defined(FRAME_AVATAR_BUTTON)
// Space between the new avatar button and the minimize button.
const int kNewAvatarButtonOffset = 5;
#endif
// Space between right edge of tabstrip and maximize button.
const int kTabstripRightSpacing = 10;
// Height of the shadow of the content area, at the top of the toolbar.
const int kContentShadowHeight = 1;
// Space between top of window and top of tabstrip for tall headers, such as
// for restored windows, apps, etc.
const int kTabstripTopSpacingTall = 7;
// Space between top of window and top of tabstrip for short headers, such as
// for maximized windows, pop-ups, etc.
const int kTabstripTopSpacingShort = 0;
// Height of the shadow in the tab image, used to ensure clicks in the shadow
// area still drag restored windows. This keeps the clickable area large enough
// to hit easily.
const int kTabShadowHeight = 4;
// Combines View::ConvertPointToTarget() and View::HitTest() for a given
// |point|. Converts |point| from |src| to |dst| and hit tests it against |dst|.
bool ConvertedHitTest(views::View* src,
views::View* dst,
const gfx::Point& point) {
DCHECK(src);
DCHECK(dst);
gfx::Point converted_point(point);
views::View::ConvertPointToTarget(src, dst, &converted_point);
return dst->HitTestPoint(converted_point);
}
} // namespace
///////////////////////////////////////////////////////////////////////////////
// BrowserNonClientFrameViewAsh, public:
// static
const char BrowserNonClientFrameViewAsh::kViewClassName[] =
"BrowserNonClientFrameViewAsh";
BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
BrowserFrame* frame,
BrowserView* browser_view)
: BrowserNonClientFrameView(frame, browser_view),
caption_button_container_(nullptr),
web_app_left_header_view_(nullptr),
window_icon_(nullptr),
frame_border_hit_test_controller_(
new ash::FrameBorderHitTestController(frame)) {
ash::Shell::GetInstance()->AddShellObserver(this);
}
BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
ash::Shell::GetInstance()->RemoveShellObserver(this);
}
void BrowserNonClientFrameViewAsh::Init() {
caption_button_container_ = new ash::FrameCaptionButtonContainerView(frame());
caption_button_container_->UpdateSizeButtonVisibility();
AddChildView(caption_button_container_);
// Initializing the TabIconView is expensive, so only do it if we need to.
if (browser_view()->ShouldShowWindowIcon()) {
window_icon_ = new TabIconView(this, nullptr);
window_icon_->set_is_light(true);
AddChildView(window_icon_);
window_icon_->Update();
}
UpdateAvatar();
// HeaderPainter handles layout.
if (UsePackagedAppHeaderStyle()) {
ash::DefaultHeaderPainter* header_painter = new ash::DefaultHeaderPainter;
header_painter_.reset(header_painter);
header_painter->Init(frame(), this, caption_button_container_);
if (window_icon_) {
header_painter->UpdateLeftHeaderView(window_icon_);
}
} else if (UseWebAppHeaderStyle()) {
web_app_left_header_view_ = new WebAppLeftHeaderView(browser_view());
AddChildView(web_app_left_header_view_);
ash::DefaultHeaderPainter* header_painter = new ash::DefaultHeaderPainter;
header_painter_.reset(header_painter);
header_painter->Init(frame(), this, caption_button_container_);
header_painter->UpdateLeftHeaderView(web_app_left_header_view_);
} else {
BrowserHeaderPainterAsh* header_painter = new BrowserHeaderPainterAsh;
header_painter_.reset(header_painter);
header_painter->Init(frame(), browser_view(), this, window_icon_,
caption_button_container_);
}
}
///////////////////////////////////////////////////////////////////////////////
// BrowserNonClientFrameView:
gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip(
views::View* tabstrip) const {
if (!tabstrip)
return gfx::Rect();
// When the tab strip is painted in the immersive fullscreen light bar style,
// the caption buttons and the avatar button are not visible. However, their
// bounds are still used to compute the tab strip bounds so that the tabs have
// the same horizontal position when the tab strip is painted in the immersive
// light bar style as when the top-of-window views are revealed.
int left_inset = GetTabStripLeftInset();
int right_inset = GetTabStripRightInset();
return gfx::Rect(left_inset,
GetTopInset(false),
std::max(0, width() - left_inset - right_inset),
tabstrip->GetPreferredSize().height());
}
int BrowserNonClientFrameViewAsh::GetTopInset(bool restored) const {
if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle())
return 0;
if (browser_view()->IsTabStripVisible()) {
return ((frame()->IsMaximized() || frame()->IsFullscreen()) && !restored) ?
kTabstripTopSpacingShort : kTabstripTopSpacingTall;
}
if (UsePackagedAppHeaderStyle() || UseWebAppHeaderStyle())
return header_painter_->GetHeaderHeight();
int caption_buttons_bottom = caption_button_container_->bounds().bottom();
// The toolbar partially overlaps the caption buttons.
if (browser_view()->IsToolbarVisible())
return caption_buttons_bottom - kContentShadowHeight;
return caption_buttons_bottom + kClientEdgeThickness;
}
int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
return ash::HeaderPainterUtil::GetThemeBackgroundXInset();
}
void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) {
if (window_icon_)
window_icon_->Update();
}
void BrowserNonClientFrameViewAsh::UpdateToolbar() {
if (web_app_left_header_view_)
web_app_left_header_view_->Update();
}
views::View* BrowserNonClientFrameViewAsh::GetLocationIconView() const {
if (web_app_left_header_view_)
return web_app_left_header_view_->GetLocationIconView();
return nullptr;
}
///////////////////////////////////////////////////////////////////////////////
// views::NonClientFrameView:
gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const {
// The ClientView must be flush with the top edge of the widget so that the
// web contents can take up the entire screen in immersive fullscreen (with
// or without the top-of-window views revealed). When in immersive fullscreen
// and the top-of-window views are revealed, the TopContainerView paints the
// window header by redirecting paints from its background to
// BrowserNonClientFrameViewAsh.
return bounds();
}
gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const {
return client_bounds;
}
int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
int hit_test = ash::FrameBorderHitTestController::NonClientHitTest(this,
caption_button_container_, point);
#if defined(FRAME_AVATAR_BUTTON)
if (hit_test == HTCAPTION && new_avatar_button() &&
ConvertedHitTest(this, new_avatar_button(), point)) {
return HTCLIENT;
}
#endif
// See if the point is actually within the web app back button.
if (hit_test == HTCAPTION && web_app_left_header_view_ &&
ConvertedHitTest(this, web_app_left_header_view_, point)) {
return HTCLIENT;
}
#if defined(ENABLE_SUPERVISED_USERS)
// ...or within the avatar label, if it's a supervised user.
if (hit_test == HTCAPTION && supervised_user_avatar_label() &&
ConvertedHitTest(this, supervised_user_avatar_label(), point)) {
return HTCLIENT;
}
#endif
// When the window is restored we want a large click target above the tabs
// to drag the window, so redirect clicks in the tab's shadow to caption.
if (hit_test == HTCLIENT &&
!(frame()->IsMaximized() || frame()->IsFullscreen())) {
// Convert point to client coordinates.
gfx::Point client_point(point);
View::ConvertPointToTarget(this, frame()->client_view(), &client_point);
// Report hits in shadow at top of tabstrip as caption.
gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight)
hit_test = HTCAPTION;
}
return hit_test;
}
void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
gfx::Path* window_mask) {
// Aura does not use window masks.
}
void BrowserNonClientFrameViewAsh::ResetWindowControls() {
// Hide the caption buttons in immersive fullscreen when the tab light bar
// is visible because it's confusing when the user hovers or clicks in the
// top-right of the screen and hits one.
bool button_visibility = !UseImmersiveLightbarHeaderStyle();
caption_button_container_->SetVisible(button_visibility);
caption_button_container_->ResetWindowControls();
}
void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
if (window_icon_)
window_icon_->SchedulePaint();
}
void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
if (!frame()->IsFullscreen())
header_painter_->SchedulePaintForTitle();
}
void BrowserNonClientFrameViewAsh::SizeConstraintsChanged() {
}
///////////////////////////////////////////////////////////////////////////////
// views::View:
void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
if (!ShouldPaint())
return;
if (UseImmersiveLightbarHeaderStyle()) {
PaintImmersiveLightbarStyleHeader(canvas);
return;
}
caption_button_container_->SetPaintAsActive(ShouldPaintAsActive());
if (web_app_left_header_view_)
web_app_left_header_view_->SetPaintAsActive(ShouldPaintAsActive());
ash::HeaderPainter::Mode header_mode = ShouldPaintAsActive() ?
ash::HeaderPainter::MODE_ACTIVE : ash::HeaderPainter::MODE_INACTIVE;
header_painter_->PaintHeader(canvas, header_mode);
if (browser_view()->IsToolbarVisible())
PaintToolbarBackground(canvas);
else if (!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle())
PaintContentEdge(canvas);
}
void BrowserNonClientFrameViewAsh::Layout() {
// The header must be laid out before computing |painted_height| because the
// computation of |painted_height| for app and popup windows depends on the
// position of the window controls.
header_painter_->LayoutHeader();
int painted_height = 0;
if (browser_view()->IsTabStripVisible()) {
painted_height = GetTopInset(false) +
browser_view()->tabstrip()->GetPreferredSize().height();
} else if (browser_view()->IsToolbarVisible()) {
// Paint the header so that it overlaps with the top few pixels of the
// toolbar because the top few pixels of the toolbar are not opaque.
painted_height = GetTopInset(false) + kFrameShadowThickness * 2;
} else {
painted_height = GetTopInset(false);
}
header_painter_->SetHeaderHeightForPainting(painted_height);
if (avatar_button()) {
LayoutAvatar();
header_painter_->UpdateLeftViewXInset(avatar_button()->bounds().right());
}
#if defined(FRAME_AVATAR_BUTTON)
if (new_avatar_button())
LayoutNewStyleAvatar();
#endif
header_painter_->UpdateLeftViewXInset(
ash::HeaderPainterUtil::GetDefaultLeftViewXInset());
BrowserNonClientFrameView::Layout();
}
const char* BrowserNonClientFrameViewAsh::GetClassName() const {
return kViewClassName;
}
void BrowserNonClientFrameViewAsh::GetAccessibleState(
ui::AXViewState* state) {
state->role = ui::AX_ROLE_TITLE_BAR;
}
gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() const {
gfx::Size min_client_view_size(frame()->client_view()->GetMinimumSize());
int min_width = std::max(header_painter_->GetMinimumHeaderWidth(),
min_client_view_size.width());
if (browser_view()->IsTabStripVisible()) {
// Ensure that the minimum width is enough to hold a minimum width tab strip
// at its usual insets.
int min_tabstrip_width =
browser_view()->tabstrip()->GetMinimumSize().width();
min_width = std::max(min_width,
min_tabstrip_width + GetTabStripLeftInset() + GetTabStripRightInset());
}
return gfx::Size(min_width, min_client_view_size.height());
}
void BrowserNonClientFrameViewAsh::
ChildPreferredSizeChanged(views::View* child) {
// FrameCaptionButtonContainerView animates the visibility changes in
// UpdateSizeButtonVisibility(false). Due to this a new size is not available
// until the completion of the animation. Layout in response to the preferred
// size changes.
if (!browser_view()->initialized())
return;
bool needs_layout = child == caption_button_container_;
#if defined(FRAME_AVATAR_BUTTON)
needs_layout = needs_layout || child == new_avatar_button();
#endif
if (needs_layout) {
InvalidateLayout();
frame()->GetRootView()->Layout();
}
}
///////////////////////////////////////////////////////////////////////////////
// ash::ShellObserver:
void BrowserNonClientFrameViewAsh::OnMaximizeModeStarted() {
caption_button_container_->UpdateSizeButtonVisibility();
InvalidateLayout();
frame()->client_view()->InvalidateLayout();
frame()->GetRootView()->Layout();
}
void BrowserNonClientFrameViewAsh::OnMaximizeModeEnded() {
caption_button_container_->UpdateSizeButtonVisibility();
InvalidateLayout();
frame()->client_view()->InvalidateLayout();
frame()->GetRootView()->Layout();
}
///////////////////////////////////////////////////////////////////////////////
// TabIconViewModel:
bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
// This function is queried during the creation of the window as the
// TabIconView we host is initialized, so we need to null check the selected
// WebContents because in this condition there is not yet a selected tab.
content::WebContents* current_tab = browser_view()->GetActiveWebContents();
return current_tab ? current_tab->IsLoading() : false;
}
gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() {
views::WidgetDelegate* delegate = frame()->widget_delegate();
if (!delegate)
return gfx::ImageSkia();
return delegate->GetWindowIcon();
}
///////////////////////////////////////////////////////////////////////////////
// views::ButtonListener:
void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender,
const ui::Event& event) {
#if !defined(FRAME_AVATAR_BUTTON)
NOTREACHED();
#else
DCHECK(sender == new_avatar_button());
int command = IDC_SHOW_AVATAR_MENU;
if (event.IsMouseEvent() &&
static_cast<const ui::MouseEvent&>(event).IsRightMouseButton()) {
command = IDC_SHOW_FAST_USER_SWITCHER;
}
chrome::ExecuteCommand(browser_view()->browser(), command);
#endif
}
///////////////////////////////////////////////////////////////////////////////
// BrowserNonClientFrameViewAsh, protected:
// BrowserNonClientFrameView:
void BrowserNonClientFrameViewAsh::UpdateNewAvatarButtonImpl() {
#if defined(FRAME_AVATAR_BUTTON)
UpdateNewAvatarButton(this, NewAvatarButton::NATIVE_BUTTON);
#endif
}
///////////////////////////////////////////////////////////////////////////////
// BrowserNonClientFrameViewAsh, private:
// views::NonClientFrameView:
bool BrowserNonClientFrameViewAsh::DoesIntersectRect(
const views::View* target,
const gfx::Rect& rect) const {
CHECK_EQ(target, this);
if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect)) {
// |rect| is outside BrowserNonClientFrameViewAsh's bounds.
return false;
}
TabStrip* tabstrip = browser_view()->tabstrip();
if (tabstrip && browser_view()->IsTabStripVisible()) {
// Claim |rect| only if it is above the bottom of the tabstrip in a non-tab
// portion.
gfx::RectF rect_in_tabstrip_coords_f(rect);
View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f);
gfx::Rect rect_in_tabstrip_coords = gfx::ToEnclosingRect(
rect_in_tabstrip_coords_f);
if (rect_in_tabstrip_coords.y() > tabstrip->height())
return false;
return !tabstrip->HitTestRect(rect_in_tabstrip_coords) ||
tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords);
}
// Claim |rect| if it is above the top of the topmost view in the client area.
return rect.y() < GetTopInset(false);
}
int BrowserNonClientFrameViewAsh::GetTabStripLeftInset() const {
const gfx::Insets insets(GetLayoutInsets(AVATAR_ICON));
const int avatar_right = avatar_button() ?
(insets.left() + browser_view()->GetOTRAvatarIcon().width()) : 0;
return avatar_right + insets.right();
}
int BrowserNonClientFrameViewAsh::GetTabStripRightInset() const {
int tabstrip_width = kTabstripRightSpacing +
caption_button_container_->GetPreferredSize().width();
#if defined(FRAME_AVATAR_BUTTON)
if (new_avatar_button()) {
tabstrip_width += kNewAvatarButtonOffset +
new_avatar_button()->GetPreferredSize().width();
}
#endif
return tabstrip_width;
}
bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const {
ImmersiveModeController* immersive_controller =
browser_view()->immersive_mode_controller();
return immersive_controller->IsEnabled() &&
!immersive_controller->IsRevealed() &&
browser_view()->IsTabStripVisible();
}
bool BrowserNonClientFrameViewAsh::UsePackagedAppHeaderStyle() const {
Browser* browser = browser_view()->browser();
// For non tabbed trusted source windows, e.g. Settings, use the packaged
// app style frame.
if (!browser->is_type_tabbed() && browser->is_trusted_source())
return true;
// Use the packaged app style for apps that aren't using the newer WebApp
// style.
return browser->is_app() && !UseWebAppHeaderStyle();
}
bool BrowserNonClientFrameViewAsh::UseWebAppHeaderStyle() const {
return browser_view()->browser()->SupportsWindowFeature(
Browser::FEATURE_WEBAPPFRAME);
}
void BrowserNonClientFrameViewAsh::LayoutAvatar() {
DCHECK(avatar_button());
#if !defined(OS_CHROMEOS)
// ChromeOS shows avatar on V1 app.
DCHECK(browser_view()->IsTabStripVisible());
#endif
gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
gfx::Insets avatar_insets = GetLayoutInsets(AVATAR_ICON);
int avatar_bottom = GetTopInset(false) + browser_view()->GetTabStripHeight() -
avatar_insets.bottom();
int avatar_y = avatar_bottom - incognito_icon.height();
if (!ui::MaterialDesignController::IsModeMaterial() &&
browser_view()->IsTabStripVisible() &&
(frame()->IsMaximized() || frame()->IsFullscreen())) {
avatar_y = GetTopInset(false) + kContentShadowHeight;
}
// Hide the incognito icon in immersive fullscreen when the tab light bar is
// visible because the header is too short for the icognito icon to be
// recognizable.
bool avatar_visible = !UseImmersiveLightbarHeaderStyle();
int avatar_height = avatar_visible ? avatar_bottom - avatar_y : 0;
gfx::Rect avatar_bounds(avatar_insets.left(), avatar_y,
incognito_icon.width(), avatar_height);
avatar_button()->SetBoundsRect(avatar_bounds);
avatar_button()->SetVisible(avatar_visible);
}
#if defined(FRAME_AVATAR_BUTTON)
void BrowserNonClientFrameViewAsh::LayoutNewStyleAvatar() {
DCHECK(new_avatar_button());
gfx::Size button_size = new_avatar_button()->GetPreferredSize();
int button_x = width() -
caption_button_container_->GetPreferredSize().width() -
kNewAvatarButtonOffset - button_size.width();
new_avatar_button()->SetBounds(
button_x,
0,
button_size.width(),
caption_button_container_->GetPreferredSize().height());
}
#endif
bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
if (!frame()->IsFullscreen())
return true;
// We need to paint when in immersive fullscreen and either:
// - The top-of-window views are revealed.
// - The lightbar style tabstrip is visible.
ImmersiveModeController* immersive_mode_controller =
browser_view()->immersive_mode_controller();
return immersive_mode_controller->IsEnabled() &&
(immersive_mode_controller->IsRevealed() ||
UseImmersiveLightbarHeaderStyle());
}
void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader(
gfx::Canvas* canvas) {
// The light bar header is not themed because theming it does not look good.
canvas->FillRect(
gfx::Rect(width(), header_painter_->GetHeaderHeightForPainting()),
SK_ColorBLACK);
}
void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) {
gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
if (toolbar_bounds.IsEmpty())
return;
gfx::Point toolbar_origin(toolbar_bounds.origin());
View::ConvertPointToTarget(browser_view(), this, &toolbar_origin);
toolbar_bounds.set_origin(toolbar_origin);
int x = toolbar_bounds.x();
int w = toolbar_bounds.width();
int y = toolbar_bounds.y();
int h = toolbar_bounds.height();
ui::ThemeProvider* tp = GetThemeProvider();
if (ui::MaterialDesignController::IsModeMaterial()) {
// Paint the main toolbar image. Since this image is also used to draw the
// tab background, we must use the tab strip offset to compute the image
// source y position. If you have to debug this code use an image editor
// to paint a diagonal line through the toolbar image and ensure it lines up
// across the tab and toolbar.
gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
canvas->TileImageInt(
*theme_toolbar,
x + GetThemeBackgroundXInset(),
y - GetTopInset(false),
x, y,
w, theme_toolbar->height());
// Draw the content/toolbar separator.
toolbar_bounds.Inset(kClientEdgeThickness, 0);
BrowserView::Paint1pxHorizontalLine(
canvas,
ThemeProperties::GetDefaultColor(
ThemeProperties::COLOR_TOOLBAR_SEPARATOR),
toolbar_bounds);
} else {
// Gross hack: We split the toolbar images into two pieces, since sometimes
// (popup mode) the toolbar isn't tall enough to show the whole image. The
// split happens between the top shadow section and the bottom gradient
// section so that we never break the gradient.
// NOTE(pkotwicz): If the computation for |bottom_y| is changed, Layout()
// must be changed as well.
int split_point = kFrameShadowThickness * 2;
int bottom_y = y + split_point;
int bottom_edge_height = h - split_point;
canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
// Paint the main toolbar image. Since this image is also used to draw the
// tab background, we must use the tab strip offset to compute the image
// source y position. If you have to debug this code use an image editor
// to paint a diagonal line through the toolbar image and ensure it lines up
// across the tab and toolbar.
gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
canvas->TileImageInt(
*theme_toolbar,
x + GetThemeBackgroundXInset(),
bottom_y - GetTopInset(false),
x, bottom_y,
w, theme_toolbar->height());
// The pre-material design content area line has a shadow that extends a
// couple of pixels above the toolbar bounds.
const int kContentShadowHeight = 2;
gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP);
canvas->TileImageInt(*toolbar_top,
0, 0,
x, y - kContentShadowHeight,
w, split_point + kContentShadowHeight + 1);
// Draw the "lightening" shade line around the edges of the toolbar.
gfx::ImageSkia* toolbar_left =
tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT);
canvas->TileImageInt(*toolbar_left,
0, 0,
x + kClientEdgeThickness,
y + kClientEdgeThickness + kContentShadowHeight,
toolbar_left->width(), theme_toolbar->height());
gfx::ImageSkia* toolbar_right =
tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT);
canvas->TileImageInt(*toolbar_right,
0, 0,
w - toolbar_right->width() - 2 * kClientEdgeThickness,
y + kClientEdgeThickness + kContentShadowHeight,
toolbar_right->width(), theme_toolbar->height());
// Draw the content/toolbar separator.
canvas->FillRect(
gfx::Rect(x + kClientEdgeThickness,
toolbar_bounds.bottom() - kClientEdgeThickness,
w - (2 * kClientEdgeThickness),
kClientEdgeThickness),
ThemeProperties::GetDefaultColor(
ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
}
}
void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) {
DCHECK(!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle());
canvas->FillRect(gfx::Rect(0, caption_button_container_->bounds().bottom(),
width(), kClientEdgeThickness),
ThemeProperties::GetDefaultColor(
ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
}