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/strings/utf_string_conversions.h"
13 #include "chrome/browser/chrome_notification_types.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_label.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/theme_image_mapper.h"
26 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
27 #include "chrome/common/pref_names.h"
28 #include "components/signin/core/common/profile_management_switches.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/web_contents.h"
31 #include "grit/chromium_strings.h"
32 #include "grit/generated_resources.h"
33 #include "grit/theme_resources.h"
34 #include "grit/ui_resources.h"
35 #include "ui/accessibility/ax_view_state.h"
36 #include "ui/base/hit_test.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "ui/base/resource/resource_bundle.h"
39 #include "ui/base/theme_provider.h"
40 #include "ui/gfx/canvas.h"
41 #include "ui/gfx/font_list.h"
42 #include "ui/gfx/image/image.h"
43 #include "ui/gfx/image/image_skia.h"
44 #include "ui/gfx/path.h"
45 #include "ui/gfx/rect_conversions.h"
46 #include "ui/views/controls/button/image_button.h"
47 #include "ui/views/controls/image_view.h"
48 #include "ui/views/controls/label.h"
49 #include "ui/views/layout/layout_constants.h"
50 #include "ui/views/views_delegate.h"
51 #include "ui/views/widget/root_view.h"
52 #include "ui/views/window/frame_background.h"
53 #include "ui/views/window/window_shape.h"
56 #include "ui/views/controls/menu/menu_runner.h"
59 using content::WebContents
;
63 // While resize areas on Windows are normally the same size as the window
64 // borders, our top area is shrunk by 1 px to make it easier to move the window
65 // around with our thinner top grabbable strip. (Incidentally, our side and
66 // bottom resize areas don't match the frame border thickness either -- they
67 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
68 const int kTopResizeAdjust
= 1;
70 // In the window corners, the resize areas don't actually expand bigger, but the
71 // 16 px at the end of each edge triggers diagonal resizing.
72 const int kResizeAreaCornerSize
= 16;
74 // The content left/right images have a shadow built into them.
75 const int kContentEdgeShadowThickness
= 2;
77 // The icon never shrinks below 16 px on a side.
78 const int kIconMinimumSize
= 16;
80 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
81 // The number of pixels to move the frame background image upwards when using
82 // the GTK+ theme and the titlebar is condensed.
83 const int kGTKThemeCondensedFrameTopInset
= 15;
88 ///////////////////////////////////////////////////////////////////////////////
89 // OpaqueBrowserFrameView, public:
91 OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame
* frame
,
92 BrowserView
* browser_view
)
93 : BrowserNonClientFrameView(frame
, browser_view
),
94 layout_(new OpaqueBrowserFrameViewLayout(this)),
95 minimize_button_(NULL
),
96 maximize_button_(NULL
),
97 restore_button_(NULL
),
101 frame_background_(new views::FrameBackground()) {
102 SetLayoutManager(layout_
);
104 minimize_button_
= InitWindowCaptionButton(IDR_MINIMIZE
,
107 IDR_MINIMIZE_BUTTON_MASK
,
108 IDS_ACCNAME_MINIMIZE
,
109 VIEW_ID_MINIMIZE_BUTTON
);
110 maximize_button_
= InitWindowCaptionButton(IDR_MAXIMIZE
,
113 IDR_MAXIMIZE_BUTTON_MASK
,
114 IDS_ACCNAME_MAXIMIZE
,
115 VIEW_ID_MAXIMIZE_BUTTON
);
116 restore_button_
= InitWindowCaptionButton(IDR_RESTORE
,
119 IDR_RESTORE_BUTTON_MASK
,
121 VIEW_ID_RESTORE_BUTTON
);
122 close_button_
= InitWindowCaptionButton(IDR_CLOSE
,
125 IDR_CLOSE_BUTTON_MASK
,
127 VIEW_ID_CLOSE_BUTTON
);
129 // Initializing the TabIconView is expensive, so only do it if we need to.
130 if (browser_view
->ShouldShowWindowIcon()) {
131 window_icon_
= new TabIconView(this, this);
132 window_icon_
->set_is_light(true);
133 window_icon_
->set_id(VIEW_ID_WINDOW_ICON
);
134 AddChildView(window_icon_
);
135 window_icon_
->Update();
138 window_title_
= new views::Label(
139 browser_view
->GetWindowTitle(),
140 gfx::FontList(BrowserFrame::GetTitleFontList()));
141 window_title_
->SetVisible(browser_view
->ShouldShowWindowTitle());
142 window_title_
->SetEnabledColor(SK_ColorWHITE
);
143 // TODO(msw): Use a transparent background color as a workaround to use the
144 // gfx::Canvas::NO_SUBPIXEL_RENDERING flag and avoid some visual artifacts.
145 window_title_
->SetBackgroundColor(0x00000000);
146 window_title_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
147 window_title_
->set_id(VIEW_ID_WINDOW_TITLE
);
148 AddChildView(window_title_
);
150 if (browser_view
->IsRegularOrGuestSession() && switches::IsNewAvatarMenu())
151 UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON
);
155 if (!browser_view
->IsOffTheRecord()) {
156 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED
,
157 content::NotificationService::AllSources());
160 platform_observer_
.reset(OpaqueBrowserFrameViewPlatformSpecific::Create(
161 this, layout_
, browser_view
->browser()->profile()));
164 OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
167 ///////////////////////////////////////////////////////////////////////////////
168 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
170 gfx::Rect
OpaqueBrowserFrameView::GetBoundsForTabStrip(
171 views::View
* tabstrip
) const {
175 return layout_
->GetBoundsForTabStrip(tabstrip
->GetPreferredSize(), width());
178 int OpaqueBrowserFrameView::GetTopInset() const {
179 return browser_view()->IsTabStripVisible() ?
180 layout_
->GetTabStripInsetsTop(false) :
181 layout_
->NonClientTopBorderHeight(false);
184 int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const {
188 void OpaqueBrowserFrameView::UpdateThrobber(bool running
) {
190 window_icon_
->Update();
193 gfx::Size
OpaqueBrowserFrameView::GetMinimumSize() const {
194 return layout_
->GetMinimumSize(width());
197 ///////////////////////////////////////////////////////////////////////////////
198 // OpaqueBrowserFrameView, views::NonClientFrameView implementation:
200 gfx::Rect
OpaqueBrowserFrameView::GetBoundsForClientView() const {
201 return layout_
->client_view_bounds();
204 gfx::Rect
OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
205 const gfx::Rect
& client_bounds
) const {
206 return layout_
->GetWindowBoundsForClientBounds(client_bounds
);
209 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point
& point
) {
210 if (!bounds().Contains(point
))
213 // See if the point is within the avatar menu button or within the avatar
215 if ((avatar_button() &&
216 avatar_button()->GetMirroredBounds().Contains(point
)) ||
217 (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point
)) ||
218 (new_avatar_button() &&
219 new_avatar_button()->GetMirroredBounds().Contains(point
)))
222 int frame_component
= frame()->client_view()->NonClientHitTest(point
);
224 // See if we're in the sysmenu region. We still have to check the tabstrip
225 // first so that clicks in a tab don't get treated as sysmenu clicks.
226 gfx::Rect
sysmenu_rect(IconBounds());
227 // In maximized mode we extend the rect to the screen corner to take advantage
229 if (layout_
->IsTitleBarCondensed())
230 sysmenu_rect
.SetRect(0, 0, sysmenu_rect
.right(), sysmenu_rect
.bottom());
231 sysmenu_rect
.set_x(GetMirroredXForRect(sysmenu_rect
));
232 if (sysmenu_rect
.Contains(point
))
233 return (frame_component
== HTCLIENT
) ? HTCLIENT
: HTSYSMENU
;
235 if (frame_component
!= HTNOWHERE
)
236 return frame_component
;
238 // Then see if the point is within any of the window controls.
239 if (close_button_
&& close_button_
->visible() &&
240 close_button_
->GetMirroredBounds().Contains(point
))
242 if (restore_button_
&& restore_button_
->visible() &&
243 restore_button_
->GetMirroredBounds().Contains(point
))
245 if (maximize_button_
&& maximize_button_
->visible() &&
246 maximize_button_
->GetMirroredBounds().Contains(point
))
248 if (minimize_button_
&& minimize_button_
->visible() &&
249 minimize_button_
->GetMirroredBounds().Contains(point
))
252 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
254 LOG(WARNING
) << "delegate is NULL, returning safe default.";
257 int window_component
= GetHTComponentForFrame(point
, TopResizeHeight(),
258 NonClientBorderThickness(), kResizeAreaCornerSize
, kResizeAreaCornerSize
,
259 delegate
->CanResize());
260 // Fall back to the caption if no other component matches.
261 return (window_component
== HTNOWHERE
) ? HTCAPTION
: window_component
;
264 void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size
& size
,
265 gfx::Path
* window_mask
) {
268 if (layout_
->IsTitleBarCondensed() || frame()->IsFullscreen())
271 views::GetDefaultWindowMask(size
, window_mask
);
274 void OpaqueBrowserFrameView::ResetWindowControls() {
275 restore_button_
->SetState(views::CustomButton::STATE_NORMAL
);
276 minimize_button_
->SetState(views::CustomButton::STATE_NORMAL
);
277 maximize_button_
->SetState(views::CustomButton::STATE_NORMAL
);
278 // The close button isn't affected by this constraint.
281 void OpaqueBrowserFrameView::UpdateWindowIcon() {
282 window_icon_
->SchedulePaint();
285 void OpaqueBrowserFrameView::UpdateWindowTitle() {
286 if (!frame()->IsFullscreen())
287 window_title_
->SchedulePaint();
290 ///////////////////////////////////////////////////////////////////////////////
291 // OpaqueBrowserFrameView, views::View overrides:
293 bool OpaqueBrowserFrameView::HitTestRect(const gfx::Rect
& rect
) const {
294 if (!views::View::HitTestRect(rect
)) {
295 // |rect| is outside OpaqueBrowserFrameView's bounds.
299 // If the rect is outside the bounds of the client area, claim it.
300 gfx::RectF
rect_in_client_view_coords_f(rect
);
301 View::ConvertRectToTarget(this, frame()->client_view(),
302 &rect_in_client_view_coords_f
);
303 gfx::Rect rect_in_client_view_coords
= gfx::ToEnclosingRect(
304 rect_in_client_view_coords_f
);
305 if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords
))
308 // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in
309 // a non-tab portion.
310 TabStrip
* tabstrip
= browser_view()->tabstrip();
311 if (!tabstrip
|| !browser_view()->IsTabStripVisible())
314 gfx::RectF
rect_in_tabstrip_coords_f(rect
);
315 View::ConvertRectToTarget(this, tabstrip
, &rect_in_tabstrip_coords_f
);
316 gfx::Rect rect_in_tabstrip_coords
= gfx::ToEnclosingRect(
317 rect_in_tabstrip_coords_f
);
318 if (rect_in_tabstrip_coords
.bottom() > tabstrip
->GetLocalBounds().bottom()) {
319 // |rect| is below the tabstrip.
323 if (tabstrip
->HitTestRect(rect_in_tabstrip_coords
)) {
324 // Claim |rect| if it is in a non-tab portion of the tabstrip.
325 return tabstrip
->IsRectInWindowCaption(rect_in_tabstrip_coords
);
328 // We claim |rect| because it is above the bottom of the tabstrip, but
329 // not in the tabstrip itself. In particular, the avatar label/button is left
330 // of the tabstrip and the window controls are right of the tabstrip.
334 void OpaqueBrowserFrameView::GetAccessibleState(
335 ui::AXViewState
* state
) {
336 state
->role
= ui::AX_ROLE_TITLE_BAR
;
339 ///////////////////////////////////////////////////////////////////////////////
340 // OpaqueBrowserFrameView, views::ButtonListener implementation:
342 void OpaqueBrowserFrameView::ButtonPressed(views::Button
* sender
,
343 const ui::Event
& event
) {
344 if (sender
== minimize_button_
) {
346 } else if (sender
== maximize_button_
) {
348 } else if (sender
== restore_button_
) {
350 } else if (sender
== close_button_
) {
352 } else if (sender
== new_avatar_button()) {
353 browser_view()->ShowAvatarBubbleFromAvatarButton(
354 BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT
);
358 void OpaqueBrowserFrameView::OnMenuButtonClicked(views::View
* source
,
359 const gfx::Point
& point
) {
360 #if defined(OS_LINUX)
361 views::MenuRunner
menu_runner(frame()->GetSystemMenuModel());
362 ignore_result(menu_runner
.RunMenuAt(browser_view()->GetWidget(),
364 window_icon_
->GetBoundsInScreen(),
365 views::MENU_ANCHOR_TOPLEFT
,
366 ui::MENU_SOURCE_MOUSE
,
367 views::MenuRunner::HAS_MNEMONICS
));
371 ///////////////////////////////////////////////////////////////////////////////
372 // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
374 bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
375 // This function is queried during the creation of the window as the
376 // TabIconView we host is initialized, so we need to NULL check the selected
377 // WebContents because in this condition there is not yet a selected tab.
378 WebContents
* current_tab
= browser_view()->GetActiveWebContents();
379 return current_tab
? current_tab
->IsLoading() : false;
382 gfx::ImageSkia
OpaqueBrowserFrameView::GetFaviconForTabIconView() {
383 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
385 LOG(WARNING
) << "delegate is NULL, returning safe default.";
386 return gfx::ImageSkia();
388 return delegate
->GetWindowIcon();
391 ///////////////////////////////////////////////////////////////////////////////
392 // OpaqueBrowserFrameView, protected:
394 void OpaqueBrowserFrameView::Observe(
396 const content::NotificationSource
& source
,
397 const content::NotificationDetails
& details
) {
399 case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED
:
400 if (browser_view() ->IsRegularOrGuestSession() &&
401 switches::IsNewAvatarMenu()) {
402 UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON
);
408 NOTREACHED() << "Got a notification we didn't register for!";
413 ///////////////////////////////////////////////////////////////////////////////
414 // OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation:
416 bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const {
417 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
418 return ShouldShowWindowTitleBar() && delegate
&&
419 delegate
->ShouldShowWindowIcon();
422 bool OpaqueBrowserFrameView::ShouldShowWindowTitle() const {
423 // |delegate| may be NULL if called from callback of InputMethodChanged while
424 // a window is being destroyed.
425 // See more discussion at http://crosbug.com/8958
426 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
427 return ShouldShowWindowTitleBar() && delegate
&&
428 delegate
->ShouldShowWindowTitle();
431 base::string16
OpaqueBrowserFrameView::GetWindowTitle() const {
432 return frame()->widget_delegate()->GetWindowTitle();
435 int OpaqueBrowserFrameView::GetIconSize() const {
437 // This metric scales up if either the titlebar height or the titlebar font
438 // size are increased.
439 return GetSystemMetrics(SM_CYSMICON
);
441 return std::max(BrowserFrame::GetTitleFontList().GetHeight(),
446 bool OpaqueBrowserFrameView::ShouldLeaveOffsetNearTopBorder() const {
447 return frame()->ShouldLeaveOffsetNearTopBorder();
450 gfx::Size
OpaqueBrowserFrameView::GetBrowserViewMinimumSize() const {
451 return browser_view()->GetMinimumSize();
454 bool OpaqueBrowserFrameView::ShouldShowCaptionButtons() const {
455 return ShouldShowWindowTitleBar();
458 bool OpaqueBrowserFrameView::ShouldShowAvatar() const {
459 return browser_view()->ShouldShowAvatar();
462 bool OpaqueBrowserFrameView::IsRegularOrGuestSession() const {
463 return browser_view()->IsRegularOrGuestSession();
466 gfx::ImageSkia
OpaqueBrowserFrameView::GetOTRAvatarIcon() const {
467 return browser_view()->GetOTRAvatarIcon();
470 bool OpaqueBrowserFrameView::IsMaximized() const {
471 return frame()->IsMaximized();
474 bool OpaqueBrowserFrameView::IsMinimized() const {
475 return frame()->IsMinimized();
478 bool OpaqueBrowserFrameView::IsFullscreen() const {
479 return frame()->IsFullscreen();
482 bool OpaqueBrowserFrameView::IsTabStripVisible() const {
483 return browser_view()->IsTabStripVisible();
486 int OpaqueBrowserFrameView::GetTabStripHeight() const {
487 return browser_view()->GetTabStripHeight();
490 gfx::Size
OpaqueBrowserFrameView::GetTabstripPreferredSize() const {
491 gfx::Size s
= browser_view()->tabstrip()->GetPreferredSize();
495 ///////////////////////////////////////////////////////////////////////////////
496 // OpaqueBrowserFrameView, views::View overrides:
498 void OpaqueBrowserFrameView::OnPaint(gfx::Canvas
* canvas
) {
499 if (frame()->IsFullscreen())
500 return; // Nothing is visible, so don't bother to paint.
502 if (layout_
->IsTitleBarCondensed())
503 PaintMaximizedFrameBorder(canvas
);
505 PaintRestoredFrameBorder(canvas
);
507 // The window icon and title are painted by their respective views.
508 /* TODO(pkasting): If this window is active, we should also draw a drop
509 * shadow on the title. This is tricky, because we don't want to hardcode a
510 * shadow color (since we want to work with various themes), but we can't
511 * alpha-blend either (since the Windows text APIs don't really do this).
512 * So we'd need to sample the background color at the right location and
513 * synthesize a good shadow color. */
515 if (browser_view()->IsToolbarVisible())
516 PaintToolbarBackground(canvas
);
517 if (!layout_
->IsTitleBarCondensed())
518 PaintRestoredClientEdge(canvas
);
521 ///////////////////////////////////////////////////////////////////////////////
522 // OpaqueBrowserFrameView, private:
524 views::ImageButton
* OpaqueBrowserFrameView::InitWindowCaptionButton(
529 int accessibility_string_id
,
531 views::ImageButton
* button
= new views::ImageButton(this);
532 ui::ThemeProvider
* tp
= frame()->GetThemeProvider();
533 button
->SetImage(views::CustomButton::STATE_NORMAL
,
534 tp
->GetImageSkiaNamed(normal_image_id
));
535 button
->SetImage(views::CustomButton::STATE_HOVERED
,
536 tp
->GetImageSkiaNamed(hot_image_id
));
537 button
->SetImage(views::CustomButton::STATE_PRESSED
,
538 tp
->GetImageSkiaNamed(pushed_image_id
));
539 if (browser_view()->IsBrowserTypeNormal()) {
540 button
->SetBackground(
541 tp
->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND
),
542 tp
->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND
),
543 tp
->GetImageSkiaNamed(mask_image_id
));
545 button
->SetAccessibleName(
546 l10n_util::GetStringUTF16(accessibility_string_id
));
547 button
->set_id(view_id
);
548 AddChildView(button
);
552 int OpaqueBrowserFrameView::FrameBorderThickness(bool restored
) const {
553 return layout_
->FrameBorderThickness(restored
);
556 int OpaqueBrowserFrameView::TopResizeHeight() const {
557 return FrameBorderThickness(false) - kTopResizeAdjust
;
560 int OpaqueBrowserFrameView::NonClientBorderThickness() const {
561 return layout_
->NonClientBorderThickness();
564 gfx::Rect
OpaqueBrowserFrameView::IconBounds() const {
565 return layout_
->IconBounds();
568 bool OpaqueBrowserFrameView::ShouldShowWindowTitleBar() const {
569 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
570 // Do not show the custom title bar if the system title bar option is enabled.
571 if (!frame()->UseCustomFrame())
575 // Do not show caption buttons if the window manager is forcefully providing a
576 // title bar (e.g., in Ubuntu Unity, if the window is maximized).
577 if (!views::ViewsDelegate::views_delegate
)
579 return !views::ViewsDelegate::views_delegate
->WindowManagerProvidesTitleBar(
583 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas
* canvas
) {
584 frame_background_
->set_frame_color(GetFrameColor());
585 frame_background_
->set_theme_image(GetFrameImage());
586 frame_background_
->set_theme_overlay_image(GetFrameOverlayImage());
587 frame_background_
->set_top_area_height(GetTopAreaHeight());
589 ui::ThemeProvider
* tp
= GetThemeProvider();
590 frame_background_
->SetSideImages(
591 tp
->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE
),
592 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER
),
593 tp
->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE
),
594 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER
));
595 frame_background_
->SetCornerImages(
596 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER
),
597 tp
->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER
),
598 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER
),
599 tp
->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER
));
600 frame_background_
->PaintRestored(canvas
, this);
602 // Note: When we don't have a toolbar, we need to draw some kind of bottom
603 // edge here. Because the App Window graphics we use for this have an
604 // attached client edge and their sizing algorithm is a little involved, we do
605 // all this in PaintRestoredClientEdge().
608 void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas
* canvas
) {
609 ui::ThemeProvider
* tp
= GetThemeProvider();
610 frame_background_
->set_frame_color(GetFrameColor());
611 frame_background_
->set_theme_image(GetFrameImage());
612 frame_background_
->set_theme_overlay_image(GetFrameOverlayImage());
613 frame_background_
->set_top_area_height(GetTopAreaHeight());
614 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
615 // The window manager typically shows a gradient in the native title bar (when
616 // the system title bar pref is set, or when maximized on Ubuntu). Hide the
617 // gradient in the tab strip (by shifting it up vertically) to avoid a
618 // double-gradient effect.
619 if (tp
->UsingSystemTheme())
620 frame_background_
->set_maximized_top_inset(kGTKThemeCondensedFrameTopInset
);
623 frame_background_
->PaintMaximized(canvas
, this);
625 // TODO(jamescook): Migrate this into FrameBackground.
626 if (!browser_view()->IsToolbarVisible()) {
627 // There's no toolbar to edge the frame border, so we need to draw a bottom
628 // edge. The graphic we use for this has a built in client edge, so we clip
629 // it off the bottom.
630 gfx::ImageSkia
* top_center
= tp
->GetImageSkiaNamed(IDR_APP_TOP_CENTER
);
631 int edge_height
= top_center
->height() - kClientEdgeThickness
;
632 canvas
->TileImageInt(*top_center
, 0,
633 frame()->client_view()->y() - edge_height
, width(), edge_height
);
637 void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas
* canvas
) {
638 gfx::Rect
toolbar_bounds(browser_view()->GetToolbarBounds());
639 if (toolbar_bounds
.IsEmpty())
641 gfx::Point
toolbar_origin(toolbar_bounds
.origin());
642 ConvertPointToTarget(browser_view(), this, &toolbar_origin
);
643 toolbar_bounds
.set_origin(toolbar_origin
);
645 int x
= toolbar_bounds
.x();
646 int w
= toolbar_bounds
.width();
647 int y
= toolbar_bounds
.y();
648 int h
= toolbar_bounds
.height();
650 // Gross hack: We split the toolbar images into two pieces, since sometimes
651 // (popup mode) the toolbar isn't tall enough to show the whole image. The
652 // split happens between the top shadow section and the bottom gradient
653 // section so that we never break the gradient.
654 int split_point
= kFrameShadowThickness
* 2;
655 int bottom_y
= y
+ split_point
;
656 ui::ThemeProvider
* tp
= GetThemeProvider();
657 gfx::ImageSkia
* toolbar_left
= tp
->GetImageSkiaNamed(
658 IDR_CONTENT_TOP_LEFT_CORNER
);
659 int bottom_edge_height
= std::min(toolbar_left
->height(), h
) - split_point
;
661 // Split our canvas out so we can mask out the corners of the toolbar
662 // without masking out the frame.
663 canvas
->SaveLayerAlpha(
664 255, gfx::Rect(x
- kClientEdgeThickness
, y
, w
+ kClientEdgeThickness
* 3,
667 // Paint the bottom rect.
668 canvas
->FillRect(gfx::Rect(x
, bottom_y
, w
, bottom_edge_height
),
669 tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
));
671 // Tile the toolbar image starting at the frame edge on the left and where the
672 // horizontal tabstrip is (or would be) on the top.
673 gfx::ImageSkia
* theme_toolbar
= tp
->GetImageSkiaNamed(IDR_THEME_TOOLBAR
);
674 canvas
->TileImageInt(*theme_toolbar
,
675 x
+ GetThemeBackgroundXInset(),
676 bottom_y
- GetTopInset(),
677 x
, bottom_y
, w
, theme_toolbar
->height());
679 // Draw rounded corners for the tab.
680 gfx::ImageSkia
* toolbar_left_mask
=
681 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK
);
682 gfx::ImageSkia
* toolbar_right_mask
=
683 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK
);
685 // We mask out the corners by using the DestinationIn transfer mode,
686 // which keeps the RGB pixels from the destination and the alpha from
689 paint
.setXfermodeMode(SkXfermode::kDstIn_Mode
);
691 // Mask the left edge.
692 int left_x
= x
- kContentEdgeShadowThickness
;
693 canvas
->DrawImageInt(*toolbar_left_mask
, 0, 0, toolbar_left_mask
->width(),
694 split_point
, left_x
, y
, toolbar_left_mask
->width(),
695 split_point
, false, paint
);
696 canvas
->DrawImageInt(*toolbar_left_mask
, 0,
697 toolbar_left_mask
->height() - bottom_edge_height
,
698 toolbar_left_mask
->width(), bottom_edge_height
, left_x
, bottom_y
,
699 toolbar_left_mask
->width(), bottom_edge_height
, false, paint
);
701 // Mask the right edge.
703 x
+ w
- toolbar_right_mask
->width() + kContentEdgeShadowThickness
;
704 canvas
->DrawImageInt(*toolbar_right_mask
, 0, 0, toolbar_right_mask
->width(),
705 split_point
, right_x
, y
, toolbar_right_mask
->width(),
706 split_point
, false, paint
);
707 canvas
->DrawImageInt(*toolbar_right_mask
, 0,
708 toolbar_right_mask
->height() - bottom_edge_height
,
709 toolbar_right_mask
->width(), bottom_edge_height
, right_x
, bottom_y
,
710 toolbar_right_mask
->width(), bottom_edge_height
, false, paint
);
713 canvas
->DrawImageInt(*toolbar_left
, 0, 0, toolbar_left
->width(), split_point
,
714 left_x
, y
, toolbar_left
->width(), split_point
, false);
715 canvas
->DrawImageInt(*toolbar_left
, 0,
716 toolbar_left
->height() - bottom_edge_height
, toolbar_left
->width(),
717 bottom_edge_height
, left_x
, bottom_y
, toolbar_left
->width(),
718 bottom_edge_height
, false);
720 gfx::ImageSkia
* toolbar_center
=
721 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER
);
722 canvas
->TileImageInt(*toolbar_center
, 0, 0, left_x
+ toolbar_left
->width(),
723 y
, right_x
- (left_x
+ toolbar_left
->width()),
726 gfx::ImageSkia
* toolbar_right
= tp
->GetImageSkiaNamed(
727 IDR_CONTENT_TOP_RIGHT_CORNER
);
728 canvas
->DrawImageInt(*toolbar_right
, 0, 0, toolbar_right
->width(),
729 split_point
, right_x
, y
, toolbar_right
->width(), split_point
, false);
730 canvas
->DrawImageInt(*toolbar_right
, 0,
731 toolbar_right
->height() - bottom_edge_height
, toolbar_right
->width(),
732 bottom_edge_height
, right_x
, bottom_y
, toolbar_right
->width(),
733 bottom_edge_height
, false);
735 // Draw the content/toolbar separator.
737 gfx::Rect(x
+ kClientEdgeThickness
,
738 toolbar_bounds
.bottom() - kClientEdgeThickness
,
739 w
- (2 * kClientEdgeThickness
),
740 kClientEdgeThickness
),
741 ThemeProperties::GetDefaultColor(
742 ThemeProperties::COLOR_TOOLBAR_SEPARATOR
));
745 void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas
* canvas
) {
746 ui::ThemeProvider
* tp
= GetThemeProvider();
747 int client_area_top
= frame()->client_view()->y();
748 int image_top
= client_area_top
;
750 gfx::Rect client_area_bounds
=
751 layout_
->CalculateClientAreaBounds(width(), height());
752 SkColor toolbar_color
= tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
);
754 if (browser_view()->IsToolbarVisible()) {
755 // The client edge images always start below the toolbar corner images. The
756 // client edge filled rects start there or at the bottom of the toolbar,
757 // whichever is shorter.
758 gfx::Rect
toolbar_bounds(browser_view()->GetToolbarBounds());
760 gfx::ImageSkia
* content_top_left_corner
=
761 tp
->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER
);
762 // TODO(oshima): Sanity checks for crbug.com/374273. Remove when it's fixed.
763 CHECK(content_top_left_corner
);
764 CHECK(!content_top_left_corner
->isNull());
766 image_top
+= toolbar_bounds
.y() + content_top_left_corner
->height();
767 client_area_top
= std::min(image_top
,
768 client_area_top
+ toolbar_bounds
.bottom() - kClientEdgeThickness
);
769 } else if (!browser_view()->IsTabStripVisible()) {
770 // The toolbar isn't going to draw a client edge for us, so draw one
772 gfx::ImageSkia
* top_left
= tp
->GetImageSkiaNamed(IDR_APP_TOP_LEFT
);
773 gfx::ImageSkia
* top_center
= tp
->GetImageSkiaNamed(IDR_APP_TOP_CENTER
);
774 gfx::ImageSkia
* top_right
= tp
->GetImageSkiaNamed(IDR_APP_TOP_RIGHT
);
775 int top_edge_y
= client_area_top
- top_center
->height();
776 int height
= client_area_top
- top_edge_y
;
778 canvas
->DrawImageInt(*top_left
, 0, 0, top_left
->width(), height
,
779 client_area_bounds
.x() - top_left
->width(), top_edge_y
,
780 top_left
->width(), height
, false);
781 canvas
->TileImageInt(*top_center
, 0, 0, client_area_bounds
.x(), top_edge_y
,
782 client_area_bounds
.width(), std::min(height
, top_center
->height()));
783 canvas
->DrawImageInt(*top_right
, 0, 0, top_right
->width(), height
,
784 client_area_bounds
.right(), top_edge_y
,
785 top_right
->width(), height
, false);
787 // Draw the toolbar color across the top edge.
788 canvas
->FillRect(gfx::Rect(client_area_bounds
.x() - kClientEdgeThickness
,
789 client_area_top
- kClientEdgeThickness
,
790 client_area_bounds
.width() + (2 * kClientEdgeThickness
),
791 kClientEdgeThickness
), toolbar_color
);
794 int client_area_bottom
=
795 std::max(client_area_top
, height() - NonClientBorderThickness());
796 int image_height
= client_area_bottom
- image_top
;
798 // Draw the client edge images.
799 gfx::ImageSkia
* right
= tp
->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE
);
800 canvas
->TileImageInt(*right
, client_area_bounds
.right(), image_top
,
801 right
->width(), image_height
);
802 canvas
->DrawImageInt(
803 *tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER
),
804 client_area_bounds
.right(), client_area_bottom
);
805 gfx::ImageSkia
* bottom
= tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER
);
806 canvas
->TileImageInt(*bottom
, client_area_bounds
.x(),
807 client_area_bottom
, client_area_bounds
.width(),
809 gfx::ImageSkia
* bottom_left
=
810 tp
->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER
);
811 canvas
->DrawImageInt(*bottom_left
,
812 client_area_bounds
.x() - bottom_left
->width(), client_area_bottom
);
813 gfx::ImageSkia
* left
= tp
->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE
);
814 canvas
->TileImageInt(*left
, client_area_bounds
.x() - left
->width(),
815 image_top
, left
->width(), image_height
);
817 // Draw the toolbar color so that the client edges show the right color even
818 // where not covered by the toolbar image. NOTE: We do this after drawing the
819 // images because the images are meant to alpha-blend atop the frame whereas
820 // these rects are meant to be fully opaque, without anything overlaid.
821 canvas
->FillRect(gfx::Rect(client_area_bounds
.x() - kClientEdgeThickness
,
822 client_area_top
, kClientEdgeThickness
,
823 client_area_bottom
+ kClientEdgeThickness
- client_area_top
),
825 canvas
->FillRect(gfx::Rect(client_area_bounds
.x(), client_area_bottom
,
826 client_area_bounds
.width(), kClientEdgeThickness
),
828 canvas
->FillRect(gfx::Rect(client_area_bounds
.right(), client_area_top
,
829 kClientEdgeThickness
,
830 client_area_bottom
+ kClientEdgeThickness
- client_area_top
),
834 SkColor
OpaqueBrowserFrameView::GetFrameColor() const {
835 bool is_incognito
= browser_view()->IsOffTheRecord();
836 ThemeProperties::OverwritableByUserThemeProperty color_id
;
837 if (ShouldPaintAsActive()) {
838 color_id
= is_incognito
?
839 ThemeProperties::COLOR_FRAME_INCOGNITO
:
840 ThemeProperties::COLOR_FRAME
;
842 color_id
= is_incognito
?
843 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE
:
844 ThemeProperties::COLOR_FRAME_INACTIVE
;
847 if (browser_view()->IsBrowserTypeNormal() ||
848 platform_observer_
->IsUsingSystemTheme()) {
849 return GetThemeProvider()->GetColor(color_id
);
852 // Never theme app and popup windows unless the |platform_observer_|
853 // requested an override.
854 return ThemeProperties::GetDefaultColor(color_id
);
857 gfx::ImageSkia
* OpaqueBrowserFrameView::GetFrameImage() const {
858 bool is_incognito
= browser_view()->IsOffTheRecord();
860 if (browser_view()->IsBrowserTypeNormal()) {
861 if (ShouldPaintAsActive()) {
862 resource_id
= is_incognito
?
863 IDR_THEME_FRAME_INCOGNITO
: IDR_THEME_FRAME
;
865 resource_id
= is_incognito
?
866 IDR_THEME_FRAME_INCOGNITO_INACTIVE
: IDR_THEME_FRAME_INACTIVE
;
868 return GetThemeProvider()->GetImageSkiaNamed(resource_id
);
870 if (ShouldPaintAsActive()) {
871 resource_id
= is_incognito
?
872 IDR_THEME_FRAME_INCOGNITO
: IDR_FRAME
;
874 resource_id
= is_incognito
?
875 IDR_THEME_FRAME_INCOGNITO_INACTIVE
: IDR_THEME_FRAME_INACTIVE
;
878 if (platform_observer_
->IsUsingSystemTheme()) {
879 // We want to use theme images provided by the system theme when enabled,
880 // even if we are an app or popup window.
881 return GetThemeProvider()->GetImageSkiaNamed(resource_id
);
884 // Otherwise, never theme app and popup windows.
885 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
886 return rb
.GetImageSkiaNamed(chrome::MapThemeImage(
887 chrome::GetHostDesktopTypeForNativeWindow(
888 browser_view()->GetNativeWindow()),
892 gfx::ImageSkia
* OpaqueBrowserFrameView::GetFrameOverlayImage() const {
893 ui::ThemeProvider
* tp
= GetThemeProvider();
894 if (tp
->HasCustomImage(IDR_THEME_FRAME_OVERLAY
) &&
895 browser_view()->IsBrowserTypeNormal() &&
896 !browser_view()->IsOffTheRecord()) {
897 return tp
->GetImageSkiaNamed(ShouldPaintAsActive() ?
898 IDR_THEME_FRAME_OVERLAY
: IDR_THEME_FRAME_OVERLAY_INACTIVE
);
903 int OpaqueBrowserFrameView::GetTopAreaHeight() const {
904 gfx::ImageSkia
* frame_image
= GetFrameImage();
905 int top_area_height
= frame_image
->height();
906 if (browser_view()->IsTabStripVisible()) {
907 top_area_height
= std::max(top_area_height
,
908 GetBoundsForTabStrip(browser_view()->tabstrip()).bottom());
910 return top_area_height
;