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/signin/signin_header_helper.h"
16 #include "chrome/browser/themes/theme_properties.h"
17 #include "chrome/browser/ui/views/frame/browser_frame.h"
18 #include "chrome/browser/ui/views/frame/browser_view.h"
19 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
20 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h"
21 #include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
22 #include "chrome/browser/ui/views/profiles/new_avatar_button.h"
23 #include "chrome/browser/ui/views/tab_icon_view.h"
24 #include "chrome/browser/ui/views/tabs/tab_strip.h"
25 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
26 #include "chrome/grit/generated_resources.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/resource_bundle.h"
35 #include "ui/base/theme_provider.h"
36 #include "ui/gfx/canvas.h"
37 #include "ui/gfx/font_list.h"
38 #include "ui/gfx/geometry/rect_conversions.h"
39 #include "ui/gfx/image/image.h"
40 #include "ui/gfx/image/image_skia.h"
41 #include "ui/gfx/path.h"
42 #include "ui/resources/grit/ui_resources.h"
43 #include "ui/views/controls/button/image_button.h"
44 #include "ui/views/controls/image_view.h"
45 #include "ui/views/controls/label.h"
46 #include "ui/views/layout/layout_constants.h"
47 #include "ui/views/views_delegate.h"
48 #include "ui/views/widget/root_view.h"
49 #include "ui/views/window/frame_background.h"
50 #include "ui/views/window/window_shape.h"
52 #if defined(ENABLE_SUPERVISED_USERS)
53 #include "chrome/browser/ui/views/profiles/supervised_user_avatar_label.h"
57 #include "ui/views/controls/menu/menu_runner.h"
60 using content::WebContents
;
64 // While resize areas on Windows are normally the same size as the window
65 // borders, our top area is shrunk by 1 px to make it easier to move the window
66 // around with our thinner top grabbable strip. (Incidentally, our side and
67 // bottom resize areas don't match the frame border thickness either -- they
68 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
69 const int kTopResizeAdjust
= 1;
71 // In the window corners, the resize areas don't actually expand bigger, but the
72 // 16 px at the end of each edge triggers diagonal resizing.
73 const int kResizeAreaCornerSize
= 16;
75 // The content left/right images have a shadow built into them.
76 const int kContentEdgeShadowThickness
= 2;
78 // The icon never shrinks below 16 px on a side.
79 const int kIconMinimumSize
= 16;
81 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
82 // The number of pixels to move the frame background image upwards when using
83 // the GTK+ theme and the titlebar is condensed.
84 const int kGTKThemeCondensedFrameTopInset
= 15;
89 ///////////////////////////////////////////////////////////////////////////////
90 // OpaqueBrowserFrameView, public:
92 OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame
* frame
,
93 BrowserView
* browser_view
)
94 : BrowserNonClientFrameView(frame
, browser_view
),
95 layout_(new OpaqueBrowserFrameViewLayout(this)),
96 minimize_button_(nullptr),
97 maximize_button_(nullptr),
98 restore_button_(nullptr),
99 close_button_(nullptr),
100 window_icon_(nullptr),
101 window_title_(nullptr),
102 frame_background_(new views::FrameBackground()) {
103 SetLayoutManager(layout_
);
105 minimize_button_
= InitWindowCaptionButton(IDR_MINIMIZE
,
108 IDR_MINIMIZE_BUTTON_MASK
,
109 IDS_ACCNAME_MINIMIZE
,
110 VIEW_ID_MINIMIZE_BUTTON
);
111 maximize_button_
= InitWindowCaptionButton(IDR_MAXIMIZE
,
114 IDR_MAXIMIZE_BUTTON_MASK
,
115 IDS_ACCNAME_MAXIMIZE
,
116 VIEW_ID_MAXIMIZE_BUTTON
);
117 restore_button_
= InitWindowCaptionButton(IDR_RESTORE
,
120 IDR_RESTORE_BUTTON_MASK
,
122 VIEW_ID_RESTORE_BUTTON
);
123 close_button_
= InitWindowCaptionButton(IDR_CLOSE
,
126 IDR_CLOSE_BUTTON_MASK
,
128 VIEW_ID_CLOSE_BUTTON
);
130 // Initializing the TabIconView is expensive, so only do it if we need to.
131 if (browser_view
->ShouldShowWindowIcon()) {
132 window_icon_
= new TabIconView(this, this);
133 window_icon_
->set_is_light(true);
134 window_icon_
->set_id(VIEW_ID_WINDOW_ICON
);
135 AddChildView(window_icon_
);
136 window_icon_
->Update();
139 window_title_
= new views::Label(
140 browser_view
->GetWindowTitle(),
141 gfx::FontList(BrowserFrame::GetTitleFontList()));
142 window_title_
->SetVisible(browser_view
->ShouldShowWindowTitle());
143 window_title_
->SetEnabledColor(SK_ColorWHITE
);
144 window_title_
->SetSubpixelRenderingEnabled(false);
145 window_title_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
146 window_title_
->set_id(VIEW_ID_WINDOW_TITLE
);
147 AddChildView(window_title_
);
151 platform_observer_
.reset(OpaqueBrowserFrameViewPlatformSpecific::Create(
152 this, layout_
, browser_view
->browser()->profile()));
155 OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
158 ///////////////////////////////////////////////////////////////////////////////
159 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
161 gfx::Rect
OpaqueBrowserFrameView::GetBoundsForTabStrip(
162 views::View
* tabstrip
) const {
166 return layout_
->GetBoundsForTabStrip(tabstrip
->GetPreferredSize(), width());
169 int OpaqueBrowserFrameView::GetTopInset() const {
170 return browser_view()->IsTabStripVisible() ?
171 layout_
->GetTabStripInsetsTop(false) :
172 layout_
->NonClientTopBorderHeight(false);
175 int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const {
179 void OpaqueBrowserFrameView::UpdateThrobber(bool running
) {
180 // TODO(robliao): Remove ScopedTracker below once crbug.com/461137 is fixed.
181 tracked_objects::ScopedTracker
tracking_profile(
182 FROM_HERE_WITH_EXPLICIT_FUNCTION(
183 "461137 OpaqueBrowserFrameView::UpdateThrobber"));
186 window_icon_
->Update();
189 gfx::Size
OpaqueBrowserFrameView::GetMinimumSize() const {
190 return layout_
->GetMinimumSize(width());
193 ///////////////////////////////////////////////////////////////////////////////
194 // OpaqueBrowserFrameView, views::NonClientFrameView implementation:
196 gfx::Rect
OpaqueBrowserFrameView::GetBoundsForClientView() const {
197 return layout_
->client_view_bounds();
200 gfx::Rect
OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
201 const gfx::Rect
& client_bounds
) const {
202 return layout_
->GetWindowBoundsForClientBounds(client_bounds
);
205 bool OpaqueBrowserFrameView::IsWithinAvatarMenuButtons(
206 const gfx::Point
& point
) const {
207 if (avatar_button() &&
208 avatar_button()->GetMirroredBounds().Contains(point
)) {
211 if (new_avatar_button() &&
212 new_avatar_button()->GetMirroredBounds().Contains(point
)) {
219 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point
& point
) {
220 if (!bounds().Contains(point
))
223 // See if the point is within the avatar menu button.
224 if (IsWithinAvatarMenuButtons(point
))
226 #if defined(ENABLE_SUPERVISED_USERS)
227 // ...or within the avatar label, if it's a supervised user.
228 if ((supervised_user_avatar_label() &&
229 supervised_user_avatar_label()->GetMirroredBounds().Contains(point
)))
233 int frame_component
= frame()->client_view()->NonClientHitTest(point
);
235 // See if we're in the sysmenu region. We still have to check the tabstrip
236 // first so that clicks in a tab don't get treated as sysmenu clicks.
237 gfx::Rect
sysmenu_rect(IconBounds());
238 // In maximized mode we extend the rect to the screen corner to take advantage
240 if (layout_
->IsTitleBarCondensed())
241 sysmenu_rect
.SetRect(0, 0, sysmenu_rect
.right(), sysmenu_rect
.bottom());
242 sysmenu_rect
.set_x(GetMirroredXForRect(sysmenu_rect
));
243 if (sysmenu_rect
.Contains(point
))
244 return (frame_component
== HTCLIENT
) ? HTCLIENT
: HTSYSMENU
;
246 if (frame_component
!= HTNOWHERE
)
247 return frame_component
;
249 // Then see if the point is within any of the window controls.
250 if (close_button_
&& close_button_
->visible() &&
251 close_button_
->GetMirroredBounds().Contains(point
))
253 if (restore_button_
&& restore_button_
->visible() &&
254 restore_button_
->GetMirroredBounds().Contains(point
))
256 if (maximize_button_
&& maximize_button_
->visible() &&
257 maximize_button_
->GetMirroredBounds().Contains(point
))
259 if (minimize_button_
&& minimize_button_
->visible() &&
260 minimize_button_
->GetMirroredBounds().Contains(point
))
263 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
265 LOG(WARNING
) << "delegate is null, returning safe default.";
268 int window_component
= GetHTComponentForFrame(point
, TopResizeHeight(),
269 NonClientBorderThickness(), kResizeAreaCornerSize
, kResizeAreaCornerSize
,
270 delegate
->CanResize());
271 // Fall back to the caption if no other component matches.
272 return (window_component
== HTNOWHERE
) ? HTCAPTION
: window_component
;
275 void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size
& size
,
276 gfx::Path
* window_mask
) {
279 if (layout_
->IsTitleBarCondensed() || frame()->IsFullscreen())
282 views::GetDefaultWindowMask(size
, window_mask
);
285 void OpaqueBrowserFrameView::ResetWindowControls() {
286 restore_button_
->SetState(views::CustomButton::STATE_NORMAL
);
287 minimize_button_
->SetState(views::CustomButton::STATE_NORMAL
);
288 maximize_button_
->SetState(views::CustomButton::STATE_NORMAL
);
289 // The close button isn't affected by this constraint.
292 void OpaqueBrowserFrameView::UpdateWindowIcon() {
294 window_icon_
->SchedulePaint();
297 void OpaqueBrowserFrameView::UpdateWindowTitle() {
298 if (!frame()->IsFullscreen())
299 window_title_
->SchedulePaint();
302 void OpaqueBrowserFrameView::SizeConstraintsChanged() {
305 ///////////////////////////////////////////////////////////////////////////////
306 // OpaqueBrowserFrameView, views::View overrides:
308 void OpaqueBrowserFrameView::GetAccessibleState(
309 ui::AXViewState
* state
) {
310 state
->role
= ui::AX_ROLE_TITLE_BAR
;
313 ///////////////////////////////////////////////////////////////////////////////
314 // OpaqueBrowserFrameView, views::ButtonListener implementation:
316 void OpaqueBrowserFrameView::ButtonPressed(views::Button
* sender
,
317 const ui::Event
& event
) {
318 if (sender
== minimize_button_
) {
320 } else if (sender
== maximize_button_
) {
322 } else if (sender
== restore_button_
) {
324 } else if (sender
== close_button_
) {
326 } else if (sender
== new_avatar_button()) {
327 BrowserWindow::AvatarBubbleMode mode
=
328 BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT
;
329 if (event
.IsMouseEvent() &&
330 static_cast<const ui::MouseEvent
&>(event
).IsRightMouseButton()) {
331 mode
= BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH
;
333 browser_view()->ShowAvatarBubbleFromAvatarButton(
335 signin::ManageAccountsParams());
339 void OpaqueBrowserFrameView::OnMenuButtonClicked(views::View
* source
,
340 const gfx::Point
& point
) {
341 #if defined(OS_LINUX)
342 views::MenuRunner
menu_runner(frame()->GetSystemMenuModel(),
343 views::MenuRunner::HAS_MNEMONICS
);
344 ignore_result(menu_runner
.RunMenuAt(browser_view()->GetWidget(),
346 window_icon_
->GetBoundsInScreen(),
347 views::MENU_ANCHOR_TOPLEFT
,
348 ui::MENU_SOURCE_MOUSE
));
352 ///////////////////////////////////////////////////////////////////////////////
353 // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
355 bool OpaqueBrowserFrameView::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 WebContents
* current_tab
= browser_view()->GetActiveWebContents();
360 return current_tab
? current_tab
->IsLoading() : false;
363 gfx::ImageSkia
OpaqueBrowserFrameView::GetFaviconForTabIconView() {
364 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
366 LOG(WARNING
) << "delegate is null, returning safe default.";
367 return gfx::ImageSkia();
369 return delegate
->GetWindowIcon();
372 ///////////////////////////////////////////////////////////////////////////////
373 // OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation:
375 bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const {
376 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
377 return ShouldShowWindowTitleBar() && delegate
&&
378 delegate
->ShouldShowWindowIcon();
381 bool OpaqueBrowserFrameView::ShouldShowWindowTitle() const {
382 // |delegate| may be null if called from callback of InputMethodChanged while
383 // a window is being destroyed.
384 // See more discussion at http://crosbug.com/8958
385 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
386 return ShouldShowWindowTitleBar() && delegate
&&
387 delegate
->ShouldShowWindowTitle();
390 base::string16
OpaqueBrowserFrameView::GetWindowTitle() const {
391 return frame()->widget_delegate()->GetWindowTitle();
394 int OpaqueBrowserFrameView::GetIconSize() const {
396 // This metric scales up if either the titlebar height or the titlebar font
397 // size are increased.
398 return GetSystemMetrics(SM_CYSMICON
);
400 return std::max(BrowserFrame::GetTitleFontList().GetHeight(),
405 bool OpaqueBrowserFrameView::ShouldLeaveOffsetNearTopBorder() const {
406 return frame()->ShouldLeaveOffsetNearTopBorder();
409 gfx::Size
OpaqueBrowserFrameView::GetBrowserViewMinimumSize() const {
410 return browser_view()->GetMinimumSize();
413 bool OpaqueBrowserFrameView::ShouldShowCaptionButtons() const {
414 return ShouldShowWindowTitleBar();
417 bool OpaqueBrowserFrameView::ShouldShowAvatar() const {
418 return browser_view()->ShouldShowAvatar();
421 bool OpaqueBrowserFrameView::IsRegularOrGuestSession() const {
422 return browser_view()->IsRegularOrGuestSession();
425 gfx::ImageSkia
OpaqueBrowserFrameView::GetOTRAvatarIcon() const {
426 return browser_view()->GetOTRAvatarIcon();
429 bool OpaqueBrowserFrameView::IsMaximized() const {
430 return frame()->IsMaximized();
433 bool OpaqueBrowserFrameView::IsMinimized() const {
434 return frame()->IsMinimized();
437 bool OpaqueBrowserFrameView::IsFullscreen() const {
438 return frame()->IsFullscreen();
441 bool OpaqueBrowserFrameView::IsTabStripVisible() const {
442 return browser_view()->IsTabStripVisible();
445 int OpaqueBrowserFrameView::GetTabStripHeight() const {
446 return browser_view()->GetTabStripHeight();
449 gfx::Size
OpaqueBrowserFrameView::GetTabstripPreferredSize() const {
450 gfx::Size s
= browser_view()->tabstrip()->GetPreferredSize();
454 ///////////////////////////////////////////////////////////////////////////////
455 // OpaqueBrowserFrameView, protected:
458 void OpaqueBrowserFrameView::OnPaint(gfx::Canvas
* canvas
) {
459 if (frame()->IsFullscreen())
460 return; // Nothing is visible, so don't bother to paint.
462 if (layout_
->IsTitleBarCondensed())
463 PaintMaximizedFrameBorder(canvas
);
465 PaintRestoredFrameBorder(canvas
);
467 // The window icon and title are painted by their respective views.
468 /* TODO(pkasting): If this window is active, we should also draw a drop
469 * shadow on the title. This is tricky, because we don't want to hardcode a
470 * shadow color (since we want to work with various themes), but we can't
471 * alpha-blend either (since the Windows text APIs don't really do this).
472 * So we'd need to sample the background color at the right location and
473 * synthesize a good shadow color. */
475 if (browser_view()->IsToolbarVisible())
476 PaintToolbarBackground(canvas
);
477 if (!layout_
->IsTitleBarCondensed())
478 PaintRestoredClientEdge(canvas
);
481 // BrowserNonClientFrameView:
482 bool OpaqueBrowserFrameView::ShouldPaintAsThemed() const {
483 // Theme app and popup windows if |platform_observer_| wants it.
484 return browser_view()->IsBrowserTypeNormal() ||
485 platform_observer_
->IsUsingSystemTheme();
488 void OpaqueBrowserFrameView::UpdateNewAvatarButtonImpl() {
489 UpdateNewAvatarButton(this, NewAvatarButton::THEMED_BUTTON
);
492 ///////////////////////////////////////////////////////////////////////////////
493 // OpaqueBrowserFrameView, private:
495 // views::NonClientFrameView:
496 bool OpaqueBrowserFrameView::DoesIntersectRect(const views::View
* target
,
497 const gfx::Rect
& rect
) const {
498 CHECK_EQ(target
, this);
499 if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect
)) {
500 // |rect| is outside OpaqueBrowserFrameView's bounds.
504 // If the rect is outside the bounds of the client area, claim it.
505 gfx::RectF
rect_in_client_view_coords_f(rect
);
506 View::ConvertRectToTarget(this, frame()->client_view(),
507 &rect_in_client_view_coords_f
);
508 gfx::Rect rect_in_client_view_coords
= gfx::ToEnclosingRect(
509 rect_in_client_view_coords_f
);
510 if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords
))
513 // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in
514 // a non-tab portion.
515 TabStrip
* tabstrip
= browser_view()->tabstrip();
516 if (!tabstrip
|| !browser_view()->IsTabStripVisible())
519 gfx::RectF
rect_in_tabstrip_coords_f(rect
);
520 View::ConvertRectToTarget(this, tabstrip
, &rect_in_tabstrip_coords_f
);
521 gfx::Rect rect_in_tabstrip_coords
= gfx::ToEnclosingRect(
522 rect_in_tabstrip_coords_f
);
523 if (rect_in_tabstrip_coords
.bottom() > tabstrip
->GetLocalBounds().bottom()) {
524 // |rect| is below the tabstrip.
528 if (tabstrip
->HitTestRect(rect_in_tabstrip_coords
)) {
529 // Claim |rect| if it is in a non-tab portion of the tabstrip.
530 return tabstrip
->IsRectInWindowCaption(rect_in_tabstrip_coords
);
533 // We claim |rect| because it is above the bottom of the tabstrip, but
534 // not in the tabstrip itself. In particular, the avatar label/button is left
535 // of the tabstrip and the window controls are right of the tabstrip.
539 views::ImageButton
* OpaqueBrowserFrameView::InitWindowCaptionButton(
544 int accessibility_string_id
,
546 views::ImageButton
* button
= new views::ImageButton(this);
547 ui::ThemeProvider
* tp
= frame()->GetThemeProvider();
548 button
->SetImage(views::CustomButton::STATE_NORMAL
,
549 tp
->GetImageSkiaNamed(normal_image_id
));
550 button
->SetImage(views::CustomButton::STATE_HOVERED
,
551 tp
->GetImageSkiaNamed(hot_image_id
));
552 button
->SetImage(views::CustomButton::STATE_PRESSED
,
553 tp
->GetImageSkiaNamed(pushed_image_id
));
554 if (browser_view()->IsBrowserTypeNormal()) {
555 button
->SetBackground(
556 tp
->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND
),
557 tp
->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND
),
558 tp
->GetImageSkiaNamed(mask_image_id
));
560 button
->SetAccessibleName(
561 l10n_util::GetStringUTF16(accessibility_string_id
));
562 button
->set_id(view_id
);
563 AddChildView(button
);
567 int OpaqueBrowserFrameView::FrameBorderThickness(bool restored
) const {
568 return layout_
->FrameBorderThickness(restored
);
571 int OpaqueBrowserFrameView::TopResizeHeight() const {
572 return FrameBorderThickness(false) - kTopResizeAdjust
;
575 int OpaqueBrowserFrameView::NonClientBorderThickness() const {
576 return layout_
->NonClientBorderThickness();
579 gfx::Rect
OpaqueBrowserFrameView::IconBounds() const {
580 return layout_
->IconBounds();
583 bool OpaqueBrowserFrameView::ShouldShowWindowTitleBar() const {
584 // Do not show the custom title bar if the system title bar option is enabled.
585 if (!frame()->UseCustomFrame())
588 // Do not show caption buttons if the window manager is forcefully providing a
589 // title bar (e.g., in Ubuntu Unity, if the window is maximized).
590 if (!views::ViewsDelegate::views_delegate
)
592 return !views::ViewsDelegate::views_delegate
->WindowManagerProvidesTitleBar(
596 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas
* canvas
) {
597 frame_background_
->set_frame_color(GetFrameColor());
598 frame_background_
->set_theme_image(GetFrameImage());
599 frame_background_
->set_theme_overlay_image(GetFrameOverlayImage());
600 frame_background_
->set_top_area_height(GetTopAreaHeight());
602 ui::ThemeProvider
* tp
= GetThemeProvider();
603 frame_background_
->SetSideImages(
604 tp
->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE
),
605 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER
),
606 tp
->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE
),
607 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER
));
608 frame_background_
->SetCornerImages(
609 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER
),
610 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER
),
611 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER
),
612 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER
));
613 frame_background_
->PaintRestored(canvas
, this);
615 // Note: When we don't have a toolbar, we need to draw some kind of bottom
616 // edge here. Because the App Window graphics we use for this have an
617 // attached client edge and their sizing algorithm is a little involved, we do
618 // all this in PaintRestoredClientEdge().
621 void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas
* canvas
) {
622 ui::ThemeProvider
* tp
= GetThemeProvider();
623 frame_background_
->set_frame_color(GetFrameColor());
624 frame_background_
->set_theme_image(GetFrameImage());
625 frame_background_
->set_theme_overlay_image(GetFrameOverlayImage());
626 frame_background_
->set_top_area_height(GetTopAreaHeight());
627 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
628 // The window manager typically shows a gradient in the native title bar (when
629 // the system title bar pref is set, or when maximized on Ubuntu). Hide the
630 // gradient in the tab strip (by shifting it up vertically) to avoid a
631 // double-gradient effect.
632 if (tp
->UsingSystemTheme())
633 frame_background_
->set_maximized_top_inset(kGTKThemeCondensedFrameTopInset
);
636 frame_background_
->PaintMaximized(canvas
, this);
638 // TODO(jamescook): Migrate this into FrameBackground.
639 if (!browser_view()->IsToolbarVisible()) {
640 // There's no toolbar to edge the frame border, so we need to draw a bottom
641 // edge. The graphic we use for this has a built in client edge, so we clip
642 // it off the bottom.
643 gfx::ImageSkia
* top_center
= tp
->GetImageSkiaNamed(IDR_APP_TOP_CENTER
);
644 int edge_height
= top_center
->height() - kClientEdgeThickness
;
645 canvas
->TileImageInt(*top_center
, 0,
646 frame()->client_view()->y() - edge_height
, width(), edge_height
);
650 void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas
* canvas
) {
651 gfx::Rect
toolbar_bounds(browser_view()->GetToolbarBounds());
652 if (toolbar_bounds
.IsEmpty())
654 gfx::Point
toolbar_origin(toolbar_bounds
.origin());
655 ConvertPointToTarget(browser_view(), this, &toolbar_origin
);
656 toolbar_bounds
.set_origin(toolbar_origin
);
658 int x
= toolbar_bounds
.x();
659 int w
= toolbar_bounds
.width();
660 int y
= toolbar_bounds
.y();
661 int h
= toolbar_bounds
.height();
663 // Gross hack: We split the toolbar images into two pieces, since sometimes
664 // (popup mode) the toolbar isn't tall enough to show the whole image. The
665 // split happens between the top shadow section and the bottom gradient
666 // section so that we never break the gradient.
667 int split_point
= kFrameShadowThickness
* 2;
668 int bottom_y
= y
+ split_point
;
669 ui::ThemeProvider
* tp
= GetThemeProvider();
670 gfx::ImageSkia
* toolbar_left
= tp
->GetImageSkiaNamed(
671 IDR_CONTENT_TOP_LEFT_CORNER
);
672 int bottom_edge_height
= std::min(toolbar_left
->height(), h
) - split_point
;
674 // Split our canvas out so we can mask out the corners of the toolbar
675 // without masking out the frame.
676 canvas
->SaveLayerAlpha(
677 255, gfx::Rect(x
- kClientEdgeThickness
, y
, w
+ kClientEdgeThickness
* 3,
680 // Paint the bottom rect.
681 canvas
->FillRect(gfx::Rect(x
, bottom_y
, w
, bottom_edge_height
),
682 tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
));
684 // Tile the toolbar image starting at the frame edge on the left and where the
685 // horizontal tabstrip is (or would be) on the top.
686 gfx::ImageSkia
* theme_toolbar
= tp
->GetImageSkiaNamed(IDR_THEME_TOOLBAR
);
687 canvas
->TileImageInt(*theme_toolbar
,
688 x
+ GetThemeBackgroundXInset(),
689 bottom_y
- GetTopInset(),
690 x
, bottom_y
, w
, theme_toolbar
->height());
692 // Draw rounded corners for the tab.
693 gfx::ImageSkia
* toolbar_left_mask
=
694 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK
);
695 gfx::ImageSkia
* toolbar_right_mask
=
696 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK
);
698 // We mask out the corners by using the DestinationIn transfer mode,
699 // which keeps the RGB pixels from the destination and the alpha from
702 paint
.setXfermodeMode(SkXfermode::kDstIn_Mode
);
704 // Mask the left edge.
705 int left_x
= x
- kContentEdgeShadowThickness
;
706 canvas
->DrawImageInt(*toolbar_left_mask
, 0, 0, toolbar_left_mask
->width(),
707 split_point
, left_x
, y
, toolbar_left_mask
->width(),
708 split_point
, false, paint
);
709 canvas
->DrawImageInt(*toolbar_left_mask
, 0,
710 toolbar_left_mask
->height() - bottom_edge_height
,
711 toolbar_left_mask
->width(), bottom_edge_height
, left_x
, bottom_y
,
712 toolbar_left_mask
->width(), bottom_edge_height
, false, paint
);
714 // Mask the right edge.
716 x
+ w
- toolbar_right_mask
->width() + kContentEdgeShadowThickness
;
717 canvas
->DrawImageInt(*toolbar_right_mask
, 0, 0, toolbar_right_mask
->width(),
718 split_point
, right_x
, y
, toolbar_right_mask
->width(),
719 split_point
, false, paint
);
720 canvas
->DrawImageInt(*toolbar_right_mask
, 0,
721 toolbar_right_mask
->height() - bottom_edge_height
,
722 toolbar_right_mask
->width(), bottom_edge_height
, right_x
, bottom_y
,
723 toolbar_right_mask
->width(), bottom_edge_height
, false, paint
);
726 canvas
->DrawImageInt(*toolbar_left
, 0, 0, toolbar_left
->width(), split_point
,
727 left_x
, y
, toolbar_left
->width(), split_point
, false);
728 canvas
->DrawImageInt(*toolbar_left
, 0,
729 toolbar_left
->height() - bottom_edge_height
, toolbar_left
->width(),
730 bottom_edge_height
, left_x
, bottom_y
, toolbar_left
->width(),
731 bottom_edge_height
, false);
733 gfx::ImageSkia
* toolbar_center
=
734 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER
);
735 canvas
->TileImageInt(*toolbar_center
, 0, 0, left_x
+ toolbar_left
->width(),
736 y
, right_x
- (left_x
+ toolbar_left
->width()),
739 gfx::ImageSkia
* toolbar_right
= tp
->GetImageSkiaNamed(
740 IDR_CONTENT_TOP_RIGHT_CORNER
);
741 canvas
->DrawImageInt(*toolbar_right
, 0, 0, toolbar_right
->width(),
742 split_point
, right_x
, y
, toolbar_right
->width(), split_point
, false);
743 canvas
->DrawImageInt(*toolbar_right
, 0,
744 toolbar_right
->height() - bottom_edge_height
, toolbar_right
->width(),
745 bottom_edge_height
, right_x
, bottom_y
, toolbar_right
->width(),
746 bottom_edge_height
, false);
748 // Draw the content/toolbar separator.
750 gfx::Rect(x
+ kClientEdgeThickness
,
751 toolbar_bounds
.bottom() - kClientEdgeThickness
,
752 w
- (2 * kClientEdgeThickness
),
753 kClientEdgeThickness
),
754 ThemeProperties::GetDefaultColor(
755 ThemeProperties::COLOR_TOOLBAR_SEPARATOR
));
758 void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas
* canvas
) {
759 ui::ThemeProvider
* tp
= GetThemeProvider();
760 int client_area_top
= frame()->client_view()->y();
761 int image_top
= client_area_top
;
763 gfx::Rect client_area_bounds
=
764 layout_
->CalculateClientAreaBounds(width(), height());
765 SkColor toolbar_color
= tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
);
767 if (browser_view()->IsToolbarVisible()) {
768 // The client edge images always start below the toolbar corner images. The
769 // client edge filled rects start there or at the bottom of the toolbar,
770 // whichever is shorter.
771 gfx::Rect
toolbar_bounds(browser_view()->GetToolbarBounds());
773 gfx::ImageSkia
* content_top_left_corner
=
774 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER
);
775 // TODO(oshima): Sanity checks for crbug.com/374273. Remove when it's fixed.
776 CHECK(content_top_left_corner
);
777 CHECK(!content_top_left_corner
->isNull());
779 image_top
+= toolbar_bounds
.y() + content_top_left_corner
->height();
780 client_area_top
= std::min(image_top
,
781 client_area_top
+ toolbar_bounds
.bottom() - kClientEdgeThickness
);
782 } else if (!browser_view()->IsTabStripVisible()) {
783 // The toolbar isn't going to draw a client edge for us, so draw one
785 gfx::ImageSkia
* top_left
= tp
->GetImageSkiaNamed(IDR_APP_TOP_LEFT
);
786 gfx::ImageSkia
* top_center
= tp
->GetImageSkiaNamed(IDR_APP_TOP_CENTER
);
787 gfx::ImageSkia
* top_right
= tp
->GetImageSkiaNamed(IDR_APP_TOP_RIGHT
);
788 int top_edge_y
= client_area_top
- top_center
->height();
789 int height
= client_area_top
- top_edge_y
;
791 canvas
->DrawImageInt(*top_left
, 0, 0, top_left
->width(), height
,
792 client_area_bounds
.x() - top_left
->width(), top_edge_y
,
793 top_left
->width(), height
, false);
794 canvas
->TileImageInt(*top_center
, 0, 0, client_area_bounds
.x(), top_edge_y
,
795 client_area_bounds
.width(), std::min(height
, top_center
->height()));
796 canvas
->DrawImageInt(*top_right
, 0, 0, top_right
->width(), height
,
797 client_area_bounds
.right(), top_edge_y
,
798 top_right
->width(), height
, false);
800 // Draw the toolbar color across the top edge.
801 canvas
->FillRect(gfx::Rect(client_area_bounds
.x() - kClientEdgeThickness
,
802 client_area_top
- kClientEdgeThickness
,
803 client_area_bounds
.width() + (2 * kClientEdgeThickness
),
804 kClientEdgeThickness
), toolbar_color
);
807 int client_area_bottom
=
808 std::max(client_area_top
, height() - NonClientBorderThickness());
809 int image_height
= client_area_bottom
- image_top
;
811 // Draw the client edge images.
812 gfx::ImageSkia
* right
= tp
->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE
);
813 canvas
->TileImageInt(*right
, client_area_bounds
.right(), image_top
,
814 right
->width(), image_height
);
815 canvas
->DrawImageInt(
816 *tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER
),
817 client_area_bounds
.right(), client_area_bottom
);
818 gfx::ImageSkia
* bottom
= tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER
);
819 canvas
->TileImageInt(*bottom
, client_area_bounds
.x(),
820 client_area_bottom
, client_area_bounds
.width(),
822 gfx::ImageSkia
* bottom_left
=
823 tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER
);
824 canvas
->DrawImageInt(*bottom_left
,
825 client_area_bounds
.x() - bottom_left
->width(), client_area_bottom
);
826 gfx::ImageSkia
* left
= tp
->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE
);
827 canvas
->TileImageInt(*left
, client_area_bounds
.x() - left
->width(),
828 image_top
, left
->width(), image_height
);
830 // Draw the toolbar color so that the client edges show the right color even
831 // where not covered by the toolbar image. NOTE: We do this after drawing the
832 // images because the images are meant to alpha-blend atop the frame whereas
833 // these rects are meant to be fully opaque, without anything overlaid.
834 canvas
->FillRect(gfx::Rect(client_area_bounds
.x() - kClientEdgeThickness
,
835 client_area_top
, kClientEdgeThickness
,
836 client_area_bottom
+ kClientEdgeThickness
- client_area_top
),
838 canvas
->FillRect(gfx::Rect(client_area_bounds
.x(), client_area_bottom
,
839 client_area_bounds
.width(), kClientEdgeThickness
),
841 canvas
->FillRect(gfx::Rect(client_area_bounds
.right(), client_area_top
,
842 kClientEdgeThickness
,
843 client_area_bottom
+ kClientEdgeThickness
- client_area_top
),