1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
10 #include "base/compiler_specific.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/profiles/profiles_state.h"
15 #include "chrome/browser/themes/theme_properties.h"
16 #include "chrome/browser/ui/views/frame/browser_frame.h"
17 #include "chrome/browser/ui/views/frame/browser_view.h"
18 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
19 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h"
20 #include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
21 #include "chrome/browser/ui/views/profiles/new_avatar_button.h"
22 #include "chrome/browser/ui/views/tab_icon_view.h"
23 #include "chrome/browser/ui/views/tabs/tab_strip.h"
24 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
25 #include "chrome/grit/generated_resources.h"
26 #include "components/signin/core/browser/signin_header_helper.h"
27 #include "components/signin/core/common/profile_management_switches.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/web_contents.h"
30 #include "grit/theme_resources.h"
31 #include "ui/accessibility/ax_view_state.h"
32 #include "ui/base/hit_test.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/resource/material_design/material_design_controller.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/base/theme_provider.h"
37 #include "ui/gfx/canvas.h"
38 #include "ui/gfx/font_list.h"
39 #include "ui/gfx/geometry/rect_conversions.h"
40 #include "ui/gfx/image/image.h"
41 #include "ui/gfx/image/image_skia.h"
42 #include "ui/gfx/path.h"
43 #include "ui/resources/grit/ui_resources.h"
44 #include "ui/views/controls/button/image_button.h"
45 #include "ui/views/controls/image_view.h"
46 #include "ui/views/controls/label.h"
47 #include "ui/views/layout/layout_constants.h"
48 #include "ui/views/resources/grit/views_resources.h"
49 #include "ui/views/views_delegate.h"
50 #include "ui/views/widget/root_view.h"
51 #include "ui/views/window/frame_background.h"
52 #include "ui/views/window/window_shape.h"
54 #if defined(ENABLE_SUPERVISED_USERS)
55 #include "chrome/browser/ui/views/profiles/supervised_user_avatar_label.h"
59 #include "ui/views/controls/menu/menu_runner.h"
62 using content::WebContents
;
66 // While resize areas on Windows are normally the same size as the window
67 // borders, our top area is shrunk by 1 px to make it easier to move the window
68 // around with our thinner top grabbable strip. (Incidentally, our side and
69 // bottom resize areas don't match the frame border thickness either -- they
70 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
71 const int kTopResizeAdjust
= 1;
73 // In the window corners, the resize areas don't actually expand bigger, but the
74 // 16 px at the end of each edge triggers diagonal resizing.
75 const int kResizeAreaCornerSize
= 16;
77 // The content left/right images have a shadow built into them.
78 const int kContentEdgeShadowThickness
= 2;
81 // The icon never shrinks below 16 px on a side.
82 const int kIconMinimumSize
= 16;
85 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
86 // The number of pixels to move the frame background image upwards when using
87 // the GTK+ theme and the titlebar is condensed.
88 const int kGTKThemeCondensedFrameTopInset
= 15;
93 ///////////////////////////////////////////////////////////////////////////////
94 // OpaqueBrowserFrameView, public:
96 OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame
* frame
,
97 BrowserView
* browser_view
)
98 : BrowserNonClientFrameView(frame
, browser_view
),
99 layout_(new OpaqueBrowserFrameViewLayout(this)),
100 minimize_button_(nullptr),
101 maximize_button_(nullptr),
102 restore_button_(nullptr),
103 close_button_(nullptr),
104 window_icon_(nullptr),
105 window_title_(nullptr),
106 frame_background_(new views::FrameBackground()) {
107 SetLayoutManager(layout_
);
109 minimize_button_
= InitWindowCaptionButton(IDR_MINIMIZE
,
112 IDR_MINIMIZE_BUTTON_MASK
,
113 IDS_ACCNAME_MINIMIZE
,
114 VIEW_ID_MINIMIZE_BUTTON
);
115 maximize_button_
= InitWindowCaptionButton(IDR_MAXIMIZE
,
118 IDR_MAXIMIZE_BUTTON_MASK
,
119 IDS_ACCNAME_MAXIMIZE
,
120 VIEW_ID_MAXIMIZE_BUTTON
);
121 restore_button_
= InitWindowCaptionButton(IDR_RESTORE
,
124 IDR_RESTORE_BUTTON_MASK
,
126 VIEW_ID_RESTORE_BUTTON
);
127 close_button_
= InitWindowCaptionButton(IDR_CLOSE
,
130 IDR_CLOSE_BUTTON_MASK
,
132 VIEW_ID_CLOSE_BUTTON
);
134 // Initializing the TabIconView is expensive, so only do it if we need to.
135 if (browser_view
->ShouldShowWindowIcon()) {
136 window_icon_
= new TabIconView(this, this);
137 window_icon_
->set_is_light(true);
138 window_icon_
->set_id(VIEW_ID_WINDOW_ICON
);
139 AddChildView(window_icon_
);
140 window_icon_
->Update();
143 window_title_
= new views::Label(
144 browser_view
->GetWindowTitle(),
145 gfx::FontList(BrowserFrame::GetTitleFontList()));
146 window_title_
->SetVisible(browser_view
->ShouldShowWindowTitle());
147 window_title_
->SetEnabledColor(SK_ColorWHITE
);
148 window_title_
->SetSubpixelRenderingEnabled(false);
149 window_title_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
150 window_title_
->set_id(VIEW_ID_WINDOW_TITLE
);
151 AddChildView(window_title_
);
155 platform_observer_
.reset(OpaqueBrowserFrameViewPlatformSpecific::Create(
156 this, layout_
, browser_view
->browser()->profile()));
159 OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
162 ///////////////////////////////////////////////////////////////////////////////
163 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
165 gfx::Rect
OpaqueBrowserFrameView::GetBoundsForTabStrip(
166 views::View
* tabstrip
) const {
170 return layout_
->GetBoundsForTabStrip(tabstrip
->GetPreferredSize(), width());
173 int OpaqueBrowserFrameView::GetTopInset() const {
174 return browser_view()->IsTabStripVisible() ?
175 layout_
->GetTabStripInsetsTop(false) :
176 layout_
->NonClientTopBorderHeight(false);
179 int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const {
183 void OpaqueBrowserFrameView::UpdateThrobber(bool running
) {
185 window_icon_
->Update();
188 gfx::Size
OpaqueBrowserFrameView::GetMinimumSize() const {
189 return layout_
->GetMinimumSize(width());
192 ///////////////////////////////////////////////////////////////////////////////
193 // OpaqueBrowserFrameView, views::NonClientFrameView implementation:
195 gfx::Rect
OpaqueBrowserFrameView::GetBoundsForClientView() const {
196 return layout_
->client_view_bounds();
199 gfx::Rect
OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
200 const gfx::Rect
& client_bounds
) const {
201 return layout_
->GetWindowBoundsForClientBounds(client_bounds
);
204 bool OpaqueBrowserFrameView::IsWithinAvatarMenuButtons(
205 const gfx::Point
& point
) const {
206 if (avatar_button() &&
207 avatar_button()->GetMirroredBounds().Contains(point
)) {
210 #if defined(FRAME_AVATAR_BUTTON)
211 if (new_avatar_button() &&
212 new_avatar_button()->GetMirroredBounds().Contains(point
)) {
220 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point
& point
) {
221 if (!bounds().Contains(point
))
224 // See if the point is within the avatar menu button.
225 if (IsWithinAvatarMenuButtons(point
))
227 #if defined(ENABLE_SUPERVISED_USERS)
228 // ...or within the avatar label, if it's a supervised user.
229 if ((supervised_user_avatar_label() &&
230 supervised_user_avatar_label()->GetMirroredBounds().Contains(point
)))
234 int frame_component
= frame()->client_view()->NonClientHitTest(point
);
236 // See if we're in the sysmenu region. We still have to check the tabstrip
237 // first so that clicks in a tab don't get treated as sysmenu clicks.
238 gfx::Rect
sysmenu_rect(IconBounds());
239 // In maximized mode we extend the rect to the screen corner to take advantage
241 if (layout_
->IsTitleBarCondensed())
242 sysmenu_rect
.SetRect(0, 0, sysmenu_rect
.right(), sysmenu_rect
.bottom());
243 sysmenu_rect
.set_x(GetMirroredXForRect(sysmenu_rect
));
244 if (sysmenu_rect
.Contains(point
))
245 return (frame_component
== HTCLIENT
) ? HTCLIENT
: HTSYSMENU
;
247 if (frame_component
!= HTNOWHERE
)
248 return frame_component
;
250 // Then see if the point is within any of the window controls.
251 if (close_button_
&& close_button_
->visible() &&
252 close_button_
->GetMirroredBounds().Contains(point
))
254 if (restore_button_
&& restore_button_
->visible() &&
255 restore_button_
->GetMirroredBounds().Contains(point
))
257 if (maximize_button_
&& maximize_button_
->visible() &&
258 maximize_button_
->GetMirroredBounds().Contains(point
))
260 if (minimize_button_
&& minimize_button_
->visible() &&
261 minimize_button_
->GetMirroredBounds().Contains(point
))
264 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
266 LOG(WARNING
) << "delegate is null, returning safe default.";
269 int window_component
= GetHTComponentForFrame(point
, TopResizeHeight(),
270 NonClientBorderThickness(), kResizeAreaCornerSize
, kResizeAreaCornerSize
,
271 delegate
->CanResize());
272 // Fall back to the caption if no other component matches.
273 return (window_component
== HTNOWHERE
) ? HTCAPTION
: window_component
;
276 void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size
& size
,
277 gfx::Path
* window_mask
) {
280 if (layout_
->IsTitleBarCondensed() || frame()->IsFullscreen())
283 views::GetDefaultWindowMask(size
, window_mask
);
286 void OpaqueBrowserFrameView::ResetWindowControls() {
287 restore_button_
->SetState(views::CustomButton::STATE_NORMAL
);
288 minimize_button_
->SetState(views::CustomButton::STATE_NORMAL
);
289 maximize_button_
->SetState(views::CustomButton::STATE_NORMAL
);
290 // The close button isn't affected by this constraint.
293 void OpaqueBrowserFrameView::UpdateWindowIcon() {
295 window_icon_
->SchedulePaint();
298 void OpaqueBrowserFrameView::UpdateWindowTitle() {
299 if (!frame()->IsFullscreen())
300 window_title_
->SchedulePaint();
303 void OpaqueBrowserFrameView::SizeConstraintsChanged() {
306 ///////////////////////////////////////////////////////////////////////////////
307 // OpaqueBrowserFrameView, views::View overrides:
309 void OpaqueBrowserFrameView::GetAccessibleState(
310 ui::AXViewState
* state
) {
311 state
->role
= ui::AX_ROLE_TITLE_BAR
;
314 ///////////////////////////////////////////////////////////////////////////////
315 // OpaqueBrowserFrameView, views::ButtonListener implementation:
317 void OpaqueBrowserFrameView::ButtonPressed(views::Button
* sender
,
318 const ui::Event
& event
) {
319 if (sender
== minimize_button_
) {
321 } else if (sender
== maximize_button_
) {
323 } else if (sender
== restore_button_
) {
325 } else if (sender
== close_button_
) {
327 #if defined(FRAME_AVATAR_BUTTON)
328 } else if (sender
== new_avatar_button()) {
329 BrowserWindow::AvatarBubbleMode mode
=
330 BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT
;
331 if ((event
.IsMouseEvent() &&
332 static_cast<const ui::MouseEvent
&>(event
).IsRightMouseButton()) ||
333 (event
.type() == ui::ET_GESTURE_LONG_PRESS
)) {
334 mode
= BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH
;
336 browser_view()->ShowAvatarBubbleFromAvatarButton(
338 signin::ManageAccountsParams());
343 void OpaqueBrowserFrameView::OnMenuButtonClicked(views::View
* source
,
344 const gfx::Point
& point
) {
345 #if defined(OS_LINUX)
346 views::MenuRunner
menu_runner(frame()->GetSystemMenuModel(),
347 views::MenuRunner::HAS_MNEMONICS
);
348 ignore_result(menu_runner
.RunMenuAt(browser_view()->GetWidget(),
350 window_icon_
->GetBoundsInScreen(),
351 views::MENU_ANCHOR_TOPLEFT
,
352 ui::MENU_SOURCE_MOUSE
));
356 ///////////////////////////////////////////////////////////////////////////////
357 // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
359 bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
360 // This function is queried during the creation of the window as the
361 // TabIconView we host is initialized, so we need to null check the selected
362 // WebContents because in this condition there is not yet a selected tab.
363 WebContents
* current_tab
= browser_view()->GetActiveWebContents();
364 return current_tab
? current_tab
->IsLoading() : false;
367 gfx::ImageSkia
OpaqueBrowserFrameView::GetFaviconForTabIconView() {
368 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
370 LOG(WARNING
) << "delegate is null, returning safe default.";
371 return gfx::ImageSkia();
373 return delegate
->GetWindowIcon();
376 ///////////////////////////////////////////////////////////////////////////////
377 // OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation:
379 bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const {
380 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
381 return ShouldShowWindowTitleBar() && delegate
&&
382 delegate
->ShouldShowWindowIcon();
385 bool OpaqueBrowserFrameView::ShouldShowWindowTitle() const {
386 // |delegate| may be null if called from callback of InputMethodChanged while
387 // a window is being destroyed.
388 // See more discussion at http://crosbug.com/8958
389 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
390 return ShouldShowWindowTitleBar() && delegate
&&
391 delegate
->ShouldShowWindowTitle();
394 base::string16
OpaqueBrowserFrameView::GetWindowTitle() const {
395 return frame()->widget_delegate()->GetWindowTitle();
398 int OpaqueBrowserFrameView::GetIconSize() const {
400 // This metric scales up if either the titlebar height or the titlebar font
401 // size are increased.
402 return GetSystemMetrics(SM_CYSMICON
);
404 return std::max(BrowserFrame::GetTitleFontList().GetHeight(),
409 bool OpaqueBrowserFrameView::ShouldLeaveOffsetNearTopBorder() const {
410 return frame()->ShouldLeaveOffsetNearTopBorder();
413 gfx::Size
OpaqueBrowserFrameView::GetBrowserViewMinimumSize() const {
414 return browser_view()->GetMinimumSize();
417 bool OpaqueBrowserFrameView::ShouldShowCaptionButtons() const {
418 return ShouldShowWindowTitleBar();
421 bool OpaqueBrowserFrameView::ShouldShowAvatar() const {
422 return browser_view()->ShouldShowAvatar();
425 bool OpaqueBrowserFrameView::IsRegularOrGuestSession() const {
426 return browser_view()->IsRegularOrGuestSession();
429 gfx::ImageSkia
OpaqueBrowserFrameView::GetOTRAvatarIcon() const {
430 return browser_view()->GetOTRAvatarIcon();
433 bool OpaqueBrowserFrameView::IsMaximized() const {
434 return frame()->IsMaximized();
437 bool OpaqueBrowserFrameView::IsMinimized() const {
438 return frame()->IsMinimized();
441 bool OpaqueBrowserFrameView::IsFullscreen() const {
442 return frame()->IsFullscreen();
445 bool OpaqueBrowserFrameView::IsTabStripVisible() const {
446 return browser_view()->IsTabStripVisible();
449 int OpaqueBrowserFrameView::GetTabStripHeight() const {
450 return browser_view()->GetTabStripHeight();
453 gfx::Size
OpaqueBrowserFrameView::GetTabstripPreferredSize() const {
454 gfx::Size s
= browser_view()->tabstrip()->GetPreferredSize();
458 ///////////////////////////////////////////////////////////////////////////////
459 // OpaqueBrowserFrameView, protected:
462 void OpaqueBrowserFrameView::OnPaint(gfx::Canvas
* canvas
) {
463 if (frame()->IsFullscreen())
464 return; // Nothing is visible, so don't bother to paint.
466 if (layout_
->IsTitleBarCondensed())
467 PaintMaximizedFrameBorder(canvas
);
469 PaintRestoredFrameBorder(canvas
);
471 // The window icon and title are painted by their respective views.
472 /* TODO(pkasting): If this window is active, we should also draw a drop
473 * shadow on the title. This is tricky, because we don't want to hardcode a
474 * shadow color (since we want to work with various themes), but we can't
475 * alpha-blend either (since the Windows text APIs don't really do this).
476 * So we'd need to sample the background color at the right location and
477 * synthesize a good shadow color. */
479 if (browser_view()->IsToolbarVisible())
480 PaintToolbarBackground(canvas
);
481 if (!layout_
->IsTitleBarCondensed())
482 PaintRestoredClientEdge(canvas
);
485 // BrowserNonClientFrameView:
486 bool OpaqueBrowserFrameView::ShouldPaintAsThemed() const {
487 // Theme app and popup windows if |platform_observer_| wants it.
488 return browser_view()->IsBrowserTypeNormal() ||
489 platform_observer_
->IsUsingSystemTheme();
492 void OpaqueBrowserFrameView::UpdateNewAvatarButtonImpl() {
493 #if defined(FRAME_AVATAR_BUTTON)
494 UpdateNewAvatarButton(this, NewAvatarButton::THEMED_BUTTON
);
498 ///////////////////////////////////////////////////////////////////////////////
499 // OpaqueBrowserFrameView, private:
501 // views::NonClientFrameView:
502 bool OpaqueBrowserFrameView::DoesIntersectRect(const views::View
* target
,
503 const gfx::Rect
& rect
) const {
504 CHECK_EQ(target
, this);
505 if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect
)) {
506 // |rect| is outside OpaqueBrowserFrameView's bounds.
510 // If the rect is outside the bounds of the client area, claim it.
511 gfx::RectF
rect_in_client_view_coords_f(rect
);
512 View::ConvertRectToTarget(this, frame()->client_view(),
513 &rect_in_client_view_coords_f
);
514 gfx::Rect rect_in_client_view_coords
= gfx::ToEnclosingRect(
515 rect_in_client_view_coords_f
);
516 if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords
))
519 // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in
520 // a non-tab portion.
521 TabStrip
* tabstrip
= browser_view()->tabstrip();
522 if (!tabstrip
|| !browser_view()->IsTabStripVisible())
525 gfx::RectF
rect_in_tabstrip_coords_f(rect
);
526 View::ConvertRectToTarget(this, tabstrip
, &rect_in_tabstrip_coords_f
);
527 gfx::Rect rect_in_tabstrip_coords
= gfx::ToEnclosingRect(
528 rect_in_tabstrip_coords_f
);
529 if (rect_in_tabstrip_coords
.bottom() > tabstrip
->GetLocalBounds().bottom()) {
530 // |rect| is below the tabstrip.
534 if (tabstrip
->HitTestRect(rect_in_tabstrip_coords
)) {
535 // Claim |rect| if it is in a non-tab portion of the tabstrip.
536 return tabstrip
->IsRectInWindowCaption(rect_in_tabstrip_coords
);
539 // We claim |rect| because it is above the bottom of the tabstrip, but
540 // not in the tabstrip itself. In particular, the avatar label/button is left
541 // of the tabstrip and the window controls are right of the tabstrip.
545 views::ImageButton
* OpaqueBrowserFrameView::InitWindowCaptionButton(
550 int accessibility_string_id
,
552 views::ImageButton
* button
= new views::ImageButton(this);
553 ui::ThemeProvider
* tp
= frame()->GetThemeProvider();
554 button
->SetImage(views::CustomButton::STATE_NORMAL
,
555 tp
->GetImageSkiaNamed(normal_image_id
));
556 button
->SetImage(views::CustomButton::STATE_HOVERED
,
557 tp
->GetImageSkiaNamed(hot_image_id
));
558 button
->SetImage(views::CustomButton::STATE_PRESSED
,
559 tp
->GetImageSkiaNamed(pushed_image_id
));
560 if (browser_view()->IsBrowserTypeNormal()) {
561 button
->SetBackground(
562 tp
->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND
),
563 tp
->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND
),
564 tp
->GetImageSkiaNamed(mask_image_id
));
566 button
->SetAccessibleName(
567 l10n_util::GetStringUTF16(accessibility_string_id
));
568 button
->set_id(view_id
);
569 AddChildView(button
);
573 int OpaqueBrowserFrameView::FrameBorderThickness(bool restored
) const {
574 return layout_
->FrameBorderThickness(restored
);
577 int OpaqueBrowserFrameView::TopResizeHeight() const {
578 return FrameBorderThickness(false) - kTopResizeAdjust
;
581 int OpaqueBrowserFrameView::NonClientBorderThickness() const {
582 return layout_
->NonClientBorderThickness();
585 gfx::Rect
OpaqueBrowserFrameView::IconBounds() const {
586 return layout_
->IconBounds();
589 bool OpaqueBrowserFrameView::ShouldShowWindowTitleBar() const {
590 // Do not show the custom title bar if the system title bar option is enabled.
591 if (!frame()->UseCustomFrame())
594 // Do not show caption buttons if the window manager is forcefully providing a
595 // title bar (e.g., in Ubuntu Unity, if the window is maximized).
596 if (!views::ViewsDelegate::GetInstance())
598 return !views::ViewsDelegate::GetInstance()->WindowManagerProvidesTitleBar(
602 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas
* canvas
) {
603 frame_background_
->set_frame_color(GetFrameColor());
604 frame_background_
->set_theme_image(GetFrameImage());
605 frame_background_
->set_theme_overlay_image(GetFrameOverlayImage());
606 frame_background_
->set_top_area_height(GetTopAreaHeight());
608 ui::ThemeProvider
* tp
= GetThemeProvider();
609 frame_background_
->SetSideImages(
610 tp
->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE
),
611 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER
),
612 tp
->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE
),
613 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER
));
614 frame_background_
->SetCornerImages(
615 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER
),
616 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER
),
617 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER
),
618 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER
));
619 frame_background_
->PaintRestored(canvas
, this);
621 // Note: When we don't have a toolbar, we need to draw some kind of bottom
622 // edge here. Because the App Window graphics we use for this have an
623 // attached client edge and their sizing algorithm is a little involved, we do
624 // all this in PaintRestoredClientEdge().
627 void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas
* canvas
) {
628 ui::ThemeProvider
* tp
= GetThemeProvider();
629 frame_background_
->set_frame_color(GetFrameColor());
630 frame_background_
->set_theme_image(GetFrameImage());
631 frame_background_
->set_theme_overlay_image(GetFrameOverlayImage());
632 frame_background_
->set_top_area_height(GetTopAreaHeight());
633 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
634 // The window manager typically shows a gradient in the native title bar (when
635 // the system title bar pref is set, or when maximized on Ubuntu). Hide the
636 // gradient in the tab strip (by shifting it up vertically) to avoid a
637 // double-gradient effect.
638 if (tp
->UsingSystemTheme())
639 frame_background_
->set_maximized_top_inset(kGTKThemeCondensedFrameTopInset
);
642 frame_background_
->PaintMaximized(canvas
, this);
644 // TODO(jamescook): Migrate this into FrameBackground.
645 if (!browser_view()->IsToolbarVisible()) {
646 // There's no toolbar to edge the frame border, so we need to draw a bottom
647 // edge. The graphic we use for this has a built in client edge, so we clip
648 // it off the bottom.
649 gfx::ImageSkia
* top_center
= tp
->GetImageSkiaNamed(IDR_APP_TOP_CENTER
);
650 int edge_height
= top_center
->height() - kClientEdgeThickness
;
651 canvas
->TileImageInt(*top_center
, 0,
652 frame()->client_view()->y() - edge_height
, width(), edge_height
);
656 void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas
* canvas
) {
657 gfx::Rect
toolbar_bounds(browser_view()->GetToolbarBounds());
658 if (toolbar_bounds
.IsEmpty())
660 gfx::Point
toolbar_origin(toolbar_bounds
.origin());
661 ConvertPointToTarget(browser_view(), this, &toolbar_origin
);
662 toolbar_bounds
.set_origin(toolbar_origin
);
664 int x
= toolbar_bounds
.x();
665 int w
= toolbar_bounds
.width();
666 int y
= toolbar_bounds
.y();
667 int h
= toolbar_bounds
.height();
669 // Gross hack: We split the toolbar images into two pieces, since sometimes
670 // (popup mode) the toolbar isn't tall enough to show the whole image. The
671 // split happens between the top shadow section and the bottom gradient
672 // section so that we never break the gradient.
673 int split_point
= kFrameShadowThickness
* 2;
674 int bottom_y
= y
+ split_point
;
675 ui::ThemeProvider
* tp
= GetThemeProvider();
676 gfx::ImageSkia
* toolbar_left
= tp
->GetImageSkiaNamed(
677 IDR_CONTENT_TOP_LEFT_CORNER
);
678 int bottom_edge_height
= std::min(toolbar_left
->height(), h
) - split_point
;
680 // Split our canvas out so we can mask out the corners of the toolbar
681 // without masking out the frame.
682 canvas
->SaveLayerAlpha(
683 255, gfx::Rect(x
- kClientEdgeThickness
, y
, w
+ kClientEdgeThickness
* 3,
686 // Paint the bottom rect.
687 canvas
->FillRect(gfx::Rect(x
, bottom_y
, w
, bottom_edge_height
),
688 tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
));
690 // Tile the toolbar image starting at the frame edge on the left and where the
691 // horizontal tabstrip is (or would be) on the top.
692 gfx::ImageSkia
* theme_toolbar
= tp
->GetImageSkiaNamed(IDR_THEME_TOOLBAR
);
693 canvas
->TileImageInt(*theme_toolbar
,
694 x
+ GetThemeBackgroundXInset(),
695 bottom_y
- GetTopInset(),
696 x
, bottom_y
, w
, theme_toolbar
->height());
698 // Draw rounded corners for the tab.
699 gfx::ImageSkia
* toolbar_left_mask
=
700 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK
);
701 gfx::ImageSkia
* toolbar_right_mask
=
702 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK
);
704 // We mask out the corners by using the DestinationIn transfer mode,
705 // which keeps the RGB pixels from the destination and the alpha from
708 paint
.setXfermodeMode(SkXfermode::kDstIn_Mode
);
710 // Mask the left edge.
711 int left_x
= x
- kContentEdgeShadowThickness
;
712 canvas
->DrawImageInt(*toolbar_left_mask
, 0, 0, toolbar_left_mask
->width(),
713 split_point
, left_x
, y
, toolbar_left_mask
->width(),
714 split_point
, false, paint
);
715 canvas
->DrawImageInt(*toolbar_left_mask
, 0,
716 toolbar_left_mask
->height() - bottom_edge_height
,
717 toolbar_left_mask
->width(), bottom_edge_height
, left_x
, bottom_y
,
718 toolbar_left_mask
->width(), bottom_edge_height
, false, paint
);
720 // Mask the right edge.
722 x
+ w
- toolbar_right_mask
->width() + kContentEdgeShadowThickness
;
723 canvas
->DrawImageInt(*toolbar_right_mask
, 0, 0, toolbar_right_mask
->width(),
724 split_point
, right_x
, y
, toolbar_right_mask
->width(),
725 split_point
, false, paint
);
726 canvas
->DrawImageInt(*toolbar_right_mask
, 0,
727 toolbar_right_mask
->height() - bottom_edge_height
,
728 toolbar_right_mask
->width(), bottom_edge_height
, right_x
, bottom_y
,
729 toolbar_right_mask
->width(), bottom_edge_height
, false, paint
);
732 canvas
->DrawImageInt(*toolbar_left
, 0, 0, toolbar_left
->width(), split_point
,
733 left_x
, y
, toolbar_left
->width(), split_point
, false);
734 canvas
->DrawImageInt(*toolbar_left
, 0,
735 toolbar_left
->height() - bottom_edge_height
, toolbar_left
->width(),
736 bottom_edge_height
, left_x
, bottom_y
, toolbar_left
->width(),
737 bottom_edge_height
, false);
739 gfx::ImageSkia
* toolbar_center
=
740 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER
);
741 canvas
->TileImageInt(*toolbar_center
, 0, 0, left_x
+ toolbar_left
->width(),
742 y
, right_x
- (left_x
+ toolbar_left
->width()),
745 gfx::ImageSkia
* toolbar_right
= tp
->GetImageSkiaNamed(
746 IDR_CONTENT_TOP_RIGHT_CORNER
);
747 canvas
->DrawImageInt(*toolbar_right
, 0, 0, toolbar_right
->width(),
748 split_point
, right_x
, y
, toolbar_right
->width(), split_point
, false);
749 canvas
->DrawImageInt(*toolbar_right
, 0,
750 toolbar_right
->height() - bottom_edge_height
, toolbar_right
->width(),
751 bottom_edge_height
, right_x
, bottom_y
, toolbar_right
->width(),
752 bottom_edge_height
, false);
754 // Draw the content/toolbar separator.
755 if (ui::MaterialDesignController::IsModeMaterial()) {
756 toolbar_bounds
.Inset(kClientEdgeThickness
, 0);
757 BrowserView::Paint1pxHorizontalLine(
759 ThemeProperties::GetDefaultColor(
760 ThemeProperties::COLOR_TOOLBAR_SEPARATOR
),
764 gfx::Rect(x
+ kClientEdgeThickness
,
765 toolbar_bounds
.bottom() - kClientEdgeThickness
,
766 w
- (2 * kClientEdgeThickness
),
767 kClientEdgeThickness
),
768 ThemeProperties::GetDefaultColor(
769 ThemeProperties::COLOR_TOOLBAR_SEPARATOR
));
773 void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas
* canvas
) {
774 ui::ThemeProvider
* tp
= GetThemeProvider();
775 int client_area_top
= frame()->client_view()->y();
776 int image_top
= client_area_top
;
778 gfx::Rect client_area_bounds
=
779 layout_
->CalculateClientAreaBounds(width(), height());
780 SkColor toolbar_color
= tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
);
782 if (browser_view()->IsToolbarVisible()) {
783 // The client edge images always start below the toolbar corner images. The
784 // client edge filled rects start there or at the bottom of the toolbar,
785 // whichever is shorter.
786 gfx::Rect
toolbar_bounds(browser_view()->GetToolbarBounds());
788 gfx::ImageSkia
* content_top_left_corner
=
789 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER
);
790 // TODO(oshima): Sanity checks for crbug.com/374273. Remove when it's fixed.
791 CHECK(content_top_left_corner
);
792 CHECK(!content_top_left_corner
->isNull());
794 image_top
+= toolbar_bounds
.y() + content_top_left_corner
->height();
795 client_area_top
= std::min(image_top
,
796 client_area_top
+ toolbar_bounds
.bottom() - kClientEdgeThickness
);
797 } else if (!browser_view()->IsTabStripVisible()) {
798 // The toolbar isn't going to draw a client edge for us, so draw one
800 gfx::ImageSkia
* top_left
= tp
->GetImageSkiaNamed(IDR_APP_TOP_LEFT
);
801 gfx::ImageSkia
* top_center
= tp
->GetImageSkiaNamed(IDR_APP_TOP_CENTER
);
802 gfx::ImageSkia
* top_right
= tp
->GetImageSkiaNamed(IDR_APP_TOP_RIGHT
);
803 int top_edge_y
= client_area_top
- top_center
->height();
804 int height
= client_area_top
- top_edge_y
;
806 canvas
->DrawImageInt(*top_left
, 0, 0, top_left
->width(), height
,
807 client_area_bounds
.x() - top_left
->width(), top_edge_y
,
808 top_left
->width(), height
, false);
809 canvas
->TileImageInt(*top_center
, 0, 0, client_area_bounds
.x(), top_edge_y
,
810 client_area_bounds
.width(), std::min(height
, top_center
->height()));
811 canvas
->DrawImageInt(*top_right
, 0, 0, top_right
->width(), height
,
812 client_area_bounds
.right(), top_edge_y
,
813 top_right
->width(), height
, false);
815 // Draw the toolbar color across the top edge.
816 canvas
->FillRect(gfx::Rect(client_area_bounds
.x() - kClientEdgeThickness
,
817 client_area_top
- kClientEdgeThickness
,
818 client_area_bounds
.width() + (2 * kClientEdgeThickness
),
819 kClientEdgeThickness
), toolbar_color
);
822 int client_area_bottom
=
823 std::max(client_area_top
, height() - NonClientBorderThickness());
824 int image_height
= client_area_bottom
- image_top
;
826 // Draw the client edge images.
827 gfx::ImageSkia
* right
= tp
->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE
);
828 canvas
->TileImageInt(*right
, client_area_bounds
.right(), image_top
,
829 right
->width(), image_height
);
830 canvas
->DrawImageInt(
831 *tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER
),
832 client_area_bounds
.right(), client_area_bottom
);
833 gfx::ImageSkia
* bottom
= tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER
);
834 canvas
->TileImageInt(*bottom
, client_area_bounds
.x(),
835 client_area_bottom
, client_area_bounds
.width(),
837 gfx::ImageSkia
* bottom_left
=
838 tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER
);
839 canvas
->DrawImageInt(*bottom_left
,
840 client_area_bounds
.x() - bottom_left
->width(), client_area_bottom
);
841 gfx::ImageSkia
* left
= tp
->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE
);
842 canvas
->TileImageInt(*left
, client_area_bounds
.x() - left
->width(),
843 image_top
, left
->width(), image_height
);
845 // Draw the toolbar color so that the client edges show the right color even
846 // where not covered by the toolbar image. NOTE: We do this after drawing the
847 // images because the images are meant to alpha-blend atop the frame whereas
848 // these rects are meant to be fully opaque, without anything overlaid.
849 canvas
->FillRect(gfx::Rect(client_area_bounds
.x() - kClientEdgeThickness
,
850 client_area_top
, kClientEdgeThickness
,
851 client_area_bottom
+ kClientEdgeThickness
- client_area_top
),
853 canvas
->FillRect(gfx::Rect(client_area_bounds
.x(), client_area_bottom
,
854 client_area_bounds
.width(), kClientEdgeThickness
),
856 canvas
->FillRect(gfx::Rect(client_area_bounds
.right(), client_area_top
,
857 kClientEdgeThickness
,
858 client_area_bottom
+ kClientEdgeThickness
- client_area_top
),