Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / frame / opaque_browser_frame_view.cc
blob15966f5efc0877b0acd4e2e83ae031fade92526e
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"
7 #include <algorithm>
8 #include <string>
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"
56 #endif
58 #if defined(OS_LINUX)
59 #include "ui/views/controls/menu/menu_runner.h"
60 #endif
62 using content::WebContents;
64 namespace {
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;
80 #if !defined(OS_WIN)
81 // The icon never shrinks below 16 px on a side.
82 const int kIconMinimumSize = 16;
83 #endif
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;
89 #endif
91 } // namespace
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,
110 IDR_MINIMIZE_H,
111 IDR_MINIMIZE_P,
112 IDR_MINIMIZE_BUTTON_MASK,
113 IDS_ACCNAME_MINIMIZE,
114 VIEW_ID_MINIMIZE_BUTTON);
115 maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE,
116 IDR_MAXIMIZE_H,
117 IDR_MAXIMIZE_P,
118 IDR_MAXIMIZE_BUTTON_MASK,
119 IDS_ACCNAME_MAXIMIZE,
120 VIEW_ID_MAXIMIZE_BUTTON);
121 restore_button_ = InitWindowCaptionButton(IDR_RESTORE,
122 IDR_RESTORE_H,
123 IDR_RESTORE_P,
124 IDR_RESTORE_BUTTON_MASK,
125 IDS_ACCNAME_RESTORE,
126 VIEW_ID_RESTORE_BUTTON);
127 close_button_ = InitWindowCaptionButton(IDR_CLOSE,
128 IDR_CLOSE_H,
129 IDR_CLOSE_P,
130 IDR_CLOSE_BUTTON_MASK,
131 IDS_ACCNAME_CLOSE,
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_);
153 UpdateAvatar();
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 {
167 if (!tabstrip)
168 return gfx::Rect();
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 {
180 return 0;
183 void OpaqueBrowserFrameView::UpdateThrobber(bool running) {
184 if (window_icon_)
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)) {
208 return true;
210 #if defined(FRAME_AVATAR_BUTTON)
211 if (new_avatar_button() &&
212 new_avatar_button()->GetMirroredBounds().Contains(point)) {
213 return true;
215 #endif
217 return false;
220 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
221 if (!bounds().Contains(point))
222 return HTNOWHERE;
224 // See if the point is within the avatar menu button.
225 if (IsWithinAvatarMenuButtons(point))
226 return HTCLIENT;
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)))
231 return HTCLIENT;
232 #endif
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
240 // of Fitts' Law.
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))
253 return HTCLOSE;
254 if (restore_button_ && restore_button_->visible() &&
255 restore_button_->GetMirroredBounds().Contains(point))
256 return HTMAXBUTTON;
257 if (maximize_button_ && maximize_button_->visible() &&
258 maximize_button_->GetMirroredBounds().Contains(point))
259 return HTMAXBUTTON;
260 if (minimize_button_ && minimize_button_->visible() &&
261 minimize_button_->GetMirroredBounds().Contains(point))
262 return HTMINBUTTON;
264 views::WidgetDelegate* delegate = frame()->widget_delegate();
265 if (!delegate) {
266 LOG(WARNING) << "delegate is null, returning safe default.";
267 return HTCAPTION;
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) {
278 DCHECK(window_mask);
280 if (layout_->IsTitleBarCondensed() || frame()->IsFullscreen())
281 return;
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() {
294 if (window_icon_)
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_) {
320 frame()->Minimize();
321 } else if (sender == maximize_button_) {
322 frame()->Maximize();
323 } else if (sender == restore_button_) {
324 frame()->Restore();
325 } else if (sender == close_button_) {
326 frame()->Close();
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(
337 mode,
338 signin::ManageAccountsParams());
339 #endif
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(),
349 window_icon_,
350 window_icon_->GetBoundsInScreen(),
351 views::MENU_ANCHOR_TOPLEFT,
352 ui::MENU_SOURCE_MOUSE));
353 #endif
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();
369 if (!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 {
399 #if defined(OS_WIN)
400 // This metric scales up if either the titlebar height or the titlebar font
401 // size are increased.
402 return GetSystemMetrics(SM_CYSMICON);
403 #else
404 return std::max(BrowserFrame::GetTitleFontList().GetHeight(),
405 kIconMinimumSize);
406 #endif
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();
455 return s;
458 ///////////////////////////////////////////////////////////////////////////////
459 // OpaqueBrowserFrameView, protected:
461 // views::View:
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);
468 else
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);
495 #endif
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.
507 return false;
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))
517 return true;
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())
523 return false;
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.
531 return false;
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.
542 return true;
545 views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton(
546 int normal_image_id,
547 int hot_image_id,
548 int pushed_image_id,
549 int mask_image_id,
550 int accessibility_string_id,
551 ViewID view_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);
570 return 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())
592 return false;
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())
597 return true;
598 return !views::ViewsDelegate::GetInstance()->WindowManagerProvidesTitleBar(
599 IsMaximized());
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);
640 #endif
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())
659 return;
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,
684 h));
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
706 // the source.
707 SkPaint paint;
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.
721 int right_x =
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);
730 canvas->Restore();
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()),
743 split_point);
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(
758 canvas,
759 ThemeProperties::GetDefaultColor(
760 ThemeProperties::COLOR_TOOLBAR_SEPARATOR),
761 toolbar_bounds);
762 } else {
763 canvas->FillRect(
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
799 // ourselves.
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(),
836 bottom->height());
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),
852 toolbar_color);
853 canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom,
854 client_area_bounds.width(), kClientEdgeThickness),
855 toolbar_color);
856 canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top,
857 kClientEdgeThickness,
858 client_area_bottom + kClientEdgeThickness - client_area_top),
859 toolbar_color);